Thursday, November 10, 2011

Get related objects by primary key in a HAS_MANY relationship in Yii

Lets say we have a relationship – a Mall HAS_MANY Shop.
We want to use CActiveRecord to return an array of Shops, while also access each shop individually by it’s id, like
//get Mall id 28
$myMall = Mall::model()->findByPk(28);
//get preferrred shop of Mall 28
$preferredShop = $myMall->shops[$preferredShopId];

To achieve this, in the Mall (CActiveRecord) model, we declare this
public function relations()
{
  return array(
    'shops' => array(self::HAS_MANY, 'Shop', 'mall_id', 'index'=>'id'),
  )
}
'index'=>'id' in above means “use column id as the index of the Shop class while accessing the ‘shops‘ relation“.

Usage example in a many-to-many relationship

This method is also useful in the case of “many-to-many” relationship.
Say a Book is written by many Authors, an Author writes many Books. Thus, a third database table stores the relationship, namely author_book. Lets also store the role (author or co-author) in the relationship.
CREATE TABLE author_book (author_id INT, book_id INT, role VARCHAR(128));
We want to access the role via CActiveRecord. Lets create a AuthorBook CActiveRecord model.
class AuthorBook extends CActiveRecord
{
  public static function model($className=__CLASS__)
  {
    return parent::model($className);
  }
 
  public function primaryKey()
  {
    return array('author_id','book_id');
  }
 
  public function tableName()
  {
    return 'author_book';
  }
}
In the Book CActiveRecord model, declare relations.
public function relations()
{
  return array(
    'authors' => array(self::MANY_MANY, 'Author', 
        'author_book(author_id, book_id)', 'index'=>'id'),
    'authorbook' => array(self::HAS_MANY, 'AuthorBook', 
        'book_id', 'index'=>'author_id'),
  )
}
By using 'index'=>'id' in the ‘authors’ relations, we can do this
//echo the author name of this book, given the author's ID
echo $myBook->authors[$authorId]->name;
While, using 'index'=>'author_id' in the ‘authorbook’ relations, we can do
//echo the author's role in this book, given the author's ID
echo $myBook->authorbook[$authorId]->role;
Scenario: In a online library system, display a Book page for a single book, also shows the Author(s) and the role of the author in this book.
Lets do it! In the BookController.php
public function actionView()
{
  //get Book id to display
  $myBook = Book::model()->findByPk($_GET['id']);
  //get the Author(s) of the book
  $myAuthors = $myBook->authors;
 
  $this->render('view',array('myBook'=>$myBook, 'myAuthors'=>$myAuthors));
}
In the view.php
//to get the Book's name
echo $myBook->name;
...
foreach($myAuthors as $myAuthor):
  //to get the Author's name
  echo $myAuthor->name;
  //to get the Author's role in the book, simply
  echo $myBook->authorbook[$myAuthor->id]->role;
endforeach
...
Also note that current Yii version (1.1.2) doesn’t support accessing additional data in the join table, other than creating a new Model for it, as what we did.

No comments:

Post a Comment