Yii Framework Forum: Handling Many To Many Relation - Yii Framework Forum

Jump to content

Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

Handling Many To Many Relation

#1 User is offline   JohnnyBeGood 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 29
  • Joined: 11-October 12

Posted 19 March 2014 - 07:50 PM

Anyone know how to deal with many to many relationships in Yii2?

I've found this excellent article http://www.larryullm...s-in-yii-forms/ but this is for Yii 1.

Many thanks in advance
0

#2 User is offline   ORey 

  • Elite Member
  • PipPipPipPipPip
  • Yii
  • Group: Members
  • Posts: 1,701
  • Joined: 20-April 09
  • Location:Moscow, Russia

Posted 19 March 2014 - 08:06 PM

Docs: https://github.com/y...ith-pivot-table
God is real unless declared as integer
0

#3 User is offline   JohnnyBeGood 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 29
  • Joined: 11-October 12

Posted 19 March 2014 - 08:14 PM

View PostORey, on 19 March 2014 - 08:06 PM, said:



Thanks for your reply.
I've already read it, but my main problem is the form. How do I present the field if I've no attribute (like in yii1) in the model?

I can manage one to many:
Example (Employee and Organization)
Employee model
public function getOrganizationList() { 
  return ArrayHelper::map(Organization::find()->asArray()->all(), 'Id', 'Name');
}

Employee _form
<?= $form->field($model, 'Organization_Id')->dropDownList($model->organizationList) ?>


But many to many:
Example (Article and Category)
Article model
public function getCategoryList() { 
  return ArrayHelper::map(Category::find()->asArray()->all(), 'Id', 'Name');
}

Article _form
<?= $form->field($model, '######')->dropDownList($model->categoryList) ?>


the '######' is my problem and how to do multiple selection
0

#4 User is offline   ORey 

  • Elite Member
  • PipPipPipPipPip
  • Yii
  • Group: Members
  • Posts: 1,701
  • Joined: 20-April 09
  • Location:Moscow, Russia

Posted 20 March 2014 - 03:33 AM

Right now I'm using virtual attributes, like
public $item_ids;
<?= $form->field($record, 'item_ids')->checkboxList(...); ?>


And then in aftersave I grab those and create pivot items.

But I'm pretty sure there can be easier way to do it (like using relation name as a field name)
God is real unless declared as integer
0

#5 User is offline   JohnnyBeGood 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 29
  • Joined: 11-October 12

Posted 20 March 2014 - 06:43 AM

View PostORey, on 20 March 2014 - 03:33 AM, said:

Right now I'm using virtual attributes, like
public $item_ids;
<?= $form->field($record, 'item_ids')->checkboxList(...); ?>


And then in aftersave I grab those and create pivot items.

But I'm pretty sure there can be easier way to do it (like using relation name as a field name)


As far as I know, there is no relation name in yii2
0

#6 User is offline   schmunk 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 578
  • Joined: 02-November 08
  • Location:Stuttgart, Germany

Posted 22 March 2014 - 06:44 AM

Is there already an example how to save related records for a model?
Phundament - Yii Application Boilerplate with composer support
Fork on github

Follow phundament on Twitter

DevSystem: Mac OS X 10.7 - PHP 5.3 - Apache2 - Yii 1.1 / trunk - Firefox or Safari
0

#7 User is offline   ORey 

  • Elite Member
  • PipPipPipPipPip
  • Yii
  • Group: Members
  • Posts: 1,701
  • Joined: 20-April 09
  • Location:Moscow, Russia

Posted 22 March 2014 - 08:31 AM

View Postschmunk, on 22 March 2014 - 06:44 AM, said:

Is there already an example how to save related records for a model?


http://www.yiiframew...post__p__243022
God is real unless declared as integer
0

#8 User is offline   robsch 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 63
  • Joined: 14-February 13
  • Location:Austria

Posted 16 September 2014 - 02:23 AM

Could anyone provide some code how to do the saving? I think the ids that come from the post request need to be walk through. Then new entries have to be created or missing ids have to be deleted in the pivot table/model.

Or is there any magic already built in Yii?

As a newbie I'm currently don't know how to it. And it would be helpful for other beginners.
0

#9 User is offline   robsch 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 63
  • Joined: 14-February 13
  • Location:Austria

Posted 16 September 2014 - 03:44 AM

View PostORey, on 20 March 2014 - 03:33 AM, said:

Right now I'm using virtual attributes, like
public $item_ids;
<?= $form->field($record, 'item_ids')->checkboxList(...); ?>



At least this can be done easier (now?):

Many-to-many relation, i.e. using a pivot table for articles and categories (so three tables are involved).

In article/_form.php:

<?= $form->field($model, 'categories')->checkboxList(
  ArrayHelper::map(Category::find()->all(), 'id', 'name')) ?>


And in Article.php:

public function getCategories() {
   return $this->hasMany(Category::className(), ['id' => 'category_id'])->viaTable(
  'category_article_mapping_table', ['article_id' => 'id']);
}


However, displaying might easy, but saving is still a problem for me. I think I have to use a transaction and do there all the needed inserts and deletions. But I don't now if I can use ActiveRecord of if I build my own sql statements.
0

#10 User is offline   Radek 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 3
  • Joined: 16-September 14

Posted 16 September 2014 - 09:37 AM

View Postrobsch, on 16 September 2014 - 03:44 AM, said:

At least this can be done easier (now?):

Many-to-many relation, i.e. using a pivot table for articles and categories (so three tables are involved).

In article/_form.php:

<?= $form->field($model, 'categories')->checkboxList(
  ArrayHelper::map(Category::find()->all(), 'id', 'name')) ?>


And in Article.php:

public function getCategories() {
   return $this->hasMany(Category::className(), ['id' => 'category_id'])->viaTable(
  'category_article_mapping_table', ['article_id' => 'id']);
}


However, displaying might easy, but saving is still a problem for me. I think I have to use a transaction and do there all the needed inserts and deletions. But I don't now if I can use ActiveRecord of if I build my own sql statements.


I use this code to update 3 table models relations:

In Article.php

public function afterSave($insert, $changedAttributes)
    {
        $oldCategories = ArrayHelper::map($this->categories, 'id', 'id');

        $categories = Yii::$app->request->post('Article')['categories'];

        $idsToInsert = array_diff($categories, $oldCategories);
        $idsToDelete = array_diff($oldCategories, $categories);

        // --- Insert ---
        foreach ($idsToInsert as $id) {
            $mArticleCategory = new category_article_mapping_table();
            $mArticleCategory->article_id = $this->id;
            $mArticleCategory->category_id = $id;

            $mArticleCategory->save(false);
        }

        // --- Delete ---
        foreach ($idsToDelete as $id) {
            category_article_mapping_table::deleteAll(['category_id' => $id, 'article_id' => $this->id]);
        }

        return parent::afterSave($insert, $changedAttributes);
    }



Hope it helps...
0

#11 User is offline   robsch 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 63
  • Joined: 14-February 13
  • Location:Austria

Posted 16 September 2014 - 10:15 AM

Thank you Radek.

I have found another approach. It is a bit more generic but not so efficient yet. It could be improved, I think. I have put it into the pivot model class since it could be used in 'both directions' (as it is a many-to-many relation), so it is in the middle now. However, since it is generic it should be somewhere else.

    /**
     * Links a list of models to a parent model by creating entries into a mapping table. All existing relations
     * will be deleted. This method is for many-to-many relations, so parent and child might be confusing.
     *
     * @param ActiveRecord $parentModel The model that is the parent.
     * @param String $propertyName The name of the property at the parent model to which the list of child models belong.
     * @param String|String[] $childModelIds The ids of the child models.
     */
    public static function setLinks(ActiveRecord $parentModel, $propertyName, $childModelIds)
    {
        $getter = 'get' . $propertyName;
        $linkedModelClassName = $parentModel->$getter()->modelClass;

        $parentModel->unlinkAll($propertyName, true);

        if (empty($childModelIds)) return;

        foreach ($childModelIds as $id) {
            $childModel = $linkedModelClassName::findOne($id);
            $parentModel->link($propertyName, $childModel);
        }
    }


It uses the ability of link() that detects pivot relations. And I have to note that gii has generated the getCategorys() and getArticles() methods in the model classes (using the viaTable method). Without $parentModel->$getter()->modelClass wouldn't work.

This function could be used like this:

SomeClass::setLinks($category, 'articles', Yii::$app->request->post('Category')['articles']);

or

SomeClass::setLinks($article, 'categorys', Yii::$app->request->post('Article')['categorys']);


There should also be a transaction, surrounding the whole update stuff.

What do you think about this? Or does one would like to improve it?
0

Share this topic:


Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users