如何将组合键放入 Laravel 5 的模型中?

How I can put composite keys in models in Laravel 5?

我的数据库中有一个带有两个主键(id 和 language_id)的 table,我需要将其放入我的模型中。模型中的默认主键(Laravel 5 中的Model.php)是 id,我希望主键是 id 和 id_language。我尝试将它与数组或带有“,”的字符串放在一起,但它不起作用。它告诉我无法将数组转换为字符串。

你不能。 Eloquent 不支持复合主键。

Here's a Github issue regarding this.

它似乎变了,因为这个至少适用于 Laravel 5.1:

$table->primary(['key1', 'key2']);

我只是 运行 迁移和我在数据库中看到的内容适合我在上面的代码中输入的内容(当然上面的名称字段仅用于演示目的)。

Update:这对于迁移来说是正确的,但是一旦你想通过 eloquent 插入它就不适用于复合键并且永远不会(最后一条):

https://github.com/laravel/framework/issues/5517

尝试查看此文档以插入与 CK 的多对多关系

https://laravel.com/docs/5.2/eloquent-relationships#inserting-many-to-many-relationships

编辑:一些额外信息
正如您在文档中看到的,attach 和 detach 函数创建了 CK 中间体 table 中所需的链接。所以你不必自己创建它们;)

你的情况是 model->languages()->attach(language_id)

我写了这个简单的 PHP trait 来适应 Eloquent 来处理复合键:

<?php

namespace App\Model\Traits; // *** Adjust this to match your model namespace! ***

use Illuminate\Database\Eloquent\Builder;

trait HasCompositePrimaryKey
{
    /**
     * Get the value indicating whether the IDs are incrementing.
     *
     * @return bool
     */
    public function getIncrementing()
    {
        return false;
    }

    /**
     * Set the keys for a save update query.
     *
     * @param  \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    protected function setKeysForSaveQuery(Builder $query)
    {
        foreach ($this->getKeyName() as $key) {
            // UPDATE: Added isset() per devflow's comment.
            if (isset($this->$key))
                $query->where($key, '=', $this->$key);
            else
                throw new Exception(__METHOD__ . 'Missing part of the primary key: ' . $key);
        }

        return $query;
    }

    // UPDATE: From jessedp. See his edit, below.
    /**
     * Execute a query for a single record by ID.
     *
     * @param  array  $ids Array of keys, like [column => value].
     * @param  array  $columns
     * @return mixed|static
     */
    public static function find($ids, $columns = ['*'])
    {
        $me = new self;
        $query = $me->newQuery();
        foreach ($me->getKeyName() as $key) {
            $query->where($key, '=', $ids[$key]);
        }
        return $query->first($columns);
    }
}

将其放在主模型目录下的 Traits 目录中,然后您可以在任何复合键模型的顶部添加一个简单的单行代码:

class MyModel extends Eloquent {
    use Traits\HasCompositePrimaryKey; // *** THIS!!! ***

    /**
     * The primary key of the table.
     * 
     * @var string
     */
    protected $primaryKey = array('key1', 'key2');

    ...


由 jessedp 添加:
这对我来说非常有效,直到我想使用 Model::find ...所以下面是一些代码(可能会更好)可以添加到上面的 hasCompositePrimaryKey 特性中:

protected static function find($id, $columns = ['*'])
{
    $me = new self;
    $query = $me->newQuery();
    $i=0;

    foreach ($me->getKeyName() as $key) {
        $query->where($key, '=', $id[$i]);
        $i++;
    }

    return $query->first($columns);
}

2016-11-17更新

我现在将其作为名为 LaravelTreats.

的开源包的一部分进行维护

2020-06-10更新

LaravelTreats 已死,但无论如何请享受代码:)

多年来,一些深入的用例引起了我的注意,其中出现了故障。这应该适用于绝大多数用例,但要知道,如果你想变得花哨,你可能不得不重新考虑你的方法。

https://laravel.com/docs/5.3/migrations#columns

是的,你可以。

我分享我的迁移代码:

    <?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class RegistroEmpresa extends Migration {

    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('registro_empresa', function(Blueprint $table)
        {
            $table->string('licencia',24);
            $table->string('rut',12);
            $table->string('nombre_empresa');
            $table->string('direccion');
            $table->string('comuna_Estado');
            $table->string('ciudad');
            $table->string('pais');
            $table->string('fono');
            $table->string('email');
            $table->string('paginaweb');
            $table->string('descripcion_tienda');
            $table->string('monedauso');
            $table->timestamps();
            $table->primary(['licencia', 'rut']);
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('registro_empresa');
    }

}

在迁移中,您可以简单地为 table 定义复合主键,如@erick-suarez 和@sba 所说,在您的 Schema::createSchema::table块写入$table->primary(['key1', 'key2']);

在代表 table 的 Eloquent 模型 中,您不能直接将复合键与 Eloquent 方法一起使用,例如find($key)save($data) 但您仍然可以使用

检索模型实例以供查看
$modelObject = ModelName->where(['key1' => $key1, 'key2' => $key2])->first();

如果您想更新 table 中的记录,您可以使用 QueryBuilder 方法,如下所示:

ModelName->where(['key1' => $key1, 'key2' => $key2])->update($data);

其中 $data 是您要用来更新模型的数据关联数组 ['attribute1' => 'value1', ..]


注意:您仍然可以安全地使用 Eloquent 关系来使用此类模型进行检索,因为它们通常用作枢轴 table 破坏许多-对多关系结构。

你可以尝试使用以下模块

https://github.com/maksimru/composite-primary-keys

只需将 HasCompositePrimaryKey 特征添加到您的模型并将数组值指定为主键

一个简单的解决方案,很适合我 (Laravel 5.7):

模型中定义主键之一:

class MyTable extends Model
{
        protected $primaryKey = 'user_id';
        protected $table = 'my_table';
        protected $fillable = ['user_id', 'post_id', 'flag'];

所以在Controller中传递两个主键,这里我用的是[=12=的两个参数中的第一个] 方法,接收两个数组:

控制器:

public function post_foo(Request $request)
{
//PK1 is user_id, PK2 is post_id
//flag - is the field that has its value changed

$task = MyTable::updateOrCreate(
    ['user_id' => $request->header('UID'),'post_id' => $request->input('post_id')],
    ['user_id' => $request->header('UID'),'post_id' => $request->input('post_id'),'flag' => $request->input('flag')]
    );
    return $task;
}

解释解决方案:

你没有id列作为主键的问题在模型中只用一个主键就解决了,在数据库中正确的行是在控制器中用两个主键找到的。