Feb 07

Под “скрытыми” записями сегодня будут пониматься “unapproved”-записи в таблице. Кому лень ходить по сылкам: иногда не все записи какой-нибудь таблицы нужно показывать пользователю, например - если я не хочу показывать некоторые посты в блоге. Обычно для этого я делаю поле, например, is_hidden, а затем выбираю все записи, где is_hidden = 0. Проблема состоит в том, что я обычный человек и могу забыть поставить нужное мне условие. Поэтому я хочу получить какое-нибудь простое, но очень эффективное решение такой проблемы. В ActiveRecord этим решением является default_scope. А я вам сегодня расскажу, как этого добиться в Doctrine.

Итак, представим, что у нас есть табличка Post:

Post:
  actAs:
    Timestampable: ~
  columns:
    title:     { type: string(128), notnull: true }
    text:      { type: text, notnull: true }
    is_hidden: { type: boolean, notnull: true, default: 0 }

Самый простой, на первый взгляд, подход - переопределять PostTable::createQuery(), чтобы этот метод возвращал уже Query с нужным нам “WHERE is_hidden = 0″. К сожалению, не всегда это помогает. Например, при выборке постов для какой-либо категории через $category->Posts этот метод не сработает.

Есть гораздо более простой способ сделать то, что нам нужно - использовать listener-ы. В Doctrine есть довольно много событий, которые мы можем “слушать” и на которые мы можем реагировать. В данном случае нам подходит событие “preDqlSelect”, которое входит в группу “DQL Hooks“, и которое вызывается перед выполнением запроса на выборку записей. Как нам нужно прореагировать на событие: взять Doctrine_Query из Doctrine_Event и добавить в него дополнительные условия выборки.

Самый простой способ - переопределить метод preDqlSelect в самой записи:

class Post extends BasePost
{
  public function preDqlSelect(Doctrine_Event $event)
  {
    $params = $event->getParams();
    $event->getQuery()->addWhere("{$params['alias']}.is_hidden = 0");
  }
}

В первой строке метода мы получаем параметры запроса, из которых нам нужен alias - можете считать это обычным alias-ом таблицы из SQL (в данном случае это alias таблицы в DQL), т.е. при таком DQL:

FROM Post p

alias-ом будет “p”.

Во второй строке мы получаем текущую query и добавляем в неё условие “is_hidden = 0″.

Мы не можем использовать ->where(), т.к. этот метод сотрет все имеющиеся части WHERE в запросе. В то же время ->addWhere и ->andWhere ведут себя так же, как и ->where при отсутствии where-части запроса.

Собственно, вот и всё - тепер у нас будут выбираться только “видимые” посты.

Во второй части будет показано, как сделать созданный в этом посте код более реюзабельным, а также как сделать так, чтобы в backend-е посты показывались полностью.

written by fxposter \\ tags: ,


11 Responses to ““Скрытые” записи с Symfony и Doctrine”

  1. 1. CharnaD Says:

    А почему нельзя сделать hidden по умолчанию 1? То есть пока вручную 0 не выстыавить – все посты скрытые.

    Может я, конечно, не въехал в тему, сам юзаю Propel.

  2. 2. FX Poster Says:

    Не въехал. :) Тебе прийдется во всех запросах писать то, что тебе нужны только не-hidden посты. Мой подход позволяет сделать так, чтобы во все запросы эта часть WHERE добавлялась автоматически.

  3. 3. CharnaD Says:

    А допустим так
    postExecute() {
    foreach () {
    если hidden == 1 убрать из выборки
    }
    }

  4. 4. FX Poster Says:

    Изврат :)

  5. 5. CharnaD Says:

    Зато более прозрачно)

  6. 6. FX Poster Says:

    Да ладно! Может мы тогда всё так фильтровать будем? По принципу “выгреб всё из бд – фильтруем в PHP”? Это не “прозрачно”, это “очень плохо”. :)

  7. 7. ingvar Says:

    Чтобы не забывать, надо тестировать приложение :). Я пока юзаю Propel.
    FX Poster, можешь сказать чем Doctrine лучше Propel, а то никак руки не дойдут поюзать.

  8. 8. dmitriy Says:

    symfony 1.4.5
    preDqlSelect вообще не вызывается

  9. 9. fxposter Says:

    Ну дык когда это было-то :) Это работало с доктриной 1.0. Сейчас в симфони 1.4 стоит доктрина 1.2. что там изменилось – я не знаю.

  10. 10. dmitriy Says:

    самое интересное, что метод в Doctrine_Record есть, но он не вызывается. коллега вроде решил проблему через кастомный select, который можно установить через generator.yml, свойство table_method

  11. 11. johnny Says:

    чтобы работали dql коллбэки нужно в databases.yml добавить параметр:
    all:
    __doctrine:
    ____param:
    ______attributes:
    ________use_dql_callbacks: true

Leave a Reply