laravel BelongsTo与不同数据库的关系不起作用

时间:2015-09-06 10:33:00

标签: laravel

我在几个地方看到过“远离”这个,但唉 - 这就是我的数据库的构建方式:

class Album extends Eloquent {

   // default connection

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

和类型表:

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

}

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

它没有正确选择(不将数据库名称添加到表“genres”):

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;

更新

到目前为止,我发现的最好的方法是使用构建器的“from”方法来专门命名正确的连接。 我发现查询中的构建器可以接收“from”

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

这是一个不错的解决方案,但它需要我在数据库中挖掘php并找到从关系'genre'获取正确的表和数据库名称的好方法。

如果其他人能够在此解决方案的基础上进一步发展并使其更加通用,我将不胜感激。

11 个答案:

答案 0 :(得分:7)

这是我自己的解决方案,它对我来说一般都适用,但它很复杂。

我正在使用构建器“from”方法在子查询中正确设置表和数据库。我只需要在里面传递正确的信息。

假设子查询可以像“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);
    });

TL:博士;

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

答案 1 :(得分:3)

您应该先克隆,否则您将更改模型的默认连接。它会产生副作用。

class Album extends Eloquent {
    public function genre() {
        $newResource = clone $this;
        return $newResource->setConnection('Resources')->belongsTo('genre');
    }
}

答案 2 :(得分: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'    => '',
        ),
    ),
);

因此,您的两个连接的别名为mysqlmysql2

然后你可以告诉雄辩使用哪个“别名”如下:

<?php

class SomeModel extends Eloquent {

    protected $connection = 'mysql2';

}

然后你可以像平常一样设置你的关系。

tl; dr:基本上不是在eloquent中将表名指定为$connection,而是在配置中指定连接别名,它应该有效。

答案 3 :(得分:2)

看起来Eager Loading会做你想做的事

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

答案 4 :(得分:2)

这是它为我工作的方式:

在我的.env和config / database.php中,我定义了其他连接=> How to use multiple databases in Laravel

我以这种方式更新了模型:

class MyOtherDBModel extends Model
{
    protected $table = 'tablename';
    protected $connection = 'mysql2';

    public function __construct(array $attributes = [])
    {
        $this->table = env('DB_DATABASE_2').'.'.$this->table;
        parent::__construct();
    }
}

class MyModel extends Model
{
    public function myOtherModel()
    {
        return $this->belongsTo(MyOtherDBModel::class, 'field', 'field');
    }
}

现在我可以打电话

$query = MyModel::whereHas('myOtherModel');

答案 5 :(得分:1)

使用类型模型上的默认连接添加连接变量:

protected $connection = 'mysql';

通过不添加此关系,我遇到了一些关系问题。

答案 6 :(得分:1)

我在Laravel 5.6上遇到了同样的问题。在多对多方案中,假设我ModelA的连接是默认连接,我所做的是以下内容:

1.-在关系中添加模式名称:

// From ModelA and default connection (a.k.a connection1)
$this->belongsToMany('ModelB', 'schema.pivot-table-name');

// From ModelB and connection2
$this->belongsToMany('ModelA', 'schema.pivot-table-name');

2.-覆盖ModelB中的连接参数以及将模式指定为覆盖的$table属性中的前缀,例如

protected $connection = 'connection2';

protected $table = 'connection2-schema-name.table';

3.-如果要求数据透视表的自定义行为,我所做的只是实现所需的模型,并通过模型关系上的->using('PivotModel');函数指定它(如{{中所述) 3}})。最后我做了与上面第2点相同的事情,但是在枢轴模型上

我还没有尝试过,但我想其他类型的关系也是如此,至少对于基本关系(一对一,一对多等)

答案 7 :(得分:1)

laravel v5.7及更高版本的解决方案

class Album extends Eloquent {

   // default connection

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

答案 8 :(得分:0)

要通过'资源'开始更改 database.php 中的'资源',会更好!

我很好奇,你能试试吗?

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

答案 9 :(得分:0)

当关系无法处理模型连接时,我遇到了同样的问题。

我的解决方案是覆盖试图建立的模型上的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);
    }
}

答案 10 :(得分:0)

当关系没有在父连接上工作时,我遇到了同样的问题。

我的解决方案是覆盖试图建立的模型上的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);
    }
}