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() or populateRelation()
  • 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!