carono/yii2-pivot

Trait for pivoting models

Fund package maintenance!
Opencollective
yiisoft

Installs: 0

Dependents: 1

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

Type:yii2-extension

pkg:composer/carono/yii2-pivot

1.0.0 2026-01-07 11:33 UTC

This package is auto-updated.

Last update: 2026-01-07 11:54:33 UTC


README

PivotTrait is a Yii2 ActiveRecord trait that helps work with “pivot” (junction) tables in many-to-many relations: it can find/get pivot rows, stage pivot changes in an internal storage, and then persist them (insert/update) or delete them.

What it provides

  • Query helpers:
    • findPivot($model, $pivotClass) / getPivot($model, $pivotClass, $condition = []) to build/query a single pivot record between “this” model and a related model.
    • findPivots($pivotClass) / getPivots($pivotClass, $condition = []) to query all pivot rows for the current model (by its PK).
  • In-memory staging (“storage”) of pivot operations:
    • storagePivot($model, $pivotClass, $attributes = []) and storagePivots($models, $pivotClass, $attributes = []) store related models (and optional extra pivot attributes) keyed by spl_object_hash.
    • getPivotStorage(), getStoragePivots($pivotClass), getStoragePivotAttribute($model, $pivotClass), clearStorage($pivotClass) to inspect/clear staged data.
  • Persistence helpers:
    • savePivots($clear = false, $condition = []) iterates stored pivots and writes them, optionally clearing existing pivots first.
    • addPivot($model, $pivotClass, $attributes = []) upserts a pivot row: if found by key condition it updates attributes, otherwise inserts a new row.
    • deletePivot($model, $pivotClass, $condition = []) deletes a single pivot row for a given related model (plus extra condition).

How pivot keys are detected

The trait builds a pivot “condition” like: [pivot_main_fk => this->PK, pivot_slave_fk => relatedModel->PK]. Pivot key field names are resolved by checking the pivot table schema primary keys and trying to match foreign keys to the involved tables; if it cannot uniquely match by FK, it falls back to the first PK column as “main” and second PK column as “slave”.

Usage examples

1) Upsert a pivot row directly

class User extends \yii\db\ActiveRecord {
    use PivotTrait;
}

$user = User::findOne(1);
$role = Role::findOne(5);

// Upsert pivot row (user_id, role_id) with extra attributes.
$user->addPivot($role, UserRole::class, ['created_at' => time()]);

This will update an existing UserRole record matching the computed condition, or create a new one otherwise.

2) Stage multiple pivots, then save them

$user->storagePivots([$role1, $role2], UserRole::class, ['created_at' => time()]);
$user->savePivots();        // writes all staged items
$user->clearStorage(UserRole::class);

Staging stores models + attributes in an internal storage array, and savePivots() persists them by calling addPivot() for each entry.

3) Replace all pivots for a class (clear then insert)

$user->storagePivots($newRoles, UserRole::class);
$user->savePivots(true); // $clear=true => delete existing pivots first, then add staged ones

When $clear is true, the trait deletes existing pivot rows for that pivot class before adding the staged ones.

Notes / assumptions

  • The trait assumes both the current model and related models have a single-column primary key (it uses primaryKey()[0]).
  • The pivot table is expected to have a composite primary key with two columns (the code accesses $pks[0] and $pks).

If you share the pivot AR class (the junction ActiveRecord) and the two related AR models, a more concrete README-style snippet can be written with real class/table/column names.