Laravel Model 使用非預設主鍵

最近遇到一個怪問題,用 laravel 時如果指定 $primaryKey 為其他非 auto-increment 的值時必需同時指定$keyType$incrementing才能 model 在使用時正常

比如說主鍵是 uuid 時要做以下設定才能正確執行,少一個都不能 work

<?php
$primaryKey = 'uuid';
$keyType = 'string';
$incrementing = false;

追了一下 source code 終於理解原因,下面說明存取 model 物件時到底這些欄位對它的影響

存值

  • Model.php

在這支約 L682 的地方有說明如果設定上有 $incrementing 的時候會使用PDO::lastInsertId來取的最後 insert 的 id

<?php
// If the model has an incrementing key, we can use the "insertGetId" method on
// the query builder, which will give us back the final inserted ID for this
// table from the database. Not all tables have to be incrementing though.
$attributes = $this->getAttributes();
if ($this->getIncrementing()) {
    $this->insertAndSetId($query, $attributes);
}

該函數在 mysql 時是使用LAST_INSERT_ID來做,根據mysql官方文件對該函數的說明可得知不管有無指定參數,回傳型別都會是 int,取回的欄位都是 auto_increment 的結果

With no argument, LAST_INSERT_ID() returns a BIGINT UNSIGNED (64-bit) value representing the first automatically generated value successfully inserted for an AUTO_INCREMENT column as a result of the most recently executed INSERT statement. The value of LAST_INSERT_ID() remains unchanged if no rows are successfully inserted.

With an argument, LAST_INSERT_ID() returns an unsigned integer.

因此只設定$keyType以及$primaryKey在 model 新增時取回的 id 就不會是預期的值,必須加上$incrementing才能正確取得回傳值

取值

model 在回傳的時候會去查看$casts裡面的東西並把它mapping起來作為回傳的 attributes,而在使用到的 Traits 裡面的裡面有去特別檢查$incrementing的設定,程式碼如下

  • HasAttributes.php
<?php
public function getCasts()
{
    if ($this->getIncrementing()) {
        return array_merge([$this->getKeyName() => $this->getKeyType()], $this->casts);
    }
    return $this->casts;
}

因此在單純設定$keyType$primaryKey時取值是不會有問題的

結論

如果主 key 不是 laravel 預設型別,一定要同時設定上面提到的三個屬性才能確保 model 能正常 work

cmd + /