Book Has Author - Many to Many relations using Kartik\Select2

I could make this work following same useful tips from this post.

But I really fell that my code could be 'smarter' (I'm a begginer), so please comment if you have any good idea to improve it.

First, you need Books, Author, and BookHasAutor db_table, Model, and CRUD.

In your models, you should declare the many_many relation, as in API:

// models\books.php

public function getBookHasAuthor()
{
   return $this->hasOne(BookHasAuthor::className(), ['book_id' => 'id']);
}

public function getAuthors()
{
   return $this->hasMany(Authors::className(), ['id' => 'author_id'])->via('bookHasAuthor');
}

//you can also declare this relations in models\authors.php

In the same books model, we need same adicional code:

// declare authorsId property
public $authorIds = [];

// you need a getter for select2 dropdown
public function getdropAuthor()
 {
    $data = Author::find()->asArray()->all();
    return ArrayHelper::map($data, 'id', 'name');
 }

// You will need a getter for the current set o Authors in this Book
public function getAuthorIds()
 {
   $this->authorIds = \yii\helpers\ArrayHelper::getColumn(
     $this->getBookHasAuthor()->asArray()->all(),
     'author_id'
   );
   return $this->authorIds;
 }

// You need to save the relations in BookHasAuthor table (adicional code for updates)
public function afterSave($insert)
 {
   $actualAuthors = [];
   $authorExists = 0; //for updates

   if (($actualAuthors = BookHasAuthor::find()
	->andWhere("book_id = $this->id")
	->asArray()
	->all()) !== null) {
      $actualAuthors = ArrayHelper::getColumn($actualAuthors, 'author_id');
      $authorExists = 1; // if there is authors relations, we will work it latter
   }

   if (!empty($this->despIds)) { //save the relations
      foreach ($this->despIds as $id) {
         $actualAuthors = array_diff($actualAuthors, [$id]); //remove remaining authors from array
	 $r = new BookHasAuthor();
	 $r->book_id = $this->id;
	 $r->author_id = $id;
	 $r->save();
	}
   }

   if ($authorExists == 1) { //delete authors tha does not belong anymore to this book
	foreach ($actualAuthors as $remove) {
	  $r = BookHasAuthor::findOne(['author_id' => $remove, 'book_id' => $this->id]);
	  $r->delete();
	}
   }

   parent::afterSave($insert); //don't forget this
}

In Your BooksController, we need same changes:

public function actionUpdate($id)
{
   $model = $this->findModel($id);
   $model->authorIds = $model->getAuthorIds(); //could it be automatically??
   ...

public function actionDelete($id)
{
   $model = $this->findModel($id);

   if ($model->load(Yii::$app->request->post())) {
     BookHasAuthor::deleteAll('book_id = :bookId', [':bookId' => $model->id]);
     $model->delete(); // you need it if you have restrict relations in your db
   ...

Finally, in your _form.php, let's use Select2:

...
<?= $form->field($model, 'AuthorIds')->widget(Select2::classname(), [
   'data'=>$model->dropAuthor,
   'options' => ['multiple' => true]
  ]);?>
...

That's all.

0 0
6 followers
Viewed: 23 317 times
Version: 2.0
Category: How-tos
Written by: thiagoc7
Last updated by: thiagoc7
Created on: Jun 1, 2014
Last updated: 6 years ago
Update Article

Revisions

View all history

Related Articles