首页 > 解决方案 > 在 CakePHP 3 中进行深度 notMatching() 关联的正确方法是什么

问题描述

当用户的 id 为 2(或其他数字)时,我试图获取在 UsersTitles 表中没有相关记录的游戏,但我得到了所有游戏。

控制器:

$this->loadModel('Games');

$games = $this->Games->find()->notMatching('Titles.TitlesUsers', function ($q){
  return $q->where(['TitlesUsers.user_id'=> 2]);
})->distinct(['Games.id']);

游戏桌

class GamesTable {    
  $this->hasMany('Titles', [
      'foreignKey' => 'game_id',
      'dependent' => true
  ]);
}

标题表

class TitlesTable {      
  $this->belongsTo('Games', ['dependent' => false]);

  $this->hasMany('TitlesUsers', [
    'classname' => 'TitlesUsers',
    'foreignKey' => 'title_id',
    'dependent' => true
  ]);    
}

TitlesUsers 表

class TitlesUsersTable {

  $this->belongsTo('Titles',['dependent'=>false]);

  $this->belongsTo('Users',['dependent'=>false]);

}

游戏表内容

id | name
---|---------
 1 | "Test A"
 2 | "Test B"
 3 | "Test C"

标题表内容

id | game_id
---|---------
 1 |       1
 2 |       1
 3 |       1
 4 |       2
 5 |       2
 6 |       2
 7 |       3
 8 |       3
 9 |       3

TitlesUsers 表内容

id | title_id | user_id
---|----------|---------
 1 |        1 |       2

生成的 SQL

SELECT 
  Games.id AS `Games__id`, 
  Games.name AS `Games__name`
FROM 
  games Games 
  LEFT JOIN titles Titles ON Games.id = (Titles.game_id) 
  LEFT JOIN titles_users TitlesUsers ON (
    TitlesUsers.user_id = 2 
    AND Titles.id = (TitlesUsers.title_id)
  ) 
WHERE 
  (TitlesUsers.id) IS NULL 
GROUP BY 
  Games.id

如果我对直接相关的表执行 notMatching() ,它可以正常工作。当我试图获得深层 notMatching() 关联时,就会出现问题。

标签: mysqlcakephpormassociationscakephp-3.x

解决方案


这是预期的结果,在您的设置中,每个游戏组有 3 个标题,并且只有一个标题分配了用户,这意味着用户将是所有其他标题NULL,例如,Test A剩下的2 个标题(TitlesUsers.id) IS NULL将为 true .

您的非分组结果如下所示:

| Games.id | Games.name | Titles.id | Titles.game_id | TitlesUsers.id | TitlesUsers.title_id | TitlesUsers.user_id |
| -------- | ---------- | --------- | -------------- | -------------- | -------------------- | ------------------- |
| 1        | Test A     | 1         | 1              | 1              | 1                    | 2                   |
| 1        | Test A     | 2         | 1              | NULL           | NULL                 | NULL                |
| 1        | Test A     | 3         | 1              | NULL           | NULL                 | NULL                |
| 2        | Test B     | 4         | 2              | NULL           | NULL                 | NULL                |
| 2        | Test B     | 5         | 2              | NULL           | NULL                 | NULL                |
| 2        | Test B     | 6         | 2              | NULL           | NULL                 | NULL                |
| 3        | Test C     | 7         | 3              | NULL           | NULL                 | NULL                |
| 3        | Test C     | 8         | 3              | NULL           | NULL                 | NULL                |
| 3        | Test C     | 9         | 3              | NULL           | NULL                 | NULL                |

可以看出,每个游戏组至少有一排TitlesUsers.idNULL

您要么需要重新考虑您的数据库架构和关联,或者如果您需要您的架构是这种方式,请更改您过滤事物的方式,因为notMatching()它不适合它,例如通过测试排除您不想要的游戏至少存在来自特定用户的一个标题的游戏:

$excludedGamesQuery = $this->Games
    ->find()
    ->select(['Games.id']);
    ->matching('Titles.TitlesUsers', function ($q){
        return $q->where(['TitlesUsers.user_id' => 2]);
    })
    ->group(['Games.id']);

$gamesQuery = $this->Games
    ->find()
    ->where([
        'Games.id NOT IN' => $excludedGamesQuery
    ]);

推荐阅读