Нумерация страниц с помощью Containable условий работает с hasOne, но не с hasMany - PullRequest
2 голосов
/ 04 января 2012

Например, у меня есть эти отношения:

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 .

Ответы [ 2 ]

3 голосов
/ 09 января 2012

Если вы посмотрите, как CakePHP создает SQL-запросы, вы увидите, что он генерирует содержащиеся «одиночные» отношения (hasOne и belongsTo) в качестве предложений объединения в основном запросе, а затем добавляет отдельные запросы для содержащихся « несколько "отношений.

Это упрощает фильтрацию по одной взаимосвязи, поскольку таблица связанной модели уже включена в основной запрос.

Чтобы выполнить фильтрацию по нескольким отношениям, вам нужно создать подзапрос:

// 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))
));
0 голосов
/ 07 января 2012

Я не могу попробовать это сейчас, но должно работать, если вы разбиваете на страницы модель ответа вместо модели контакта.

...