Configuration (Magic) Data: Difference between revisions

From IHRIS Wiki
 
(24 intermediate revisions by 2 users not shown)
Line 1: Line 1:
Los Datos Magic son un mecanismo para manejar datos dinámicos de congifuracion a nivel del sitio. Es la base de mucha de la funcionalidad que brinda el Intrahealth Informatics Core Engine (I2CE) incluyendo como se sirven las páginas y como se realizan los informes personalizados.   
Magic Data is a mechanism intended to handle dynamic site-level configuration data. It is the basis of much of the functionality provided by the IntraHealth Informatics Core Engine (I2CE), including how pages are served and how custom reports are made.   


<span style='color:red'>Advertencia:</span> El API para los datos Magic se ha actualizado de manera significativa de la version 3.1 a la versión 4.0.  Aunque este artículo aplica a la versión API 4.0, mucho de esta es relevante para la version 3.1. Vea algunos de los [[#Changes from 3.1|Cambios]]
<span style='color:red'>Warning:</span>The API for Magic Data has been fairly significantly updated from version 3.1 to version 4.0.  Although this article applies to the version 4.0 API, much of it is relevant to version 3.1. See some of the [[#Changes from 3.1|Changes]]
=¿Qué son los Datos Magic?=
=What is Magic Data?=


Los Datos Magic son una estructura de arbol con raíz con beneficios. Si usted lo desea, puede pensar en ellos como el análogo del Registro de Windows para su aplicación en la red. También los puede ver como un arreglo anidado.
Magic Data is a rooted tree structure with benefits. If you wish you may think of it as the analogue of the Windows Registry for your web application. You may also think of it as a nested array.


Los datos magic tienen dos partes. La clase del nodo de magic data, definido en ''I2CE/lib/I2CE_MagicDataNode'' y los mecanismos de almacenamiento para los datos magic. Usted puede utilizar los datos magic sin utilizar un mecanismo de almacenamiento, en tal caso los datos magic guardados no persisten a lo largo de todas las sesiones. Por defecto utiliza los siguientes mecanismos de almacenamiento para los datos Magic:
There are two parts two parts to magic data. The magic data node class, defined in ''I2CE/lib/I2CE_MagicDataNode'' and the storage mechanisms for magic data. You may use magic data without using a storage mechanism, in which case the magic data saved does on persist across sessions. By default uses the following storage mechanisms for Magic Data:
*Base de datos: Los datos se guardan en una table en la base de datos. En I2CE, esta se establece como la table ''config'' .
*Database: The data is stored into a table in the database. In I2CE, this is set to be the ''config'' table.
*APC: Los datos se almacenan en un caché de memoria proporcionado por  http://pecl.php.net/package/APC apc] que persiste a lo largo de las sesiones apache.
*APC: The data is stored in to a memory cache provided by [http://pecl.php.net/package/APC apc] which persists across apaches session.


Dado que APC es más rápido, las lecturas se realizan primero desde APC. Si los datos no se encuentran ahí, se lee desde la base de datos. Los datos se escriben primero en la base de datos y después en APC.
As APC is faster, reads are first done from APC. If the data is not found there, it is read from the database. Data is written first to the database and then to APC.


=Escalar y No-Escalar=
=Scalar and Non-Scalar=
Hay dos tipos principals de nodos escalares y un nodo primario  
There are two main types of nodes scalar and a parent node  


Un nodo escalar no tiene nodos secundarios. Tiene un valor que es posiblemente una cadena vaciíaUn nodo escalar uede marcarse como localizado. En tal caso el valor que regresa depende de las preferencias de localización del usuario.
A scalar node does not have any children. It does have a value which is a possibly empty stringA scalar node can be marked as being localized. In which case the value returned depends on the localization preferences for the user.


Un nod primario puede tener tantos nodos secundarios como quiera. Cada nodo secundario de un primario debe tener [[#Names and Paths|names]] diferentes, no tiene un valor y no es localozable.
A parent node can have as many children as it wants. Each child of a parent node must have distinct [[#Names and Paths|names]].  It does not have a value.  It is not localizable.


=Nombres y Rutas=
=Names and Paths=
Con la exepción del nodo raíz, cada nodo de datos magic tiene un nombre. Para el nombre se permite cualquier valor numeric. Cualquier cadena que no esté vacía es válida siempre y cuando no empiece con '=' no contenga una  '/' y no sea '.' o '..'.  Sin embargo, es mejor si se limita a que las cadenas contengan solamente los siguientes caractéres:
With the exception of the root node, every magic data node has a name. For a name, any numeric value is allowed. Any non-empty string is valid as long as it does not start with '=' does not contain a '/' and is not '.' or '..'.  However, it is best if you limit the strings to contain the following characters only:
  _-+.0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
  _-+.0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ


==Rutas absolutas==
==Absolute Paths==
Los nodos de los Datos Magic pueden referenciarse por su ruta, la que es una concatenación de sus nombres por '/.'
Magic Data nodes can be referenced by their path which is a concatenation of their names by '/.'


El nodo raíz tiene la ruta '/'.
The root node has path '/'.


Si el nodo raíz tiene un secundario con el nombre 'alguna', se referencia por la ruta '/alguna'
If the root node has a child with name 'some', it is referenced by the path '/some'


Si alguna es un nodo primario con un secundario de nombre 'cosa', entonces ese secundario se referencia por la ruta  '/alguna/cosa'
If some is a parent node with a child with name 'thing', then that child is referenced by the path '/some/thing'


==Rutas Relativas==
==Relative Paths==
Las rutas también pueden ser relativas. en el ejemplo anterior, si estuviera en el nodo '/alguna' entonces podría referenciar a los otros nodos con:
Paths can also be relative. In the example above, if you were at the node '/some' then could reference the other nodes by:
*'./' referencia '/alguna'
*'./' references '/some'
*'../' referencia '/'
*'../' references '/'
*'./cosa' referencia al nodo '/alguna/cosa'
*'./thing' references the node '/some/thing'


=Definición de Datos Magic en los Archivos de Configuración=
=Defining Magic Data in Configuration Files=
Una forma conveniente de cargar datos magic en el sistema es hacerlo a través de un [[Module Structure#Module Configuration File|module configuration file]] especificando un nodo de <configurationGroup> después del nodo de <metadata>. Esta sección tiene un doble propósito. Le permite especificar datos magic asi como proporcionar una manera de editar los datos del módulo.
A convenient way to get magic data loaded into the system is to do so through a [[Module Structure#Module Configuration File|module configuration file]] by specifying a <configurationGroup> node after the <metadata> node. This section serves double duty. It allows you to specify magic data as well as to provide a way to edit the data for the module.


El nodo de <configurationGroup> es opcional. Si está presente debe tener el nombre del atributo que tiene el mismo valor que el nombre del modulo, que es el nombre del atributo en la etiqueta que contiene <I2CEConfiguration>.
The <configurationGroup> node is optional. If it is present it has to have the attribute name which has the same value as the module name, whhich is the name attribute in the containing <I2CEConfiguration> tag.


Todos los datos magic son relativos a la ruta definida por este configurationGroup. Hay tres opciones:
All magic data is relative to the path defined by the this configurationGroup. There are three options:
* La ruta del atributo no esta presente. en el siguiente ejempli,los datos magic se guardan bajo /modules/mercury_javascript_path.  
* The attribute path is not present. In the following example, the magic data is stored under /modules/mercury_javascript_path.  
  Ejemplo:
  Example:
   <configurationGroup name='mercury_javascript_popup'>
   <configurationGroup name='mercury_javascript_popup'>
     <span style='color:red'>SOME STUFF GOES HERE</span>
     <span style='color:red'>SOME STUFF GOES HERE</span>
  </configurationGroup>
  </configurationGroup>
*La ruta del atributo está pressente. En el siguiente ejemplo, los datos magic se guardan en  /algun/otro/lugar.  
* The attribute path is present. In the following example, the magic data is stored under /some/other/place.  
  Ejemplo:
  Example:
  <configurationGroup name='mercury_javascript_popup' path='/algun/otro/lugar'>
  <configurationGroup name='mercury_javascript_popup' path='/some/other/place'>
   <span style='color:red'>SOME STUFF GOES HERE</span>
   <span style='color:red'>SOME STUFF GOES HERE</span>
  </configurationGroup>  
  </configurationGroup>  
* El módulo es 'I2CE'. Los datos magic se guardan relativos a /I2CE
* The module is 'I2CE'. The magic data is stored relative to /I2CE


En el resto de esta sección decribiremos <span style='color:red'>WHAT STUFF GOES THERE</span> que son [[#<configurationGroup>|<configurationGroup>]] y [[#<configuration>|<configurations>]] etiquetas.
In the remainder of this section we will describe <span style='color:red'>WHAT STUFF GOES THERE</span> which are [[#<configurationGroup>|<configurationGroup>]] and [[#<configuration>|<configurations>]] tags.


==<configurationGroup>==
==<configurationGroup>==
Una <configurationGroup> puede tener varias subetiquetas en este orden:
A <configurationGroup> can several subtags in this order::
*Un <displayName> opcional. Un nombre mostrado en la configuración del módulo para esta agrupación de datos
*An optional <displayName>. A name displayed in the module configuration for this grouping of data
*Una <description> opcional. Una descripción de la funcionalidad de la agrupación.
*An optional <description>. A description of the functionality of the grouping.
*Una [[#<version>|<version>]] opcional de la etiqueta.
*An optional [[#<version>|<version>]] tag.
*Cualquier número (incluyendo al cero) de etiquetas de [[#<status>|<status>]].
*Any number (including zero) of [[#<status>|<status>]] tags.
*Cualquier número (incluyendo al cero)de <configurationGroup> o etiquetas de [[#<configuration>|<configuration>]].
*Any number (including zero) of <configurationGroup> or [[#<configuration>|<configuration>]] tags.


==<configuration>==
==<configuration>==
Una <configuration> puede tener varias subetiquetas en este orden:
A <configuration> can several subtags in this order::
*Un <displayName> opcional. Un nombre mostrado en la configuración de este modulo para esa agrupación de datos
*An optional <displayName>. A name displayed in the module configuration for this grouping of data
*Una <description> opcional. Una descripción de la funcionalidad de la agrupación.
*An optional <description>. A description of the functionality of the grouping.
*Una etiqueta de [#<version>|<version>]] opcional.
*An optional [[#<version>|<version>]] tag.
*Cualquier número (incluyendo al cero) de etiquetas de [[#<value>|<value>]].
*Any number (including zero) of [[#<value>|<value>]] tags.


==Attributos==
==Attributes==
Hay varios atributos que pueden tener una[[#<configuration>|<configuration>]] y una [[#<configurationGroup>|<configurationGroup>]]:
There are several attributes that both a [[#<configuration>|<configuration>]] and a [[#<configurationGroup>|<configurationGroup>]] may have:
*name: Este es un atributo requerido. Cada <configuration> o <configurationGruop> secundaria de un <configurationGroup> debe tener un nombre distinto. Si la ruta del atributo no se ha establecido, también dice que este nodo de configuración no debe aplicarse al nodo de los datos magic con el nombre dado y que es un nodo secundario de datos magic del nodo primario de configurationGroup.
*name: This is a required attribute. Every child <configuration> or <configurationGruop> of a <configurationGroup> should have a distinct name. If the path attribute is not set, it is also says that this configuration node should apply to the magic data node with the given name and which is a child magic data node of the parent configurationGroup node.
*path: Esto es opcional Puede ser una ruta absoluta u relative en los datos magic y describe los datos magic en ls cuales se debe guarder este valor. Si esta es una ruta relativa, es relative a la ruta de datos magic de su nodo primario.
*path: This is optional. It may be an absolute or relative path in magic data and describes the magic data at which this value should be stored at.   If it is a relative path, it is relative to the magic data path of its parent node.
*locale: Esto es opcional. Si esta establecido, significa que el valor bajo este nodo debe considerarse como localizable.
*locale: This is optional. If this is set, it means that the values under this node should be consider to be localizable.
*config: Esto es opcionalSi esta establecido, establece el objeto de I2CE_Swiss que se utiliza par mostrar los datos en el menu para configurar los módulos.
*config: This is optionalIf set, it sets the I2CE_Swiss object used to display the data in the configure modules menu.
Una <configuration> puede también tener los siguientes atributos:
A <configuration> may also have the following attributes:
*type: Por defecto es establece como 'string' y describe el tipo de datos establecidos por las etiquetas de <value> de este nodo.
*type: Defaults to 'string' and describes the type of data that is being set by the <value> tags of this node.
*values: Por defecto se establece como 'single' y describe si los datos establecidos por este nodo Deben ser un arreglo de valores o un único valor establecido en lo que se almacena en el nodo de <values>  
*values: Defaults to 'single' and describes if the data being set by this node should be an array of values or a single value based on what is stored in the <values> node


==<value>==
==<value>==
La etiqueta de <value> es ua subetiqueta de una etiqueta de [[#<configuration>|<configuration>]] .  Contiene el o los valores que se almacenan en los datos magic y depende de los [[#Attributes|attributes]] de ''type'' y ''values''.
The <value> tag is a sub-tag of a [[#<configuration>|<configuration>]] tagIt contains the value(s) which are stored in the magic data and depends on the ''type'' and ''values'' [[#Attributes|attributes]].


Algunos tipos y valores communes son:
Some common types and values are:
*type='string' values='single':  El nodo de datos magic es de tipo escalar con valor de los contenidos de la etiqueta de <value> única.
*type='string' values='single':  The magic data node is a scalar type with value the contents of the single <value> tag.
*type='string' values='many':  El nodo de datos magic es de tipo primario. Tiene un nodo secundario de tipo escalar para cada etiqueta de <value>.
*type='string' values='many':  The magic data node is a parent type. The magic data node has a child node of scalar type for each <value> tag.
*type='delimited': El nodo de datos magic es de tipo primario. Se espera que las etiquetas de valor sean de la forma <value>'key':'value'</value> en cual caso se crea un nodo secundario de datos magic de tipo escalar de nombre 'key' y valor 'value'
*type='delimited': The magic data node is a parent type. The value tags are expected to be of the form <value>'key':'value'</value> in which case a child magic data node is created of scalar type with name 'key' and value 'value'
*type='boolean':  Los valores en la etiqueta de <value> se interpretan como booleans:  F,f,False,false,0, etc. se guardan en datos magic es 0.  De lo contrario, el valor que se guarda es 1.
*type='boolean':  The values in the <value> tag are interpreted as booleans:  F,f,False,false,0, etc. are stored in magic data is 0.  Otherwise the value stored is 1.




==<version>==
==<version>==
Se puede tener accesso a los mismos datos magic desde multiples archivos de configuración. Suponga que un módulo moduleA require un modulo moduleB y que ambos establecen /some/data para tener valores valA y valB respectivamente. Suponga que ambos módulos tienen la version 1.0.
The same magic data can be accessed by multiple modules configuration files. Suppose that a module moduleA requires a module moduleB and that they both set /some/data to have values valA and valB respectively. Suppose that both modules have version 1.0.
  <span style='color:tomato'>Excerpt from moduleA's configuration file</span>
  <span style='color:tomato'>Excerpt from moduleA's configuration file</span>
  <configurationGroup name='some' path='/some'>
  <configurationGroup name='some' path='/some'>
Line 115: Line 115:




Durante la inicialización del sitio, ya que el moduleA require al moduleB, el valor primero se establece a valB se establece primero por el moduleB.  Luego se sobreescribe para que sea el valor valA por el moduleA.  De manera similar, después de la inicialización, el valor de '/some/data2' es 'valA2'
On site intialization, since moduleA requires moduleB, the value is first set to valB is first set by moduleB.  It is then overwritten to be the value valA by moduleA.  Similarly, after initialization, the value of '/some/data2' is 'valA2'


Suponga que la version del moduleB se aumenta a la version 1.1 pero no hay otros cambios al archive de configuración. Esto causarà que el archive de configuración se reprocese. El ''configurator'' recordará que ya ha procesado todos los datos hasta la version 1.01. Por lo tanto, no volverá a leer la sobrescritura que ya está guardada en los datos magic.
Suppose that version of moduleB is increased to version 1.1 but there are no other changes to the configuration file. This will cause the configuration file to be reprocessed. The ''configurator'' will remember that has already processed all the data up to and including version 1.0. Thus it will not re-read the or overwrite what is already stored in magic data.


Suponga ahora que moduleB quiere cambiar el valor que guarda en /some/data a newValB, así como crear un Nuevo valor en /some/other_data. Sería necesario aumentar el número de la version del modulo a 1.2 y agregar una etiqueta de <version> para que el configurador sepa que al actualizar el modulo a la version 1.2 debemos leer de Nuevo los datos de configuracióm para todo lo mayor a la version cargada previamente de 3.1:
Suppose now that moduleB wants to change the value it stores at /some/data to be newValB, as well as create a new value at /some/other_data We would need to increase the version number of the module to 1.2 and add a <version> tag so that the configurator knows that in upgrading the module to version 1.2, we should reread the configuration data for anything greater than the previously loaded version of 3.1:
  <configurationGroup name='some' path='/some'>
  <configurationGroup name='some' path='/some'>
   <configuration name='data'>
   <configuration name='data'>
Line 133: Line 133:
   </configuration>
   </configuration>
  </configurationGroup>
  </configurationGroup>
Ahora el valor de /some/data se actualizará a 'newValB' y agregaremos en el valor 'The new stuff' en '/some/other_data.'  El valor en '/some/data2' se mantiene igual y es 'valA2.'
Now the value at /some/data will be updated to be 'newValB' and we will add in the value 'The new stuff' at '/some/other_data.'  The value at '/some/data2' remains unchanged and is 'valA2.'


==<status>==
==<status>==
Una etiqueta de estado consiste en pares de valores clave:
A status tag consists of key value pairs:
  <status>key:value</status>
  <status>key:value</status>
Aunque se puede utilizer cualquier cosa para la clave (siempre y cuando no contenga ':'), las claves que tienen significado son:
Although you can use anything for the key (as long as it does not have a ':' in it!), the keys which have meaning are:
*version: Funciones como la [[#<version>|<version>]]
*version: Functions just like [[#<version>|<version>]]
*overwrite: El valor puede ser verdadero o falso. Por defecto se establece como falso. Si es verdadero los datos magic se sobreescribirán aún cuando la version no lo haya hecho.
*overwrite: The value can be either true of false. Defaults to false. If true the magic data will be overwritten even if the version has not.
*merge:  El valor puede ser verdadero o falso. Por defecto se establece como falso. Si es verdadero los valores leídos se fusionan con los valores existentes utilizando ''array_merge()''
*merge:  The value can be either true of false. Defaults to false. If true, the values read in are merged into the existing values by ''array_merge()''
*mergerecursive:  El valor puede ser verdadero o falso. Por defecto se establece como falso. Si es verdadero, los valores leídos se fusionan con los valores existentes utilizando ''I2CE_Util::merge_recursive().''
*mergerecursive:  The value can be either true of false. Defaults to false. If true, the values read in are merged into the existing by ''I2CE_Util::merge_recursive().''
*uniquemerge:  El valor puede ser verdadero o falso. Por defecto se establece como falso excepto si un nodo de <configuration> tiene el tipo='string' y los valores='many.'  Si es verdadero, los valores leídos se fusionan con los valores existentes utilizando  ''I2CE_Util::merge_recursive()'' y solamente los valores únicos se mantienen utilizando ''I2CE_Util::array_unique().''
*uniquemerge:  The value can be either true of false. Defaults to false except in the case where a <configuration> node has type='string' and values='many.'  If true, the values read in are merged into the existing values by ''I2CE_Util::merge_recursive()'' and only the unique values are kept by ''I2CE_Util::array_unique().''
*visible: El valor puede ser verdadero o falso. Por defecto se establece como verdadero. Si es verdadero, este nodo se muestra en el menu de configuración del módulo.
*visible: The value can be either true of false. Defaults to true. If true, this node is displayed in the module configuration menu.
*advanced:  El valor puede ser verdadero o falso. Por defecto se establece como falso. Si es verdadero, se considera una opción avanzada para el menu de configuración del módulo
*advanced:  The value can be either true of false. Defaults to false. If true, this is considered an an advanced option for the module configuration menu
*required: El valor puede ser verdadero o falso. Por defecto se establece como verdadero. Dice qie los valores resultants en el nodo de <configuration> deben establecerse
*required: The value can be either true of false. Defaults to true. It says the the resulting values at the <configuration> node must be set
*showIndex: El valor puede ser verdadero o falso. Por defecto se establece como verdaderoEn tal caso mostramos el índice en el menú de configuración del módulo
*showIndex: The value can be either true of false. Defaults to trueIn which case we show the index in the module configuration menu




Los valores de las claves de estado se heredan a medida que se baja de nodo.
The values of the status keys are inherited as you go down a node.


=Uso de Datos Magic en PHP=
=Using Magic Data in PHP=
Cada nodo de datso magic es una instancia de la clase '''I2CE_MagicDataNode'''.  Las "variables públicas" de un nodo son sus nodos secundarios que se realiza utilizando el método ''__get()'' .  El I2CE_MagicDataNode implementa las Interfaces RecusriveIterator, ArrayAccess, SeekableIterator, y Countable.
Each node of magic data is an instance of the class '''I2CE_MagicDataNode'''.  The "public variables" of a node are its child nodes which is done by making use of the ''__get()'' method.  I2CE_MagicDataNode implements the RecusriveIterator, ArrayAccess, SeekableIterator, and Countable Interfaces.




I2CE tiene una instancia de datos magic de raíz que se pueden recuperar utilizando:
I2CE has a root magic data instance which can be retrieved by:
  $config=I2CE::getConfig();
  $config=I2CE::getConfig();
==Acceso Básico==
==Basic Access==
Suponga que $data es un nodo de datos magic con un nodo secundario llamado 'my_list' y 'amount' que son de tipo primario y escalar respectivamante. Suponga que el nodo secundario 'amount' tien in valor de 10.  Suponga que no hay ningún secundario llamado 'bad.'  Se puede tenre acceso al secundario de varias maneras:
Suppose $data is a magic data node, with a children named 'my_list' and 'amount' which are parent type and scalar type respectively. Suppose that the child node 'amount' has value 10.  Suppose that there is no child named 'bad.'  The children can be accessed in many ways:
<center>
<center>
{| class='wikitable' border="1" cellspacing="5" cellpadding="2"
{| class='wikitable' border="1" cellspacing="5" cellpadding="2"
Line 177: Line 177:
| $data->bad
| $data->bad
| I2CE_MagicDataNode
| I2CE_MagicDataNode
| El nodo no existía, de modo que se creó.  <br/>Tiene un tipo indeterminado por el momento.
| The node did not exist, so it was created.  <br/>It has an indeterminate type at the moment.
|-
|-
| $data['my_list']
| $data['my_list']
Line 185: Line 185:
| $data['amount']
| $data['amount']
| 10  
| 10  
| el valor del nodo 'amount'  
| the value of the 'amount' node
|-
|-
| $data['bad']
| $data['bad']
| I2CE_MagicDataNode
| I2CE_MagicDataNode
| Creamos el nodo 'bad' que no existía y lo regresamos
| We created the non-existent 'bad' node and returned it
|-
|-
|}
|}
</center>
</center>


==Acceso Refinado==
==Refined Access==
Para obtener acceso más refinado a los nodos de datos magic puede utilizer la función '''traverse('''$path,$create=false,$return_value=true''')''':
To get more refined access to magic data nodes you may use the '''traverse('''$path,$create=false,$return_value=true''')''' function:
<center>
<center>
{| class='wikitable' border="1" cellspacing="5" cellpadding="2"
{| class='wikitable' border="1" cellspacing="5" cellpadding="2"
| $data->traverse('my_list',false,false)
| $data->traverse('my_list',false,false)
| I2CE_MagicDataNode
| I2CE_MagicDataNode
| Este es el nodo 'my_list'  
| This is the 'my_list' node
|-
|-
| $data->traverse('bad',false,false)
| $data->traverse('bad',false,false)
| null
| null
| El Segundo argumento dice que no se cree un nodo que no existe
| The second argument says not to create a node that doesn't exist
|-
|-
| $data->traverse('amount',false,false)
| $data->traverse('amount',false,false)
| I2CE_MagicDataNode
| I2CE_MagicDataNode
| Este el el nodo 'amount'  
| The is the 'amount' node
|-
|-
| $data->traverse('my_list',false,true)
| $data->traverse('my_list',false,true)
| I2CE_MagicDataNode
| I2CE_MagicDataNode
| Este es el nodo 'my_list'
| This is the 'my_list' node
|-
|-
| $data->traverse('amount',false,true)
| $data->traverse('amount',false,true)
Line 220: Line 220:
| $data->traverse('bad',false,true)
| $data->traverse('bad',false,true)
| null
| null
| El Segundo argumento dice que no se cree un nodo que no existe
| The second argument says not to create a node that doesn't exist
|-
|-
| $data->traverse('my_list',true,true)
| $data->traverse('my_list',true,true)
Line 228: Line 228:
| $data->traverse('amount',true,true)
| $data->traverse('amount',true,true)
| 10
| 10
| el valor del nodo 'amount'  
| The value of the 'amount' node
|-
|-
| $data->traverse('bad',true,true)
| $data->traverse('bad',true,true)
| I2CE_MagicDataNode
| I2CE_MagicDataNode
| Creamos el nodo 'bad' que no existía y lo regresamos
| We created the non-existent 'bad' node and returned it
|}
|}
</center>
</center>


Si un nodo es de tipo escalar, se puede obtener su valor con ''getValue()''.  Si llama ''getAsArray()'' en el mismo, tambien regresará su valor.
If a node has scalar type, you can get its value by ''getValue()''.  If you call ''getAsArray()'' on it, it will also return its value.


Si un nodo tiene un tipo primario o indeterminado, llamar ''getValue()'' regresa el nodo en si. Si llama ''getAsArray()'' en el nodo regresará un arreglo anidado. La claves de cada profundidad son los nombres de los nodos secundarios. Los valores pueden ser un arreglo o una cadena, en dependencia de si el secundario es escalar o no.
If a node has parent or indeterminate type, calling ''getValue()'' returns the node itself. If you call ''getAsArray()'' on it it will return a nested array. The keys at each depth are the child node's names.   The values are either an array or a sting, depending on if that child is scalar or not.


==Revisar la Existencia y el Tipo==
==Checking Existence and Type==
Puede utilizer el siguiente método para ver si un nodo de datos magic existe y de que tipo es:
You can use the following method to see if a Magic Data node, exists and what its type is:
*'''pathExists('''$path''')'''
*'''pathExists('''$path''')'''
*'''is_scalar('''$path=null''')'''
*'''is_scalar('''$path=null''')'''
Line 247: Line 247:
*'''is_indeterminate('''$path=null''')'''
*'''is_indeterminate('''$path=null''')'''
*'''is_root('''$path=null''')'''
*'''is_root('''$path=null''')'''
Aquí, donde la ruta se establece a ''null'' por defecto, el valor que el método se llama en el nodo mismo (esto no sería lo mismo que llamarlo en $path='./').
Here, we the path defaults to ''null'', the value that method is called on the node itself (this would be the same as calling it on $path='./').


Podría hacer algo como:
You may do something like:
function set_node_to_scalar($node) {
function set_node_to_scalar($node) {
   if (!$node instanceof I2CE_MagicDataNode) {
   if (!$node instanceof I2CE_MagicDataNode) {
     echo "Why are you giving me garbage data?\n";
     echo "Why are you giving me garbage data?\n";
Line 268: Line 268:
   }
   }
  }
  }
Otras dos funciones útiles son
Two other useful functions are
* '''getAsArray('''$path=null''')''' que regresa el nodo y todos sus secundarios(recursivamente) como un arreglo
* '''getAsArray('''$path=null''')''' which return the node and all of its children (recursively) as an array
* '''setIfIsSet('''&$var,$path,$as_array=false''')''' revisará si $path existeSi no existe, regresa falso. Si existe, regresa verdadero y puede que llame a getValue() o a getAsArray() en el nodo referido por la ruta.
* '''setIfIsSet('''&$var,$path,$as_array=false''')''' which will check to see if $path existsIf it does not, it returns false. If it does, it returns true and calls either getValue() or getAsArray() on the node referred to by the path.


==Nombres de nodos secundarios e Iteradores==
==Child Names and Iterators==
Para obtener los nombres de los nodos secundarios de un nodo, utilizamos el método '''getKeys()'''.
To get the names of the child nodes of a node, we use the '''getKeys()''' method.
Suponga que establecemos los datos magic de la siguiente manera:
Suppose that we magic data set up as follows:
<center>
<center>
{| class='wikitable' border="1" cellspacing="5" cellpadding="2"
{| class='wikitable' border="1" cellspacing="5" cellpadding="2"
Line 307: Line 307:
|}
|}
</center>
</center>
Tambien podría hacer algo similar como:
You may also something similar as:
echo "I like the color " . $config->color . "\n";
echo "I like the color " . $config->color . "\n";
  $keys = $config->getKeys();
  $keys = $config->getKeys();
  foreach ($keys as $key) {
  foreach ($keys as $key) {
Line 323: Line 323:




Ya que un nodo de datos magic es un iterador, podemos hacer cosas como:
As a magic data node is an iterator, we can do things like:
  foreach ($config as $key=>$node) {
foreach ($config as $key=>$node) {
   if ($node instanceof I2CE_MagicDataNode) {
   if ($node instanceof I2CE_MagicDataNode) {
     echo "The node named $key under at " . $config->getPath(false) . " is a parent node.  It has children " . implode(',', $node->getKeys()) . ".\n";
     echo "The node named $key under at " . $config->getPath(false) . " is a parent node.  It has children " . implode(',', $node->getKeys()) . ".\n";
Line 331: Line 331:
   }
   }
  }
  }
 
which would result in:
lo que resultaría en:
  The node named modules under / is a parent node. It has children modA,modB.
  El nodo llmaado modulo bajo / es un nodo primario. tiene secundarios modA,modB.
  The node named color under / is a scalar node with value red.
  El nodo llamado color bajo / es un nodo escalar con el valor rojo.
or:
o:
  $modules = $config->modules;
  $modules = $config->modules;
  foreach ($modules as $module=>$data) {
  foreach ($modules as $module=>$data) {
Line 342: Line 341:
   }
   }
  }
  }
resultaría en:
would result in:
   The module modA thinks Mr. Bill is a super star!
   The module modA things Mr. Bill is a super star!
 
=Changes from 3.1=
*Removed the __ from method calls.
*Relaxed the rules for the names of Magic Data nodes.
*Implemented the various interfaces
*Added in support for localization of values


=Cambios de 3.1=
[[Category:Developer Resources]]
*Eliminamos los __ de los métodos de llamada.
*Relajamos las reglas de los nombres de los nodos de los datos magic.
*Implementamos las varias interfaces
*Agregamos soporte para localización de valores
[[Category:Technical Overview]][[Category:Modules]][[Category:Magic Data]][[Category:Review2013]][[Category:Needs Intro]]

Latest revision as of 19:22, 1 March 2019

Magic Data is a mechanism intended to handle dynamic site-level configuration data. It is the basis of much of the functionality provided by the IntraHealth Informatics Core Engine (I2CE), including how pages are served and how custom reports are made.

Warning:The API for Magic Data has been fairly significantly updated from version 3.1 to version 4.0. Although this article applies to the version 4.0 API, much of it is relevant to version 3.1. See some of the Changes

What is Magic Data?

Magic Data is a rooted tree structure with benefits. If you wish you may think of it as the analogue of the Windows Registry for your web application. You may also think of it as a nested array.

There are two parts two parts to magic data. The magic data node class, defined in I2CE/lib/I2CE_MagicDataNode and the storage mechanisms for magic data. You may use magic data without using a storage mechanism, in which case the magic data saved does on persist across sessions. By default uses the following storage mechanisms for Magic Data:

  • Database: The data is stored into a table in the database. In I2CE, this is set to be the config table.
  • APC: The data is stored in to a memory cache provided by apc which persists across apaches session.

As APC is faster, reads are first done from APC. If the data is not found there, it is read from the database. Data is written first to the database and then to APC.

Scalar and Non-Scalar

There are two main types of nodes scalar and a parent node

A scalar node does not have any children. It does have a value which is a possibly empty string. A scalar node can be marked as being localized. In which case the value returned depends on the localization preferences for the user.

A parent node can have as many children as it wants. Each child of a parent node must have distinct names. It does not have a value. It is not localizable.

Names and Paths

With the exception of the root node, every magic data node has a name. For a name, any numeric value is allowed. Any non-empty string is valid as long as it does not start with '=' does not contain a '/' and is not '.' or '..'. However, it is best if you limit the strings to contain the following characters only:

_-+.0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ

Absolute Paths

Magic Data nodes can be referenced by their path which is a concatenation of their names by '/.'

The root node has path '/'.

If the root node has a child with name 'some', it is referenced by the path '/some'

If some is a parent node with a child with name 'thing', then that child is referenced by the path '/some/thing'

Relative Paths

Paths can also be relative. In the example above, if you were at the node '/some' then could reference the other nodes by:

  • './' references '/some'
  • '../' references '/'
  • './thing' references the node '/some/thing'

Defining Magic Data in Configuration Files

A convenient way to get magic data loaded into the system is to do so through a module configuration file by specifying a <configurationGroup> node after the <metadata> node. This section serves double duty. It allows you to specify magic data as well as to provide a way to edit the data for the module.

The <configurationGroup> node is optional. If it is present it has to have the attribute name which has the same value as the module name, whhich is the name attribute in the containing <I2CEConfiguration> tag.

All magic data is relative to the path defined by the this configurationGroup. There are three options:

  • The attribute path is not present. In the following example, the magic data is stored under /modules/mercury_javascript_path.
Example:
 <configurationGroup name='mercury_javascript_popup'>
   SOME STUFF GOES HERE
</configurationGroup>
  • The attribute path is present. In the following example, the magic data is stored under /some/other/place.
Example:
<configurationGroup name='mercury_javascript_popup' path='/some/other/place'>
  SOME STUFF GOES HERE
</configurationGroup> 
  • The module is 'I2CE'. The magic data is stored relative to /I2CE

In the remainder of this section we will describe WHAT STUFF GOES THERE which are <configurationGroup> and <configurations> tags.

<configurationGroup>

A <configurationGroup> can several subtags in this order::

  • An optional <displayName>. A name displayed in the module configuration for this grouping of data
  • An optional <description>. A description of the functionality of the grouping.
  • An optional <version> tag.
  • Any number (including zero) of <status> tags.
  • Any number (including zero) of <configurationGroup> or <configuration> tags.

<configuration>

A <configuration> can several subtags in this order::

  • An optional <displayName>. A name displayed in the module configuration for this grouping of data
  • An optional <description>. A description of the functionality of the grouping.
  • An optional <version> tag.
  • Any number (including zero) of <value> tags.

Attributes

There are several attributes that both a <configuration> and a <configurationGroup> may have:

  • name: This is a required attribute. Every child <configuration> or <configurationGruop> of a <configurationGroup> should have a distinct name. If the path attribute is not set, it is also says that this configuration node should apply to the magic data node with the given name and which is a child magic data node of the parent configurationGroup node.
  • path: This is optional. It may be an absolute or relative path in magic data and describes the magic data at which this value should be stored at. If it is a relative path, it is relative to the magic data path of its parent node.
  • locale: This is optional. If this is set, it means that the values under this node should be consider to be localizable.
  • config: This is optional. If set, it sets the I2CE_Swiss object used to display the data in the configure modules menu.

A <configuration> may also have the following attributes:

  • type: Defaults to 'string' and describes the type of data that is being set by the <value> tags of this node.
  • values: Defaults to 'single' and describes if the data being set by this node should be an array of values or a single value based on what is stored in the <values> node

<value>

The <value> tag is a sub-tag of a <configuration> tag. It contains the value(s) which are stored in the magic data and depends on the type and values attributes.

Some common types and values are:

  • type='string' values='single': The magic data node is a scalar type with value the contents of the single <value> tag.
  • type='string' values='many': The magic data node is a parent type. The magic data node has a child node of scalar type for each <value> tag.
  • type='delimited': The magic data node is a parent type. The value tags are expected to be of the form <value>'key':'value'</value> in which case a child magic data node is created of scalar type with name 'key' and value 'value'
  • type='boolean': The values in the <value> tag are interpreted as booleans: F,f,False,false,0, etc. are stored in magic data is 0. Otherwise the value stored is 1.


<version>

The same magic data can be accessed by multiple modules configuration files. Suppose that a module moduleA requires a module moduleB and that they both set /some/data to have values valA and valB respectively. Suppose that both modules have version 1.0.

Excerpt from moduleA's configuration file
<configurationGroup name='some' path='/some'>
 <configuration name='data' >
   <value>valA</value>
 </configuration>
 <configuration name='data2'> 
   <value>valA2</value>
 </configuration>
</configurationGroup>
Excerpt from moduleB's configuration file
<configurationGroup name='some' path='/some'>
 <configuration name='data'>
   <value>valB</value>
 </configuration>
 <configuration name='data2'> 
   <value>valB2</value>
 </configuration>
</configurationGroup>


On site intialization, since moduleA requires moduleB, the value is first set to valB is first set by moduleB. It is then overwritten to be the value valA by moduleA. Similarly, after initialization, the value of '/some/data2' is 'valA2'

Suppose that version of moduleB is increased to version 1.1 but there are no other changes to the configuration file. This will cause the configuration file to be reprocessed. The configurator will remember that has already processed all the data up to and including version 1.0. Thus it will not re-read the or overwrite what is already stored in magic data.

Suppose now that moduleB wants to change the value it stores at /some/data to be newValB, as well as create a new value at /some/other_data We would need to increase the version number of the module to 1.2 and add a <version> tag so that the configurator knows that in upgrading the module to version 1.2, we should reread the configuration data for anything greater than the previously loaded version of 3.1:

<configurationGroup name='some' path='/some'>
 <configuration name='data'>
   <version>1.2</version>
   <value>newValB</value>
 </configuration>
 <configuration name='other_data'>
   <version>1.2</version>
   <value>The new stuff</value>
 </configuration>
 <configuration name='data2'> 
   <value>valB2</value>
 </configuration>
</configurationGroup>

Now the value at /some/data will be updated to be 'newValB' and we will add in the value 'The new stuff' at '/some/other_data.' The value at '/some/data2' remains unchanged and is 'valA2.'

<status>

A status tag consists of key value pairs:

<status>key:value</status>

Although you can use anything for the key (as long as it does not have a ':' in it!), the keys which have meaning are:

  • version: Functions just like <version>
  • overwrite: The value can be either true of false. Defaults to false. If true the magic data will be overwritten even if the version has not.
  • merge: The value can be either true of false. Defaults to false. If true, the values read in are merged into the existing values by array_merge()
  • mergerecursive: The value can be either true of false. Defaults to false. If true, the values read in are merged into the existing by I2CE_Util::merge_recursive().
  • uniquemerge: The value can be either true of false. Defaults to false except in the case where a <configuration> node has type='string' and values='many.' If true, the values read in are merged into the existing values by I2CE_Util::merge_recursive() and only the unique values are kept by I2CE_Util::array_unique().
  • visible: The value can be either true of false. Defaults to true. If true, this node is displayed in the module configuration menu.
  • advanced: The value can be either true of false. Defaults to false. If true, this is considered an an advanced option for the module configuration menu
  • required: The value can be either true of false. Defaults to true. It says the the resulting values at the <configuration> node must be set
  • showIndex: The value can be either true of false. Defaults to true. In which case we show the index in the module configuration menu


The values of the status keys are inherited as you go down a node.

Using Magic Data in PHP

Each node of magic data is an instance of the class I2CE_MagicDataNode. The "public variables" of a node are its child nodes which is done by making use of the __get() method. I2CE_MagicDataNode implements the RecusriveIterator, ArrayAccess, SeekableIterator, and Countable Interfaces.


I2CE has a root magic data instance which can be retrieved by:

$config=I2CE::getConfig();

Basic Access

Suppose $data is a magic data node, with a children named 'my_list' and 'amount' which are parent type and scalar type respectively. Suppose that the child node 'amount' has value 10. Suppose that there is no child named 'bad.' The children can be accessed in many ways:

Access Method Result Notes
$data->my_list I2CE_MagicDataNode This is the 'my_list' node
$data->amount 10
$data->bad I2CE_MagicDataNode The node did not exist, so it was created.
It has an indeterminate type at the moment.
$data['my_list'] I2CE_MagicDataNode the 'my_list' node
$data['amount'] 10 the value of the 'amount' node
$data['bad'] I2CE_MagicDataNode We created the non-existent 'bad' node and returned it

Refined Access

To get more refined access to magic data nodes you may use the traverse($path,$create=false,$return_value=true) function:

$data->traverse('my_list',false,false) I2CE_MagicDataNode This is the 'my_list' node
$data->traverse('bad',false,false) null The second argument says not to create a node that doesn't exist
$data->traverse('amount',false,false) I2CE_MagicDataNode The is the 'amount' node
$data->traverse('my_list',false,true) I2CE_MagicDataNode This is the 'my_list' node
$data->traverse('amount',false,true) 10 The value of the 'amount' node
$data->traverse('bad',false,true) null The second argument says not to create a node that doesn't exist
$data->traverse('my_list',true,true) I2CE_MagicDataNode This is the 'my_list' node
$data->traverse('amount',true,true) 10 The value of the 'amount' node
$data->traverse('bad',true,true) I2CE_MagicDataNode We created the non-existent 'bad' node and returned it

If a node has scalar type, you can get its value by getValue(). If you call getAsArray() on it, it will also return its value.

If a node has parent or indeterminate type, calling getValue() returns the node itself. If you call getAsArray() on it it will return a nested array. The keys at each depth are the child node's names. The values are either an array or a sting, depending on if that child is scalar or not.

Checking Existence and Type

You can use the following method to see if a Magic Data node, exists and what its type is:

  • pathExists($path)
  • is_scalar($path=null)
  • is_parent($path=null)
  • is_indeterminate($path=null)
  • is_root($path=null)

Here, we the path defaults to null, the value that method is called on the node itself (this would be the same as calling it on $path='./').

You may do something like:

function set_node_to_scalar($node) {
  if (!$node instanceof I2CE_MagicDataNode) {
    echo "Why are you giving me garbage data?\n";
    return false;
  } 
  if ($node->is_scalar()) {
    echo "This node is already a scalar.  It has a value " . $node->getValue(). "\nI don't need to do anything.\n";
    return true; 
  } else {
    echo "This node is a parent node.  Although it may or may not have children, I can't set it to be scalar.\n";
    return false;
  } else{
    //$node->is_indeterminate() will return true
    echo "This node is indeterminate. Setting it to be scalar\n";
    $node->set_scalar();
    return true;
  }
}

Two other useful functions are

  • getAsArray($path=null) which return the node and all of its children (recursively) as an array
  • setIfIsSet(&$var,$path,$as_array=false) which will check to see if $path exists. If it does not, it returns false. If it does, it returns true and calls either getValue() or getAsArray() on the node referred to by the path.

Child Names and Iterators

To get the names of the child nodes of a node, we use the getKeys() method. Suppose that we magic data set up as follows:

Path Type Value
/ parent NONE
/color scalar red
/modules parent NONE
/modules/modA parent NONE
/modules/modB parent NONE
/modules/modA/favorite_clay_animation scalar Mr. Bill

You may also something similar as:

echo "I like the color " . $config->color . "\n";
$keys = $config->getKeys();
foreach ($keys as $key) {
  if ($config->is_parent($key)) {
    echo "The node named $key under at " . $config->getPath(false) . " is a parent node.  It has children " . implode(',', $config->$key->getKeys()) . ".\n";
  } else if ($config->is_scalar($key)) {
   echo "The node named $key under at " . $config->getPath(false) . " is a scalar with value " . $config->$key ".\n";
  }
}

which would result in:

I like the color red.
The node named modules under / is a parent node.  It has children modA,modB.
The node named color under / is a scalar node with value red.


As a magic data node is an iterator, we can do things like:

foreach ($config as $key=>$node) {
  if ($node instanceof I2CE_MagicDataNode) {
   echo "The node named $key under at " . $config->getPath(false) . " is a parent node.  It has children " . implode(',', $node->getKeys()) . ".\n";
  } else {
   echo "The node named $key under at " . $config->getPath(false) . " is a scalar with value " . $node .".\n";
  }
}

which would result in:

The node named modules under / is a parent node.  It has children modA,modB.
The node named color under / is a scalar node with value red.

or:

$modules = $config->modules;
foreach ($modules as $module=>$data) {
  if ($data->is_scalar('favorite_clay_animation')) {
     echo "The module $module thinks " . $data->favorite_clay_animation . " is a super star!\n";
  }
}

would result in:

 The module modA things Mr. Bill is a super star!

Changes from 3.1

  • Removed the __ from method calls.
  • Relaxed the rules for the names of Magic Data nodes.
  • Implemented the various interfaces
  • Added in support for localization of values