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

I have in my DataBase a table with two primary keys (id and language_id) and I need put it in my models. The default primaryKey in Models (Model.php in Laravel 5) is id, and I want that the primaryKeys will be id and id_language. I tried put it with arrays or a String with ',' but it doesn't work. It says me that the array could not be converted in String.

Please Help.

Thanks!

Answers


You can't. Eloquent doesn't support composite primary keys.

Here's a Github issue regarding this.


I wrote this simple PHP trait to adapt Eloquent to handle composite keys:

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

Place that in a Traits directory under your main model directory, then you can add a simple one-liner to the top of any composite-key model:

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

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

    ...


addded by jessedp: This worked wonderfully for me until I wanted to use Model::find ... so the following is some code (that could probably be better) that can be added to the hasCompositePrimaryKey trait above:

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

Update 2016-11-17

I'm now maintaining this as part of an open-source package called LaravelTreats.


It seems it changed, since this one works with at least Laravel 5.1:

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

I just run the migration and what i see in the database fits to what i put in code above (of course the name fields above are just for presentation purposes).

Update: this is true for migrations, but as soon as you want to insert via eloquent it doesn´t work with composite keys and will never do (last entry):

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


In migrations you can simply define composite primary keys for a table as @erick-suarez and @sba said, in your Schema::create or Schema::table block write $table->primary(['key1', 'key2']);

In the Eloquent model that represents that table you can't directly use that composite key with Eloquent methods e.g. find($key) nor save($data) but you can still retrieve the model instance for viewing purposes using

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

And if you want to update a record in that table you can use QueryBuilder methods like this:

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

Where $data is the data associative array you want to update your model with like so ['attribute1' => 'value1', ..].


Note: You can still use Eloquent relationships safely for retrieval with such models, as they are commonly used as pivot tables that break many-to-many relationship structures.


Try looking at this documentation for inserting many to many relations with CK

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

Edit: some extra info As you can see in the documentation, the attach and detach functions create the links needed in the CK intermediate table. So yo don't have to create them yourself ;)

In your case it would be model->languages()->attach(language_id)


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

Yes, u can.

i share my migration code:

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

}

you can try to use following module

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

simply add HasCompositePrimaryKey trait to your model and specify array value as primary key


Here is my migration file, it solved my problem of managing keys in migration.. You might need update the model class later, if get Illegal offset type error or something like that. The solution is here - here

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

Please visit these two links below, these solution solved my composite key or multi-column primary key problem -

  1. Creating database with composite keys

  2. Illegal offset type error


Need Your Help

backend/frontend separation in laravel

php laravel laravel-4

I come from a Codeignitor background. At the moment I am building a CMS in Laravel.

Detect repeating letter in an string in Javascript

javascript regex

code for detecting repeating letter in a string.