1
0
mirror of https://github.com/php/doc-es.git synced 2026-03-26 08:22:08 +01:00
Files
archived-doc-es/reference/sdodasrel/examples.xml
Pedro Antonio Gil Rodríguez 7d321b6305 Correcciones menores
git-svn-id: https://svn.php.net/repository/phpdoc/es/trunk@325576 c90b9560-bf6c-de11-be94-00142212c4b1
2012-05-08 06:16:06 +00:00

1166 lines
47 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<!-- EN-Revision: 74f46122078b4e4d442939cb1955ca34390dfb69 Maintainer: seros Status: ready -->
<!-- Reviewed: no -->
<chapter xml:id="sdodasrel.examples" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
&reftitle.examples;
<section xml:id="sdodasrel.examples-crud">
<title>Crear, recuperar, actualizar y borrar datos</title>
<para>
Esta sección ilustra cómo se puede usar el DAS Relacional para crear,
recuperar, actualizar y borrar datos de una base de datos relacional.
La mayoría de los ejemplos están ilustrados con un base de datos de tres tablas que
contienen compañías, departamentos dentro de esas compañías, y empleados
que trabajan para esos departamentos. Este ejemplo se usa en varios
puntos de la literatura de SDO. Vea la sección de ejemplos de la
<link xlink:href="&url.ibm.sdo.spec;">especificación de Objetos de Datos de Servicio</link>
o la sección
<link linkend="sdo.examples">Ejemplos</link>
de la documentación de la extensión SDO.
</para>
<para>
El DAS Relacional se construye con metadatos que definen la
base de datos relacional y cómo deberían ser referenciados a SDO.
La amplia sección que sigue describe estos metadatos y cómo
construir el DAS Relacional. Los ejemplos que le siguen asumen que
estos metadatos son un fichero php incluido.
</para>
<para>
Los ejemplos de abajo y otros se pueden encontrar en el directorio
<filename>Scenarios</filename>
del paquete del DAS Relacional.
</para>
<para>
El DAS Relacional lanza excepciones en el momento que encuentre errores
en los metadatos o errores al ejecutar sentencias SQL en la
base de datos. Por brevedad, los ejemplos de abajo omiten es uso de bloques
try/catch en las llamadas al DAS Relacional.
</para>
<para>
Todos estos ejemplos difieren del uso esperadode SDO en dos
aspectos importantes.
</para>
<para>
Primero, muestran todas las interacciones con la base de datos completadas con
un script. En este aspecto estos escenarios no son realistas aunque se han
elegido para ilustrar el uso del DAS Relacional.
Se espera que las interacciones con la base de datos sean separadas
con el tiempo y el grafo de datos sea serializado y deserializado en la sesión de
PHP una o más veces al interactuar la aplicación con un usuario final.
</para>
<para>
Segundo, todas las consultas ejecutadas en la base de datos usan consultas
'hard-coded' sin variables de sustittución. En este case es seguro
usar la simple llamada
<function>executeQuery</function>,
y esto es lo que muestran los ejemplos.
Sin embargo, en la práctica es improbable que la sentencia SQL sea conocida
enteramente con anticipación. Para permitir que las variables sean sustituidas
de forma segura en las consultas SQL, sin correr el riesgo de
inyectar SQL con efectos desconocidos, es más seguro usar el método
<function>executePreparedQuery</function>
el cual toma una sentencia SQL preparada que contiene parámetros de sustitución
y una lista de valores a ser sustituidos.
</para>
</section>
<section xml:id="sdodasrel.metadata">
<title>Especificar los metadatos</title>
<para>
Esta primera amplia sección describe en detalle cómo los metadatos describen
la base de datos y cómo se proporciona el modelo SDO requerido al
DAS Relaiconal.
</para>
<para>
Cuando se invoca al constructor del DAS Relacional, es necesario
pasarle varias piezas de información. El volumen de información,
pasado como un array asociativo al primer argumento del constructor,
indica al DAS Relacional qué necesita conocer sobre la base de datos
relacional. Describe los nombres de las tablas, columnas, claves primarias
y claves foráneas. and foreign keys. Debería ser bastante sencillo comprender los que se
requiere, y una vez escrito se puede colocar en un fichero PHP e incluido
cuando sea necesario. El resto de información, pasada en el segundo
y tercer parámetros al constructor, indica al DAS Relacional qué
necesita conocer sobre las relaciones entre objetos y la forma
del grafo de datos; en última instancia determina cómo se van a normalizar los datos
de la base de dtaos en un grafo.
</para>
<section xml:id="sdodasrel.metadata.database">
<title>Medatos de la base de datos</title>
<para>
El primer argumento del constructor describe la base de datos
relacional objetivo.
</para>
<para>
Cada tabla se describe por un array asociativo con hasta cuatro claves.
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Clave</entry>
<entry>Valor</entry>
</row>
</thead>
<tbody>
<row>
<entry>name</entry>
<entry>El nombre de la tabla.</entry>
</row>
<row>
<entry>columns</entry>
<entry>
Un array listando los nombres de las columnas, en cualquier orden.
</entry>
</row>
<row>
<entry>PK</entry>
<entry>El nombre de la columna que contiene la clave primaria.</entry>
</row>
<row>
<entry>FK</entry>
<entry>Un array con dos entradas, 'from' (desde) y 'to' (hacia), que define
una columna de que contiene una clave foránea, y una tabla hacia la que apunta
la clave foránea. Si no existen claves foráneas en la tabla, no es necesario
especificar la entrada 'FK'. Sólo se puede especificar
una clave foránea. Sólo se puede especificar una clave foránea que apunte a la
clave primaria de la tabla.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</para>
<programlisting role="php">
<![CDATA[
<?php
/*****************************************************************
* METADATOS QUE DEFINEN LA BASE DE DATOS
******************************************************************/
$tabla_compañía = array (
'name' => 'compañía',
'columns' => array('id', 'nombre', 'empleado_del_mes'),
'PK' => 'id',
'FK' => array (
'from' => 'empleado_del_mes',
'to' => 'empleado',
),
);
$tabla_departamento = array (
'name' => 'departamento',
'columns' => array('id', 'nombre', 'ubicación', 'número', 'id_comp'),
'PK' => 'id',
'FK' => array (
'from' => 'id_comp',
'to' => 'compañía',
)
);
$employee_table = array (
'name' => 'empleado',
'columns' => array('id', 'nombre', 'NS', 'director', 'id_dept'),
'PK' => 'id',
'FK' => array (
'from' => 'id_dept',
'to' => 'departamento',
)
);
$metadatos_bd = array($tabla_compañía, $tabla_departamento, $employee_table);
?>
]]>
</programlisting>
<para>
Estos metadatos corresponden a una base de datos relacional que podría haber
sido definida para MySQL como:
</para>
<programlisting role="sql">
<![CDATA[
create table compañía (
id integer auto_increment,
nombre char(20),
empleado_del_mes integer,
primary key(id)
);
create table departamento (
id integer auto_increment,
nombre char(20),
ubicación char(10),
número integer(3),
id_comp integer,
primary key(id)
);
create table empleado (
id integer auto_increment,
nombre char(20),
NS char(4),
director tinyint(1),
id_dept integer,
primary key(id)
);
]]>
</programlisting>
<para>
o para DB2 como:
</para>
<programlisting role="sql">
<![CDATA[
create table compañía ( \
id integer not null generated by default as identity, \
nombre varchar(20), \
empleado_del_mes integer, \
primary key(id) )
create table departamento ( \
id integer not null generated by default as identity, \
nombre varchar(20), \
ubicación varchar(10), \
número integer, \
id_comp integer, \
primary key(id) )
create table empleado ( \
id integer not null generated by default as identity, \
nombre varchar(20), \
NS char(4), \
director smallint, \
id_dept integer, \
primary key(id) )
]]>
</programlisting>
<para>
Observe que aunque en este ejemplo no existen claves foráneas especificadas
en la base de datos y por lo tanto no se espera que la base de datos fuerce
la integridad referencial, la intención de la columna
<varname>id_comp</varname>
de la tabla departamento y la columna
<varname>id_dept</varname>
de la table empleado es que deberían contener la clave primaria
de su compañía contenedora o el registro del departamento, respectivamente.
Así, estas dos columnas están actuando como claves forámeas.
</para>
<para>
Existe una tercera clave foránes en este ejemplo, que proviene de la columna
<varname>empleado_del_mes</varname>
del registro de la compañía a una única fila de la tabla empleado.
Observe que la diferencia a propósito entre esta clave foránea y las otras
dos. La columna
<varname>employee_of_the_month</varname>
representa una relación monoevaluada: sólo puede existir
un empleado del mes para una compañía dada.
Las columnas
<varname>id_comp</varname>
e
<varname>id_dept</varname>
representan relaciones polievaluadas: una compañía puede contener
muchos departamentos y un departamento puede contener muchos empleados.
Esta distinción será evidente cuando el resto de los metadatos
identifiquen las relaciones compañia-departamento y departamento-empleado
como relaciones de contención.
</para>
<para>
Existen unas pocas sencillas reglas a seguir al construir los
metadatos de la base de datos:
</para>
<itemizedlist>
<listitem>
<para>
Todas las tablas deben tener claves primarias, y las claves primarias deben ser
especificadas en los metadatos. Sin claves primarias no es posible
seguir la pista de identidades de objetos. Como se puede obcervar con las sentencias
SQL que crean las tablas, las claves primarias pueden ser
autogeneradas, esto es, generadas y asignadas por la base de datos cuando
se inserta un registro. En este caso la clave primaria autogenerada
se obtiene de la base de datos y es insertada en el objeto de datos
inmediatamente después que insertar la fila en la base de datos.
</para>
</listitem>
<listitem>
<para>
No es necesario especificar en los metadatos todas las columnas
que existen en la base de datos, solamente aquellas que serán usadas.
Por ejemplo, si la compañía tuviera otra columna con la que la
aplicación no quiere acceder con SDO, no es necesario que sea
especificada en los metadatos. Por otra parte, tampoco sería
perjudicial especificarla: si se especifica en los metadatos pero nunca
se recupera,o se asigna mediante la aplicación, la columna no usada
no afectará a nada.
</para>
</listitem>
<listitem>
<para>
En los metadatos de la base de datos observe que las definiciones de la clave foránea
no identifican la columna destino en la tabla a la que apunta,
sino a la tabla misma. Estrictamente, el modelo relacional
permite que el destino de una clave foránea sea una clave no primaria.
Solo las claves foráneas que apuntes a una clave primaria son útiles para
construir el modelo SDO, por lo que los metadatos especifican el nombre de la table.
Se entiende que la clave foránea apunte a la clave primaria de
la tabla dada.
</para>
</listitem>
</itemizedlist>
<para>
Dadas estas reglas, y dadas las sentencias SQL que definen la
base de datos, debería ser fácil construir los metadatos de la base de datos.
</para>
<section xml:id="sdodasrel.metadata.database.model">
<title>Qué hace el DAS Relacional con los metadatos</title>
<para>
El DAS Relacional usa los metadatos de la base de datos para formar la mayoría del
modelo SDO. Para cada table de los metadatos de la base de datos se define un
tipo SDO. Cada columna que pueda representar un valor primitivo
(las columnas que no están definidas como claves foráneas) se añade
como una propiedad del tipo SDO.
</para>
<para>
A todas las propiedades primitivas se les da un tipo string en el modelo SDO,
sin tener en cuenta su tipo SQL. Cuando se vuelve a escribir valores en la
base de datos, el DAS Relacional creará sentencias SQL que tratan
los valores como string, y la base de datos los convertirá al
tipo apropiado.
</para>
<para>
Las claves foráneas son interpretadas en una de dos formas, dependiendo de los
metadatos del tercer argumento del constructor que define
las relaciones de contención de SDO.
Por lo tanto se aplaza una discusión de esto hasta la sección sobre
<link linkend="sdodasrel.metadata.crels">
relaciones de contención de SDO
</link>
más abajo.
</para>
</section>
<section xml:id="sdodasrel.metadata.approottype">
<title>Especificar el tipo raíz de la aplicación</title>
<para>
El segundo argumento del constructor es el tipo raíz de la aplicación.
La verdadera raíz de cada grafo de datos es un objeto de un tipo raíz especial
y todos los objetos de datos de la aplicación están en algún lugar por debajo de él. De los
varios tipos de aplicación del modelo SDO, uno tiene que ser el
tipo de aplicación inmediatemente por debajo de la raíz del grafo de datos.
Si solo existe una tabla en los metadatos de la base de datos, el tipo raíz
de la aplicación puede ser deducido, y este argumento se puede omitir.
</para>
</section>
<section xml:id="sdodasrel.metadata.crels">
<title>Especificar las relaciones de contención de SDO</title>
<para>
El tercer argumento del constructor define cómo van a ser vinculados los
tipos del modelo entre ellos para formar un grafo. Identifica las
relaciones padre-hijo entre los tipos que colectivamente forman un
grafo. Las relaciones necesitan ser soportadas por claves foráneas para
ser encontradas en los datos, de una forma descrita dentro de poco.
</para>
<para>
Los metadatos son un array que contiene uno o más arrays asociativos,
cada uno identificando un padre y un hijo. El ejemplo de abajo muestra
una relación padre-hijo desde la compañía hacia el departamento, y otra
desde el departamento hacia el empleado. Cada una se convertirá en una propiedad SDO
que define una relación de contención polievaluada del modelo SDO.
</para>
<programlisting role="php">
<![CDATA[
<?php
$contenedor_departamento = array( 'parent' => 'compañía', 'child' => 'departamento');
$contenedor_empleado = array( 'parent' => 'departamento', 'child' => 'empleado');
$metadatos_contenedor_SDO = array($contenedor_departamento, $contenedor_empleado);
?>
]]>
</programlisting>
<para>
Las claves foráneas de los metadatos de la base de datos son interpretadas como propiedades
con relaciones de contención polievaluadas o referencias de no contención
monoevaluadas, dependiendo de si tienen una
relación de contención correspondiente especificada en los metadatos.
En el ejemplo, las claves foráneas desde el departamento a la compañía (la columna
<varname>id_comp</varname>
de la tabla departamento)
y desde el empleado al departamento (la columna
<varname>id_dept</varname>
de la tabla empleado) son interpretadas como soportando las
relaciones de contención de SDO.
Cada relación de contención mencionada en los metadatos de las relaciones de contención de
SDO deben tener una clave foránea correspondiente presente en la
base de datos y ser definidas en los metadatos de la base de datos. Los valores de las
columnas de clave foránea para las relaciones de contención no aparecen en los
objetos de datos, en su lugar cada uno es representado por una relación de contención
desde el padra al hijo. Así, la columna
<varname>id_comp</varname>
de la fila departamento de la base de datos, por ejemplo, no
aparece como una propieadad del tipo departamento, sino como una
relación de contención llamada
<varname>departamento</varname>
en el tipo compañía.
Observe que la clave foránea y la relación padre-hijo parecen
tener significados opuestos: la clave foránea apunta desde el departamento hacia
la compañía, mientras que la relación padre-hijo apunta desde la compañía hacia
el departamento.
</para>
<para>
La tercera clave foránea de este ejemplo, el
<varname>empleado_del_mes</varname>
,
es tratada de forma diferente.
No es mencionadaen los metadatos de las relaciones de contención de SDO.
Como consecuencia es interpretada de la segunda forma: se convierte
en una referencia de no contención monoevaluada del objeto compañía, a la
que se le pueden asignar referencias a objetos de datos de SDO del tipo
empleado. No aparece como una propiedad del tipo compañía. La forma de
asignarle un valor en el grafo de datos SDO es tener un grafo que
contenga un objeto empleado a través de las relaciones de contención, y
asignarle el objeto. Esto es ilustrado en los ejemplos de más
de abajo.
</para>
</section>
</section> <!--specifying the metadata sdodasrel.metadata.database.model -->
</section> <!-- sdodasrel.metadata -->
<section xml:id="sdodasrel.examples.one-table">
<title>Ejemplos con una tabla</title>
<para>
El conjunto siguiente de ejemplos usa el DAS Relacional para trabajar con
un grafo de datos que contiene sólo un objeto de datos de aplicación, una sóla
compañía y los datos que se encuentran en la tabla compañía. Estos ejemplos
no ejercitan el poder de SDO o del DAS Relacional y, por supuesto,
se podría lograr el mismo resultado de forma más económica con sentencias SQL
directas, pero su intención es ilustrar cómo funcionan con el
DAS Relacional.
</para>
<para>
Para este sencillísimo escenario sería posible simplificar los
metadatos de la base de datos para incluir sólo la tabla compañía - si se hiciera,
el segundo y tercer argumento del constructor y el especificador de
columna usado en el ejemplo de consulta serían opcionales.
</para>
<para>
<example>
<title>Crear un objeto de datos</title>
<para>
El ejemplo más simple es crear un único objeto de datos y
escribirlo en la base de datos. En este ejemplo se crea un único objeto
compañía, su nombre se establece a 'Acme', y se llama al DAS Relacional
para escribir los cambios en la base de datos. El nombre de la compañía se
establece aquí usando el método de nombre de propiedad. Véase la sección
<link linkend="sdo.examples">Ejemplos</link>
en la extensión SDO para otras maneras de acceder a las
propiedades de un objeto.
</para>
<para>
Sin embargo, los objetos de datos solo se pueden crear cuando se tiene un objeto de datos
con el que empezar. Es por esta razón que la primera llamada
al DAS Relacional aquí sea para obtener un objeto raíz. Este es,
en efecto, cómo preguntar por un grafo de datos vacío - El objeto raíz especial
es la raíz verdadera del árbol. El objeto de datos compañía es
entonces creado con una llamada a
<function>createDataObject</function>
sobre el objeto raíz. Esto crea el objeto de datos compañía y lo inserta
en el grafo insertando en una propiedad de contención polievaluada
en el objeto raíz llamado 'compañía'.
</para>
<para>
Cuando se llama al DAS Relacional para aplicar los cambios se construirá
y ejecutará una única sentencia de inserción
insert statement 'INSERT INTO compañía (name) VALUES ("Acme");',
La clave primaria autogenerada
se establecerá en el objeto de datos y el resumen de cambios será reiniciado,
por lo que sería posible continuar trabajando con el mismo objeto
de datos, modificarlo, y aplicar los nuevos cambios una segunda vez.
</para>
<programlisting role="php" xml:id="sdodasrel.examples.1c-c">
<![CDATA[
<?php
require_once 'SDO/DAS/Relational.php';
require_once 'company_metadata.inc.php';
/**************************************************************
* Construir el DAS con los metadatos
***************************************************************/
$das = new SDO_DAS_Relational ($metadatos_bd,'compañía',$metadatos_contenedor_SDO);
/**************************************************************
* Obtener un objeto raíz y crear un objeto compañía bajo él.
* Realizar un único cambio al objeto de datos.
***************************************************************/
$raíz = $das -> createRootDataObject();
$acme = $raíz -> createDataObject('compañía');
$acme->name = "Acme";
/**************************************************************
* Obtener una conexión a la base de datos y escribir el objeto en ella
***************************************************************/
$gbd = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
$das -> applyChanges($gbd, $raíz);
?>
]]>
</programlisting>
</example>
</para>
<para>
<example>
<title>Recuperar un objeto de datos</title>
<para>
En este ejemplo se recupera un único objeto de datos de la base de datos
- o posiblemente más de uno si existe más de una compañía
llamada 'Acme'. Se utiliza echo con las propiedades
<varname>nombre</varname>
e
<varname>id</varname>
por cada compañía devuelta.
</para>
<para>
En este ejemplo, el tercer argumento de
<function>executeQuery</function>,
el especificador de columna es necesario ya que existen otras tablas en los
metadatos con nombre de columnas
<varname>nombre</varname>
e
<varname>id</varname>.
Si no hubiera posibilidad de ambigüedad se podría omitir.
</para>
<programlisting role="php" xml:id="sdodasrel.examples.1c-r">
<![CDATA[
<?php
require_once 'SDO/DAS/Relational.php';
require_once 'company_metadata.inc.php';
/**************************************************************
* Construir el DAS con los metadatos
***************************************************************/
$das = new SDO_DAS_Relational ($metadatos_bd,'company',$metadatos_contenedor_SDO);
/**************************************************************
* Obtener una conexión a la base de datos
***************************************************************/
$gbd = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
/**************************************************************
* Emitir una consulta para obtener un objeto compañía - posiblemente más si existen
***************************************************************/
$raíz = $das->executeQuery($gbd,
'select nombre, id from compañía where nombre="Acme"',
array('compañía.name', 'compañía.id') );
/**************************************************************
* Usar echo con el nombre y el id
***************************************************************/
foreach ($raíz['compañía'] as $compañía) {
echo "La compañía obtenida de la base de datos tiene como nombre = " .
$compañía['nombre'] . " e id " . $compañía['id'] . "\n";
}
?>
]]>
</programlisting>
</example>
</para>
<para>
<example>
<title>Actualizar un objeto de datos</title>
<para>
Este ejemplo combina los dos anteriores, en el sentido de que para
que sea actualizado, el objeto debe primero ser recuperado. El código de la
aplicación invierte el nombre de la compañía (así, 'Acme' se convierte en 'emcA') y luego
se escriben los cambios en la base de datos de la misma forma que
estuvieran cuando el objeto fue creado. Ya que la consulta busca
el nombre de ambas maneras, el programa puede ejecutarse repetidamente para encontrar
la compañía e invertir su nombre cada vez.
</para>
<para>
En este ejemplo se reutiliza la misma instancia del DAS Relacional
para el método
<function>applyChanges</function>,
ya que es el gestor de la base de datos de PDO. Esto es completamente correcto; también es
correcto permitir a las instancias anteriores que sean puestas en el recolector de basura
y obtener nuevas instancias. El DAS Relacional no contiene datos de estado
en lo que se refiere al grafo una vez que ha sido devuelto un grafo de datos a
la aplicación. Todos los datos necesarios están dentro del grafo en sí,
o se pueden reconstruir desde los metadatos.
</para>
<programlisting role="php" xml:id="sdodasrel.examples.1c-ru">
<![CDATA[
<?php
require_once 'SDO/DAS/Relational.php';
require_once 'company_metadata.inc.php';
/**************************************************************
* Construir el DAS con los metadatos
***************************************************************/
$das = new SDO_DAS_Relational ($metadatos_bd,'compañía',$metadatos_contenedor_SDO);
/**************************************************************
* Obtener una conexión a la base de datos
***************************************************************/
$gbd = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
/**************************************************************
* Emitir una consulta para obtener un objeto compañía - posiblemente más si existen
***************************************************************/
$raíz = $das->executeQuery($gbd,
'select nombre, id from compañía where nombre="Acme" or nombre="emcA"',
array('compañía.nombre', 'compañía.id') );
/**************************************************************
* Cambiar el nombre de la primera compañía unicamente
***************************************************************/
$compañía = $raíz['compañía'][0];
echo "obtenida una compañía con el nombre " . $compañía->nombre . "\n";
$compañía->nombre = strrev($compañía->nombre);
/**************************************************************
* Escribir los cambios
***************************************************************/
$das->applyChanges($dbh,$raíz);
?>
]]>
</programlisting>
</example>
</para>
<para>
<example>
<title>Borrar un objeto de datos</title>
<para>
Se recuperan cualesquiera compañías llamadas 'Acme' o su inverso 'emcA'.
Entonces son borradas del grafo con unset.
</para>
<para>
En este ejemplo todas son borradas de una sola vez desestableciendo la
propiedad de contención (la propiedad que define la relación
de contención). También es posible borrarlas individualmente.
</para>
<programlisting role="php" xml:id="sdodasrel.examples.1c-rd">
<![CDATA[
<?php
require_once 'SDO/DAS/Relational.php';
require_once 'company_metadata.inc.php';
/**************************************************************
* Construir el DAS con los metadatos
***************************************************************/
$das = new SDO_DAS_Relational ($metadatos_bd,'compañía',$metadatos_contenedor_SDO);
/**************************************************************
* Obtener una conexión a la base de datos
***************************************************************/
$gbd = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
/**************************************************************
* Emitir una consulta para obtener un objeto compañía - posiblemente más si existen
***************************************************************/
$raíz = $das->executeQuery($gbd,
'select nombre, id from compañía where nombre="Acme" or nombre="emcA"',
array('compañía.nombre', 'compañía.id') );
/**************************************************************
* Borrar cualquier compañía encontrada del grafo de datos
***************************************************************/
unset($raíz['compañía']);
/**************************************************************
* Escribir el/los /cambio/s
***************************************************************/
$das->applyChanges($gbd,$raíz);
?>
]]>
</programlisting>
</example>
</para>
</section> <!-- one-table -->
<section xml:id="sdodasrel.examples.two-table">
<title>Ejemplos con dos tablas</title>
<para>
El siguiente conjunto de ejemplos usa dos tablas de la base de datos
compañía: las tables compañía y departamento. Estos ejemplos ejercitan
más la función del DAS Relacional.
</para>
<para>
En esta serie de ejemplos se crean, se recuperan, se actualizan y finalmente
se borran una compañía y un departamento. Esto ilustra el
ciclo de vida de un grafo de datos que contiene más de un objeto. Observe que
estos ejemplos limpian las tablas compañía y departamento al inicio
por lo que se pueden saber los resultados exactos de las consultas.
</para>
<para>
Se pueden encontrar estos ejemplos combinados en un script llamado
<filename>1cd-CRUD</filename>
en el direcorio
<filename>Scenarios</filename>
del paquete del DAS Relacional.
</para>
<para>
<example>
<title>Una compañía, un departamento - Crear</title>
<para>
Como en el anterior ejemplo de crear solo un objeto de datos compañía,
la primera acción después de construir el DAS Relacional es llamar a
<function>createRootDataObject</function>
para obtener el objeto raíz especial del grafo de datos en principio vacío.
El objeto compañía se crea entonces como un hijo del objeto raíz,
y el objeto departamento como un hijo del objeto compañía.
</para>
<para>
Cuando se aplican los cambios, el DAS Relacional tiene que
llevar a cabo un procesamiento especial para mantener las claves foráneas que
soportan las relaciones de contención, especialmente están involucradas claves
primarias autogeneradas. En este ejemplo, la relación entre la
clave primaria autogenerada
<varname>id</varname>
de la tabla compañía y la columna
<varname>id_comp</varname>
de la tabla departamento se debe mantener. La primera vez que se inserta
una compañía y un departamento, el DAS Relacional tiene que
insertar primero la fila de lacompañía, luego llamar al método
<function>getLastInsertId</function>
de PDO para obtener la clave primaria autogenerada, y luego añadir tal
valor de la columna
<varname>id_comp</varname>
al insertar la fila del departamento.
</para>
<programlisting role="php" xml:id="sdodasrel.examples.1cd-c">
<![CDATA[
<?php
require_once 'SDO/DAS/Relational.php';
require_once 'company_metadata.inc.php';
/*************************************************************************************
* Vaciar las dos tablas
*************************************************************************************/
$gbd = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
$sentencia_pdo = $gbd->prepare('DELETE FROM COMPAÑÍA;');
$filas_afectadas = $sentencia_pdo->execute();
$sentencia_pdo = $gbd->prepare('DELETE FROM DEPARTAMENTO;');
$filas_afectadas = $sentencia_pdo->execute();
/**************************************************************
* Crear una compañía de nombre Acme y un departamento, el departamento de Zapatos
***************************************************************/
$gbd = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
$das = new SDO_DAS_Relational ($metadatos_bd,'compañía',$metadatos_contenedor_SDO);
$raíz = $das -> createRootDataObject();
$acme = $raíz -> createDataObject('compañía');
$acme -> nombre = "Acme";
$zapatos = $acme->createDataObject('departamento');
$zapatos->nombre = 'Zapatos';
$das -> applyChanges($gbd, $raíz);
?>
]]>
</programlisting>
</example>
</para>
<para>
<example>
<title>Una compañía, un departamento - Recuperar y actualizar</title>
<para>
En este caso, la consulta SQL pasada a
<function>executeQuery</function>
realiza una unión interna para unir los datos de las tablas
compañía y departamento. Las claves primarias de ambas tablas
deben incluirse en la consulta. El conjunto de resultados
es renormalizado para formar un grafo de datos normalizado. Observe que se
proporciona un especificador de columna como tercer argumento a la llamada a
<function>executeQuery</function>,
habilitando al DAS Relacional para conocer cual es cada columna en
el conjunto de resultados.
</para>
<para>
Observe que, aunque la columna
<varname>id_comp</varname>
se usa en la consulta, no es necesaria en el conjunto de resultados.
Para poder comprender lo que el DAS Relacional hace cuando construye
el grafo de datos, puede ser de ayuda visualizar a lo que se asemeja el
conjunto de resultados. Aunque los datos de la base de datos estén normalizados, por
los que varias filas de departamentos puedan apuntar a través de sus claves foráneas a una
fila de compañía, los datos del conjunto de resultados no están normalizados, esto es,
si existe una compañía y múltiples departamentos, los valores de la
compañía se repiten en cada fila. El DAS Relacional tiene que invertir
este proceso y volver a convertir el conjunto de resultados en un grafo de datos normalizado,
con sólo un objeto compañía.
</para>
<para>
En este ejemplo el DAS Relacional examinará el conjunto de resultados y
el especificador de columna, buscará datos para las tablas compañía y
departamento, buscará claves primarias para ambas tablas, e interpretará cada fila como
contenedora de datos de un departamento y su compañía madre. Si no encontró
datos para esa compañía antes (usa la clave primaria para
comprobarlo), crea un objeto compañía y luego un objeto departamento
bajo él. Si ya encontró datos para esa compañía antes y
ya creó el objeto compañía, entonces creará el
objeto departamento bajo ella.
</para>
<para>
De esta forma, el DAS Relacional puede recuperar y renormalizar datos
para múltiples compañías y múltiples departamentos bajo ellas.
</para>
<programlisting role="php" xml:id="sdodasrel.examples.1cd-ru">
<![CDATA[
<?php
require_once 'SDO/DAS/Relational.php';
require_once 'company_metadata.inc.php';
/**************************************************************
* Recuperar la compañía y el departamento Zapatos, luego borrar Zapatos y añadir TI
***************************************************************/
$gbd = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
$das = new SDO_DAS_Relational ($metadatos_bd,'compañía',$metadatos_contenedor_SDO);
$raíz = $das->executeQuery($gbd,
'select c.id, c.nombre, d.id, d.nombre from compañía c, departamento d where d.id_comp = c.id',
array('compañía.id','compañía.nombre','departamento.id','departamento.nombre'));
$acme = $raíz['compañía'][0]; // obtener la primera compañía - será 'Acme'
$zapatos = $acme['departamento'][0]; // obtener el primer departamento bajo ella - será 'Zapatos'
unset($acme['departamento'][0]);
$ti = $acme->createDataObject('departamento');
$ti->nombre = 'TI';
$das -> applyChanges($gbd, $raíz);
?>
]]>
</programlisting>
</example>
</para>
<para>
<example>
<title>Una compañía, dos departamentos - Recuperar y eliminar</title>
<para>
En este ejemplo la compañía y el departamento son recuperados y
luego eliminados. No es necesario eliminarlos individualmente
(aunque sería posible) - al eliminar el objeto compañía
del grafo de datos también se elimina cualquier departamento bajo él.
</para>
<para>
Observe la manera en que el objeto compañía es eliminado realmente usando la
llamada a unset de PHP. unset se ha de realizar sobre la propiedad
de contención que, en este caso, es
la propiedad compañía del objeto
raíz especial. Se debe usar:
<programlisting role="php" xml:id="sdodasrel.examples.1cd-crud.good-delete">
<![CDATA[
<?php
unset($raíz['compañía'][0]);
?>
]]>
</programlisting>
y no:
<programlisting role="php" xml:id="sdodasrel.examples.1cd-crud.bad-delete">
<![CDATA[
<?php
unset($acme); //ERRÓNEO
?>
]]>
</programlisting>
Al usar unset con
<varname>$acme</varname>
destruiría la variable, pero dejaría los daos del grafo
de datos intactos.
</para>
<programlisting role="php" xml:id="sdodasrel.examples.1cd-rd">
<![CDATA[
<?php
require_once 'SDO/DAS/Relational.php';
require_once 'company_metadata.inc.php';
/**************************************************************
* Recuperar la compañía y el departamento TI, luego borrar la compañía completa
***************************************************************/
$gbd = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
$das = new SDO_DAS_Relational ($metadatos_bd,'compañía',$metadatos_contenedor_SDO);
$raíz = $das->executeQuery($gbd,
'select c.id, c.nombre, d.id, d.nombre from compañía c, departamento d where d.id_comp = c.id',
array('compañía.id','compañía.nombre','departamento.id','departamento.nombre'));
$acme = $raíz['compañía'][0];
$ti = $acme['departamento'][0];
unset($raíz['compañía'][0]);
$das -> applyChanges($gbd, $raíz);
?>
]]>
</programlisting>
</example>
</para>
</section>
<section xml:id="sdodasrel.examples.three-table">
<title>Ejemplos con tres tablas</title>
<para>
Los siguientes ejemplos usan tres tablas de la base de datos compañía:
las tablas compañía, departamento y empleado. Estas introducen la pieza
final de la función no ejercitada por los ejemplo anteriores: la
referencia de no contención
<varname>empleado_del_mes</varname>.
</para>
<para>
Al igual que en los ejemplo de arraiba para la compañía y el departamento, este conjunto de ejemplos
intentan ilustrar el ciclo de vida completo de un grafo de datos.
</para>
<para>
<example>
<title>Una compañía, un departamento, un empleado - Crear</title>
<para>
En este ejemplo se crea una compañía que contiene un departamento y
sólo un empleado. Observe que este ejemplo limpia las tres tablas
al inicio, por lo que se conocerán los resultados exactos de las consultas.
</para>
<para>
Observe cómo una vez ha sido creados la compañía, el departamento y el empleado,
se puede hacer que la propiedad
<varname>empleado_del_mes</varname>
de la compañía apunte al nuevo empleado.
Ya que esta propiedad es una referencia de no contención, no puede hacerse hasta
que el objeto empleado haya sido creado dentro del grafo.
Las referencias de no contención necesitan ser manejadas cuidadosamente.
Por ejemplo, si el empleado ahora fuera eliminado del departamento al que pertenece,
no sería correcto intentar guardar el grafo sin
primero limpiar o reasignar la propiedad
<varname>empleado_del_mes</varname>
.
La regla de cierre de los grafos de datos de SDO requiere que cualquier objeto que sea
apuntado por una referencia de no contención también debe ser alcanzable por
relaciones de contención.
</para>
<para>
Cuando se va a insertar el grafo en la base de datos, el procedimiento
es similar al ejemplo de insertar la compañía y el departamento,
excepto que
<varname>empleado_del_mes</varname>
introduce complejidad extra.
El DAS Relacional necesita insertar los objetos bajando por el árbol
formado por las relaciones de contención, compañía, luego departamento, y luego
empleado. Esto es necesario por lo que siempre tiene la clave primaria
autogenerada de un padre a mano para incluir una fila hija. Pero cuando
se inserta la fila de la compañía, el empleado que es empleado del mes
aún no ha sido insertado y la clave primaria no se conoce. El
procedimiento es que después de insertar el registro del empleado y se
conoce su clave primaria, se lleva a cabo un paso final en el que el
registro de la compañía se actualiza con la clave primaria del empleado.
</para>
<programlisting role="php" xml:id="sdodasrel.examples.1cde-c">
<![CDATA[
<?php
require_once 'SDO/DAS/Relational.php';
require_once 'company_metadata.inc.php';
/*************************************************************************************
* Empty out the three tables
*************************************************************************************/
$gbd = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
$sentencia_pdo = $gbd->prepare('DELETE FROM COMPAÑÍA;');
$filas_afectadas = $sentencia_pdo->execute();
$sentencia_pdo = $gbd->prepare('DELETE FROM DEPARTAMENTO;');
$filas_afectadas = $sentencia_pdo->execute();
$sentencia_pdo = $gbd->prepare('DELETE FROM EMPLEADO;');
$filas_afectadas = $sentencia_pdo->execute();
/*************************************************************************************
* Crear una diminuta pero completa compañía.
* El nombre de la compañía es Acme.
* Existe un departamento, Zapatos.
* Existe un empleado, Sue.
* El empleado del mes es Sue.
*************************************************************************************/
$das = new SDO_DAS_Relational ($metadatos_bd,'compañía',$metadatos_contenedor_SDO);
$gbd = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
$raíz = $das -> createRootDataObject();
$acme = $raíz -> createDataObject('compañía');
$acme -> nombre = "Acme";
$zapatos = $acme -> createDataObject('departamento');
$zapatos -> nombre = 'Zapatos';
$zapatos -> ubicación = 'A-block';
$sue = $zapatos -> createDataObject('employee');
$sue -> nombre = 'Sue';
$acme -> empleado_del_mes = $sue;
$das -> applyChanges($gbd, $raíz);
echo "Escritos Acme con un departamento y un empleado\n";
?>
]]>
</programlisting>
</example>
</para>
<para>
<example>
<title>Una compañía, un departamento, un empleado - Recuperar y actualizar</title>
<para>
La sentencia SQL pasada al DAS Relacional esta vez es una unión
interna que recupera datos de las tres tablas. De otro modo este ejemplo
no introduciría nada que ya no apareciera en los ejemplos anteriores.
</para>
<para>
El grafo se actualiza por la adición de un nuevo departamento y un nuevo empleado,
y algunas alteraciones en las propiedades nombre de los objetos existentes
en el grafo. Los cambios combinados son luego escritos. El
DAS Relacional procesará y aplicará una mezcla arbitraria de
adicioines, modificaciones y eliminaciones hacia y desde el grafo de datos.
</para>
<programlisting role="php" xml:id="sdodasrel.examples.1cde-ru">
<![CDATA[
<?php
require_once 'SDO/DAS/Relational.php';
require_once 'company_metadata.inc.php';
/*************************************************************************************
* Buscar otra vez la compañía y cambiar varios aspectos.
* Cambiar el nombre de la compañía, del departamento y del empleado.
* Añadir un segundo departamento y un nuevo empleado.
* Cambiar el empleado del mes.
*************************************************************************************/
$das = new SDO_DAS_Relational ($metadatos_bd,'compañía',$metadatos_contenedor_SDO);
$gbd = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
$raíz = $das->executeQuery($gbd,
"select c.id, c.nombre, c.empleado_del_mes, d.id, d.nombre, e.id, e.nombre " .
"from compañía c, departamento d, empleado e " .
"where e.id_dept = d.id and d.id_comp = c.id and c.nombre='Acme'",
array('compañía.id','compañía.nombre','compañía.empleado_del_mes',
'departamento.id','departamento.nombre','empleado.id','empleado.nombre'));
$acme = $raíz['compañía'][0];
$zapatos = $acme->departamento[0];
$sue = $zapatos -> employee[0];
$ti = $acme->createDataObject('departamento');
$ti->nombre = 'TI';
$ti->ubicación = 'G-block';
$billy = $ti->createDataObject('empleado');
$billy->nombre = 'Billy';
$acme->nombre = 'MegaCorp';
$zapatos->nombre = 'Calzado';
$sue->nombre = 'Susan';
$acme->empleado_del_mes = $billy;
$das -> applyChanges($gbd, $raíz);
echo "Escritos la compañía con un departamento y un empleado extras y cambiados los nombres (Megacorp/Calzado/Susan)\n";
?>
]]>
</programlisting>
</example>
</para>
<para>
<example>
<title>Una compañía, dos departamentos, dos empleados - Recuperar y eliminar</title>
<para>
La compañía se recupera como un grafo de datos completo que contiene cinco
objetos de datos - la compañía, dos departamentos y dos empleados.
Se eliminan todos eliminando el objeto compañía. Al eliminar un
objeto de grafo se eliminan todos los objetos bajo él en el grafo.
Se generarán y ejecutarán cinco sentencias DELETE de SQL. Ya que siempre
estarán cualificadas con una cláusula WHERE que contiene todos los
campos que fueron recuperados, cualquier actualización en los datos de la
base de datos en el transcurso de otro proceso será detectada.
</para>
<programlisting role="php" xml:id="sdodasrel.examples.1cde-rd">
<![CDATA[
<?php
require_once 'SDO/DAS/Relational.php';
require_once 'company_metadata.inc.php';
/*************************************************************************************
* Ahora leerla una vez más y eliminarla.
* Se puede eliminar parte, aplicar los cambios, y luego seguir trabajando con el mismo
* grafo, pero se ha de tener cuidado de mantener el cierre - no se puede eliminar el
* empleado que es emplado del mes sin reasignar. Por seguridad, aquí se elimina la
* compañía de una sola vez.
*************************************************************************************/
$das = new SDO_DAS_Relational ($metadatos_bd,'compañía',$metadatos_contenedor_SDO);
$gbd = new PDO(PDO_DSN,DATABASE_USER,DATABASE_PASSWORD);
$raíz = $das->executeQuery($gbd,
"select c.id, c.nombre, c.empleado_del_mes, d.id, d.nombre, e.id, e.nombre " .
"from compañía c, departamento d, empleado e " .
"where e.id_dept = d.id and d.id_comp = c.id and c.nombre='MegaCorp';",
array('compañía.id','compañía.nombre','compañía.empleado_del_mes',
'departamento.id','departamento.nombre','empleado.id','empleado.nombre'));
$megacorp = $raíz['compañía'][0];
unset($raíz['compañía']);
$das -> applyChanges($gbd, $raíz);
echo "Eliminados la compañía, departamentos y empleados de una sola vez.\n";
?>
]]>
</programlisting>
</example>
</para>
</section>
</chapter>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->