sql >> Databasteknik >  >> RDS >> Mysql

tillhör många relationer i Laravel över flera databaser

Mycket enkelt:

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

Jag får databasnamnet dynamiskt eftersom min anslutning är konfigurerad utifrån en miljövariabel. Laravel verkar anta att pivottabellen existerar i samma databas som målrelationen, så detta tvingar den att istället titta på databasen som motsvarar modellen som denna metod är i, din "A"-sfär.

Om du inte är orolig för SQLite-databaser, det vill säga inom ramen för ett enhetstest, är det allt du behöver. Men om du är det, fortsätt att läsa.

För det första är det tidigare exemplet inte tillräckligt i sig. Värdet på $databas skulle sluta vara en filsökväg, så du måste aliasa det till något som inte bryter en SQL-sats och göra den tillgänglig för den aktuella anslutningen. "ATTACH DATABASE '$database' AS $name" är hur du gör 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');
}

Varning:Transaktioner stör detta: Om den aktuella anslutningen använder transaktioner kommer ATTACH DATABASE-satsen att misslyckas. Du kan använda transaktioner på den efter kör det uttalandet dock.

Om de relaterade anslutning använder transaktioner, kommer den resulterande informationen att i tysthet göras osynlig för den aktuella. Detta gjorde mig galen längre än jag skulle vilja erkänna, eftersom mina frågor gick utan fel, men fortsatte att komma upp tomma. Det verkar som att bara data som verkligen skrivits till den bifogade databasen faktiskt är tillgänglig för den den är kopplad till.

Så efter att ha tvingats skriva till din bifogade databas kanske du fortfarande vill att ditt test ska städa upp efter sig självt. En enkel lösning där skulle vara att bara använda $this->artisan('migrate:rollback', ['--database' => $attachedConnectionName]); . Men om du har flera tester som behöver samma tabeller är detta inte särskilt effektivt, eftersom det tvingar dem att behöva bygga om dem varje gång.

Ett bättre alternativ skulle vara att trunkera tabellerna, men lämna deras struktur 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'");
    });
}

Detta raderar all data från den anslutningen , men det finns ingen anledning till att du inte kan använda något slags filter på det som du tycker är lämpligt. Alternativt kan du dra fördel av det faktum att SQLite DB:er är lättillgängliga filer, och bara kopiera den bifogade till en temporär fil och använda den för att skriva över källan efter att testet har körts. Resultatet skulle vara funktionellt identiskt med en transaktion.



  1. Hur man trimmar en sträng i SQLite

  2. Hur man hittar dubbletter av värden i en SQL-tabell

  3. VÄLJ FÖR XML AUTO och returnera datatyper

  4. rake db:create - sorteringsproblem