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.
you need to declare authorIds as safe in model::rules()
Like this:
// models\books.php ... public function rules() { return [ ... [['authorIds'], 'safe'], ... ]; }
Tidy up books model
In your books model you can simplify. In my example I am doing a many to many relation between Entity and Location with a join model (table) EntityLocation
// models\ar\Location public function getEntities() { // Return entities related to this location return $this->hasMany(Entity::className(), ['id' => 'location_id']) ->viaTable(EntityLocation::tableName(), ['entity_id' => 'id']); }
Example of a controller action:
// find one location $l = Location::find()->one(); // get array of entities related to that location $entities = $l->entities;
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.