Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Relations between models

Tables are linked to each other: a post has an author, a post has many comments. A relation in Yii is a model method that explains how to find the related rows. After that they are accessed like an ordinary property.

Preparation

A second table is needed. Create the migration php yii migrate/create create_comment_table with the columns id, post_id (an integer) and text (text), run it and create the model:

// models/Comment.php
namespace app\models;
 
use yii\db\ActiveRecord;
 
class Comment extends ActiveRecord
{
}

The tableName() method is not needed here: the table name comment equals the class name.

One to many: hasMany

A relation is described in the model class by a method whose name starts with get. Add to models/Post.php:

// models/Post.php
public function getComments()
{
    return $this->hasMany(Comment::class, ['post_id' => 'id']);
}

In the array the left side is a column of the related table (comment.post_id), the right side a column of the current one (post.id). A relation is accessed as a property, without get and without parentheses:

$post = Post::findOne(1);
 
foreach ($post->comments as $comment) {
    echo $comment->text;
}

The code prints the texts of all comments of the first post. The query to the comment table runs on first access to the property.

The inverse relation: hasOne

A comment has one post. Add to models/Comment.php:

// models/Comment.php
public function getPost()
{
    return $this->hasOne(Post::class, ['id' => 'post_id']);
}

Now $comment->post->title returns the post title. The author is described in Post the same way: hasOne(User::class, ['id' => 'user_id']).

Eager loading

A list of a hundred posts that prints the number of comments for each one runs a hundred and one queries: one for the posts and one per post. The with() method loads the relation with a single extra query:

// controllers/PostController.php
$posts = Post::find()->with('comments')->all();

There will be two queries for any number of posts. Several relations are listed in an array: with(['comments', 'author']).

$posts = Post::find()
    ->joinWith('comments')
    ->where(['like', 'comment.text', 'thanks'])
    ->all();

joinWith() joins the tables in one query, so the condition can use comment columns. The result is the posts that have a comment with the word "thanks".

$comment = new Comment();
$comment->text = 'Great post';
$comment->link('post', $post);

The link() method writes the post number into post_id and saves the comment. The first argument is the relation name.

Common mistake

$post->comments and $post->getComments() are not the same. The property returns a ready array of objects, and the method returns a query that can be refined: $post->getComments()->orderBy(['id' => SORT_DESC])->limit(5)->all(). Looping over the method result without all() gives no comments.

Thanks for reading!

Donate to the site — we work hard for you.