Skip to content

Relations with 2 link keys

macropay-solutions edited this page Jun 4, 2025 · 1 revision

Multiple link keys relations with laravel-crud-wizard (paid)

This is how it can be accomplished by using HasManyThrough from Laravel and laravel-crud-wizard’s new type of relation HasManyThrough2LinkTables.

Consider a situation where an user speaks more languages, meaning the user-language pair (2 keys) replaces the user (1 key) in current logic, with the following tables:

users: id

users_languages_pivot: id, user_id, language

users_roles_pivot: id, user_id, language, role_id

roles: id

The relation defined in Role to UserLanguagePivot model would look like this:

    /**
     * Relation with 2 link keys
     */
    public function userLanguagePivots(): HasManyThrough
    {
        return $this->newHasManyThrough(
            ($r = (new UserLanguagePivot()))->newQuery(),
            $this, // Role
            ($t = (new UserRolePivot()))->setTable($t->getTable() . ' as t'),
            't.role_id', // 2 UserRolePivot
            'user_id', // 4 UserLanguagePivot
            'id', // 1 Role
            't.user_id' // 3 UserRolePivot
        )->whereRaw($t->getConnection()->getTablePrefix() . 't.language = ' .
            $r->getConnection()->getTablePrefix() . $r->getTable() . '.language');
    }

Notice how the language condition/column is NOT in the current model but in the through table.

The relation defined in UserLanguagePivot to Role model would look like this:

    /**
     * Relation with 2 link keys
     */
    public function roles(): HasManyThrough2LinkTables
    {
        return (new HasManyThrough2LinkTables(
            $this->newRelatedInstance(Role::class)->newQuery(),
            $this, // UserLanguagePivot
            ($f = (new self()))->setTable($f->getTable() . ' as f'),
            'f.id', // 2 self UserLanguagePivot
            'id', // 6 Role
            'id', // 1 UserLanguagePivot
            'f.user_id', // 3 self UserLanguagePivot
            ($s = (new UserRolePivot()))->setTable($s->getTable() . ' as s'),
            's.user_id', // 4 UserRolePivot
            's.role_id', // 5 UserRolePivot
            $this // UserLanguagePivot - needed for https://github.com/laravel/framework/issues/51825
        ))->whereColumn('f.language', '=', 's.language');
    }

Notice how the language condition/column is in the current model, thus making the through self mandatory (to avoid sql errors).