【问题标题】:What´s the right way to do deep notMatching() association in CakePHP 3在 CakePHP 3 中做深度 notMatching() 关联的正确方法是什么
【发布时间】:2020-11-03 02:23:04
【问题描述】:

当用户的 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"

Titles 表格内容

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() 关联时,就会出现问题。

【问题讨论】:

    标签: mysql cakephp orm associations cakephp-3.x


    【解决方案1】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-08-29
      • 1970-01-01
      • 2017-08-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-19
      相关资源
      最近更新 更多