Laravel BelongsTo отношения с разными базами данных не работают

Я видел в нескольких местах, чтобы "держаться подальше" от этого, но, увы, именно так построена моя БД:

class Album extends Eloquent {

   // default connection

   public function genre() {
       return $this->belongsTo('genre');
   }

и таблица жанров:

class Genre extends Eloquent {
    protected $connection = 'Resources';

}

My database.php:

'Resources' => array(
                    'driver'    => 'mysql',
                    'host'      => 'localhost',
                    'database'  => 'resources',
                    'username'  => 'user',
                    'password'  => 'password',
                    'charset'   => 'utf8',
                    'collation' => 'utf8_unicode_ci',
                    'prefix'    => '',
            ),

 'mysql' => array(
                    'driver'    => 'mysql',
                    'host'      => 'localhost',
                    'database'  => 'my_data',
                    'username'  => 'user',
                    'password'  => 'password',
                    'charset'   => 'utf8',
                    'collation' => 'utf8_unicode_ci',
                    'prefix'    => '',
            ),

и когда я пытаюсь запустить

Album::whereHas('genre', function ($q) {
   $q->where('genre', 'German HopScotch'); 
});

он не выбирается должным образом (не добавляет имя базы данных в таблицу "жанры" ):

Next exception 'Illuminate\Database\QueryException' with message 'SQLSTATE[42S02]: Base table or view not found: 1146 Table 'my_data.genres' doesn't exist

Важно отметить, что это отлично работает:

Album::first()->genre;

Update

Самое лучшее, что я нашел до сих пор, это использовать метод "from" для создания конкретного соединения. Я обнаружил, что построитель внутри запроса может получить "от"

Album::whereHas('genre', function ($q) {
   $q->from('resources.genres')->where('genre', 'German HopScotch'); 
});

Это достойное решение, но оно требует от меня копать базу данных php и найти хороший способ получить правильную таблицу и имя базы данных из жанра отношения.

Я буду признателен, если кто-то еще сможет использовать это решение и сделать его более общим.

Ответ 1

Это мое собственное решение, и оно работает вообще для меня, но оно очень сложное.

Я использую метод builder от метода, чтобы правильно установить таблицу и базу данных внутри подзапроса. Мне просто нужно передать правильную информацию внутри.

Предположим, что подзапрос может быть таким же сложным, как "genres.sample" или даже глубже (что означает, что альбомы имеют отношение к жанрам, а жанры имеют отношение к образцам) это как

$subQuery = 'genres.samples';
$goDeep = (with (new Album));

$tableBreakdown =  preg_split('/\./', $subQuery); //  = ['genres', 'samples']

// I recurse to find the innermost table $album->genres()->getRelated()->sample()->getRelated()
foreach ($tableBreakdown as $table)
    $goDeep = $goDeep->$table()->getRelated();

// now I have the innermost, get table name and database name

$alternativeConnection =  Config::get("database.connections." . $goDeep->getConnectionName() . ".database"); // should be equal to the correct database name

$tableName = $goDeep->getTable(); // I have to use the table name in the "from" method below

Album::whereHas($subQuery, function ($q) use ($alternativeConnection, $tableName) {
$q->from("$alternativeConnection.$tableName"); 
$q->where(....... yadda yadda);
    });

ТЛ: др;

Album::whereHas('genres', function ($q) { 
    $q->from('resources.genres')->where(....); 
});

Ответ 2

Я нашел для себя действительно хорошую статью: http://fideloper.com/laravel-multiple-database-connections

В основном вы должны указать свои два соединения в вашем файле конфигурации следующим образом:

<?php
return array(

    'default' => 'mysql',

    'connections' => array(

        # Our primary database connection
        'mysql' => array(
            'driver'    => 'mysql',
            'host'      => 'host1',
            'database'  => 'database1',
            'username'  => 'user1',
            'password'  => 'pass1'
            'charset'   => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix'    => '',
        ),

        # Our secondary database connection
        'mysql2' => array(
            'driver'    => 'mysql',
            'host'      => 'host2',
            'database'  => 'database2',
            'username'  => 'user2',
            'password'  => 'pass2'
            'charset'   => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix'    => '',
        ),
    ),
);

Итак, ваши два соединения сглажены до mysql и mysql2.

Затем вы можете сказать красноречивому, что "псевдоним" использовать так:

<?php

class SomeModel extends Eloquent {

    protected $connection = 'mysql2';

}

Затем вы можете настроить свои отношения как обычные.

tl; dr: В основном вместо указания имени таблицы как $connection в красноречии укажите псевдоним соединения в вашей конфигурации, и он должен работать.

Ответ 3

Похоже, Eager Loading будет делать то, что вы хотите сделать

Album::with(['genre' => function ($q) {
   $q->connection('Resources')
     ->where('genre', 'German HopScotch'); 
}]);

Ответ 4

Чтобы начать "Ресурсы" в database.php с помощью ресурсов, будет лучше!

Мне интересно, можете ли вы попробовать?

Album::whereHas('genre', function ($q) {
   $q->setConnection('resources')->where('genre', 'German HopScotch'); 
});

Ответ 5

У меня была такая же проблема, когда отношения не работали с подключением модели.

Мое решение состояло в том, чтобы переопределить метод belongsToMany на модели, пытающейся установить. См. Пример ниже.

<?php

namespace App\Model;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class ConnectionModel extends Model
{
    /**
     * Override method to allow inheriting connection of parent
     *
     * Define a many-to-many relationship.
     *
     * @param  string  $related
     * @param  string  $table
     * @param  string  $foreignKey
     * @param  string  $otherKey
     * @param  string  $relation
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany|BelongsToMany
     */
    public function belongsToMany($related, $table = null, $foreignKey = null, $otherKey = null, $relation = null)
    {
        // If no relationship name was passed, we will pull backtraces to get the
        // name of the calling function. We will use that function name as the
        // title of this relation since that is a great convention to apply.
        if (is_null($relation)) {
            $relation = $this->getBelongsToManyCaller();
        }

        // First, we'll need to determine the foreign key and "other key" for the
        // relationship. Once we have determined the keys we'll make the query
        // instances as well as the relationship instances we need for this.
        $foreignKey = $foreignKey ?: $this->getForeignKey();

        $instance = new $related;

        // get connection from parent
        $instance->setConnection(parent::getConnectionName());

        $otherKey = $otherKey ?: $instance->getForeignKey();

        // If no table name was provided, we can guess it by concatenating the two
        // models using underscores in alphabetical order. The two model names
        // are transformed to snake case from their default CamelCase also.
        if (is_null($table)) {
            $table = $this->joiningTable($related);
        }

        // Now we're ready to create a new query builder for the related model and
        // the relationship instances for the relation. The relations will set
        // appropriate query constraint and entirely manages the hydrations.
        $query = $instance->newQuery();

        return new BelongsToMany($query, $this, $table, $foreignKey, $otherKey, $relation);
    }
}

Ответ 6

У меня была такая же проблема, когда отношения не работали с родительским соединением.

Мое решение состояло в том, чтобы переопределить метод belongsToMany на модели, пытающейся установить. См. Пример ниже.

<?php

namespace App\Model;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class ConnectionModel extends Model
{
    /**
     * Override method to allow inheriting connection of parent
     *
     * Define a many-to-many relationship.
     *
     * @param  string  $related
     * @param  string  $table
     * @param  string  $foreignKey
     * @param  string  $otherKey
     * @param  string  $relation
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany|BelongsToMany
     */
    public function belongsToMany($related, $table = null, $foreignKey = null, $otherKey = null, $relation = null)
    {
        // If no relationship name was passed, we will pull backtraces to get the
        // name of the calling function. We will use that function name as the
        // title of this relation since that is a great convention to apply.
        if (is_null($relation)) {
            $relation = $this->getBelongsToManyCaller();
        }

        // First, we'll need to determine the foreign key and "other key" for the
        // relationship. Once we have determined the keys we'll make the query
        // instances as well as the relationship instances we need for this.
        $foreignKey = $foreignKey ?: $this->getForeignKey();

        $instance = new $related;

        $instance->setConnection(parent::getConnectionName());

        $otherKey = $otherKey ?: $instance->getForeignKey();

        // If no table name was provided, we can guess it by concatenating the two
        // models using underscores in alphabetical order. The two model names
        // are transformed to snake case from their default CamelCase also.
        if (is_null($table)) {
            $table = $this->joiningTable($related);
        }

        // Now we're ready to create a new query builder for the related model and
        // the relationship instances for the relation. The relations will set
        // appropriate query constraint and entirely manages the hydrations.
        $query = $instance->newQuery();

        return new BelongsToMany($query, $this, $table, $foreignKey, $otherKey, $relation);
    }
}

Ответ 7

Добавьте переменную соединения с соединением по умолчанию в жанровой модели:

protected $connection = 'mysql';

У меня были некоторые проблемы с отношениями, не добавляя это.