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';
У меня были некоторые проблемы с отношениями, не добавляя это.