Cómo trabajar con datos de tablas relacionales multinivel en CakePHP

CakePHP es increible a la hora de trabajar con queries complejas en MySQL.

Estos dos últimos días he estado tratando de encontrar la manera de obtener en una solo Array toda la información relevante de registros en una tabla con varias relaciones de profundidad. En StackOverflow me dieron la solución, realmente sencilla, usando recursive y el Behaviour Containable.

Pongamos que tenemos la tabla Rutas. Ésta a su vez tiene dos foreignKey a la tabla Localizaciones, una como Origen de la ruta y otra como Destino de la ruta. A su vez, cada Localizacion tiene tres foreignKeys: Pais, Provincia y Localidad.

Rutas
|- id
|- nombre
|- id_origen (Localizacion)
|- id_destino (Localizacion)

Localizaciones
|- id
|- direccion
|- id_localidad
|- id_provincia
|- id_pais

Localidad
|- id
|- id_provincia
|- nombre

Provincia
|- id
|- id_pais
|- nombre

Pais
|- id
|- nombre

Hay muchas maneras de representar una dirección postal en tablas relacionales, pero esta es la que he escogido.

Queremos representar en una sola tupla toda la información relevante, haciendo una sola query a la base de datos. El objetivo es obtener esto:

Array
(
    [Trip] => Array
        (
            [id] => 6
            [Start] => Array
                (
                    [id] => 2
                    [user_id] => 4
                    [Country] => Array
                        (
                            [id] => 1
                            [name] = Spain
                        )
                    [State] => Array
                        (
                            [id] => 1
                            [name] = Barcelona
                        )
                    [City] => Array
                        (
                            [id] => 1
                            [name] = La Floresta
                        )
                    [address] => Carrer Pere Planes, 69
                )
            [End] =>  Array
                (
                    [id] => 5
                    [user_id] => 4
                    [Country] => Array
                        (
                            [id] => 1
                            [name] = Spain
                        )
                    [State] => Array
                        (
                            [id] => 1
                            [name] = Madrid
                        )
                    [City] => Array
                        (
                            [id] => 1
                            [name] = Torrelodones
                        )
                    [address] => Calle Gardenias, 14
                )
        )
)

Para poder representar la lista de Rutas en PHP de una forma sencilla:

Código View (/views/rutas/index.ctp)

  1. <ul>
  2.  <li>
  3.  
  4. Origen:  
  5.  
  6. Destino:</li>
  7. </ul>

Primero, lo más importante es tener bien creado el sistema de Models.

Código Model (/models/Trip.php)

array(
  1.                 'className' =&gt; 'User',
  2.                 'foreignKey' =&gt; 'user_id'
  3.             ),
  4.             'Start' =&gt; array(
  5.                 'className' =&gt; 'Place',
  6.                 'foreignKey' =&gt; 'start_id'
  7.             ),
  8.             'End' =&gt; array(
  9.                 'className' =&gt; 'Place',
  10.                 'foreignKey' =&gt; 'end_id'
  11.             ),
  12.             'Transport' =&gt; array(
  13.                 'className' =&gt; 'Transport',
  14.                 'foreignKey' =&gt; 'transport_id'
  15.             )
  16.         );
  17.  
  18.      }
  19. ?&gt;

Código Model (/models/Place.php)

array(
  1.             'className' =&gt; 'User',
  2.             'foreignKey' =&gt; 'user_id'
  3.         ),
  4.         'Country' =&gt; array(
  5.             'className' =&gt; 'Country',
  6.             'foreignKey' =&gt; 'country_id'
  7.         ),
  8.         'State' =&gt; array(
  9.             'className' =&gt; 'State',
  10.             'foreignKey' =&gt; 'state_id'
  11.         ),
  12.         'City' =&gt; array(
  13.             'className' =&gt; 'City',
  14.             'foreignKey' =&gt; 'city_id'
  15.         )
  16.     );
  17.     var $hasMany = array(
  18.         'PlaceStart' =&gt; array(
  19.             'className' =&gt; 'trip',
  20.             'foreignKey' =&gt; 'start_id',
  21.             'dependent' =&gt; false
  22.         ),
  23.         'PlaceEnd' =&gt; array(
  24.             'className' =&gt; 'trip',
  25.             'foreignKey' =&gt; 'end_id',
  26.             'dependent' =&gt; false
  27.         )
  28.     );
  29.  
  30. }
  31. ?&gt;

Código Model (/models/State.php)

array(
  1.             'className' =&gt; 'Country',
  2.             'foreignKey' =&gt; 'country_id',
  3.             'conditions' =&gt; '',
  4.             'fields' =&gt; '',
  5.             'order' =&gt; ''
  6.         )
  7.     );
  8.  
  9.     var $hasMany = array(
  10.         'City' =&gt; array(
  11.             'className' =&gt; 'City',
  12.             'foreignKey' =&gt; 'city_id',
  13.             'dependent' =&gt; false
  14.         )
  15.     );
  16.  
  17. }
  18. ?&gt;

Los Models de Pais (Country) y Localidad (City) son muy similares al de Provincia (State).

Después simplemente hay que usar la propiedad recursive para especificar cuántos niveles de profundidad de las relaciones quieres obtener en el resultado, y el Behavior Containable para especificar qué quieres incluir en los resultados (hay que añadir la linea $actsAs = array('Containable'); en el modelo Ruta/Trip).

Código Controller (/controllers/rutas_controller.php)

Ruta->contain( array(
  1.     'Origen.Localidad',
  2.     'Origen.Provincia',
  3.     'Origen.Pais',
  4.     'Destino.Localidad',
  5.     'Destino.Provincia',
  6.     'Destino.Pais',
  7.    )
  8.   );
  9.   $this-&gt;Ruta-&gt;recursive = 2;
  10.   $this-&gt;set('rutas', $this-&gt;paginate());
  11.  }
  12. }
  13. ?&gt;

Este código tan simple dará el Array deseado mencionado al principio.

Nota: perdón si los nombres no cuadran porque están en inglés. StackOverflow es una comunidad increible de programadores que se ayudan, pero para hay que escribir en inglés si quieres que te entiendan!

Trip -> Ruta
Start -> Origen
End -> Destino
Place -> Localizacion
Address -> Direccion
City -> Localidad
State -> Provincia
Country -> Pais

Espero que sea de ayuda.

Esta entrada fue publicada en CakePHP y etiquetada , , , , , , , , , , . Guarda el enlace permanente.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

*

Puedes usar las siguientes etiquetas y atributos HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>