简体   繁体   English

如何将CakePHP 2模型关联更改为与bindModel和自定义查找器进行深层链接?

[英]How to change CakePHP 2 Model associations to deep linking with bindModel and custom finder?

I am trying to replicate this SQL query result which works: 我正在尝试复制此有效的SQL查询结果:

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 我正在使用CakePHP 2模型

  1. bindModel to change Model associations on the fly bindModel可以动态更改模型关联
  2. 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".' 错误消息是:'模型“ Ddbroute”未与模型”交付”关联。 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. 我已经阅读了有关StackOverflow的大多数相关文章。

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. 尽管Delivery hasOne Ddbrouteslot(并且将为hasMany-已修改-现在仍为hasOne),但对于任何单个Ddbroute,每个Ddbrouteslot都只有一个与之关联的Delivery。 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). 我不知道是否需要先使用unbindModel(它没有更改错误消息)。

My code in Ddbroute.php Model file (only as far as the Delivery table) 我在Ddbroute.php模型文件中的代码(仅在Delivery表中)

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: 在另一个控制器中,运行find操作:

$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. 我还尝试了使用长连接数组的进一步尝试 ,但是该查询似乎没有运行,但是该查询没有引入任何Delivery,Trackingstatus或Statusstage信息。

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

After some help, I now have four solutions to get my data, though really three of them are variants on the first one. 经过一番帮助,我现在有四个解决方案来获取我的数据,尽管实际上其中三个是第一个的变体。 I am relatively inexperienced and there were some basic things I didn't appreciate. 我相对缺乏经验,有些基本的东西我不喜欢。

1. IN A CONTROLLER 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"
   )
  )
 )
);

Timings from DebugKit: main query was 20ms; DebugKit的时间:主查询为20毫秒; Trackingstatus and Statusstage were additional queries of 18ms each x 4 for four associated deliveries; Trackingstatus和Statusstage是四个关联交付的额外查询,每个查询18ms x 4。 total time was 164ms. 总时间为164毫秒。 This is quite slow which is not ideal. 这相当慢,这是不理想的。

This started from the second model, Ddbrouteslot, because this had direct relationships with both Ddbroute and Delivery. 这是从第二个模型Ddbrouteslot开始的,因为它与Ddbroute和Delivery都有直接关系。 There were no changes to any of the associations. 任何关联都没有更改。 The belongsTo relationship from Ddbrouteslot to Delivery worked fine. 从Ddbrouteslot到Delivery的belongsTo关系工作正常。 There was already a hasMany relationship between Delivery and Trackingstatus on delivery_id. delivery_id上的Delivery和Trackingstatus之间已经存在hasMany关系。


2. USING SQL 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);

Timings: this took 19ms. 时间:这花了19ms。 This means it was much faster. 这意味着它要快得多。 This is not recommended in the Cake documentation, and clearly it is not as portable between databases as a pure Cake find. Cake文档中不建议这样做,显然,它在数据库之间的可移植性不如纯粹的Cake查找。


3. CHANGING THE BASE MODEL 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);

Timings: Main query was 18ms; 时间:主要查询时间是18毫秒; Delivery, Trackingstatus and Statusstage were 18ms each x 4 for four associated deliveries; 四个关联交付的交付,跟踪状态和Statusstage均为18ms x 4。 total time was 234ms. 总时间为234毫秒。 It was slower because Delivery needed to be run for each despatch because it was not within the model of Ddbroute. 速度较慢,因为需要为每个发货运行交付,因为它不在Ddbroute模型中。 Changing recursive didn't make a difference. 更改递归没有什么不同。


4. USING A CUSTOM FIND This was the same query as 1.) above, but just with a custom find method. 4.使用自定义查找这个查询与上面的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;
}  

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM