【问题标题】:Pagination with Containable conditions work with hasOne, but not with hasMany包含条件的分页适用于 hasOne,但不适用于 hasMany
【发布时间】:2012-01-04 11:12:09
【问题描述】:

比如我有这样的关系:

UserContact hasMany Contact
Contact hasOne Info
Contact hasMany Response

而且我需要对联系人进行分页,所以我使用了 Containable:

$this->paginate = array(
            'limit'=>50,
            'page'=>$page,
            'conditions' =>array('Contact.id'=>$id),
            'contain'=>array(
                'Response',
                'Info'
                )
            );

我想通过 Info.name 和 Response.description 添加搜索。它非常适合 Info.name,但如果我尝试使用 Response.description 会引发错误,提示该列不存在。

另外,我尝试将关系更改为Contact hasOne Response,然后它正确过滤,但它只返回第一个响应,这不是正确的关系。

因此,例如,如果我有一个搜索键 $filter,我只想返回那些具有匹配 Info.name 或至少一个匹配的联系人匹配 Response.description

【问题讨论】:

    标签: cakephp cakephp-1.3 containable


    【解决方案1】:

    如果您查看 CakePHP 如何构建 SQL 查询,您会发现它在主查询中生成包含的“单一”关系(hasOnebelongsTo)作为连接子句,然后它为包含的“添加单独的查询”多个”关系。

    这使得通过单个关系进行过滤变得轻而易举,因为相关模型的表已经连接到主查询中。

    为了通过多重关系进行过滤,您必须创建一个子查询:

    // in contacts_controller.php:
    $conditionsSubQuery = array(
      'Response.contact_id = Contact.id',
      'Response.description LIKE' => '%'.$filter.'%'
    );
    $dbo = $this->Contact->getDataSource();
    $subQuery = $dbo->buildStatement(array(
        'fields' => array('Response.id'),
        'table' => $dbo->fullTableName($this->Contact->Response),
        'alias' => 'Response',
        'conditions' => $conditionsSubQuery
    ), $this->Contact->Response);
    $subQuery = ' EXISTS (' . $subQuery . ') ';
    
    $records = $this->paginate(array(
        'Contact.id' => $id,
        $dbo->expression($subQuery)
    ));
    

    但您应该只在需要按Response 字段过滤时生成子查询,否则您将过滤掉没有响应的联系人。

    PS。这段代码太大太丑,无法出现在控制器中。对于我的项目,我将其重构为 app_model.php,以便每个模型都可以生成自己的子查询:

    function makeSubQuery($wrap, $options) {
        if (!is_array($options))
            return trigger_error('$options is expected to be an array, instead it is:'.print_r($options, true), E_USER_WARNING);
        if (!is_string($wrap) || strstr($wrap, '%s') === FALSE)
            return trigger_error('$wrap is expected to be a string with a placeholder (%s) for the subquery. instead it is:'.print_r($wrap, true), E_USER_WARNING);
    
        $ds = $this->getDataSource();
    
        $subQuery_opts = array_merge(array(
            'fields' => array($this->alias.'.'.$this->primaryKey),        
            'table' => $ds->fullTableName($this),        
            'alias' => $this->alias,   
            'conditions' => array(),     
            'order' => null, 
            'limit' => null,
            'index' => null, 
            'group' => null
        ), $options);
    
        $subQuery_stm = $ds->buildStatement($subQuery_opts, $this);
        $subQuery = sprintf($wrap, $subQuery_stm);
        $subQuery_expr = $ds->expression($subQuery);
        return $subQuery_expr;
    }
    

    那么你控制器中的代码就变成了:

    $conditionsSubQuery = array(
        'Response.contact_id = Contact.id',
        'Response.description LIKE' => '%'.$filter.'%'
    );
    $records = $this->paginate(array(
        'Contact.id' => $id,
        $this->Contact->Response->makeSubQuery('EXISTS (%s)', array('conditions' => $conditionsSubQuery))
    ));
    

    【讨论】:

    • 看起来很棒!我肯定会在接下来的几天内(在赏金到期之前)试一试,并让你知道。
    • 原来我这次不会使用这段代码,因为我实际上需要同时搜索多个东西,所以删除没有响应的联系人会导致问题。尽管如此,答案是正确的(没有蛋糕的方式),并且提供的代码在另一种情况下肯定会派上用场,所以我会奖励你的赏金。谢谢!
    【解决方案2】:

    我现在不能尝试,但如果您对 Response 模型而不是 Contact 模型进行分页,应该可以工作。

    【讨论】:

    • 分页可以工作,但不会做我需要的。我需要对所有联系人进行分页(响应按联系人分组)。
    猜你喜欢
    • 2015-02-19
    • 2012-05-21
    • 2020-01-09
    • 2017-09-14
    • 2016-03-05
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多