Skip to content
ActiveRecord behavior for saving many-to-many relations
PHP
Branch: master
Clone or download
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.github add github templates Jul 3, 2019
src move source code to 'src' directory Feb 2, 2018
tests move source code to 'src' directory Feb 2, 2018
.gitattributes add github templates Jul 3, 2019
.gitignore Initial commit Aug 25, 2015
.travis.yml update Travis Jul 3, 2019
CHANGELOG.md release 1.0.3 Feb 9, 2018
LICENSE.md fix licence Jul 3, 2019
README.md compatibility with PHPUnit < 6.x Jul 27, 2017
composer.json switch to 'asset-packagist.org' Feb 2, 2018
phpunit.xml.dist Initial commit Aug 25, 2015

README.md

ActiveRecord Many-to-Many Saving Extension for Yii2


This extension provides support for ActiveRecord many-to-many relation saving. For example: single "item" may belong to several "groups", each group may be linked with several items. Unlike regular [[\yii\db\BaseActiveRecord::link()]] usage, this extension automatically checks references existence, removes excluded references and provide support for web form composition.

For license information check the LICENSE-file.

Latest Stable Version Total Downloads Build Status

Installation

The preferred way to install this extension is through composer.

Either run

php composer.phar require --prefer-dist yii2tech/ar-linkmany

or add

"yii2tech/ar-linkmany": "*"

to the require section of your composer.json.

Usage

This extension provides support for ActiveRecord many-to-many relation saving. This support is granted via [[\yii2tech\ar\linkmany\LinkManyBehavior]] ActiveRecord behavior. You'll need to attach it to your ActiveRecord class and point the target "has-many" relation for it:

class Item extends ActiveRecord
{
    public function behaviors()
    {
        return [
            'linkGroupBehavior' => [
                'class' => LinkManyBehavior::className(),
                'relation' => 'groups', // relation, which will be handled
                'relationReferenceAttribute' => 'groupIds', // virtual attribute, which is used for related records specification
            ],
        ];
    }

    public static function tableName()
    {
        return 'Item';
    }

    public function getGroups()
    {
        return $this->hasMany(Group::className(), ['id' => 'groupId'])->viaTable('ItemGroup', ['itemId' => 'id']);
    }
}

Being attached [[\yii2tech\ar\linkmany\LinkManyBehavior]] adds a virtual proprty to the owner ActiveRecord, which name is determined by [[\yii2tech\ar\linkmany\LinkManyBehavior::$relationReferenceAttribute]]. You will be able to specify related models primary keys via this attribute:

// Pick up related model primary keys:
$groupIds = Group::find()
    ->select('id')
    ->where(['isActive' => true])
    ->column();

$item = new Item();
$item->groupIds = $groupIds; // setup related models references
$item->save(); // after main model is saved referred related models are linked

The above example is equal to the following code:

$groups = Group::find()
    ->where(['isActive' => true])
    ->all();

$item = new Item();
$item->save();

foreach ($groups as $group) {
    $item->link('groups', $group);
}

Attention: do NOT declare relationReferenceAttribute attribute in the owner ActiveRecord class. Make sure it does not conflict with any existing owner field or virtual property.

Virtual property declared via relationReferenceAttribute serves not only for saving. It also contains existing references for the relation:

$item = Item::findOne(15);
var_dump($item->groupIds); // outputs something like: array(2, 5, 11)

You may as well edit the references list for existing record, while saving linked records will be synchronized:

$item = Item::findOne(15);
$item->groupIds = array_merge($item->groupIds, [17, 21]);
$item->save(); // groups "17" and "21" will be added

$item->groupIds = [5];
$item->save(); // all groups except "5" will be removed

Note: if attribute declared by relationReferenceAttribute is never invoked for reading or writing, it will not be processed on owner saving. Thus it will not affect pure owner saving.

Creating relation setup web interface

The main purpose of [[\yii2tech\ar\linkmany\LinkManyBehavior::$relationReferenceAttribute]] is support for creating many-to-many setup web interface. All you need to do is declare a validation rule for this virtual property in your ActiveRecord, so its value can be collected from the request:

class Item extends ActiveRecord
{
    public function rules()
    {
        return [
            ['groupIds', 'safe'] // ensure 'groupIds' value can be collected on `populate()`
            // ...
        ];
    }

    // ...
}

Inside the view file you should use relationReferenceAttribute property as an attribute name for the form input:

<?php
use yii\helpers\ArrayHelper;
use yii\helpers\Html;
use yii\widgets\ActiveForm;

/* @var $model Item */
?>
<?php $form = ActiveForm::begin(); ?>

<?= $form->field($model, 'name'); ?>
<?= $form->field($model, 'price'); ?>

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

<div class="form-group">
    <?= Html::submitButton('Save', ['class' => 'btn btn-primary']) ?>
</div>

<?php ActiveForm::end(); ?>

Inside the controller you don't need any special code:

use yii\web\Controller;

class ItemController extends Controller
{
    public function actionCreate()
    {
        $model = new Item();

        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            return $this->redirect(['view']);
        }

        return $this->render('create', [
            'model' => $model,
        ]);
    }

    // ...
}
You can’t perform that action at this time.