Managing many-to-many relationships in Yii2 just got a lot easier.
The yii2-m2m-behavior
package allows you to assign related models directly using virtual attributes like:
$item->tagIds = [1, 2, 3];
Under the hood, the behavior handles all the syncing through the junction table — no need to manually call link()
or unlink()
.
This means less boilerplate, fewer mistakes, and cleaner code when dealing with junction tables.
🧩 What does it solve?
- Automatically links/unlinks models in many-to-many relations
- Supports virtual attributes (e.g.,
tagIds
,categoryIds
) - Allows saving extra columns in the junction table (e.g., timestamps, flags)
- Keeps everything in sync on insert, update, and delete
- Uses native Yii2 APIs (
link()
,unlink()
,populateRelation()
)
📌 Example
class Item extends ActiveRecord
{
public function behaviors()
{
return [
'tags' => [
'class' => LinkManyToManyBehavior::class,
'relation' => 'tags',
'referenceAttribute' => 'tagIds',
],
];
}
public function getTags()
{
return $this->hasMany(Tag::class, ['id' => 'tag_id'])
->viaTable('item_tag', ['item_id' => 'id']);
}
}
Then, just do:
$item->tagIds = [1, 2, 3];
$item->save();
And the junction table will be updated automatically.
Controller + View Example
// Controller
public function actionCreate()
{
$model = new Item();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
}
return $this->render('create', [
'model' => $model,
'availableTags' => ArrayHelper::map(Tag::find()->all(), 'id', 'name'),
]);
}
= $form->field($model, 'tagIds')->checkboxList($availableTags) ?>
✅ Quality & Coverage
This package was built with robustness in mind:
- 100% code coverage with PHPUnit
- CI-enforced coverage gate
- Every edge case tested, including misconfigured relations and corrupted states
- Focused on DX: helpful exception messages and fail-fast behavior
🔍 What’s in version 2.1.0
Version 2.1.0 brings deeper introspection and new tools:
- Automatic synchronization even when relations are populated with
with()
orpopulateRelation()
-
isReferenceRelationDirty()
method to detect mismatch between relation and reference attribute -
computeRelationHash()
for internal tracking of changes - Better internal consistency and refactors
⚙️ Requirements
- PHP 8.1 to 8.4
- Yii2 ~2.0.0
🧭 What’s next?
We’re working on:
- Validators for virtual attributes (e.g.
tagIds
) - Better internal caching and lazy loading
- Custom events for hooks before/after sync
- Debug tools for inspecting link/unlink actions
🚀 Try it now
composer require jonatas-sas/yii2-m2m-behavior
📚 Full documentation and examples on GitHub
Feedback and contributions are very welcome!