【问题标题】:Using a UUID as primary key with Laravel 5在 Laravel 5 中使用 UUID 作为主键
【发布时间】:2015-10-27 07:37:30
【问题描述】:

我正在尝试创建具有主键的表,该主键是定义为 binary(16) 的 UUID,而不是默认的自动递增 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;");

但是,我无法让模型正常工作。我遵循了可用的教程here。我已经这样定义了我的模型:

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

其中Uuid 是Webpatser\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: 我发现 static::creating 事件在创建 UuidModel 的实例时不会被调用。

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

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(){}

}

【问题讨论】:

  • 你试过 id 和 char(36) 吗?
  • 不,我没有。我想 char(36) 的效率会更低。
  • 我真的不知道,但我认为比较二进制文件在比较中没有相同的精度,如果你使用,你应该转换成二进制,对吧?这篇文章可能会有所帮助kccoder.com/mysql/uuid-vs-int-insert-performance
  • 我认为,在位级别,binary(16) 只是一个数字,仅比常规 int(4) 大四倍。整数比较总是比 char(36) 比较快。您指出的文章证明 char 比较很慢。如果我冒险猜测,我会说作者看到了放缓,因为他使用 char(36) 列作为 pk 而不是 binary(16)。
  • 所以你是说你得到一个错误,Eloquent 类没有找到?如果是这样,请发布您遇到的确切错误。

标签: php mysql laravel laravel-5 eloquent


【解决方案1】:

也可以尝试使用这个包,它会在你的模型中自动生成和分配 UUID 字段,也可以通过 UUIDs 键显示和更新。

https://github.com/EmadAdly/laravel-uuid

【讨论】:

    【解决方案2】:

    如何将 36chr UUID 存储为 Binary(16) 的想法:

    IMO 有一个 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 是一个较旧的 (Laravel 4) 构造。我们在 Laravel 5 中使用 class UuidModel extends Model
      2. 解决方法是移动

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

        从AppServiceProvider::boot() 到EventServiceProvider::boot()。无需进行其他更改。一切都按预期进行。

      我仍然不知道为什么 (2) 在 EventServiceProvider 中有效,而不是在 AppServiceProvider 中有效,正如官方 docs 中所解释的那样。但从名字来看,这也许就是它的本意。

      【讨论】:

      • 我会使用一个 trait 而不是创建一个“UUID 模型”类来扩展基础 Model 类。这样,您可以添加和删除 UUID 功能,而无需更改它扩展的类。另外,我不知道您为什么在迁移中使用原始 SQL?
      • @MartinBean 是的,现在我已经开始工作了,我将尝试使用特征。谢谢你的提示。我使用的是原始 sql,因为 AFAIK,SchemaBuilder 不支持二进制列。我的 UUID 列是二进制 (16) 以实现最高效率。
      • laravel.com/docs/master/migrations:在“可用列类型”子标题下,您可以找到二进制文件。
      • @MartinBean 我看到了,但这是映射到没有指定长度的 BLOB 类型。不完全确定 BLOB 是否会像 BINARY(16) 一样有效,而 BINARY(16) 的数量基本上是 INT 的 4 倍。我相信 MySQL 不会将 BLOB 值存储在同一个表中。
      • 我做了一个简单的 trait 来让模型使用 uuid 而不是自动增量。您可以在github.com/alsofronie/eloquent-uuid 找到它。它尽可能简单,因为测试很多选项会减慢已经很慢的过程:)。
      【解决方案4】:

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

      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 函数。实际上,我可以在基类的实现中看到insertAndSetId 仅在$this-&gt;incrementing 为true 时才被调用。在我的例子中,由于uuid 列没有增加,我将$this-&gt;incrementing 设置为false。
      • 使用这种方式递增属性必须为true(默认),看Car model就行了
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-12-20
      • 1970-01-01
      • 2020-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多