cakephp - 具有 Containable 条件的分页适用于 hasOne,但不适用于 hasMany

标签 cakephp cakephp-1.3 containable

例如,我有这样的关系:

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 如何构建 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。这段代码太大而且丑陋,无法出现在 Controller 中。对于我的项目,我将其重构为 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;
}

然后 Controller 中的代码变为:

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

关于cakephp - 具有 Containable 条件的分页适用于 hasOne,但不适用于 hasMany,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8725867/

相关文章:

mysql - CakePhp 包含太多查询

cakephp - 在 CakePHP 中向 Containable 添加条件

mysql - cakePHP - 加入条件虚拟字段

php - CakePhp 中的严格标准错误?

php - 使用 CakePHP 1.3 将图像上传到服务器

cakephp - 重置 auth 组件加载的用户数据

CakePHP 包含不适用于递归

cakephp - 对 hasMany 关系中的相关数据进行分页

security - 将 Cakephp2 身份验证迁移到 Cakephp 3

php - CakePHP 1.3 使用 CONCAT 多个字段查找条件接下来一小时的记录