You are viewing revision #7 of this wiki article.
This version may not be up to date with the latest version.
You may want to view the differences to the latest version.
Note: This article is under development.
In Yii 1.1, we sometimes got lost trying to search by a HAS_MANY relation using CActiveRecord or CActiveDataProvider. The wiki article Drills : Search by a HAS_MANY relation in Yii 1.1 was an effort to offer some practical techniques of searching by a HAS_MANY relation in Yii 1.1.
Now, we have Yii 2. Let's examine what can we do with the new ActiveRecord of Yii 2 to solve the same tasks of searching by a HAS_MANY relation.
Relation ¶
Two entities are sometimes connected with a relation of 1:N. Or we may say that 1:N is the only possible relation between 2 entities as long as we are in the RDB world. 1:1 relation is just a particular kind of 1:N where N is always assumed to be 1 at the maximum. And N:N relation can be considered as a combination of two 1:N relations.
Yii supports this 1:N relation in ActiveRecord as BELONGS_TO and HAS_ONE relations. 1:N relation seen from the side of N is HAS_ONE, and from the side of 1 it is HAS_MANY.
Now, let's construct an example of 1:N relation.
Example of HAS_MANY ¶
Think about blog posts and their authors. It's a relation of "An Author has many Posts" and "A Post has an Author" at the same time.
<?php
/**
* Author model
* @property integer $id
* @property string $name the author's name
...
*/
class Author extends \yii\db\ActiveRecord
{
...
/**
* @return \yii\db\ActiveQuery
*/
public function getPosts()
{
return $this->hasMany(Post::className(), ['author_id' => 'id']);
}
...
<?php
/**
* Post model
* @property integer $id
* @property integer $author_id FK to the author's id
* @property string $title the title of the post
...
*/
class Post extends \yii\db\ActiveRecord
{
...
/**
* @return \yii\db\ActiveQuery
*/
public function getAuthor()
{
return $this->hasOne(Author::className(), ['id' => 'author_id']);
}
...
We are going to solve the possible use cases of search regarding this example.
Task #1 ¶
Show all posts that has a word in post title
I want to start with an easy one. Let's retrieve all the posts that has a cirtain word in their title.
<?php
// get all the posts that has a keyword in their title
$posts = Post::find()
->where(['like', 'title', $searchword])
->all();
// display the results
foreach($posts as $post) {
echo "Title = {$post->title}\n";
}
Ah, it was too easy. And no relation is involved in this task.
OK, let's move on to the next.
Task #2 ¶
Show all posts with their authors that has a certain word in post title
Now we have to retrieve the authors' name, too.
<?php
// get all the posts that has a keyword in their title, with their authors
$posts = Post::find()
->where(['like', 'title', $searchword])
->with('author')
->all();
// display the results
foreach($posts as $post) {
echo "Title = {$post->title}\n";
echo "Author = {$post->author->name}\n";
}
Well, you may say "You don't have to do it. Just add a line to echo $post->author->name
to the code of the 1st task."
Yes, you are right. Post model has author
relation, so all you have to do to retrieve its Author is just $post->author. How convenient is it! It's so-called lazy loading approach.
But the lazy loading approach has a drawback in this case, because you have to execute one query for retrieving an entire array of Posts plus every one query per each Post for retrieving its author. It will end up in 1+N queries to be executed.
Here we are retrieveing Posts and their Authors at the same time by using yii\db\ActiveQuery::with(). It enables you to do the job with only 2 queries. It will get all of the related models at once just after retrieving the main models. This is so-called eager loading approach.
The eager loading is preferrable in this particular case, because it's more effective. But, you have to note, it is not always so.
Thank you
It was helpful
Column not found error
I am following Task #4-B (Show all authors who have a post that has a certain word in its title, with all of their relevant posts) and got following error:
Exception (Database Exception) 'yii\db\Exception' with message 'SQLSTATE[42S22]: Column not found: 1054 Unknown column 'post.title' in 'where clause' The SQL being executed was: SELECT tbl_author.* FROM tbl_author LEFT JOIN tbl_post ON tbl_author.id = tbl_post.author_id WHERE post.title like '%keyword%'
It seems the table alias is missing.
RE: Column not found error
dearken
Use 'tbl_post.title' instead of 'post.title' in 'where' clause, because your table name for Post model is not 'post' but 'tbl_post'.
As a side note, the handling of table names and their aliases are kind of 'quick and dirty' in this article, because I wanted to give more focus on the basic logic.
Yii2 active record, use limit with relation
Can I use limit with relation?
public function getMessage(){ return $this->hasMany(Message::className(), ['id'=>'msgid']) ->orderBy(['field'=>SORT_DESC]) ->limit(3); }
Using above code it returns nothing.
Thnx buddy good topic is really very helpful
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.