Yii Реляционная запись с условием из другой таблицы - PullRequest
7 голосов
/ 30 декабря 2011

У меня есть реляционная запись, подобная следующей:

  • Есть пользователи (пользователи таблицы) с идентификаторами
  • Существуют категории (категории таблиц) с идентификатором, именем
  • Есть статьи (таблицы статей) с id, body, category_id
  • Тогда есть таблица read_articles с article_id, user_id

Таким образом, пользователь может видеть список всех статей в категории. Они могут щелкнуть статью, и она добавит запись (user_id, article_id) в таблицу read_articles.

Я хочу иметь возможность использовать ActiveRecord от Yii, чтобы получать все статьи из данной категории, которые не были прочитаны текущим пользователем, и отображать их. Я немного думал об этом с точки зрения SQL, но я не уверен, как вписать его в мои настройки ActiveRecord. Моей первой идеей была параметризованная область действия активной записи Статьи:

public function unread( $userId )
{
    $this->getDbCriteria()->mergeWith( array(
        'alias' => 'articles',
        'condition' => 'not exists (SELECT * '
            . 'FROM user_read_articles '
            . 'WHERE articles.id=user_read_articles.article_id '
            . 'AND read_articles.user_id=' . (int)$userId
            . ')',
    ) );
}

Кажется, это работает, но мне это кажется очень грязным.

Есть ли более чистый способ сделать то, о чем я говорю в ActiveRecord? Или мне стоит подумать о переходе к более простому SQL и самому обрабатывать подобные вещи (но потерять много приятных возможностей AR)?

Редактировать Вот отношения для вышеупомянутых моделей: (Отказ от ответственности, они усечены только для соответствующих частей)

UserActiveRecord

public function relations()
{
    return array(
        'categories' => array(
            self::HAS_MANY,
            'UserCategoryActiveRecord',
            'user_id' ),
        // I added this early using gii, never really used it...
        'readArticles' => array(
            self::HAS_MANY,
            'UserReadArticleActiveRecord',
            'user_id' ),
    );
}

UserCategoryActiveRecord

public function relations()
{
    return array(
        'user' => array(
            self::BELONGS_TO,
            'UserActiveRecord',
            'user_id' ),
        'articles' => array(
            self::HAS_MANY,
            'ArticleActiveRecord',
            'category_id',
            'order' => 'articles.pub_date DESC' ),
    );
}

ArticleActiveRecord

public function relations()
{
    return array(
        'category' => array(
            self::BELONGS_TO,
            'CategoryActiveRecord',
            'category_id' ),
    );
}

Я также создал «UserReadArticleActiveRecord» еще при создании, но я никогда не использовал эту модель, и она почти пуста.

Честно говоря, я думал о том, чтобы в любом случае перейти на Symfony2 и Doctrine и просто убрать активную запись. Я знаю, что это не решает эту проблему, но я мог бы просто отбросить это и оставить свое текущее решение на время.

1 Ответ

4 голосов
/ 16 января 2012

Чтобы получить статьи read , вы можете определить отношение MANY_MANY для вашей модели пользователя, как описано здесь:

http://www.yiiframework.com/doc/guide/1.1/en/database.arr

Не думаю, что вам нужна прокси-модель UserReadArticles, вам просто нужно правильно определить отношения.

Чтобы получить непрочитанных статей, я также испытываю затруднения, думая о способе Yii сделать это, но более эффективный способ, чем использование запроса на дополнительный выбор, состоит в том, чтобы выполнить LEFT JOIN для user_read_articles и проверьте, не равен ли столбец из user_read_articles значения NULL (не было соответствующей строки):

$this->getDbCriteria()->mergeWith( array(
    'alias' => 'articles',
    'join' => 'LEFT JOIN user_read_articles ON articles.id=user_read_articles.article_id',
    'condition' => 'user_read_articles.article_id IS NULL',
) );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...