Relaciones

Propel soporta relaciones baásicas uno-a-uno. Relaciones más complejas como muchos-a-muchos son también posibles definiendo las tablas de referencia en su modelo de datos y explícitamente usando estas tablas de referencia cuando se seleccionan registros. Este capítulo describe cómo crear entidades inter-relacionadas y usar métodos optimizados para retornar entidades relacionadas.

Definiendo Relaciones de Entidad

La representación de Propel de relaciones de entidades corresponde muy cercanamente a la forma en que las relaciones son representadas al nivel de la base de datos: llamado a través de llaves foraneas. En el modelo de datos XML, usted puede usar el tag<llave foranea> para especificar una columna como llave foranea.

<table name="book">
 <column name="book_id" type="INTEGER" required="true" primaryKey="true"/>
 <column name="title" type="VARCHAR" size="100" required="true"/>
 <column name="author_id" type="INTEGER" required="true"/>
 <foreign-key foreignTable="author">
   <reference
     local="author_id"
     foreign="author_id"/>
 </foreign-key>
</table>
<table name="author">
 <column name="author_id" type="INTEGER" required="true" primaryKey="true"/>
 <column name="fullname" type="VARCHAR" size="40" required="true"/>
</table>

Propel generara definiciones SQL que usan llaves foraneas nativas si el controlador de su base de datos lo soporta. Propel usará también información de llaves foraneas para generar métodos en sus clases objeto para traer objetos relacionados.

Trayendo Objetos Relacionados

Usando el ejemplo anterior (basado sobre el esquema provisto bookstore (tienda de libro)), tendrá un Book->getAuthor() que retornará un objeto Author usando la llave foranea específica.

$books = BookPeer::doSelect(new Criteria());
foreach($books as $book) {
 $author = $book->getAuthor();
}

El código anterior resultará en la ejecución de 2 estamentos SQL:

  1. SELECT * FROM book
  2. SELECT * FROM author WHERE author_id = $book->getAuthorId()

Aunque estos 2 métoodos trabajan claramente, no son óptimos -- especialmente si su base de datos posee un soporte nativo de llaves foraneas. Propel también genera metodos en su base de datos clase peer para traer informacién sobre book(libro) y author(autor) en una simple consulta.

$books = BookPeer::doSelectJoinAuthor(new Criteria());
foreach($books as $book) {
 $author = $book->getAuthor();
}

En el caso anterior sólo una simple consulta es mejorada en rendimiento:

  1. SELECT * FROM book INNER JOIN author ON author.author_id = book.author_id

Nota: En orden de limitar los métodos en un API público, ambos métodos son protejidos de la clase base peer; en orden de usarlos ambos usted debe crear un método público en su clase peer que invoque el método padre protejido.

class BookPeer {
 public function doSelectJoinAuthor(Criteria $c) {
  return parent::doSelectJoinAuthor($c);
 }

}

Relacioes muchos-a-muchos

Como se mencionó en el capítulo de introducción, El soporte de Propel de relaciones de muchos-a-muchos involucra un paso medio: definiendo la tabla de referencia en su modelo de datos, y usando resultados para optimizar.

Mire el siguiente ejemplo, una necesidad es relacionar libros con las personas que los leen -- muchas personas leyendo un solo libro, una persona leyendo muchos libros:

<table name="book_reader_ref">
 <column name="book_id" type="INTEGER" required="true" primaryKey="true"/>
 <column name="reader_id" type="INTEGER" required="true" primaryKey="true"/>
 <foreign-key foreignTable="book">
   <reference
     local="book_id"
     foreign="book_id"/>
 </foreign-key>
 <foreign-key foreignTable="reader">
   <reference
     local="reader_id"
     foreign="reader_id"/>
 </foreign-key>
</table>

En su script PHP necesitara hacer uso de la tabla de referencia "middleman" para recuperar las entidades relacionadas:

$books = BookPeer::doSelect(new Criteria());

// for every book get all readers
foreach($books as $book) {
 $readers = $book->getBookReaderRefsJoinReader();
}

El código anterior ejecutará 2 estamentos SQL:

  1. SELECT * FROM book
  2. SELECT * FROM book_reader_ref INNER JOIN reader ON reader.reader_id = book_reader_ref.reader_id WHERE book_reader_ref.book_id = $book->getBookId()

Mientras este método no es excesivamente gastado o utilizado -- como optimizando una simple selección para recuperar resultados unidos de muchos-a-muchos usualmente no tienen sentido -- pero es también menos elegante que el soporte para los unidos uno-a-uno. Requiriendo la referencia explícita de la tabla de referencia es una desventaja para usar el muy literal modelando de datos aproximados adoptado por Propel(heredado de Torque).

Borrado en cascada

Propel también soporta la eliminación en cascada, que puede ser especificado usando la opción onDelete="cascade" de la etiqueta <foreign-key>. También soportado por torque una opción onUpdate="cascade", pero desde entonces Propel no permite actualizaciones para incluir cambios para la llave primaria, actualmente este nunca es invocado -- y algunas bases de datos simplemente no soporta este tipo de acción . Propel proporciona una nueva emulación de eliminación en cascada que no soporta support este trigger (ej. MySQL).

<table name="review">
 <column name="review_id" type="INTEGER" primaryKey="true" required="true"/>
 <column name="reviewer" type="VARCHAR" size="50" required="true"/>
 <column name="book_id" required="true" type="INTEGER"/>
<foreign-key foreignTable="book" onDelete="CASCADE">
<reference local="book_id" foreign="book_id"/>
</foreign-key>
</table>

En el ejemplo anterior, las filas revisadas seran automaticamente removidas si la relacionada libro es eliminada.