【问题标题】:How do I filter deep associations in CakePHP如何在 CakePHP 中过滤深层关联
【发布时间】:2025-12-24 09:40:11
【问题描述】:

我有以下表格:活页夹、文档、用户、docs_users。 Doc belongsTo Binder,Doc hasAndBelongsToMany User。

我想为当前登录的用户获取活页夹及其相关文档(docs_users 表中的相关 user_id)。

我已经尝试使用 Containable 和 find('all') 连接、条件等,但我不知道如何删除来自未在 docs_users 表中关联的用户的文档。

此代码不起作用:

$binders = $this->Binder->find( 
        'all',                  
        array( 
            'joins' => array(
                array( 
                    'table' => 'binders_users', 
                    'alias' => 'BindersUser', 
                    'type' => 'inner', 
                    'foreignKey' => false, 
                    'conditions'=> array(
                        'BindersUser.binder_id = Binder.id',
                        'BindersUser.user_id = ' . $this->Auth->user('id')
                    )
                ),
                array( 
                    'table' => 'docs', 
                    'alias' => 'Doc', 
                    'type' => 'left', 
                    'foreignKey' => false, 
                    'conditions'=> array(
                        'Doc.binder_id = Binder.id',
                    )
                ),                          
                array( 
                    'table' => 'docs_users', 
                    'alias' => 'DocsUser', 
                    'type' => 'left', 
                    'foreignKey' => false, 
                    'conditions'=> array(
                        'DocsUser.doc_id = Doc.id',
                        'DocsUser.user_id = ' . $this->Auth->user('id')
                    )
                )           
            ),
            'recursive'=>0
        )
    );
$this->set('binders', $binders);

这也不是:

$this->Binder->recursive = 2;
$this->Binder->Behaviors->attach('Containable');
$this->Binder->contain(array(
    'Branch',
    'Doc' => array(                     
        'User' => array(
            'DocsUser' => array(
                'conditions' => array('id = "17"')
            )
        )
    )
));
$binders = $this->Binder->find('all');

经验丰富的专业人士的任何帮助都会很棒!谢谢!

替代/简化解决方案?

如果我只想获取用户有权访问的活页夹,这很有效。短而甜。但是,它仍然会发送所有相关的文档,这不是我想要的行为。它只需要传递用户有权访问的文档(如前所述)。

$binders = $this->Binder->find( 
    'all',                  
    array( 
        'joins' => array(
            array( 
                'table' => 'binders_users', 
                'alias' => 'BindersUser', 
                'type' => 'inner', 
                'foreignKey' => false, 
                'conditions'=> array(
                    'BindersUser.binder_id = Binder.id',
                    'BindersUser.user_id = ' . $this->Auth->user('id')
                )
            )
        )
    )
);

【问题讨论】:

  • 如果您将debug 设置为 2 并查看生成的查询蛋糕可能会有所帮助。然后进行所需的更改以获得所需的结果。
  • 另外,您能否将您的数据库架构粘贴到 pastebin 或其他东西上并链接它。
  • 好主意。这里是:pastebin.com/0bRn9USD 我现在没有使用 ACL 表,因为我认为在记录级别这样做会创建一个庞大的数据库。有点违背了目的。
  • 如果@Leo 的方法不适合你,为什么不把它分成两个查询来得到你想要的确切输出呢?我发现在使用 Cake 和 $this->find() 时,我往往会忘记这样一个事实,即最终都是 SQL,而且你总是可以分解问题而不是在一个查询中解决所有问题 :)跨度>
  • JohnP - 你是对的。我只是不知道如何在 CakePHP 的框架内完成这个。还是有点绿。

标签: cakephp cakephp-1.3


【解决方案1】:

【讨论】:

  • 这些链接很棒。绝对让我走到这一步。但我不确定他们是否真的回答了这个问题。我认为一些相关的代码在这里真的会有所帮助。
【解决方案2】:

这是我根据收到的所有优秀反馈提出的最终解决方案。我认为这是一个优雅的解决方案,可以在需要深度关联的任何场景中重复使用。

在 binder_controller 中,我解除了 Doc 模型的绑定,并使用 finderQuery 将其绑定回来,以仅选择用户有权查看的 Docs。然后加入 binders_users 表,仅选择用户有权访问的活页夹。

感谢大家的帮助!

$this->Binder->unbindModel(array('hasMany' => array('Doc')));
$this->Binder->bindModel(
    array('hasMany' => array(
            'Doc' => array(
                'className' => 'Doc',
                'foreignKey' => 'binder_id',
                'dependent' => false,
                'finderQuery' => 'SELECT Doc.* FROM docs AS Doc INNER JOIN docs_users AS DocsUser ON DocsUser.doc_id = Doc.id AND DocsUser.user_id = ' . $this->Auth->user('id')
            )
        )
    )
);

$binders = $this->Binder->find( 
    'all',                  
    array( 
        'joins' => array(
            array( 
                'table' => 'binders_users', 
                'alias' => 'BindersUser', 
                'type' => 'inner', 
                'foreignKey' => false, 
                'conditions'=> array(
                    'BindersUser.binder_id = Binder.id',
                    'BindersUser.user_id = ' . $this->Auth->user('id')
                )
            )               
        )
    )
);

更多关于binding/unbinding models

【讨论】:

    【解决方案3】:

    在这一行,你需要告诉 Cake 你说的是哪个 Model 的 id:

    'conditions' => array('id = "17"')
    

    例如DocsUser.id

    ...并且您不使用可包含的递归。摆脱它。

    【讨论】:

    • 其实就是由数组树指定的。如果我只是希望它是一个级别,你是对的,我可以指定 Doc.User.Docsuser.id = 17 但仍然存在同样的问题。
    【解决方案4】:

    您是否尝试过从用户的角度进入?

    $this->Binder->Doc->User->Behaviors->attach('Containable');
    $this->Binder->Doc->User->contain(array('Doc'=>'Binder'));
    $user = $this->Binder->Doc->User->find('all',array('conditions'=>'User.id'=>$user_id));
    

    无论如何都应该检测到“Doc​​sUser”关联。

    也许从 Binder 的角度来看

    在您的 Binders MODEL 中添加(请检查表、键和模型名称,以防我打错字)

    function getBindersByUserSql($user_id)
        {       
            $dbo = $this->getDataSource();
            $subQuery = $dbo->buildStatement(
                array(
                        'fields' => array('DISTINCT(Doc.binder_id)'),
                        'table' => "docs_users",                                       
                        'joins' => array(
                                    array('table' => 'users',
                                        'alias' => 'User',
                                        'type' => 'INNER',
                                        'conditions' => array('DocsUser.user_id = User.id')
                                    ),
                                    array('table' => 'docs',
                                        'alias' => 'Doc',
                                        'type' => 'INNER',
                                        'conditions' => array('Doc.id = DocsUser.doc_id')
                                    )
                ),
                        'alias'=>"DocsUser",                                            
                        'conditions' => array("User.id"=>$user_id),
                        'order' => null,
                        'group' => "Doc.binder_id"
                        ),
                        $this
                        );
            return $dbo->expression($subQuery);
        }
    

    然后在你的活页夹中试试 CONTROLLER

    $this->Binder->Behaviors->attach('Containable');
    $this->Binder->contain(array('Doc'));
    $conditions = array();
    $conditions = $this->Binder->getBindersByUserSql($this->Auth->user('id'));
    $binders = $this->Binder->find('all',array('conditions'=>$conditions)));
    

    有什么好处吗?

    【讨论】:

    • 这绝对是在正确的轨道上。我想问题是你如何在 Binder 的上下文中做到这一点?
    • 是的。有用。必须进行一些更改(字段名称),但它可以工作。不要看嘴里的礼物马,但您能否给我一些提示,告诉我如何显示用户有权访问的所有活页夹,以及用户有权访问活页夹的文档。非常感谢你的帮助。超级有启发性!
    • 权限如何表示?