表结构

article文章表:aritlceid,content,id(文章发表人的id)

图片描述

comment评论表:commentid,content,id(评论人的id),articleid(外键)

图片描述

reply回复表:replyid,content,id(回复人的id),commentid(外键)

图片描述

user用户表:id,name

图片描述

关联关系

基于yii 1.16
article->comment->reply,从左到右,两两之间是一对多关系,当然发过来,从右到左是多对一关系。
上面每个表与user之间的关系是多对一关系.
article.php

<?php
class Article extends CActiveRecord{
    ...
    public function tableName()
    {
        return 'article';
    }
    public function relations()
    {
        return array(
            'comments'=>array(self::HAS_MANY, 'comment', 'articleid'),
            'user'=>array(self::BELONGS_TO, 'user', 'id')
        );
    }
}

comment.php

<?php
class Comment extends CActiveRecord{
    ...
    public function tableName()
    {
        return 'comment';
    }
    public function relations()
    {
        return array(
            'article'=>array(self::BELONGS_TO, 'article', 'articleid'),
            'replys'=>array(self::HAS_MANY, 'reply', 'commentid'),
            'user'=>array(self::BELONGS_TO, 'user', 'id')
        );
    }
}

reply.php

<?php
class Reply extends CActiveRecord{
    ...
    public function tableName()
    {
        return 'reply';
    }
    public function relations()
    {
        return array(
            'user'=>array(self::BELONGS_TO, 'user', 'id'),
            'comment'=>array(self::BELONGS_TO,'comment','commentid')
        );
    }
}

user.php

<?php
class User extends CActiveRecord{
    ...
    public function tableName()
    {
        return 'user';
    }
}

查询

符合要求的article

所有字段

选取articleid=1的文章

$article=Article::model()->find(array(
    'condition'=>'articleid=:articleid',
    'params'=>array(':articleid'=>1)
));
foreach ($article as $key => $value) {
    var_dump($key);
    var_dump($value);
    echo "<br>";
}

图片描述

部分字段

$article=Article::model()->find(array(
    'select'=>'content',
    'condition'=>'articleid=:articleid',
    'params'=>array(':articleid'=>1)
));

图片描述

选取article及其对应的comment

这里有一对多关联,就不用懒加载了.

$articles=Article::model()->with(array('comments'))->findAll();

输出

        foreach ($articles as $article) {
            echo "<br>";
            var_dump($article->articleid);
            var_dump($article->id);
            var_dump($article->content);
            echo "<br>";
            foreach ($article->comments as $key => $value) {
                var_dump($value->commentid);
                var_dump($value->content);
                var_dump($value->id);
                echo "<br>";
            }
        }

图片描述
可以看到,active record确实选取了第一篇文章及其下面的5条评论,第二篇文章及其下面的2条评论,以及后面的三篇文章,它们下面没有评论。

  • findAll()方法返回一个列表,find()方法永远只返回一个结果。如果有多个结果,则只返回第一个。

  • with()方法一次性加载关联

加载article关联的user表

$articles=Article::model()->with(array('user','comments'))->findAll();

输出

        foreach ($articles as $article) {
            ...
            var_dump($article->user->name);
            echo "<br>";
        }

图片描述

加载comment关联的user表

$articles=Article::model()->with(array('user','comments','comments.user'))->findAll();

这时会出现Syntax error or access violation: 1066 Not unique table/alias: 'user',因为本屌懒,把所有多对一关联都设置成'user'=>array(self::BELONGS_TO, 'user', 'id').如果comment设置成'comment_user'=>array(self::BELONGS_TO, 'user', 'id'),查询变成

$articles=Article::model()->with(array('user','comments','comments.comment_user'))->findAll();

就不会报错了.
那么如果不改关联怎么办?

Article::model()->with(array(
    'user',
    'comments',
    'comments.user'=>array('alias'=>'comment_user')))
->findAll();

with()方法也可以像find()方法那样,传入关联数组作为参数。这里为comments.user设置一个别名就行了。

array(
    'alias'=>'article',
    ...
)

选取comment部分字段

前面选取article部分字段时,用的是findAll(array('select'=>'...')),这里不能

Article::model()->with(array(
    'user',
    'comments',
    'comments.user'=>array('alias'=>'comment_user')))
->findAll(array(
    'select'=>array('comments.content')
));

应该像上面为关联添加别名那样

Article::model()->with(
    array('user',
          'comments'=>array('select'=>'content'),
          'comments.user'=>array('alias'=>'comment_user')
))->findAll();

输出

        foreach ($articles as $article) {
            ...
            foreach ($article->comments as $key => $value) {
                ...
                var_dump($value->user->name);
                echo "<br>";
            }
        }

图片描述
可以看到,comment表的id字段没有值,content有值。
comment表的id字段可以用$value->user->id从关联的user那获得,但那是user对象里面的。

选取comment对应的reply

这下有两个一对多关系了

$articles=Article::model()->with(array(
    'user',
    'comments'=>array('select'=>'content'),
    'comments.user'=>array('alias'=>'comment_user'),
    'comments.replys'))
->findAll();

输出

        foreach ($articles as $article) {
            ...
            foreach ($article->comments as $key => $value) {
                ...
                foreach ($value->replys as $key => $value) {
                    var_dump($value->replyid);
                    var_dump($value->content);
                    var_dump($value->id);
                    echo "<br>";
                }
                echo "<br>";
            }
        }

图片描述
可以看到,只有第1,2条评论有回复。
事实上,如果不选取comment部分字段的话,可以直接

$articles=Article::model()->with(array(
    'user',
    'comments.user'=>array('alias'=>'comment_user'),
    'comments.replys'))
->findAll();

加载reply关联的user表

和前面comment关联user一样

Article::model()->with(array(
    'user',
    'comments'=>array('select'=>'content'),
    'comments.user'=>array('alias'=>'comment_user'),
    'comments.replys',
    'comments.replys.user'=>array('alias'=>'reply_user')))
->findAll();

选取reply部分字段

Article::model()->with(array(
    'user',
    'comments'=>array('select'=>'content'),
    'comments.user'=>array('alias'=>'comment_user'),
    'comments.replys'=>array('select'=>'content'),
    'comments.replys.user'=>array('alias'=>'reply_user')))
->findAll();

条件查询

选取articleid=1的文章以及其评论,还有对应的回复

Article::model()->with(array(
    'user',
    'comments.user'=>array('alias'=>'comment_user'),
    'comments.replys',
    'comments.replys.user'=>array('alias'=>'reply_user')))
->findAll(array(
    'condition'=>'articleid=:articleid',
    'params'=>array(':articleid'=>1),
    'alias'=>'article'
));

这时会报错: Integrity constraint violation: 1052 Column 'articleid' in where clause is ambiguous.还是命名冲突.在findAll()方法的参数里设置article别名,条件列前加上设置好的别名就行了。

Article::model()->with(array(
    'user',
    'comments.user'=>array('alias'=>'comment_user'),
    'comments.replys',
    'comments.replys.user'=>array('alias'=>'reply_user')))
->findAll(array(
    'condition'=>'article.articleid=:articleid',
    'params'=>array(':articleid'=>1),
    'alias'=>'article'
));

图片描述

选取articleid=1且commentid=1的文章以及其评论,还有对应的回复

Article::model()->with(array(
    'user',
    'comments.user'=>array('alias'=>'comment_user'),
    'comments.replys',
    'comments.replys.user'=>array('alias'=>'reply_user')))
->findAll(array(
    'condition'=>'article.articleid=:articleid and comments.commentid=:commentid',
    'params'=>array(':articleid'=>1,':commentid'=>1),
    'alias'=>'article'
));

注意,条件是comments.commentid=:commentid

comment,reply分页

mybatis一样,只能通过复杂的自定义sql实现,参见本屌的mybatis Result Maps对结果分组3--一对多使用limit.写完那纠结的sql后,调用Article::model()->findBySql($sql,$params)
至于最后结果能否映射到对象上,或者说映射到对象上对选取的列的字段名有什么要求,本屌没试过,不知道。


以后如果有什么新的发现,会不定期更新此文。


TheViper
465 声望16 粉丝