使用UUID作为Laravel 5的主键

6

我尝试创建表格,其中有一个主键作为UUID,并定义为binary(16),而不是默认的自增id字段。

我已经成功创建了迁移,使用原始的SQL语句通过DB::statement,就像这样:

DB::statement("CREATE TABLE `binary_primary_keys` (
                      `uuid` binary(16) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
                      `created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
                      `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
                      PRIMARY KEY (`uuid`)
                  ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;");

然而,我在让这个模型工作方面遇到了困难。我已经按照这里提供的教程进行操作。我的模型定义如下:

class UuidModel extends Model
{
    public $incrementing = false;
    public $primaryKey = 'uuid';

    /**
     * The "booting" method of the model.
     *
     * @return void
     */
    protected static function boot()
    {
        parent::boot();

        /**
         * Attach to the 'creating' Model Event to provide a UUID
         * for the `id` field (provided by $model->getKeyName())
         */
        static::creating(function ($model) {
            $model->{$model->getKeyName()} = (string)$model->generateNewId();
            echo($model->{$model->getKeyName()});
        });
    }

    /**
     * Get a new version 4 (random) UUID.
     */
    public function generateNewId()
    {
        return Uuid::generate();
    }
}

UuidWebpatser\Uuid的别名。

我遇到的一个问题是,无法像教程中所述从Eloquent派生UuidModel。事实上,我没有看到Eloquent类。我改为从Model派生。我猜测该教程是针对Laravel 4编写的。

我需要帮助在Laravel 5中实现使用UUID作为主键的表。

编辑1: 因此,如果我这样定义我的类:

use Illuminate\Database\Eloquent
class UuidModel extends Eloquent { ... }

我收到了以下错误:

PHP Fatal error:  Class 'Illuminate\Database\Eloquent' not found in /home/vagrant/transactly/app/UuidModel.php on line 8

如果我删除use Illuminate\Database\Eloquent这行代码,会出现以下错误:

PHP Fatal error:  Class 'App\Eloquent' not found in /home/vagrant/transactly/app/UuidModel.php on line 8

编辑2: 我发现当创建UuidModel的实例时,static::creating事件从未被调用。

我尝试在AppServiceProvider中设置creating事件监听器,但也没有被调用。有趣的是,对于常规的Laravel生成的模型Usercreating事件也不会被调用。

class AppServiceProvider extends ServiceProvider
{
/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    /**
     * Attach to the 'creating' Model Event to provide a UUID
     * for the `id` field (provided by $model->getKeyName())
     */
    echo "Booting...\n";
    UuidModel::creating(function ($model) {
        echo "Creating Uuid Model...\n";
        $model->{$model->getKeyName()} = (string)$model->generateNewId();
    });

    User::creating(function($user){
        echo "Creating User Model...";
        $user->name = 'Forced Name in boot()';
    });
}
public function register(){}

}

你尝试使用char(36)来进行id操作了吗? - Leonardo Jorge
我没有。我想char(36)的效率会更低。 - Code Poet
1
我认为,在位级别上,二进制(16)只是一个数字,比常规int(4)大四倍。整数比较总是比char(36)比较快。你指出的文章证明了char比较慢。如果我猜测的话,我会说作者看到减速是因为他正在使用char(36)列作为pk而不是binary(16)。 - Code Poet
所以你的意思是说你遇到了“Eloquent”类找不到的错误?如果是这样,请发布您收到的确切错误。 - Bogdan
@Bogdan 我已经在编辑1中添加了错误信息。 - Code Poet
显示剩余10条评论
4个回答

4
如何将36位UUID以二进制格式存储的想法如何?
在我看来,不让Laravel生成UUID是有优势的。换句话说,如果将来从应用程序外部插入新记录到数据库中,则UUID字段会被正确填充。
我的建议是:使用迁移创建UUID默认值触发器。
(该触发器使数据库服务器在每次插入新客户时生成UUID)
<?php namespace MegaBank\HighInterestLoans\Updates;
    use Illuminate\Database\Schema\Blueprint;
    use Illuminate\Database\Migrations\Migration;

class MigrationTriggerForCustomers extends Migration
{
public function up()
    {
    
        DB::unprepared('CREATE TRIGGER before_insert_customers
                      BEFORE INSERT ON    
                    `megabank_highinterestloans_customers` 
                      FOR EACH ROW
                      SET new.uuid = UNHEX(REPLACE(UUID(), "-","");');
    }

    public function down()
    {
        DB::unprepared('DROP TRIGGER `before_insert_customers`');
    }
}

最后,如果你想获得一个可读的UUID版本,请按照以下步骤操作:

SELECT HEX(UUID) FROM customers;

无论如何,希望这能帮助到某些人 :-)

3

所以,我已成功让这个东西运行得像魔法一样(未经单元测试):

  1. class UuidModel extends Eloquent is an older (Laravel 4) construct. We use class UuidModel extends Model in Laravel 5
  2. The solution was to move the

    UuidModel::creating(function ($model) {
        echo "Creating Uuid Model...\n";
        $model->{$model->getKeyName()} = (string)$model->generateNewId();
    });
    

    from AppServiceProvider::boot() to EventServiceProvider::boot(). No other changes were required. Everything worked as expected.

我仍然不知道为什么(2)在EventServiceProvider中有效,而在官方文档中解释的AppServiceProvider中无效。但从名称来看,这可能是设计意图。

1
我会使用trait而不是创建一个扩展基本“Model”类的“UUID model”类。这样,您可以添加和删除UUID功能,而无需更改它所继承的类。另外,我不知道为什么您在迁移中使用原始SQL? - Martin Bean
@MartinBean 是的,现在我已经让它工作了,我会尝试使用traits。谢谢你的提示。我正在使用原始SQL,因为据我所知,SchemaBuilder不支持二进制列。我的UUID列是二进制(16)以达到最大效率。 - Code Poet
在“可用列类型”子标题下,您可以找到二进制。请参考http://laravel.com/docs/master/migrations。 - Martin Bean
3
@MartinBean 我看到了,但是这将映射到没有指定长度的 BLOB 类型。不确定BLOB是否像比 INT 大 4 倍的实质上是数字的 BINARY(16) 一样有效。我相信 MySQL 不会将 BLOB 值存储在同一张表中。 - Code Poet
我创建了一个简单的 trait,用于使模型使用 uuid 而不是自增。您可以在 https://github.com/alsofronie/eloquent-uuid 找到它。它尽可能地简单,因为测试很多选项会减慢已经缓慢的过程 :)。 - Alex

0

这是一种不使用事件的快速解决方案。

UUidModel.php

<?php        namespace App;

    use \Illuminate\Database\Eloquent\Model;

    use \Illuminate\Database\Eloquent\Builder;

    class UuidModel extends Model

    {

    /**

    * Insert the given attributes and set the ID on the model.

    *

    * @param  \Illuminate\Database\Eloquent\Builder  $query

    * @param  array  $attributes

    * @return void

    */

        protected function insertAndSetId(Builder $query, $attributes)

        {
            $keyName = $this->getKeyName();
            $id = $attributes[$keyName] = $this->generateNewId();
            $query->insert($attributes);
            $this->setAttribute($keyName, $id);
        }

    /**
    * Get a new version 4 (random) UUID.
    */
        public function generateNewId()
        {
            return 'uuid!!' ;// just for test
        }
    }
?>

模型示例 Car.php

<?php namespace App;

    class Car extends UuidModel {
    }

我尝试了这种方法,但是insertAndSetId函数从未被调用。实际上,我可以看到在基类的实现中,只有当$this->incrementingtrue时才会调用insertAndSetId。在我的情况下,由于uuid列不是递增的,因此我将$this->incrementing设置为false - Code Poet
使用这种方式,递增属性必须为true(默认值),只需查看Car模型即可。 - Leonardo Jorge

-3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接