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']).
A condition on the related table
$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".
Saving a related record
$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.