Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

relacionamento belongsToMany em Laravel em vários bancos de dados


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

Estou obtendo o nome do banco de dados dinamicamente porque minha conexão está configurada com base em uma variável de ambiente. O Laravel parece assumir que a tabela dinâmica existe no mesmo banco de dados que a relação de destino, então isso o forçará a procurar o banco de dados correspondente ao modelo em que esse método está, seu domínio 'A'.

Se você não está preocupado com bancos de dados SQLite, ou seja, no escopo de um teste de unidade, isso é tudo que você precisa. Mas se estiver, continue lendo.

Em primeiro lugar, o exemplo anterior não é suficiente por si só. O valor de $database acabaria sendo um caminho de arquivo, então você precisa criar um alias para algo que não quebre uma instrução SQL e torná-lo acessível à conexão atual. "ATTACH DATABASE '$database' AS $name" é como você faz isso:
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');
}

Aviso:as transações atrapalham: Se a conexão atual estiver usando transações, a instrução ATTACH DATABASE falhará. Você pode usar transações nele depois executando essa instrução embora.

Considerando que, se o relacionado conexão usa transações, os dados resultantes serão silenciosamente tornados invisíveis para o atual. Isso me deixou louco por mais tempo do que gostaria de admitir, porque minhas consultas foram executadas sem erros, mas continuaram vazias. Parece que apenas os dados realmente gravados no banco de dados anexado são realmente acessíveis ao que está anexado.

Portanto, depois de ser forçado a gravar em seu banco de dados anexado, você ainda pode querer que seu teste seja limpo depois de si mesmo. Uma solução simples seria usar apenas $this->artisan('migrate:rollback', ['--database' => $attachedConnectionName]); . Mas se você tiver vários testes que precisam das mesmas tabelas, isso não é muito eficiente, pois os obriga a reconstruí-los a cada vez.

Uma opção melhor seria truncar as tabelas, mas deixar sua estrutura intacta:
//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'");
    });
}

Isso limpará todos os dados dessa conexão , mas não há razão para que você não possa aplicar algum tipo de filtro a isso da maneira que achar melhor. Como alternativa, você pode aproveitar o fato de que os bancos de dados SQLite são arquivos de fácil acesso e apenas copiar o anexo para um arquivo temporário e usá-lo para substituir a fonte após a execução do teste. O resultado seria funcionalmente idêntico a uma transação.