Mysql
 sql >> Database >  >> RDS >> Mysql

Appartiene a una relazione in Laravel su più database

Molto semplicemente:

public function bs()
{
    $database = $this->getConnection()->getDatabaseName();
    return $this->belongsToMany('B', "$database.a_bs", 'a_id', 'b_id');
}

Sto ottenendo il nome del database in modo dinamico perché la mia connessione è configurata in base a una variabile di ambiente. Laravel sembra presumere che la tabella pivot esista nello stesso database della relazione target, quindi questo lo costringerà a guardare invece al database corrispondente al modello in cui si trova questo metodo, il tuo regno "A".

Se non sei preoccupato per i database SQLite, ad esempio nell'ambito di uno unit-test, è tutto ciò di cui hai bisogno. Ma se lo sei, continua a leggere.

In primo luogo, l'esempio precedente non è sufficiente da solo. Il valore di $ database finirebbe per essere un percorso di file, quindi è necessario assegnarlo a qualcosa che non interrompa un'istruzione SQL e renderlo accessibile alla connessione corrente. "ATTACH DATABASE '$database' AS $name" ecco come lo fai:

public function bs()
{
    $database = $this->getConnection()->getDatabaseName();
    if (is_file($database)) {
        $connection = app('B')->getConnection()->getName();
        $name = $this->getConnection()->getName();
        \Illuminate\Support\Facades\DB::connection($connection)->statement("ATTACH DATABASE '$database' AS $name");
        $database = $name;
    }
    return $this->belongsToMany('B', "$database.a_bs", 'a_id', 'b_id');
}

Attenzione:le transazioni sbagliano: Se la connessione corrente utilizza transazioni, l'istruzione ATTACH DATABASE avrà esito negativo. puoi utilizzare le transazioni su di esso dopo eseguire questa affermazione però.

Considerando che, se il correlato la connessione utilizza transazioni, i dati risultanti saranno silenziosamente resi invisibili a quello corrente. Questo mi ha fatto impazzire più a lungo di quanto volessi ammettere, perché le mie domande sono state eseguite senza errori, ma continuavano a essere vuote. Sembra che solo i dati veramente scritti nel database allegato siano effettivamente accessibili a quello a cui è allegato.

Quindi, dopo essere stato costretto a scrivere nel database allegato, potresti comunque desiderare che il test si pulisca da solo. Una soluzione semplice sarebbe usare semplicemente $this->artisan('migrate:rollback', ['--database' => $attachedConnectionName]); . Ma se hai più test che richiedono le stesse tabelle, questo non è molto efficiente, poiché li costringe a ricostruirli ogni volta.

Un'opzione migliore sarebbe troncare le tabelle, ma lasciare intatta la loro struttura:

//Get all tables within the attached database
collect(DB::connection($database)->select("SELECT name FROM sqlite_master WHERE type = 'table'"))->each(function ($table) use ($name) {
        //Clear all entries for the table
        DB::connection($database)->delete("DELETE FROM '$table->name'");
        //Reset any auto-incremented index value
        DB::connection($database)->delete("DELETE FROM sqlite_sequence WHERE name = '$table->name'");
    });
}

Questo cancellerà tutti i dati da quella connessione , ma non c'è motivo per cui non potresti applicare un filtro di tipo a quello che ritieni opportuno. In alternativa, è possibile sfruttare il fatto che i DB SQLite sono file facilmente accessibili e copiare semplicemente quello allegato in un file temporaneo e utilizzarlo per sovrascrivere l'origine al termine dell'esecuzione del test. Il risultato sarebbe funzionalmente identico a una transazione.