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)
-
<ul>
-
<li>
-
-
Origen:
-
-
Destino:</li>
-
</ul>
Primero, lo más importante es tener bien creado el sistema de Models.
Código Model (/models/Trip.php)
-
'className' => 'User',
-
'foreignKey' => 'user_id'
-
),
-
'Start' => array(
-
'className' => 'Place',
-
'foreignKey' => 'start_id'
-
),
-
'End' => array(
-
'className' => 'Place',
-
'foreignKey' => 'end_id'
-
),
-
'Transport' => array(
-
'className' => 'Transport',
-
'foreignKey' => 'transport_id'
-
)
-
);
-
-
}
-
?>
Código Model (/models/Place.php)
-
'className' => 'User',
-
'foreignKey' => 'user_id'
-
),
-
'Country' => array(
-
'className' => 'Country',
-
'foreignKey' => 'country_id'
-
),
-
'State' => array(
-
'className' => 'State',
-
'foreignKey' => 'state_id'
-
),
-
'City' => array(
-
'className' => 'City',
-
'foreignKey' => 'city_id'
-
)
-
);
-
var $hasMany = array(
-
'PlaceStart' => array(
-
'className' => 'trip',
-
'foreignKey' => 'start_id',
-
'dependent' => false
-
),
-
'PlaceEnd' => array(
-
'className' => 'trip',
-
'foreignKey' => 'end_id',
-
'dependent' => false
-
)
-
);
-
-
}
-
?>
Código Model (/models/State.php)
-
'className' => 'Country',
-
'foreignKey' => 'country_id',
-
'conditions' => '',
-
'fields' => '',
-
'order' => ''
-
)
-
);
-
-
var $hasMany = array(
-
'City' => array(
-
'className' => 'City',
-
'foreignKey' => 'city_id',
-
'dependent' => false
-
)
-
);
-
-
}
-
?>
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)
-
'Origen.Localidad',
-
'Origen.Provincia',
-
'Origen.Pais',
-
'Destino.Localidad',
-
'Destino.Provincia',
-
'Destino.Pais',
-
)
-
);
-
$this->Ruta->recursive = 2;
-
$this->set('rutas', $this->paginate());
-
}
-
}
-
?>
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.