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

Posted

技术标签:

【中文标题】如何将 CakePHP 2 模型关联更改为与 bindModel 和自定义查找器的深度链接?【英文标题】:How to change CakePHP 2 Model associations to deep linking with bindModel and custom finder? 【发布时间】:2017-01-01 01:22:14 【问题描述】:

我正在尝试复制这个有效的 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

我正在使用 Cakephp 2 模型与

    bindModel 动态更改模型关联 自定义 Find 将逻辑放入模型中

第二级以外的底部表格中没有公共字段。 错误消息是:“模型“Ddbroute”与模型“交付”无关。因此,我尝试了在“包含”数组中使用和不使用 Delivery 的情况,但都没有引入 Delivery 字段。 如果合适的话,我很乐意使用连接。我已经阅读了 *** 上我能找到的最相关的帖子。

我的代码和更多信息如下。感谢您提供任何帮助。


我有五个表(包括以下字段):

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)

虽然 Delivery hasOne Ddbrouteslot(这将是 hasMany - 已修改 - 现在保持为 hasOne),但对于任何单独的 Ddbroute,只有一个 Delivery 关联到每个 Ddbrouteslot。可包含在所有模型中设置。我不知道我是否需要先使用 unbindModel (它没有改变错误信息)。

我在 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;
    

在另一个控制器中,运行查找操作:

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

我还对长连接数组进行了进一步尝试,但查询没有带来任何 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;

【问题讨论】:

我现在有一个解决方案,但是使用 CakePHP 模型的查询比下面的 SQL 解决方案 (2.) 慢得多。字段“Delivery.laststatusid”可以链接到“Trackingstatus.id”,则不需要“订单”约束。我尝试使用 unbindModel 和 bindModel,尝试 hasOne 和 belongsTo 关联。由于没有使用主键 Delivery.id,我的语法包括 'foreignKey' => false, 'conditions' => array(' Delivery.laststatusid` = Trackingstatus.id ' )`。我收到一条错误消息,指出 laststatusid 是未知列。 关于如何更快的任何想法? 【参考方案1】:

在一些帮助之后,我现在有四种解决方案来获取我的数据,尽管其中三个确实是第一个的变体。我比较缺乏经验,有些基本的东西我不欣赏。

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 的计时:主查询为 20 毫秒; Trackingstatus 和 Statusstage 是 18 毫秒 x 4 的附加查询,用于四个关联的交付;总时间为 164 毫秒。这很慢,并不理想。

这从第二个模型 Ddbrouteslot 开始,因为它与 Ddbroute 和 Delivery 都有直接关系。任何关联都没有变化。 从 Ddbrouteslot 到 Delivery 的 belongsTo 关系运行良好。 Delivery_id 上的 Delivery 和 Trackingstatus 之间已经存在 hasMany 关系。


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

时间:这需要 19 毫秒。这意味着它要快得多。在 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);

Timings:主查询为 18ms; Delivery、Trackingstatus 和 Statusstage 分别为 18 毫秒 x 4,用于四个关联的交付;总时间为 234 毫秒。它比较慢,因为 Delivery 需要为每次发送运行,因为它不在 Ddbroute 的模型中。 更改递归并没有什么不同。


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;
  

【讨论】:

以上是关于如何将 CakePHP 2 模型关联更改为与 bindModel 和自定义查找器的深度链接?的主要内容,如果未能解决你的问题,请参考以下文章

CakePHP 3 跨 2 个表保存关联数据

CakePHP - 如何在保存时将关联记录添加到模型中?

CakePHP 2.0:从模型或其控制器访问未关联的模型?

如何使用 cakephp 3 在不同数据库中的模型之间进行关联?

在 CakePHP 中保存时如何验证关联模型是不是存在

如何使用cakephp 3进行不同数据库中模型之间的关联?