Как я могу поставить составные ключи в моделях в Laravel 5?

В моей базе данных есть таблица с двумя первичными ключами (id и language_id), и мне нужно поместить ее в мои модели. PrimaryKey по умолчанию в Models (Model.php в Laravel 5) - это id, и я хочу, чтобы primaryKeys были id и id_language. Я пытался поместить его с массивами или String с ',', но это не работает. Он говорит мне, что массив не может быть преобразован в строку.

Пожалуйста помоги.

Спасибо!

12 ответов

Решение

Ты не можешь Eloquent не поддерживает составные первичные ключи.

Вот проблема Github по этому поводу.

Я написал эту простую черту PHP, чтобы адаптировать 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.

Кажется, это изменилось, так как этот работает по крайней мере с Laravel 5.1:

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

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

Обновление: это верно для миграций, но как только вы захотите вставить через eloquent, он не будет работать с составными ключами и никогда не будет работать (последняя запись):

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

В миграциях вы можете просто определить составные первичные ключи для таблицы, как @ erick-suarez и @sba, Schema::create или же Schema::table блок записи $table->primary(['key1', 'key2']);

В модели Eloquent, представляющей эту таблицу, вы не можете напрямую использовать этот составной ключ с методами Eloquent, например find($key) ни save($data) но вы все равно можете получить экземпляр модели для просмотра, используя

$modelObject = ModelName->where(['key1' => $key1, 'key2' => $key2])->first();

И если вы хотите обновить запись в этой таблице, вы можете использовать QueryBuilder методы как это:

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

куда $data ассоциативный массив данных, с которым вы хотите обновить свою модель ['attribute1' => 'value1', ..],


Примечание: Вы все еще можете безопасно использовать отношения Eloquent для извлечения с такими моделями, так как они обычно используются в качестве сводных таблиц, которые нарушают структуры отношений "многие ко многим".

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

Да ты можешь.

я делюсь своим кодом миграции:

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

}

Простое решение, которое хорошо работает для меня (Laravel 5.7):

Определите один из первичных ключей в модели:

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

Итак, передайте оба первичных ключа в Контроллер, здесь я использовал первый из двух параметровupdateOrCreate метод, который получает два массива:

Контроллер:

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 столбец, поскольку первичный ключ решается только с одним первичным ключом в модели, правильная строка в БД находится с двумя первичными ключами в контроллере.

Попробуйте посмотреть в этой документации, чтобы вставить много-много отношений с CK

https://laravel.com/docs/5.2/eloquent-relationships

Изменить: некоторая дополнительная информация
Как видно из документации, функции присоединения и отсоединения создают ссылки, необходимые в промежуточной таблице CK. Так что вам не нужно создавать их самостоятельно;)

В вашем случае это будет model->languages()->attach(language_id)

Пожалуйста, посетите эти две ссылки ниже, это решение решило мою проблему с составным ключом или первичным ключом из нескольких столбцов -

  1. Создание базы данных с составными ключами

  2. Недопустимая ошибка типа смещения

Вот мой файл миграции, он решил мою проблему управления ключами в процессе миграции. Возможно, вам потребуется обновить класс модели позже, если получена ошибка типа недопустимого смещения или что-то в этом роде. Решение здесь - здесь

<?php

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

class CreateAccountSessionsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('account_sessions', function (Blueprint $table) {
            $table->string('session', 64);//creates a string column, that can use in primary key. don't let it exceeds 797 bytes
            $table->integer('account_id')->unsigned();//unsigned integer column
            $table->timestamps();

            $table->primary(['session', 'account_id']);//added the primary keys

            $table->foreign('account_id')->references('id')->on('accounts');//foreign key
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('account_sessions');
    }
}
      <?php

use Illuminate\Database\Eloquent\Builder;

class YourModel extends Model 
{
    protected function setKeysForSaveQuery(Builder $query)
    {
        $query
            ->where('key1', '=', $this->getAttribute('key1'))
            ->where('key2', '=', $this->getAttribute('key2'));

        return $query;
    }
    
}

Вы можете попробовать использовать следующий модуль

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

просто добавьте свойство HasCompositePrimaryKey к вашей модели и укажите значение массива в качестве первичного ключа

Надеюсь , это поможет

Schema::create('vw_term_relationships', function (Blueprint $table) {
            $table->bigInteger('object_id')->unsigned()->default('0');
            $table->bigInteger('term_taxonomy_id')->unique()->unsigned()->default('0');
            $table->integer('term_order')->default('0');
            $table->primary(['object_id', 'term_taxonomy_id']);
        });

Другие вопросы по тегам