I am trying to replicate this SQL query result which works:
SELECT r.id, r.day, s.ddbroute_id, s.delivery_id, d.id, d.laststatusid, t.id, t.delivery_id, t.statusstage_id, st.id, st.stage
FROM ddbroutes r
LEFT JOIN ddbrouteslots s on r.id = s.ddbroute_id
LEFT JOIN deliveries d on s.delivery_id = d.id
LEFT JOIN trackingstatuses t on d.laststatusid = t.id
LEFT JOIN statusstages st on t.statusstage_id = st.id
I am using CakePHP 2 Models with
- bindModel to change Model associations on the fly
- custom Find put the logic in a Model
There is no common field from the bottom table beyond the second level. The error message is: 'Model "Ddbroute" is not associated with model "Delivery".' I therefore tried it with and without Delivery in the 'contain' array, and neither way brought in the Delivery fields. I would be happy to use joins if appropriate. I have read most relevant posts on StackOverflow that I could find.
My code with further information is below. Any help gratefully received.
I have five tables (including the following fields):
ddbroutes (id, day)
ddbrouteslots (id, ddbroute_id, delivery_id)
deliveries (id, laststatusid)
trackingstatuses (id, statusstage_id)
statusstages (id, stage)
There are the following relationships set up in the Models:
Ddbroute hasMany Ddbrouteslot (Ddbrouteslot belongsTo Ddbroute)
Delivery hasOne Ddbrouteslot (Ddbrouteslot belongsTo Delivery)
Delivery hasMany Trackingstatus (Trackingstatus belongsTo Delivery)
Statusstage hasMany Trackingstatus (Trackingstatus belongsTo Statusstage)
Though Delivery hasOne Ddbrouteslot (and this will be hasMany - revised - this is now staying as hasOne), for any individual Ddbroute, there is only one Delivery associated which each Ddbrouteslot. Containable is set up in all the Models. I didn't know if I needed to use unbindModel first (it didn't change the error message).
My code in Ddbroute.php Model file (only as far as the Delivery table)
public $findMethods = array('ddbstatuses' => true);
protected function _findDdbstatuses($state, $query, $results = array()) {
if ($state === 'before') {
$ddbrouteslotmodel = ClassRegistry::init('Ddbrouteslot');
$ddbrouteslotmodel->unbindModel(
array('belongsTo' => array('Delivery'))
);
$ddbrouteslotmodel->bindModel(
array('hasOne' => array(
'Delivery' => array(
'className' => 'Delivery',
'foreignKey' => 'id',
'dependent' => false,
'fields' => array(
'id', 'laststatusid'
)
)
))
);
$deliverymodel = ClassRegistry::init('Delivery');
$deliverymodel->unbindModel(
array('hasOne' => array('Ddbrouteslot'))
);
$deliverymodel->bindModel(
array('belongsTo' => array(
'Delivery' => array(
'className' => 'Delivery',
'foreignKey' => 'delivery_id'
)
)
)
);
$query['contain'] = array(
'Ddbrouteslot', 'Delivery'
);
return $query;
}
return $results;
}
In another controller, to run the find operation:
$this->LoadModel('Ddbroute');
$ddbstatuses = $this->Ddbroute->find('ddbstatuses');
$this->set(compact('ddbstatuses')); // to make available in a view
I also had a further attempt with a long join array, but the query did not bring in any Delivery, Trackingstatus or Statusstage information, though the query seems to have run.
public $findMethods = array('ddbstatuses' => true);
protected function _findDdbstatuses($state, $query, $results = array()) {
if ($state === 'before') {
ClassRegistry::init('Delivery'); // not sure these three lines were needed so I tried with and without them
ClassRegistry::init('Trackingstatus');
ClassRegistry::init('Statusstage');
$query['joins'] = array(
array(
'table' => 'ddbrouteslots',
'alias' => 'Ddbrouteslot',
'type' => 'LEFT',
'conditions' => array(
'Ddbroute.id = Ddbrouteslot.ddbroute_id'
)),
array(
'table' => 'deliveries',
'alias' => 'Delivery',
'type' => 'LEFT',
'conditions' => array(
'Ddbrouteslot.id = Delivery.id'
)),
array(
'table' => 'trackingstatuses',
'alias' => 'Trackingstatus',
'type' => 'LEFT',
'conditions' => array(
'Delivery.laststatusid = Trackingstatus.id'
)),
array(
'table' => 'statusstages',
'alias' => 'Statusstage',
'type' => 'LEFT',
'conditions' => array(
'Trackingstatus.statusstage_id = Statusstage.id'
))
);
$query['contain'] = array(
'Ddbrouteslot',
'Delivery', // Not sure I should be adding these other models, so I tried with and without them
'Trackingstatus',
'Statusstage'
);
return $query;
}
return $results;
}