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

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


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

<?phpnamespace 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.

Update 2020-06-10

LaravelTreats is dead, but enjoy the code anyways :)

Over the years, a few in-depth use cases have been brought to my attention where this breaks down. This should work for the vast majority of use cases, but know that if you try to get fancy, you might have to rethink your approach.


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