php - framework - yii2 join



Recupera i dati dalla tabella di giunzione in Yii2 (3)

Poiché non ho ricevuto risposta per quasi 14 giorni, posterò come ho risolto questo problema. Questo non è esattamente quello che avevo in mente ma funziona, per ora basta. Quindi ... questo è quello che ho fatto:

  1. Aggiunto un modello UserGroup per la tabella di giunzione
  2. Aggiunta una relazione al gruppo

    public function getUserGroups()
    {
        return $this->hasMany(UserGroup::className(), ['user_id' => 'id']);
    }
  3. Iscritto UserGroup nella funzione del mio modello di ricerca

    $query = Group::find()->where('id =' . $id)->with('users')->with('userGroups');

Questo è ciò che volevo, il Gruppo con tutti gli utenti e, rappresentato dal mio nuovo modello UserGroup , i dati dalla tabella di giunzione.

Ho pensato prima di estendere la query sulla costruzione della funzione Yii2 - questo potrebbe essere un modo migliore per risolvere questo problema. Ma dato che non conosco molto bene Yii2, ho deciso di non farlo per ora.

Per favore fatemi sapere se avete una soluzione migliore.

Sto cercando di ottenere i dati da una tabella di join in Yii2 senza una query aggiuntiva. Ho 2 modelli (utente, gruppo) associati tramite la tabella di giunzione (user_group). Nella tabella user_group, voglio memorizzare dati extra (flag admin, ...) per questa relazione.

  • Qual è il modo migliore per aggiungere dati alla tabella di giunzione? Il metodo link accetta un parametro extraColumns ma non riesco a capire come funziona.

  • Qual è il modo migliore per recuperare questi dati? Ho scritto una query aggiuntiva per ottenere i valori dalla tabella di giunzione. Ci deve essere un modo più pulito per fare questo ?!

Cordiali saluti, questo è il modo in cui ho definito la relazione nei modelli:

Group.php

public function getUsers() {
    return $this->hasMany(User::className(), ['id' => 'user_id'])
        ->viaTable('user_group', ['group_id' => 'id']);
}

user.php

public function getGroups() {
    return $this->hasMany(Group::className(), ['id' => 'group_id'])
        ->viaTable('user_group', ['user_id' => 'id']);
}

Answer #1

In breve : l'utilizzo di un ActiveRecord per la tabella di giunzioni come suggerito è IMHO nel modo giusto perché è possibile impostare via() per utilizzare l' ActiveRecord esistente. Ciò consente di utilizzare il metodo link() di Yii per creare elementi nella tabella di giunzione mentre si aggiungono dati (come il flag admin) allo stesso tempo.

La Guida ufficiale di Yii 2.0 indica due modi di usare una tabella di giunzione: usando viaTable() e usando via() (vedi qui ). Mentre il primo si aspetta che il nome della tabella di giunzione sia un parametro, quest'ultimo si aspetta un nome di relazione come parametro.

Se è necessario accedere ai dati all'interno della tabella di giunzione, utilizzare ActiveRecord per la tabella di giunzione come suggerito e utilizzare via() :

class User extends ActiveRecord
{
    public function getUserGroups() {
        // one-to-many
        return $this->hasMany(UserGroup::className(), ['user_id' => 'id']);
    }
}

class Group extends ActiveRecord
{
    public function getUserGroups() {
        // one-to-many
        return $this->hasMany(UserGroup::className(), ['group_id' => 'id']);
    }

    public function getUsers()
    {
        // many-to-many: uses userGroups relation above which uses an ActiveRecord class
        return $this->hasMany(User::className(), ['id' => 'user_id'])
            ->via('userGroups');
    }
}

class UserGroup extends ActiveRecord
{
    public function getUser() {
        // one-to-one
        return $this->hasOne(User::className(), ['id' => 'user_id']);
    }

    public function getGroup() {
        // one-to-one
        return $this->hasOne(Group::className(), ['id' => 'userh_id']);
    }
}

In questo modo è possibile ottenere i dati della tabella di giunzione senza ulteriori query utilizzando la relazione userGroups (come con qualsiasi altra relazione uno a molti):

$group = Group::find()->where(['id' => $id])->with('userGroups.user')->one();
// --> 3 queries: find group, find user_group, find user
// $group->userGroups contains data of the junction table, for example:
$isAdmin = $group->userGroups[0]->adminFlag
// and the user is also fetched:
$userName = $group->userGroups[0]->user->name

Tutto questo può essere fatto usando la relazione hasMany . Quindi potresti chiederti perché dovresti dichiarare la relazione molti-a-molti usando via() : Perché puoi usare il metodo link() di Yii per creare oggetti nella tabella di giunzione:

$userGroup = new UserGroup();
// load data from form into $userGroup and validate
if ($userGroup->load(Yii::$app->request->post()) && $userGroup->validate()) {
    // all data in $userGroup is valid
    // --> create item in junction table incl. additional data
    $group->link('users', $user, $userGroup->getDirtyAttributes())
}

Answer #2

Non so per certo che sia la soluzione migliore. Ma per il mio progetto sarà buono per ora :)

1) Join sinistro

Aggiungi un nuovo attributo di classe nel modello User public $flag; . Aggiungi due linee alla tua relazione di base ma non rimuovere viaTable questo può (e dovrebbe) rimanere.

public function getUsers()
{
    return $this->hasMany(User::className(), ['id' => 'user_id'])
        ->viaTable('user_group', ['group_id' => 'id'])
        ->leftJoin('user_group', '{{user}}.id=user_id')
        ->select('{{user}}.*, flag') //or all ->select('*');
}

leftJoin rende possibile selezionare i dati dalla tabella di giunzione e con select per personalizzare le colonne di ritorno. Ricorda che viaTable deve rimanere perché link () si basa su di esso.

2) query di sottoselezione

Aggiungi un nuovo attributo di classe nel modello User public $flag;

E nella relazione getUsers() modificata dal modello di Group :

public function getUsers()
{
    return $this->hasMany(User::className(), ['id' => 'user_id'])
        ->viaTable('user_group', ['group_id' => 'id'])
        ->select('*, (SELECT flag FROM user_group WHERE group_id='.$this->id.' AND user_id=user.id LIMIT 1) as flag');
}

Come puoi vedere ho aggiunto la sotto-selezione per l'elenco di selezione predefinito. Questa selezione è per gli utenti non per il modello di gruppo. Sì, sono d'accordo che questo è un po 'brutto ma fa il lavoro.

3) Relazioni di condizione

Un'opzione diversa è quella di creare una relazione in più solo per gli amministratori:

// Select all users
public function getUsers() { .. }

// Select only admins (users with specific flag )
public function getAdmins()
{
    return $this->hasMany(User::className(), ['id' => 'user_id'])
        ->viaTable('user_group', ['group_id' => 'id'],
        function($q){
             return $q->andWhere([ 'flag' => 'ADMIN' ]); 
        });
}

$Group->admins - ottiene utenti con flag di amministrazione specifici. Ma questa soluzione non aggiunge l'attributo $flag . Devi sapere quando selezioni solo amministratori e quando tutti gli utenti. Lato negativo: è necessario creare una relazione separata per ogni valore di bandiera.

La tua soluzione con l'utilizzo del modello separato UserGroup è ancora più flessibile e universale per tutti i casi. Come puoi aggiungere la validazione e le informazioni di base di ActiveRecord. Queste soluzioni sono più a senso unico - per ottenere informazioni.





yii2