【问题标题】:CakePHP 3 is using primary key (setPrimaryKey) to do a JOIN even though I've specified a foreign keyCakePHP 3 使用主键 (setPrimaryKey) 进行 JOIN,即使我指定了外键
【发布时间】:2019-10-15 10:12:42
【问题描述】:

CakePHP 3.7

我有 2 个模型表类如下:

  1. /src/Model/Table/SubstancesTable.php
  2. /src/Model/Table/TblOrganisationSubstancesTable.php

MySQL中每张表的schema如下:

1.

mysql> describe substances;
+-------------+-----------------------+------+-----+---------+----------------+
| Field       | Type                  | Null | Key | Default | Extra          |
+-------------+-----------------------+------+-----+---------+----------------+
| id          | mediumint(8) unsigned | NO   | PRI | NULL    | auto_increment |
| app_id      | varchar(8)            | NO   | UNI | NULL    |                |
| name        | varchar(1500)         | NO   |     | NULL    |                |
| date        | date                  | NO   |     | NULL    |                |
+-------------+-----------------------+------+-----+---------+----------------+

2.

mysql> describe tbl_organisation_substances;
+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| o_sub_id    | int(255)     | NO   | PRI | NULL    | auto_increment |
| o_id        | int(255)     | NO   | MUL | NULL    |                |
| app_id      | varchar(15)  | YES  |     | NULL    |                |
| os_name     | varchar(255) | YES  |     | NULL    |                |
| ec          | varchar(35)  | YES  |     | NULL    |                |
| cas         | varchar(255) | YES  |     | NULL    |                |
| upload_id   | int(100)     | YES  |     | NULL    |                |
+-------------+--------------+------+-----+---------+----------------+

我编写了一个自定义查找器,它需要在这两个表之间执行JOIN。自定义查找器如下所示,位于SubstancesTable.php

public function findDistinctSubstancesByOrganisation(Query $query, array $options)
{
    $o_id = $options['o_id'];

    $query = $this->find()->select('id')->contain('TblOrganisationSubstances')->where(['TblOrganisationSubstances.o_id' => $o_id]);

    return $query;
}

每个表都有一个app_id 列,这是链接两个表的外键。

最初我遇到了一个错误:

没有在 Substances 上定义 TblOrganisationSubstances 关联。

这是有道理的,因为没有定义。

所以在SubstancesTable.php 我已经定义了这个:

$this->setPrimaryKey('id');

// ...

$this->belongsTo('TblOrganisationSubstances', [
    'foreignKey' => 'app_id',
    'joinType' => 'INNER'
]);

但这会产生以下 SQL 语句:

SELECT Substances.id AS `Substances__id` FROM substances Substances INNER JOIN tbl_organisation_substances TblOrganisationSubstances ON TblOrganisationSubstances.o_sub_id = (Substances.app_id) WHERE TblOrganisationSubstances.o_id = :c0

这是行不通的,因为TblOrganisationSubstances.o_sub_id = (Substances.app_id) 是错误的。它需要基于app_id JOIN,也就是说应该是:

TblOrganisationSubstances.app_id = (Substances.app_id)

我还尝试将 belongsTo 更改为 hasMany 关联(甚至不确定哪个是正确的!):

$this->hasMany('TblOrganisationSubstances', [
    'foreignKey' => 'app_id',
]);

但加入似乎并没有发生。生成的 SQL 是:

SELECT Substances.id AS `Substances__id` FROM substances Substances WHERE TblOrganisationSubstances.o_id = :c0

我还尝试在TblOrganisationSubstances.php 中添加belongsTo 来尝试定义双方的关系:

$this->belongsTo('Substances', [
    'foreignKey' => 'app_id',
    'joinType' => 'INNER'
]);

同样,这不起作用。它生成没有连接的 SQL。

请有人帮忙提供正确的关联类型(hasMany vs belongsTo)以及如何根据app_id(链接两个表的外键)执行连接。

【问题讨论】:

    标签: php cakephp cakephp-3.x


    【解决方案1】:

    BelongsTo 关联中,source 表保存外键,因此根据您的配置,它将在Substances 上使用app_id,并与TblOrganisationSubstances 的主键进行比较.

    为了得到你正在寻找的查询,你还必须定义bindingKey,即target表的外键,这样它就不会使用它的主键:

    $this->belongsTo('TblOrganisationSubstances', [
        'foreignKey' => 'app_id', // Substances.app_id
        'bindingKey' => 'app_id', // TblOrganisationSubstances.app_id
        'joinType' => 'INNER'
    ]);
    

    HasMany 还是BelongsTo 取决于您的数据,以及您想要检索它的内容和方式。一般如果一个Substances有0或1个TblOrganisationSubstances1:1),那么它就是BelongsToHasOne。如果一个Substances 可以有多个TblOrganisationSubstances (1:n),那么它就是HasMany

    如果你想通过HasMany关联过滤,那么你需要使用匹配或连接(matching()innerJoinWith()leftJoinWith()),因为HasMany关联的记录将在使用 contain() 时的单独查询。

    另见

    【讨论】:

    • 这是正确的,并且 +1 用于提及匹配,因为我稍后也会这样做。
    【解决方案2】:

    SELECT * FROM sample LEFT JOIN user ON sample.rac_id=user.userrac_id UNION ALL SELECT * FROM sample RIGHT JOIN user ON @987654328=.@98 user.userrac_id

    【讨论】:

    • 它的演示示例用于在此创建您自己的代码的帮助下清楚地了解内部连接的概念。有两个表示例和用户,我尝试在 sql 查询中应用 innerjoin
    • 这实际上与所提出的问题无关,并且不使用问题中给出的任何等效表格。
    【解决方案3】:
        public $hasAndBelongsToMany = array(
            'Ingredient' =>
                array(
                    'className' => 'Ingredient',
                    'joinTable' => 'ingredients_recipes',
                    'foreignKey' => 'recipe_id',
                    'associationForeignKey' => 'ingredient_id',
                    'unique' => true,
                    'conditions' => '',
                    'fields' => '',
                    'order' => '',
                    'limit' => '',
                    'offset' => '',
                    'finderQuery' => '',
                    'with' => ''
                )
        );
    }````
    

    【讨论】:

    • 这实际上与所提出的问题无关,并且不使用问题中给出的任何等效表格。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-09
    • 1970-01-01
    相关资源
    最近更新 更多