Mysql
 sql >> Datenbank >  >> RDS >> Mysql

gehörtToMany-Beziehung in Laravel über mehrere Datenbanken hinweg

Ganz einfach:

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

Ich erhalte den Datenbanknamen dynamisch, da meine Verbindung basierend auf einer Umgebungsvariablen konfiguriert wird. Laravel scheint davon auszugehen, dass die Pivot-Tabelle in derselben Datenbank wie die Zielrelation existiert, also wird es gezwungen, stattdessen nach der Datenbank zu suchen, die dem Modell entspricht, in dem sich diese Methode befindet, Ihrem „A“-Bereich.

Wenn Sie sich keine Sorgen um SQLite-Datenbanken machen, z. B. im Rahmen eines Unit-Tests, ist das alles, was Sie brauchen. Aber wenn ja, lesen Sie weiter.

Erstens ist das vorherige Beispiel allein nicht ausreichend. Der Wert von $database wäre am Ende ein Dateipfad, also müssen Sie ihn mit etwas aliasieren, das eine SQL-Anweisung nicht unterbricht, und ihn für die aktuelle Verbindung zugänglich machen. "ATTACH DATABASE '$database' AS $name" So machst du das:

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

Warnung:Transaktionen vermasseln das: Wenn die aktuelle Verbindung Transaktionen verwendet, schlägt die ATTACH DATABASE-Anweisung fehl. Sie können Transaktionen darauf nach verwenden Ausführen dieser Anweisung jedoch.

Wenn die verwandten Verbindung Transaktionen verwendet, werden die resultierenden Daten stillschweigend für die aktuelle unsichtbar gemacht. Das hat mich länger verrückt gemacht, als ich zugeben möchte, weil meine Abfragen fehlerfrei liefen, aber immer wieder leer waren. Es scheint, dass nur Daten, die wirklich in die angeschlossene Datenbank geschrieben werden, tatsächlich für diejenige zugänglich sind, an die sie angeschlossen sind.

Nachdem Sie gezwungen wurden, in Ihre angehängte Datenbank zu schreiben, möchten Sie möglicherweise immer noch, dass Ihr Test nach sich selbst bereinigt wird. Eine einfache Lösung wäre, einfach $this->artisan('migrate:rollback', ['--database' => $attachedConnectionName]); zu verwenden . Wenn Sie jedoch mehrere Tests haben, die dieselben Tabellen benötigen, ist dies nicht sehr effizient, da sie gezwungen sind, sie jedes Mal neu zu erstellen.

Eine bessere Option wäre, die Tabellen zu kürzen, aber ihre Struktur intakt zu lassen:

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

Dadurch werden alle Daten von dieser Verbindung gelöscht , aber es gibt keinen Grund, warum Sie darauf keinen Filter anwenden könnten, wie Sie es für richtig halten. Alternativ können Sie die Tatsache nutzen, dass SQLite-DBs leicht zugängliche Dateien sind, und einfach die angehängte in eine temporäre Datei kopieren und damit die Quelle überschreiben, nachdem der Test ausgeführt wurde. Das Ergebnis wäre funktional identisch mit einer Transaktion.