Как я могу свести к минимуму запросы «содержать» в CakePHP?

У меня есть три модели: Пользователи, Комментарии и Страницы.

Пользователи имеют много комментариев, и комментарии принадлежат страницам эм>.

Все модели используют поведение containable и по умолчанию recursive -1.

Если я вызову запрос find() для Comments с запросом contain, включающим поле модели Page, это правильно вернет результаты, используя один запрос, автоматическое присоединение таблицы Page к пользователю.

Если я вызову аналогичный запрос из модели User (содержащей Comment и Comment.Page), результатом будет запрос для получения Комментариев, за которым следует запрос для каждого комментария для получения соответствующей Страницы.

Есть ли способ настроить модели для поддержания оптимизации JOIN? Я предположил, что объявление ownTo в связанной модели (Комментарии) будет следовать в основной модели (Пользователи).

ОБНОВЛЕНИЕ

Я должен уточнить, что в моем вопросе использовалась упрощенная версия моего фактического тематического исследования. Хотя минимальное решение, которое мне требуется, будет включать эту начальную Model hasMany Model belongsTo Model структуры, я также ищу решение в одной или нескольких дополнительных belongsTo моделях вниз по цепочке (которые, как мне кажется, будут автоматически использовать ЛЕВЫЕ СОЕДИНЕНИЯ, поскольку это было бы возможно ).


person Rhys    schedule 24.10.2012    source источник


Ответы (5)


Хм, это интересно. Это своего рода оптимизация, которая должна быть реализована в ядре :)

В любом случае, я думаю, вы могли бы получить те же результаты (возможно, в другом формате), построив запрос немного по-другому:

$this->User->Comment->find('all', array(
  'conditions' => array(
    'Comment.user_id' => $userId
  ),
  'contain' => array(
    'User',
    'Page'
  )
));

При поиске в модели комментариев для объединения данных следует использовать два левых соединения, поскольку они оба являются отношениями 1:1. Примечание. Массив результатов может выглядеть немного иначе, чем при поиске в модели User.

person jeremyharris    schedule 24.10.2012
comment
Спасибо, Джереми Харрис. Это сработало бы, но мне нужно было уточнить свой вопрос выше, чтобы показать, что в реальном примере требуется больше уровней соединений принадлежности. Я буду помнить об этом как о возможности, хотя, если мне придется создавать свои запросы вручную, это сэкономит немного времени. - person Rhys; 25.10.2012

Итак, вы спрашиваете, есть ли более простой способ просто содержать все ваши запросы? Если вы хотите содержать все в текущем контроллере. Вы можете сделать это в обратном вызове beforeFilter(), и он будет применяться ко всем вашим запросам в этом контроллере.

person Colby Guyer    schedule 24.10.2012
comment
Привет, Колби. У вас есть такой пример на практике? Поскольку я думаю, что этот подход может помочь с одним из моих собственных проектов, а также с оригинальным плакатом. - person Martin Bean; 24.10.2012
comment
Я считаю, что что-то подобное в вашем контроллере будет содержать ваши запросы для всего контроллера. function beforeFilter() { parent::beforeFilter(); $this->Model->contain('RelatedModelStuff'); }//beforeFilter - person Colby Guyer; 24.10.2012
comment
Спасибо, Колби, это не совсем то, что мне нужно, но объявление beforeFilter contains — полезный фрагмент, ура. - person Rhys; 25.10.2012

Я не совсем уверен, понимаю ли я ваш вопрос, но я думаю, что у вас есть проблема с многочисленными sql-вызовами для связи Комментарий -> Страница? Если это правильно, то

  1. попробуйте связываемое поведение, которое уменьшает количество вызовов sql и работает почти так же, как и contains
  2. или, если это почти те же данные, которые вам нужны, создайте функцию в конкретной модели, из которой вы довольны вызовами hte sql (например, Comment-Model), и вызовите ее из пользовательской модели с помощью $this->Comment->myFindFct($params);

надеюсь, это поможет

РЕДАКТИРОВАТЬ: одна вещь, которая приходит мне на ум. Вы смогли изменить тип соединения в массиве ассоциаций на inner, что сделало торт единственным вызовом ассоциированной модели.

person harpax    schedule 24.10.2012

Я считаю, что хороший способ сделать это — создать ссылку метод пользовательского поиска.

Например, я бы создал метод внутри вашей модели пользователя, скажем, под названием _findUserComments(). Затем вы будете выполнять все соединения, содержащие и т. д. внутри этого метода. Затем в ваших контроллерах, где бы вам ни нужно было получить все комментарии вашего пользователя, вы бы назвали это так:

$this->User->find('UserComments', array(
    "conditions" => array(
        'User.id' => $userId
    )
));

Надеюсь, это поможет.

person Rob Forrest    schedule 31.10.2012
comment
Спасибо, Роб. Я довольно широко использую пользовательские методы поиска, они полезны, но это все равно не обойдет ситуацию создания чрезвычайно ручных версий запроса (объединений и т. д.) для достижения того, что я должен был понять, будет автоматически оптимизирован ядром. - person Rhys; 01.11.2012

Если определение модели, как показано ниже:

  1. Модель комментариев принадлежит Странице и Пользователю.
  2. Страница принадлежит Пользователю и имеет много комментариев.
  3. У пользователя много страниц и комментариев

приведенный ниже код вернет один объединенный запрос:

$this->loadModel('Comment');
$this->Comment->Behaviors->attach('Containable');
$queryResult = $this->Comment->find('all', array(
   'contain' => array(
       'User', 
       'Page'
    )
));

Приведенный ниже код вернет два запроса. Страница и пользователь объединены в один запрос, а все комментарии — в другой запрос.

$this->loadModel('Page');
$this->Page->Behaviors->attach('Containable');
$queryResult = $this->Page->find('all', array(
   'contain' => array(
        'User', 
    'Comment'
   )
));

а также приведенный ниже код вернет три запроса, по одному для каждой модели:

$this->loadModel('User');
$this->User->Behaviors->attach('Containable');
$queryResult = $this->User->find('all', array(
    'contain' => array(
        'Page', 
        'Comment'
    )
));
person Habibillah    schedule 03.11.2012
comment
Спасибо, хотя меня беспокоит структура модели, которая имеет несколько соединений в цепочке, а не модель, которая принадлежит двум разным моделям. Например. Возвращает один запрос, в котором Model1 принадлежит Model2, Model2 принадлежит Model3, а Model3 принадлежит Model4. Данные могут быть возвращены в одном запросе с использованием пользовательского оператора, но я понимаю, что ядро ​​​​в любом случае должно обрабатывать эту оптимизацию. - person Rhys; 04.11.2012
comment
Извините, если я не понимаю ваше заявление здесь. Но по вашему вопросу у пользователей много комментариев, а комментарии принадлежат страницам, эквивалентным моему определению модели выше (см. пункты 3 и 1). И результат запроса, если вы звоните из пользовательской модели, был оптимизирован, просто возвращая три запроса (а не повторно запрашивая страницу для каждого комментария). - person Habibillah; 04.11.2012
comment
Извините, я думаю, что мое обновление прояснило более подробное тематическое исследование (сцепленных ownTos), когда я понял, что решение исходного тематического исследования будет решаемо без фактического охвата контекста моего вопроса. - person Rhys; 04.11.2012