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

belongsToMany-relation i Laravel på tværs af flere databaser

Meget enkelt:

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

Jeg henter databasenavnet dynamisk, fordi min forbindelse er konfigureret baseret på en miljøvariabel. Laravel lader til at antage, at pivottabellen eksisterer i den samme database som målrelationen, så dette vil tvinge den til i stedet at se på databasen, der svarer til den model, som denne metode er i, dit 'A'-rige.

Hvis du ikke er bekymret for SQLite-databaser, dvs. i forbindelse med en enhedstest, er det alt, du behøver. Men hvis du er det, så fortsæt med at læse.

For det første er det foregående eksempel ikke tilstrækkeligt i sig selv. Værdien af ​​$database ville ende med at blive en filsti, så du skal alias den til noget, der ikke vil bryde en SQL-sætning, og gøre den tilgængelig for den aktuelle forbindelse. "ATTACH DATABASE '$database' AS $name" sådan gør du det:

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');
}

Advarsel:Transaktioner ødelægger dette: Hvis den aktuelle forbindelse bruger transaktioner, vil ATTACH DATABASE-sætningen mislykkes. Du kan brug transaktioner på det efter udfører dog den erklæring.

Hvorimod, hvis de relaterede forbindelse bruger transaktioner, vil de resulterende data stille og roligt blive gjort usynlige for den aktuelle. Dette gjorde mig sindssyg i længere tid, end jeg ville indrømme, fordi mine forespørgsler kørte uden fejl, men blev ved med at komme tomme. Det ser ud til, at kun data, der virkelig er skrevet til den vedhæftede database, faktisk er tilgængelige for den, den er knyttet til.

Så efter at være blevet tvunget til at skrive til din vedhæftede database, vil du muligvis stadig have din test til at rydde op efter sig selv. En simpel løsning ville være at bruge $this->artisan('migrate:rollback', ['--database' => $attachedConnectionName]); . Men hvis du har flere tests, der har brug for de samme tabeller, er dette ikke særlig effektivt, da det tvinger dem til at skulle genopbygge dem hver gang.

En bedre mulighed ville være at afkorte tabellerne, men lade deres struktur være intakt:

//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'");
    });
}

Dette sletter alle data fra den forbindelse , men der er ingen grund til, at du ikke kunne anvende et eller andet venligt filter på det, som du finder passende. Alternativt kan du drage fordel af det faktum, at SQLite DB'er er let tilgængelige filer, og bare kopiere den vedhæftede fil til en midlertidig fil og bruge den til at overskrive kilden, efter at testen er udført. Resultatet ville være funktionelt identisk med en transaktion.



  1. Docker - Kør Apache på vært og container til forskellige websteder

  2. Løsning af forkert tegnkodning ved visning af MySQL-databaseresultater efter opgradering til PHP 5.3

  3. Mysql join-forespørgsel for flere tags (mange-til-mange forhold), der matcher ALLE tags?

  4. Hurtigste måde at indsætte 134675 værdier i ekstern database