2017-01-01 8 views
0

Я пытаюсь повторить этот SQL результат запроса, который работает:Как изменить ассоциации моделей CakePHP 2 на глубокую связь с bindModel и пользовательским поиском?

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 

Я использую CakePHP 2 Модели с

  1. bindModel изменить Модель ассоциации на лету
  2. заказ Найти поставить логику в модели

Нет основного поля из нижней таблицы за второй уровень. Сообщение об ошибке: «Модель« Ddbroute »не связана с моделью« Доставка ». Поэтому я попробовал его с и без доставки в массиве «содержать», и ни один из способов не привел в поля «Поставка». Я был бы рад использовать соединения, если это необходимо. Я прочитал самые актуальные сообщения в StackOverflow, которые я смог найти.

Мой код с дополнительной информацией приведен ниже. Любая помощь с благодарностью получила.


У меня есть пять таблиц (в том числе в следующих областях):

ddbroutes (id, day) 
ddbrouteslots (id, ddbroute_id, delivery_id) 
deliveries (id, laststatusid) 
trackingstatuses (id, statusstage_id) 
statusstages (id, stage) 

Существуют следующие соотношения, установленные в моделях:

Ddbroute hasMany Ddbrouteslot (Ddbrouteslot belongsTo Ddbroute) 
Delivery hasOne Ddbrouteslot (Ddbrouteslot belongsTo Delivery) 
Delivery hasMany Trackingstatus (Trackingstatus belongsTo Delivery) 
Statusstage hasMany Trackingstatus (Trackingstatus belongsTo Statusstage) 

Хотя поставки hasOne Ddbrouteslot (и это будет hasMany - пересмотрено - теперь он остается как hasOne), для любого отдельного Ddbroute есть только одна связанная с доставкой, которая имеет каждый Ddbroutelot. Контейнер устанавливается во всех моделях. Я не знал, нужно ли сначала использовать unbindModel (это не изменило сообщение об ошибке).

Мой код в файле модели Ddbroute.php (только до стола поставки)

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; 
    } 

В другом контроллере, чтобы запустить операцию поиска:

$this->LoadModel('Ddbroute'); 
$ddbstatuses = $this->Ddbroute->find('ddbstatuses'); 
$this->set(compact('ddbstatuses')); // to make available in a view 

Я также была предпринята еще одна попытка с длинным массивом объединения, но запрос не привел к какой-либо информации о доставке, отслеживании или статусе, хотя запрос, похоже, запущен.

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; 
} 
+0

У меня теперь есть решение, но запрос значительно медленнее с использованием моделей CakePHP, чем SQL-решение (2.) ниже. Поле «Delivery.laststatusid» может ссылаться на «Trackingstatus.id», тогда ограничение «порядок» не понадобится. Я пробовал это с помощью unbindModel и bindModel, пытаясь использовать hasOne и принадлежать ассоциациям. Поскольку Delivery.id, первичный ключ, не использовался, мой синтаксис включал '' foreignKey '=> false,' conditions '=> array (' 'Delivery'.'laststatusid' =' Trackingstatus'.'id' ') '. Я получил сообщение об ошибке, что laststatusid был неизвестным столбцом. ** Любые идеи о том, как быть быстрее? ** – netuser1

ответ

0

После некоторой помощи, у меня теперь есть четыре решения, чтобы получить мои данные, хотя на самом деле три из них представляют собой варианты на первом. Я относительно неопытен, и были некоторые основные вещи, которые я не оценил.

1. в контроллере

$this->LoadModel("Ddbrouteslot"); 
$res = $this->Ddbrouteslot->find("all", array(
    "conditions" => array(
    "Ddbrouteslot.delivery_id > 0", 
    "Ddbrouteslot.ddbroute_id" => 45 
), 
"contain" => array(
    "Ddbroute", 
    "Delivery" => array(
"Trackingstatus" => array(
    "order" => array(
    "Trackingstatus.id" => "desc" 
    ), 
    "limit" => 1, 
    "Statusstage" 
    ) 
) 
) 
); 

тайминги из DebugKit: основной запрос был 20ms; Trackingstatus и Statusstage были дополнительными запросами в 18 мс каждый х 4 для четырех связанных поставок; общее время составляло 164 мс. Это довольно медленно, что не идеально.

Это началось со второй модели Ddbrouteslot, потому что это имело прямые отношения как с Ddbroute, так и с Delivery. Никаких изменений в какой-либо из ассоциаций не произошло. Отношения от Ddbrouteslot до Delivery работали нормально. Уже было hasMany отношения между Delivery и Trackingstatus на delivery_id.


2. Использование SQL

$this->LoadModel("Ddbroute"); 
$qres = $this->Ddbroute->query(
    "SELECT * 
    FROM 
    ddbroutes AS 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 
    WHERE s.delivery_id > 0 AND s.ddbroute_id = 45 
;" 
debug($qres); 

Часы работы: это заняло 19ms. Это означает, что это было намного быстрее. Это не рекомендуется в документации Cake, и, очевидно, она не настолько переносима между базами данных, как чистая находка Cake.


3. Изменение базовой модели

$rres = $this->Ddbroute->find("all", array(
    "conditions" => array(
    "Ddbroute.id" => 45 
), 
"recursive" => -1, 
"contain" => array(

     "Ddbrouteslot" => array(
      "conditions" => array(
       "Ddbrouteslot.delivery_id > 0" 
      ), 
      "Delivery" => array(
       "Trackingstatus" => array(
        "order" => array(
         "Trackingstatus.id" => "desc" 
        ), 
        "limit" => 1, 
        "Statusstage" 
       ) 
      ) 
     ) 
    ) 
)); 
debug($rres); 

Тайминги: Основной запрос был 18мс; Поставка, Trackingstatus и Statusstage составляли 18 мс каждый х 4 для четырех связанных поставок; общее время составляло 234 мс. Это было медленнее, потому что доставка требовалась для каждой отправки, потому что она не находилась в пределах модели Ddbroute. Изменение рекурсивности не помогло.


4. Использование пользовательского FIND Это был тот же запрос как 1.) выше, но только с помощью метода пользовательского поиска.

public $findMethods = array('ddbstatuses' => true); 
protected function _findDdbstatuses($state, $query, $results = array()) { 
    if ($state === 'before') {  
     $query['conditions'] = array(
      "Ddbrouteslot.delivery_id > 0", 
      "Ddbrouteslot.ddbroute_id" => 45 
     ); 
     $query['contain'] = array(
      "Ddbroute", 
      "Delivery"=> array(
      "Trackingstatus" => array(
       "order" => array(
       "Trackingstatus.id" => "desc" 
      ), 
      "limit" => 1, 
       "Statusstage" 
      ) 
      ) 
     ); 
     return $query; 
     } 
    return $results; 
}