Revision #20 has been created by softark on Sep 29, 2018, 11:53:29 PM with the memo:
Changed a tab to 4 spaces
[...]
* @property integer $name Author's name
...
*/
class Author extends CActiveRecord
{
...
public function relations()
{
{
return array(
'posts' => array(self::HAS_MANY, 'Post', 'author_id');
);
}
);
}
...
```[...]
class Post extends CActiveRecord
{
...
public function relations()
{
{
return array(
'author' => array(self::BELONGS_TO, 'Author', 'author_id');
);
}
);
}
...
```[...]
public static function GetPostsByTitle($searchWord)
{
// query criteria
$criteria = new CDbCriteria();
// compare title
$criteria->compare('title', $searchWord, true);
// find
$posts = Post::model()->findAll($criteria);
// show
foreach($posts as $post)
{
{
echo "Title = " . $post->title . "\n";
}
}
```[...]
public static function GetPostsWithAuthorByTitle($searchWord)
{
// query criteria
$criteria = new CDbCriteria();
// with Author model
$criteria->with = array('author');
// compare title
$criteria->compare('t.title', $searchWord, true);
// find all posts
$posts = Post::model()->findAll($criteria);
// show all posts
foreach($posts as $post)
{
{
echo "Title = " . $post->title . "\n";
echo "Author = " . $post->author->name . "\n";
}
}
```[...]
```php
...
// compare Author's name
$criteria->compare('author.name', $searchName, true);
...
```[...]
public static function GetAuthorsByPostTitle($searchWord)
{
// query criteria
$criteria = new CDbCriteria();
// with Post model
$criteria->with = array('posts');
// compare title
$criteria->compare('posts.title', $searchWord, true);
// find all authors
$authors = Author::model()->findAll($criteria);
// show all authors
foreach($authors as $author)
{
{
echo "Author = " . $author->name . "\n";
}
}
```[...]
public static function GetAuthorsWithPostsByPostTitle($searchWord)
{
// query criteria
$criteria = new CDbCriteria();
// with Post model
$criteria->with = array('posts');
// compare title
$criteria->compare('posts.title', $searchWord, true);
// find all authors
$authors = Author::model()->findAll($criteria);
// show all authors and his/her posts
foreach($authors as $author)
{
{
echo "Author = " . $author->name . "\n";
foreach($author->posts as $post)
{
{
echo "Post = " . $post->title . "\n";
}
}
}
}
```[...]
public static function GetAuthorsWithPostsByPostTitle($searchWord)
{
// query criteria
$criteria = new CDbCriteria();
// join Post model (without selecting)
$criteria->with = array(
'posts' => array(
'select' => false,
),
);
),
);
// compare title
$criteria->compare('posts.title', $searchWord, true);
// find all authors
$authors = Author::model()->findAll($criteria);
// show all authors and his/her posts
foreach($authors as $author)
{
{
echo "Author = " . $author->name . "\n";
foreach($author->posts as $post)
{
{
echo "Post = " . $post->title . "\n";
}
}
}
}
```[...]
public static function GetTop5AuthorsWithPostsByPostTitle($searchWord)
{
// query criteria
$criteria = new CDbCriteria();
// with Post model
$criteria->with = array('posts');
// compare title
$criteria->compare('posts.title', $searchWord, true);
// order by author name
$criteria->order = 't.name ASC';
// limit to 5 authors
$criteria->limit = 5;
// find all authors
$authors = Author::model()->findAll($criteria);
// show all authors and his/her posts
foreach($authors as $author)
{
{
echo "Author = " . $author->name . "\n";
foreach($author->posts as $post)
{
{
echo "Post = " . $post->title . "\n";
}
}
}
}
```[...]
```php
...
// force to join Post
$criteria->with = array(
'posts' => array(
'together' => true,
),
);
),
);
...
```[...]
```php
...
// force to join Post (without selecting)
$criteria->with = array(
'posts' => array(
'together' => true,
'select' => false,
),
);
),
);
...
```[...]
- Author = Andy
- Post = Don't use **foo**
- Post = Use yoo for **foo**
- Post = Don't use bar
- Post = Use yar for bar[...]
```php
...
// force to join Post (without selecting)
$criteria->with = array(
'posts' => array(
'together' => true,
'select' => false,
),
);
),
);
...
// group by Author's id
$criteria->group = 't.id';
...
```[...]
public static function GetTop5AuthorsWithPostsByPostTitle($searchWord)
{
// query criteria
$criteria = new CDbCriteria();
// force to join Post (without selecting)
$criteria->with = array(
'posts' => array(
'together' => true,
'select' => false,
),
);
),
);
// compare title
$criteria->compare('posts.title', $searchWord, true);
// group by Author's id
$criteria->group = 't.id';
// order by author name
$criteria->order = 't.name ASC';
// limit to 5 authors
$criteria->limit = 5;
// find all authors
$authors = Author::model()->findAll($criteria);
// show all authors and his/her posts
foreach($authors as $author)
{
{
echo "Author = " . $author->name . "\n";
foreach($author->posts as $post)
{
echo "Post = " . $post->title . "\n";
}
}
}
```
> Info|Notice : Unfortunately, this trick seems to work only with MySQL, which has an extended implementation of GROUP BY.
Now, here is the last task.
## Task #6
**Show top 5 authors in the order of name with his/her relevant posts who has at least one post that has a certain word in post title**
Probably the filtering in lazy loading should be the only answer. I cant't think of another solution.
### Example of Answer
```php
public static function GetTop5AuthorsWithPostsByPostTitle($searchWord)
{
// query criteria
$criteria = new CDbCriteria();
// force to join Post (without selecting)
$criteria->with = array(
'posts' => array(
'together' => true,
'select' => false,
),
);
// compare title
$criteria->compare('posts.title', $searchWord, true);
// group by Author's id
$criteria->group = 't.id';
// order by author name
$criteria->order = 't.name ASC';
// limit to 5 authors
$criteria->limit = 5;
// find all authors
$authors = Author::model()->findAll($criteria);
// show all authors and his/her posts
foreach($authors as $author)
{
echo "Author = " . $author->name . "\n";
// lazy loading posts with filtering
$filteredPosts = $author->posts(
array(
'condition' => 'title LIKE :search_word',
'params' => array(
':search_word' => '%' . $searchWord . '%',
),
)
);
foreach($filteredPosts as $post)
{
echo "Post = " . $post->title . "\n";
}
{
echo "Post = " . $post->title . "\n";
}
}
}
```
> Info|Notice : Unfortunately, this trick seems to work only with MySQL, which has an extended implementation of GROUP BY.
Now, here is the last task.
## Task #6
**Show top 5 authors in the order of name with his/her relevant posts who has at least one post that has a certain word in post title**
Probably the filtering in lazy loading should be the only answer. I cant't think of another solution.
### Example of Answer
```php
public static function GetTop5AuthorsWithPostsByPostTitle($searchWord)
{
// query criteria
$criteria = new CDbCriteria();
// force to join Post (without selecting)
$criteria->with = array(
'posts' => array(
'together' => true,
'select' => false,
),
);
// compare title
$criteria->compare('posts.title', $searchWord, true);
// group by Author's id
$criteria->group = 't.id';
// order by author name
$criteria->order = 't.name ASC';
// limit to 5 authors
$criteria->limit = 5;
// find all authors
$authors = Author::model()->findAll($criteria);
// show all authors and his/her posts
foreach($authors as $author)
{
echo "Author = " . $author->name . "\n";
// lazy loading posts with filtering
$filteredPosts = $author->posts(
array(
'condition' => 'title LIKE :search_word',
'params' => array(
':search_word' => '%' . $searchWord . '%',
),
)
);
foreach($filteredPosts as $post)
{
echo "Post = " . $post->title . "\n";
}
}
}
```[...]
public function relations()
{
return array(
'posts' => array(self::HAS_MANY, 'Post', 'author_id'),
'posts_search' => array(self::HAS_MANY, 'Post', 'author_id'),
);
}
public static function GetAuthorsWithPostsByPostTitle($searchWord)
{
// query criteria
$criteria = new CDbCriteria();
// join Post model (one for fetching data, the other for filtering)
$criteria->with = array(
'posts' => array( // this is for fetching data
'together' => false,
),
'posts_search' => array( // this is for filtering
'select' => false,
'together' => true,
),
);
// compare title
$criteria->compare('posts_search.title', $searchWord, true);
// find all authors with his/her posts
$authors = Author::model()->findAll($criteria);
// show all authors and his/her posts
foreach($authors as $author)
{
echo "Author = " . $author->name . "\n";
foreach($author->posts as $post) // no queries executed here !!
{
echo "Post = " . $post->title . "\n";
}
}
}
```
The difference between the original answer and the optimized one lies in the **performance**.
There were **1 + N** queries executed in the original answer, because every `$author->posts` would trigger a query to fetch the posts.
But there are only **2** queries here. There's no query executed for each `$author->posts`, because all the posts have been fetched in `findAll`. Yes, findAll executes 2 queries: one for the relations with 'together', and the other for those without 'together'. (For filtering in the 2nd query, Yii will use the primary keys fetched in the 1st query in `IN` condition.)
Likewise, the answers to the task #5 and #6 could be optimized like the following:
### Optimized Answer to Task #5
```php
public static function GetTop5AuthorsWithPostsByPostTitle($searchWord)
{
// query criteria
$criteria = new CDbCriteria();
// join Post model (one for fetching data, the other for filtering)
$criteria->with = array(
'posts' => array( // this is for fetching data
'together' => false,
),
'posts_search' => array( // this is for filtering
'select' => false,
'together' => true,
),
);
// compare title
$criteria->compare('posts_search.title', $searchWord, true);
// group by Author's id
$criteria->group = 't.id';
// order by author name
$criteria->order = 't.name ASC';
// limit to 5 authors
$criteria->limit = 5;
// find all authors with his/her posts
$authors = Author::model()->findAll($criteria);
// show all authors and his/her posts
foreach($authors as $author)
{
echo "Author = " . $author->name . "\n";
foreach($author->posts as $post) // no queries executed here !!
{
echo "Post = " . $post->title . "\n";
}
}
}
```
### Optimized Answer to Task #6
```php
public static function GetTop5AuthorsWithPostsByPostTitle($searchWord)
{
// query criteria
$criteria = new CDbCriteria();
// join Post model (one for fetching data, the other for filtering)
$criteria->with = array(
'posts' => array( // this is for fetching data
'together' => false,
'condition' => 'posts.title LIKE :search_word',
'params' => array(
':search_word' => '%' . $searchWord . '%',
),
),
'posts_search' => array( // this is for filtering
'select' => false,
'together' => true,
),
);
// compare title
$criteria->compare('posts.title', $searchWord, true);
// group by Author's id
$criteria->group = 't.id';
// order by author name
$criteria->order = 't.name ASC';
// limit to 5 authors
$criteria->limit = 5;
// find all authors
$authors = Author::model()->findAll($criteria);
// show all authors and his/her posts
foreach($authors as $author)
{
echo "Author = " . $author->name . "\n";
foreach($author->posts as $post) // no queries executed here !!
{
echo "Post = " . $post->title . "\n";
}
}
}
```
return array(
'posts' => array(self::HAS_MANY, 'Post', 'author_id'),
'posts_search' => array(self::HAS_MANY, 'Post', 'author_id'),
);
}
public static function GetAuthorsWithPostsByPostTitle($searchWord)
{
// query criteria
$criteria = new CDbCriteria();
// join Post model (one for fetching data, the other for filtering)
$criteria->with = array(
'posts' => array( // this is for fetching data
'together' => false,
),
'posts_search' => array( // this is for filtering
'select' => false,
'together' => true,
),
);
// compare title
$criteria->compare('posts_search.title', $searchWord, true);
// find all authors with his/her posts
$authors = Author::model()->findAll($criteria);
// show all authors and his/her posts
foreach($authors as $author)
{
echo "Author = " . $author->name . "\n";
foreach($author->posts as $post) // no queries executed here !!
{
echo "Post = " . $post->title . "\n";
}
}
}
```
The difference between the original answer and the optimized one lies in the **performance**.
There were **1 + N** queries executed in the original answer, because every `$author->posts` would trigger a query to fetch the posts.
But there are only **2** queries here. There's no query executed for each `$author->posts`, because all the posts have been fetched in `findAll`. Yes, findAll executes 2 queries: one for the relations with 'together', and the other for those without 'together'. (For filtering in the 2nd query, Yii will use the primary keys fetched in the 1st query in `IN` condition.)
Likewise, the answers to the task #5 and #6 could be optimized like the following:
### Optimized Answer to Task #5
```php
public static function GetTop5AuthorsWithPostsByPostTitle($searchWord)
{
// query criteria
$criteria = new CDbCriteria();
// join Post model (one for fetching data, the other for filtering)
$criteria->with = array(
'posts' => array( // this is for fetching data
'together' => false,
),
'posts_search' => array( // this is for filtering
'select' => false,
'together' => true,
),
);
// compare title
$criteria->compare('posts_search.title', $searchWord, true);
// group by Author's id
$criteria->group = 't.id';
// order by author name
$criteria->order = 't.name ASC';
// limit to 5 authors
$criteria->limit = 5;
// find all authors with his/her posts
$authors = Author::model()->findAll($criteria);
// show all authors and his/her posts
foreach($authors as $author)
{
echo "Author = " . $author->name . "\n";
foreach($author->posts as $post) // no queries executed here !!
{
echo "Post = " . $post->title . "\n";
}
}
}
```
### Optimized Answer to Task #6
```php
public static function GetTop5AuthorsWithPostsByPostTitle($searchWord)
{
// query criteria
$criteria = new CDbCriteria();
// join Post model (one for fetching data, the other for filtering)
$criteria->with = array(
'posts' => array( // this is for fetching data
'together' => false,
'condition' => 'posts.title LIKE :search_word',
'params' => array(
':search_word' => '%' . $searchWord . '%',
),
),
'posts_search' => array( // this is for filtering
'select' => false,
'together' => true,
),
);
// compare title
$criteria->compare('posts.title', $searchWord, true);
// group by Author's id
$criteria->group = 't.id';
// order by author name
$criteria->order = 't.name ASC';
// limit to 5 authors
$criteria->limit = 5;
// find all authors
$authors = Author::model()->findAll($criteria);
// show all authors and his/her posts
foreach($authors as $author)
{
echo "Author = " . $author->name . "\n";
foreach($author->posts as $post) // no queries executed here !!
{
echo "Post = " . $post->title . "\n";
}
}
}
```