, (*1)
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., (*2)
For license information check the LICENSE-file., (*3)
, (*4)
Installation
The preferred way to install this extension is through composer., (*5)
Either run, (*6)
php composer.phar require --prefer-dist yii2tech/ar-linkmany
or add, (*7)
"yii2tech/ar-linkmany": "*"
to the require section of your composer.json., (*8)
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:, (*9)
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:, (*10)
// 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:, (*11)
$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., (*12)
Virtual property declared via relationReferenceAttribute
serves not only for saving. It also contains existing references
for the relation:, (*13)
$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:, (*14)
$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., (*15)
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:, (*16)
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:, (*17)
= $form->field($model, 'name'); ?>
= $form->field($model, 'price'); ?>
= $form->field($model, 'groupIds')->checkboxList(ArrayHelper::map(Group::find()->all(), 'id', 'name')); ?>
= Html::submitButton('Save', ['class' => 'btn btn-primary']) ?>
Inside the controller you don't need any special code:, (*18)
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,
]);
}
// ...
}