簡體   English   中英

CakePHP ORM:使用非標准關聯對同一表進行連接的查詢

[英]CakePHP ORM : Query with a join on the same table with a non-standard association

我有一個非常特殊的用例,我找不到 ORM 的干凈解決方案。 我搜索了很多,也許我的數據庫 model 不正確,我不確定。

我使用 CakePHP 3.8.11。

所以,我有一個表“MaintenanceTypes”,其中包含 3 個重要字段:id、名稱和周期性。 周期性(以天為單位)表示“此維護將每(例如)30 天進行一次”。

周期性如 7(周)、30(月)、90(三個月)等。

我還有一個“Operations”表,它們是屬於“MaintenanceType”的小單元測試(字段是 id、name、maintenance_type_id)。

在這種情況下,特殊之處在於,作為業務規則,屬於具有 7 天周期的維護類型的操作被“包含”在具有更大周期的每個維護類型中; 這意味着每個三個月,您都應該執行與三個月直接相關的每個操作,應該執行與月、周等相關的每個操作。

在原始 SQL 中,這是微不足道的:slight_smile:

mt_ref是參考維護類型, mt_inc是包含的維護類型(具有較小的周期性),最后,每個操作都屬於找到的任何維護類型。

    SELECT mt_ref.id, mt_ref.name, mt_ref.periodicity, 
        mt_inc.name, mt_inc.periodicity, o.name 
    FROM maintenance_types mt_ref 
        LEFT JOIN maintenance_types mt_inc 
            ON (mt_inc.periodicity <= mt_ref.periodicity) 
        LEFT JOIN operations o ON (o.maintenance_type_id = mt_inc.id) 
    WHERE mt_ref.id = 3 

我試圖聲明MaintenanceTypes之間的關聯,但我找不到一種方法來聲明關聯是在周期性字段上完成的,而且,額外的點,不是在相等上而是在“小於或等於”上。

To add extra difficulties, I use this query for a (very good) JQuery Datatables CakePHP plugin ( https://github.com/allanmcarvalho/cakephp-datatables ), so I can't simply pass the raw SQL, and I must use ORM...

我希望這很清楚,並且有人可以幫助我解決這個問題!

非常感謝 !

如果您需要查詢構建器實例,那么這里幾乎有兩個或多或少簡單的選項,即使用具有自定義條件的關聯或手動連接。

自定義關聯條件

有了關聯,您可能會使用禁用的外鍵和自定義條件與MaintenanceTypes進行自我關聯,就像在MaintenanceTypesTable class 中一樣:

$this
    ->hasMany('MaintenanceTypesInc')
    ->setClassName('MaintenanceTypes')
    ->setForeignKey(false)
    ->setConditions(function (
        \Cake\Database\Expression\QueryExpression $exp,
        \Cake\ORM\Query $query
    ) {
        return $exp->lte(
            $query->identifier('MaintenanceTypesInc.periodicity'),
            $query->identifier('MaintenanceTypes.periodicity')
        );
    });

禁用外鍵將阻止 ORM 在加入關聯時創建默認的A.fk = B.fk條件。 需要注意的是,你不能包含一個禁用外鍵的hasMany關聯,你只能加入它! 您可以改用hasOne甚至belongsTo關聯,但這有點謊言,因為您在這里沒有 1:1 的關系(至少據我所知)。

另請注意,您不必對條件的所有表達式使用回調,您可以將條件作為key => value數組與手動實例化的標識符表達式或什至作為簡單字符串傳遞(但后者不會使用自動標識符引用時被識別):

->setConditions([
    'MaintenanceTypesInc.periodicity <=' =>
        new \Cake\Database\Expression\IdentifierExpression('MaintenanceTypes.periodicity');
]);
->setConditions('MaintenanceTypesInc.periodicity <= MaintenanceTypes.periodicity');

假設您在MaintenanceTypesTable class 中也有Operations關聯,您應該能夠通過查詢構建器*JoinWith()方法加入新關聯和Operations關聯,如下所示:

$query = $maintenanceTypesTable
    ->find()
    ->select([
        'MaintenanceTypes.id', 'MaintenanceTypes.name', 'MaintenanceTypes.periodicity',
        'MaintenanceTypesInc.name', 'MaintenanceTypesInc.periodicity',
        'Operations.name',
    ])
    ->leftJoinWith('MaintenanceTypesInc.Operations');

在結果中,關聯數據將放在_matchingData鍵下,即您可以像$entity->_matchingData->MaintenanceTypesInc$entity->_matchingData->Operations一樣獲取它。 如果您不希望這樣,那么您需要為關聯的字段使用別名,例如:

->select([
    'MaintenanceTypes.id', 'MaintenanceTypes.name', 'MaintenanceTypes.periodicity',
    'mt_inc_name' => 'MaintenanceTypesInc.name', 'mt_inc_periodicity' => 'MaintenanceTypesInc.periodicity',
    'op_name' => 'Operations.name',
])

如果您不想每次都 select 所有字段,請使用自定義查找器,如下面的手動連接示例中所示。

手動連接

使用手動連接為您提供了完全的自由,使用查詢構建器*Join()方法,您可以創建您喜歡的任何連接,並且您不必使用關聯的可能解決方法。

您可以將它們添加到自定義查找器中以獲得適當的可重用性,它在您的MaintenanceTypesTable class 中可能如下所示:

public function findWithIncludedMaintenanceTypes(\Cake\ORM\Query $query, array $options)
{
    return $query
        ->select(/* ... */)
        ->leftJoin(
            ['MaintenanceTypesInc' => 'maintenance_types'],
            function (
                \Cake\Database\Expression\QueryExpression $exp,
                \Cake\ORM\Query $query
            ) {
                return $exp->lte(
                    $query->identifier('MaintenanceTypesInc.periodicity'),
                    $query->identifier('MaintenanceTypes.periodicity')
                );
            }
        )
        ->leftJoin(
            ['Operations' => 'operations'],
            function (
                \Cake\Database\Expression\QueryExpression $exp,
                \Cake\ORM\Query $query
            ) {
                return $exp->equalFields(
                    'Operations.maintenance_type_id ',
                    'MaintenanceTypesInc.id'
                );
            }
        );
}

然后,您只需在需要的地方使用查找器,如下所示:

$query = $maintenanceTypesTable
    ->find('withIncludedMaintenanceTypes');

請注意,就像在關聯示例中一樣,您也可以將字符串或數組條件用於自定義連接。

也可以看看

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM