Giter Site home page Giter Site logo

laravel-binary-uuid's Introduction

THIS PACKAGE IS NOT MAINTAINED ANYMORE

Alternatives: https://github.com/michaeldyrynda/laravel-efficient-uuid & https://github.com/michaeldyrynda/laravel-model-uuid

Using optimised binary UUIDs in Laravel

Latest Version on Packagist Build Status Code coverage Quality Score StyleCI Total Downloads

Using a regular uuid as a primary key is guaranteed to be slow.

This package solves the performance problem by storing slightly tweaked binary versions of the uuid. You can read more about the storing mechanism here: http://mysqlserverteam.com/storing-uuid-values-in-mysql-tables/.

The package can generate optimized versions of the uuid. It also provides handy model scopes to easily retrieve models that use binary uuids.

Want to test the perfomance improvements on your system? No problem, we've included benchmarks.

The package currently only supports MySQL and SQLite.

Installation

You can install the package via Composer:

composer require spatie/laravel-binary-uuid

Usage

To let a model make use of optimised UUIDs, you must add a uuid field as the primary field in the table.

Schema::create('table_name', function (Blueprint $table) {
    $table->uuid('uuid');
    $table->primary('uuid');
});

To get your model to work with the encoded UUID (i.e. to use uuid as a primary key), you must let your model use the Spatie\BinaryUuid\HasBinaryUuid trait.

use Illuminate\Database\Eloquent\Model;
use Spatie\BinaryUuid\HasBinaryUuid;

class TestModel extends Model
{
    use HasBinaryUuid;
}

If don't like the primary key named uuid you can manually specify the getKeyName method. Don't forget set $incrementing to false.

use Illuminate\Database\Eloquent\Model;
use Spatie\BinaryUuid\HasBinaryUuid;

class TestModel extends Model
{
    use HasBinaryUuid;

    public $incrementing = false;
    
    public function getKeyName()
    {
        return 'custom_uuid';
    }
}

If you try converting your model to JSON with binary attributes, you will see errors. By declaring your binary attributes in $uuidAttributes on your model, you will tell the package to cast those UUID's to text whenever they are converted to array. Also, this adds a dynamic accessor for each of the uuid attributes.

use Illuminate\Database\Eloquent\Model;
use Spatie\BinaryUuid\HasBinaryUuid;

class TestModel extends Model
{
    use HasBinaryUuid;
    
    /**
     * The suffix for the uuid text attribute 
     * by default this is '_text'
     * 
     * @var
     */
    protected $uuidSuffix = '_str';
    
    /**
     * The binary UUID attributes that should be converted to text.
     *
     * @var array
     */
    protected $uuids = [
        'country_uuid' // foreign or related key
    ];
}

In your JSON you will see uuid and country_uuid in their textual representation. If you're also making use of composite primary keys, the above works well enough too. Just include your keys in the $uuids array or override the getKeyName() method on your model and return your composite primary keys as an array of keys. You can also customize the UUID text attribute suffix name. In the code above, instead of '_text' it's '_str'.

The $uuids array in your model defines fields that will be converted to uuid strings when retrieved and converted to binary when written to the database. You do not need to define these fields in the $casts array in your model.

A note on the uuid blueprint method

Laravel currently does not allow adding new blueprint methods which can be used out of the box. Because of this, we decided to override the uuid behaviour which will create a BINARY column instead of a CHAR(36) column.

There are some cases in which Laravel's generated code will also use uuid, but does not support our binary implementation. An example are database notifications. To make those work, you'll have to change the migration of those notifications to use CHAR(36).

// $table->uuid('id')->primary();

$table->char('id', 36)->primary();

Creating a model

The UUID of a model will automatically be generated upon save.

$model = MyModel::create();

dump($model->uuid); // b"\x11þ╩ÓB#(ªë\x1FîàÉ\x1EÝ." 

Getting a human-readable UUID

UUIDs are only stored as binary in the database. You can however use a textual version for eg. URL generation.

$model = MyModel::create();

dump($model->uuid_text); // "6dae40fa-cae0-11e7-80b6-8c85901eed2e" 

If you want to set a specific UUID before creating a model, that's also possible.

It's unlikely though that you'd ever want to do this.

$model = new MyModel();

$model->uuid_text = $uuid;

$model->save();

Querying the model

The most optimal way to query the database:

$uuid = 'ff8683dc-cadd-11e7-9547-8c85901eed2e'; // UUID from eg. the URL.

$model = MyModel::withUuid($uuid)->first();

The withUuid scope will automatically encode the UUID string to query the database. The manual approach would be something like this.

$model = MyModel::where('uuid', MyModel::encodeUuid($uuid))->first();

You can also query for multiple UUIDs using the withUuid scope.

$models = MyModel::withUuid([
    'ff8683dc-cadd-11e7-9547-8c85901eed2e',
    'ff8683ab-cadd-11e7-9547-8c85900eed2t',
])->get();

Note: Version 1.3.0 added simplified syntax for finding data using a uuid string.

$uuid = 'ff8683dc-cadd-11e7-9547-8c85901eed2e'; // UUID from eg. the URL.

$model = MyModel::find($uuid);  

$model = MyModel::findOrFail($uuid);

Version 1.3.0 query for multiple UUIDs.

$uuids = [
    'ff8683dc-cadd-11e7-9547-8c85901eed2e',
    'ff8683ab-cadd-11e7-9547-8c85900eed2t',
];

$model = MyModel::findMany($uuids);

Querying relations

You can also use the withUuid scope to query relation fields by specifying a field to query.

$models = MyModel::withUuid('ff8683dc-cadd-11e7-9547-8c85901eed2e', 'relation_field')->get();

$models = MyModel::withUuid([
    'ff8683dc-cadd-11e7-9547-8c85901eed2e',
    'ff8683ab-cadd-11e7-9547-8c85900eed2t',
], 'relation_field')->get();

Running the benchmarks

The package contains benchmarks that prove storing uuids in a tweaked binary form is really more performant.

Before running the tests you should set up a MySQL database and specify the connection configuration in phpunit.xml.dist.

To run the tests issue this command.

phpunit -d memory_limit=-1 --testsuite=benchmarks

Running the benchmarks can take several minutes. You'll have time for several cups of coffee!

While the test are running average results are outputted in the terminal. After the tests are complete you'll find individual query stats as CSV files in the test folder.

You may use this data to further investigate the performance of UUIDs in your local machine.

Here are some results for the benchmarks running on our machine.

A comparison of the normal ID, binary UUID and optimised UUID approach. Optimised UUIDs outperform all other on larger datasets.

Comparing different methods

Testing

composer test

Changelog

Please see CHANGELOG for more information what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security

If you discover any security related issues, please email [email protected] instead of using the issue tracker.

Postcardware

You're free to use this package, but if it makes it to your production environment we highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using.

Our address is: Spatie, Samberstraat 69D, 2060 Antwerp, Belgium.

We publish all received postcards on our company website.

Credits

Support us

Spatie is a webdesign agency based in Antwerp, Belgium. You'll find an overview of all our open source projects on our website.

Does your business depend on our contributions? Reach out and support us on Patreon. All pledges will be dedicated to allocating workforce on maintenance and new awesome stuff.

License

The MIT License (MIT). Please see License File for more information.

laravel-binary-uuid's People

Contributors

akoepcke avatar brendt avatar carusogabriel avatar d1am0nd avatar dcorrea777 avatar faustbrian avatar fduchateau avatar freekmurze avatar isocroft avatar lonnylot avatar louisl avatar slashequip avatar tvbeek avatar vpratfr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

laravel-binary-uuid's Issues

Can we have an incremental id (for internal relations) too?

The package handles UUID greatly but using UUID for everything doesn't make sense.

For example, we can use UUID as external key to requested resource and an incremental id to handle internal requests (and relations). But by disabling $incrementing, we're unable to have both.

So my question is how is it possible to have an incremental id alongside an optimized UUID and actually use them?

Running DB::purge() is unloading extension

Using a DB::purge() to reset the database connection is unloading this UUID plugin, causing an issue with multi-database sites.

I'm using a multi-database approach on our website. I'm building a multi-db migration using a method of overloading the migration commands I found online.

However, I discovered an issue when attempting to use this extension in combination with the DB::purge() method for re-initializing the DB resources for the second database. When I use DB::purge() this extension seems to get unloaded. When I try to use DB::disconnect() I run into migration issues, due to latent knowledge of migration table.

Is there anyway to re-instantiate this plugin when I need to purge out existing db connections?

`HasUuidPrimaryKey` trait is redundant

The HasBinaryUuid trait already assumes that the primary key of the model should be a binary UUID:

protected static function bootHasBinaryUuid()
{
    static::creating(function (Model $model) {
        if ($model->{$model->getKeyName()}) {
            return;
        }

        $model->{$model->getKeyName()} = static::encodeUuid(Uuid::uuid1());
    });
}

and also more or less expects the primary key name to be uuid (rather than id or whatever).

public function getUuidTextAttribute(): string
public function setUuidTextAttribute(string $uuid)

So HasUuidPrimaryKey trait doesn't influence whether the primary key will be treated as UUID, which is kind of bad, if you just want to use HasBinaryUuid trait for other attributes.
But even if the intention of HasUuidPrimaryKey is just an overkill way to change the primary key name from id to uuid, it makes no sense tha HasUuidPrimaryKey implements

public function getIncrementing()
{
    return false;
}

while HasBinaryUuid doesn't.

The best behavior would be for HasBinaryUuid to leave the primary key alone, unless HasUuidPrimaryKey is set.

Can't work with Laravel Passport

Laravel Passport will throw errors if using binary UUID.
I just created a package laravel-passport-binary-uuid-adapter to allow Passport accept binary UUID to work with this package.

I found some people encountering the same issue here, so if you don't mind, add a link to this package to your README will be helpful for people who are using Passport I think😃

Expose method to generate binary UUID

Hi,

I have a use case where I need to insert lots of rows to a table. To do so, my approach is to use Eloquent's insert method and provide it an array of data to insert, instead of creating lots of instances of my model and then save() each - which will run a single query per row. The goal is to optimize my INSERT process, since I require lots of data to be inserted to the table.
Since my table uses binary UUID as IDs, I need to include a binary ID in my array which I pass to the insert method. To do this, I used:
MyModel::encodeUuid(\Ramsey\Uuid\Uuid::uuid1())

It would be nice if the package provide a way to generate binary UUIDs.
Else - any suggestions for an alternative approach for INSERTing lots of rows?

Thanks a lot

Not compatible with Scout

On laraval 5.6 in combination with Scout I get a json utf 8 error

Imported [App\Models\Users] models up to ID: ���^�҆�����
and
ERROR: JSON parsing error: malformed UTF-8 characters, possibly incorrectly encoded

If i uninstall the packages and use uuid as a string then the combination with scout is not a problem.

How can i fix the malformed utf-8 error?

Error in HasBinaryUuid trait...?

Isn't this method in Spatie\BinaryUuid\HasBinaryUuid likely to cause errors when explicitly setting the primary key...?

    public function getKeyName()
    {
        return 'uuid';
    }

The examples seem to allow for explicitly setting the primary key to something other than uuid:

If don't like the primary key named uuid you can leave off the HasUuidPrimaryKey trait and manually specify $primaryKey.

Make models with HasBinaryUuid trait serializable when the model key is not selected

Right now this injects the key to the returned array, even when the key is not selected. Which makes Model::select(fields) a pain to use when the primary key is not selected.

Problem:

// Model with `HasBinaryUuid` trait
Model::select('created_at')->first()->toArray();

Throws an error saying uuid cannot be null:

TypeError: Argument 1 passed to Model::decodeUuid() must be of the type string, null given, called in spatie/laravel-binary-uuid/src/HasBinaryUuid.php on line 89

Is it necessary to explicitly set incrementing to false...?

The docs suggest that "[t]o get your model to work with the encoded UUID (i.e. to use uuid as a primary key), you must let your model use the Spatie\BinaryUuid\HasBinaryUuid" but "[i]f [you] don't like the primary key named uuid you can leave off the HasUuidPrimaryKey trait and manually specify $primaryKey. Don't forget set $incrementing to false."

However, Spatie\BinaryUuid\HasBinaryUuid has the following method:

    public function getIncrementing()
    {
        return false;
    }

So...shouldn't it be enough to just use Spatie\BinaryUuid\HasBinaryUuid and not explicitly set $incrementing to false...?

Incompatible with Laravel Horizon

Laravel Horizon has a feature where it tries to tag a jobs intelligently if it sees that the object passed into the job is an Eloquent model. When they do this, they use $model->getKey(). With binary UUIDs, getKey() returns a binary string, so things kinda explode in there when it tries to pass the string to Redis.

I'm not sure what other side effects overriding getKey() to instead pass the UUID as a string would have, but I think this needs to be fixed in some way or another.

Edit: Did a quick find in all files for ->getKey()... Apparently it gets used all over the place to do WHERE SQL queries, so I don't think overriding it would do much good (without additional changes). I'm not sure where to go from here. I reported it on laravel/horizon to see what happens.

Use ID and UUID

Its possible use ID and UUID in same table with relations?

I use, but cause Malformed UTF-8 characters, possibly incorrectly encoded on save with relations.

Unknown database type binary requested, Doctrine\DBAL\Platforms\SqlitePlatform may not support it.

Hey there,

I'm using this alongside Laravel Dusk with SQLite for testing purposes, problem is when I try and run the db migrations I get this error:

Doctrine\DBAL\DBALException: Unknown database type binary requested, Doctrine\DBAL\Platforms\SqlitePlatform may not support it.

I've not really used Dusk or SQLite before and after some digging SQLite doesn't have the "binary" datatype but instead uses "blob".

I checked the SQLiteGrammar file and it specifies binary(16) for the typeUuid - changing this to blob(256) fixes the issue.

Just wondering if it's something I'm doing wrong or should the Grammar file specify the blob data type?

thanks

Undefined property when declearing uuid method

Heya, I'm trying to defined the following method on a modal which uses this library's trait but I get the following exception. Any idea why this is happening? Can't seem to wrap my head around it.

    public function uuid(): UuidInterface
    {
        return Uuid::fromString($this->uuid_text);
    }
ErrorException: Undefined property: App\Models\Subscription::$uuid in /Users/driesvints/Sites/laravelio/vendor/spatie/laravel-binary-uuid/src/HasBinaryUuid.php:81

Let me know if you want more info.

Error when using with Implicit route model binding

Getting the following error when using implicit route model binding:

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'uuid_text' in 'where clause' (SQL: select * from projects where uuid_text = e775e0aa-f407-11e7-8a11-080027a8df8b limit 1)

Seems like it's using uuid_text as defined in getRouteKeyName().

My current workaround is to add a middleware to encode the UUID before the route model binding kicks in, and change the routeKeyName to uuid, which seem to work. But would love to hear if there's any better solution.

Great package for a very common use-case anyhow, thanks!

uuid() blueprint method creates a char(36) column instead of binary(16)

I am using v1.3.0 of this package with Laravel v5.6.39.

According to the docs, $table->uuid('uuid') is supposed to create a binary(16) column in database, but for some reason a char(36) is created instead.

I am changing database connections on the fly, so maybe that's the problem? Maybe the override behavior of Laravel's uuid() method is not working or something is not registering correctly?

JSON serialization still doesn't work

PHP Fatal error:  Method App\Models\Demo\Account::__toString() must not throw an exception, caught Illuminate\Database\Eloquent\JsonEncodingException: Error encoding model [App\Models\Demo\Account] with ID [���}E�A����B�� in /var/www/laravel/vendor/laravel/framework/src/Illuminate/Database/MySqlConnection.php on line 80

In MySqlConnection.php line 80:
                                                                                                                                                                                                               
  Method App\Models\Demo\Account::__toString() must not throw an exception, caught Illuminate\Database\Eloquent\JsonEncodingException: Error encoding model [App\Models\Demo\Account] with ID [���}E�A����B��

This is when trying to create the model during a DB seeder. I'm just doing this:

$person = new App\Models\Demo\Account;
$person->identifier = $code;
$person->name = $person;
$person->address = $address;
$person->date_of_birth = date('Y-m-d');
$person->save();

I'm using the latest, v1.1.1

with('relation') . cause utf8 encode error

the problem is that laravel toArray add pivot data and if it include uuid it will make utf8 error

i fixed this by removing pivot from the array
if (isset($array['pivot'])){
unset($array['pivot']);
}

this is the new trait toArray


    public function toArray()
    {
        $uuidAttributes = $this->getUuidAttributes();
        $array = parent::toArray();
        if (! $this->exists || ! is_array($uuidAttributes)) {
            return $array;
        }
        foreach ($uuidAttributes as $attributeKey) {
            if (! array_key_exists($attributeKey, $array)) {
                continue;
            }
            $uuidKey = $this->getRelatedBinaryKeyName($attributeKey);
            $array[$attributeKey] = $this->{$uuidKey};
        }

  if (isset($array['pivot'])){
            unset($array['pivot']);
        }


        return $array;
    }

     Schema::create('roles', function (Blueprint $table) {
            $table->uuid('id');
            $table->string('name')->unique();
        });

        Schema::create('permissions', function (Blueprint $table) {
            $table->uuid('id')->primary();
            $table->string('name');
        });
    Schema::create('permission_role', function (Blueprint $table) {
            $table->uuid('permission_id')->index()->foreign('permission_id')->references('slug')->on('permissions')->onDelete('cascade');;
            $table->uuid('role_id')->index()->foreign('role_id')->references('id')->on('roles')->onDelete('cascade');
        });

class Role extends Model
{
    use HasBinaryUuid;

    protected $uuids = [
        'id'
    ];
  public function permissions()
    {
        return $this->belongsToMany(Permission::class,'permission_role','role_id','permission_id');

    }

}
class Permission extends Model
{
    use HasBinaryUuid;

    protected $uuids = [
        'id'
    ];

 public function roles()
    {
        return $this->belongsToMany(Role::class,'permission_role','permission_id','role_id');
    }

}



    Route::any('roles', function () {
        return Role::with('permissions')->get() ;
    });


Malformed UTF-8 characters, possibly incorrectly encoded
i think the model to Array not working on collections and join

any help

Using Eloquent find* methods with string UUID

Hi,

Currently we can only use the find* methods (find, findOrFail, etc) with binary UUIDs.

It would be nice to get those methods to work when given a valid UUID string too.

Would you be ok for a PR which would override those find methods in the trait to make use of the withUuid scode instead of the withKey scope when a UUID string is detected?

Rename typeUuid to typeUuidBinary to allow use of normal UUIDs

Currently it is not possible to use normal UUIDs for other fields as the typeUuid method overwrites the original uuid method that Laravel comes with.

It would be good if it would be possible to have 2 distinct functions for uuid and uuidBinary to allow use of both as the package doesn't automatically store every uuid type field as an optimised UUID.

A use case for that would be invite codes where you maybe choose to use UUIDs as tokens but if you use this package you will have to either use string or char(36).

[Idea] Split up HasBinaryUUID trait

The HasBinaryUUID trait is quite 'all in'. It's not so easy to mix how your models work.

If your model A does not have binary uuid, but B has, and you're working with relations, it's nice to have the toArray() from the trait, but I don't want the creating listener code.

Relies on static UUID factory not being changed

Currently the library sets the static default Uuid factory with OrderedTimeCodec at boot in the package service provider.

The issue is where somewhere else in the application changes the static factory and uses a different codec.

Solution is to register a singleton OrderedTimeCodec in the service container, and resolve the instance whenever it is needed.

Will submit a PR.

Thanks for the library :) Although it's not quite suitable for my needs, I was able to adapt some ideas for my own project.

Accessors for non-primary-key UUID fields

The HasBinaryUuid trait seems to forcefully convert the primary key to its text representation during toArray. Even if this was the wanted behavior, any other UUID fields are left on their own. For example, a "belongs to" scenario, where a user_id or user_uuid binary UUID exists, nothing is done to convert that field to text. That leads to unclear exceptions due to json_decode failing for the binary field.

For consistency, the primary key should probably not forcefully be converted to text. Instead we could add something like public $uuidFields = ['id', 'user_id']; to the models. A default value for $uuidFields could take care of the automatic conversion of the primary key, while the user may set $uuidFields = [] to disable that behavior.

Model using the HasBinaryUuid trait is not booted correctly

Problem occurs when you try to convert newly created model instance to an array:

(new App\SomeModel(['foobar' => 'whatever']))->toArray();
TypeError: Argument 1 passed to App\SomeModel::decodeUuid() must be of the type string, null given, called in […]/vendor/spatie/laravel-binary-uuid/src/HasBinaryUuid.php on line 81

This happens because toArray() tries to get an "uuid text" attribute which is null at this point:

public function getUuidTextAttribute(): string
{
    return static::decodeUuid($this->{$this->getKeyName()});
}

Migration ignoring table prefix

After installing this package, and runing php artisan migration

 SQLSTATE[HY000]: General error: 1005 Can't create table 'boljacij_new.#sql-357d_1c82b7' (errno: 150) (SQL: alter table `promotion_pages` add constraint `promotion_pages_image_id_foreign` foreign key (`image_id`) references `images`
  (`id`) on delete restrict on update restrict)

config/database.php:

'connections' => [

        'mysql' => [
            'driver' => 'mysql',
            'host' => env('DB_HOST', 'localhost'),
            'port' => env('DB_PORT', '3306'),
            'database' => env('DB_DATABASE', 'forge'),
            'username' => env('DB_USERNAME', 'forge'),
            'password' => env('DB_PASSWORD', ''),
            'charset' => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix' => 'bc_',
            'strict' => false,
            'engine' => 'InnoDB',
        ],

db migration file

Schema::create('promotion_pages', function (Blueprint $table) {
            $table->uuid('uuid');
            $table->string('title');
            $table->text('content');
            $table->integer('image_id')->unsigned()->default(1);
            $table->foreign('image_id')->references('id')->on('images')
                ->onUpdate('restrict')->onDelete('restrict');
            $table->primary('uuid');
        });

When I remove this package, everyting runs normal.

json_encode results in JSON_ERROR_UTF8 because of uuid field

Currently it is not possible to json serialize any model that uses this package because the uuid field is in binary format.

json_encode(\App\Announcement::first());

dd(json_last_error()); // Results in 5 which equals JSON_ERROR_UTF8

I am currently solving this issue by using protected $hidden = ['uuid']; and protected $appends = ['id'] where ID is the method from here #12

It might be worth considering to replace the uuid field data with the uuid_text attribute value for the array format to allow working with it without any extra hacks like I do right now.

Using pivot tables help with uuid

So my User model has uuid enabled and its successfully migrating and has a binary uuid
Now my Role model has a Pivot relation with User as role_user table with role_id and user_id.

My question is instead of user_id, what will I have to use?

    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->uuid('uuid');
            $table->primary('uuid');
            $table->string('name');
            $table->string('email')->unique();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

My role_user table
   public function up()
    {
        Schema::create('role_user', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('role_id')->unsigned()->nullable()->index();
            $table->uuid('user_uuid')->unsigned()->nullable()->index();
            
        });
    }

My Roles Table

 public function up()
    {
        Schema::create('roles', function (Blueprint $table) {
            $table->increments('id');
           // $table->string('display_name');
            $table->string('name');
            $table->timestamps();
        });
    }

Problem: When I try to save the relation like this it gives me error:
Route::get('/test', function(){

    $uuid = '478d7068-ae64-11e8-a665-2c600cf6267b';

    $model = User::withUuid($uuid)->first();

    $model->roles()->save(new Role(['name' => 'Admin']));

 });

Ordered UUID for Laravel MySQL grammar query compilation via custom builder

Once pending PRs are reviewed/attended to, it will be pertinent to consider ordered uuids for setting up partial sequential ids in database tables making it easy to sort/index at DB level. The custom eloquent builder and grammar PHP classes in this package will be updated to have this new behaviour. This could be the basis of a version 2 of the package focusing on performance with respect to UUIDs.

For context, see:

https://www.percona.com/blog/2014/12/19/store-uuid-optimized-way

Usage with Laravel Nova

Has anyone successfully managed to setup Nova with Binary UUIDs?

Currently trying to do so, but it seems to be a loooonnnnnggggg way till I get it to work.

Several reports here and there of people not being able to use it even with String UUIDs ...

Laravel Scout: Issue with newQueryForRestoration($id) (Add Array Support)

Greetings,

Firstly, excellent package and nice work being done here!

The issue:

I am trying to get this to play nice with Laravel Scout and am encountering an issue when using queues with Scout. In the HasBinaryUuid trait, the following function is implemented in your package:

public function newQueryForRestoration($id)
{
		return $this->newQueryWithoutScopes()->whereKey(base64_decode($id));
}

When performing operations on models with the Searchable trait, it seems to be passing an array to this function and causing an exception. It seems to be a one element array based on my testing as such:

array(1) { [0]=> string(36) "2d6a1fc0-b4a8-11e8-8375-ac220bbf9530" } (example)

The error: base64_decode() expects parameter 1 to be string, array given

The (very quick) workaround for me:

public function newQueryForRestoration($id)
{
		if ( is_array($id) ) {
			$id = $id[0];
		}
	  return $this->newQueryWithoutScopes()->whereKey(base64_decode($id));
}

This is the official source of this function from Laravel:

public function newQueryForRestoration($ids)
{
	return is_array($ids)
			? $this->newQueryWithoutScopes()->whereIn($this->getQualifiedKeyName(), $ids)
			: $this->newQueryWithoutScopes()->whereKey($ids);
}

https://github.com/laravel/framework/blob/5.6/src/Illuminate/Database/Eloquent/Model.php#L972

Does it make sense to make your function override support arrays as well?

Thanks

Package breaks Laravel Notifications because of overwrites

Laravels database notifications use $table->uuid('id')->primary(); by default so using this package breaks them which results in SQLSTATE[22001]: String data, right truncated: 1406 Data too long for column 'id' at row 1.

$table->char('id', 36)->primary(); solves the issue but having to change the default migrations is kinda off-putting.

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateNotificationsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('notifications', function (Blueprint $table) {
            $table->uuid('id')->primary();
            $table->string('type');
            $table->morphs('notifiable');
            $table->text('data');
            $table->timestamp('read_at')->nullable();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('notifications');
    }
}

laravel passport support

i add this to app provider boot function

 Passport::ignoreMigrations();
   
 Passport::$tokenModel= \App\Token::class;

and changed user_id in oauth_access_tokens to uuid in migration

the problem is i need to leave the string token index in the \App\Token::class;

<?php

namespace App;

use Laravel\Passport\Token as BaseToken;

use App\Traits\HasBinaryUuid;

class Token extends BaseToken
{
    use HasBinaryUuid;

    protected $uuids = [
        'user_id' // foreign or related key
    ];

}

but i am getting this error i am sure its problem because of uuid

RuntimeException
Error while encoding to JSON: Malformed UTF-8 characters, possibly incorrectly encoded

i need to use the trait HasBinaryUuid
but disable primary key just for foreign one

[Question] Infinite recursion for getAttribute when present in multiple traits

I am using this library along with some other libraries which also provide traits for the getAttribute method.

I cannot manage to find the right way to use both traits for the same model class.

Here is the corresponding StackOverflow question: https://stackoverflow.com/questions/51823409/detect-recursion-when-overridding-trait-methods which has some more details about the use case.

Any input? Ideas? You may have already solve that as your libraries often override that method.

Lumen support

What is the reason for the dependency "laravel/framework": "~5.5.22"? I created a fork and replaced it with "laravel/lumen-framework": "~5.5" and it works successfully.

And reasons not to include Lumen support?

[BUG]Stop overriding `getKeyName` method of Model class.

Hi!
Thanks for the great works!!

I found a problem to redefine primary key of models.

In the spatie/laravel-binary-uuid/src/HasBinaryUuid.php

where this getKeyName method is overriding Illuminate\Database\Eloquent\Model::getKeyName. Which cause problems.

trait HasBinaryUuid{
    public function getKeyName()
    {
        return 'uuid';
    }
}

according to your documentation:

If don't like the primary key named uuid you can manually specify $primaryKey. Don't forget set $incrementing to false.

use Illuminate\Database\Eloquent\Model;
use Spatie\BinaryUuid\HasBinaryUuid;

use Illuminate\Database\Eloquent\Model;
use Spatie\BinaryUuid\HasBinaryUuid;

class TestModel extends Model
{
    use HasBinaryUuid;

    public $incrementing = false;
    
    public $primaryKey = 'uuid';  // THIS WON'T WORK! Because primaryKey is hardcoded in the `getKeyName` method.
}

To fix it just remove the getKeyName method from the HasBinaryUuid trait.

Am I missing something…!? Because obviously overriding parent's method and hardcoding options is a bad idea in the first place… or maybe providing a config file would be nicer just like the https://github.com/EmadAdly/laravel-uuid/blob/master/src/LaravelUuidServiceProvider.php#L25 dose so.

Can't store values in a model with UUID in it

So I set it up everything perfectly but when I create a 'store' function it says this

``SQLSTATE[HY000]: General error: 1364 Field 'id' doesn't have a default value (SQL: insert into users (`name`, `email`, `password`, `uuid`, `updated_at`, `created_at`) values\

Do i also need to specificy the uuid inside the add function? Doesn't it get generated automatically like 'id' use to?

"Malformed UTF-8 characters, possibly incorrectly encoded" with uuid relations

I have some hard times with uuid relations and json representation.

My work model has two foreign keys (user_id and workstation_id) in binary format.
My user model has a hasMany relationship with the work model.

My controller looks like :

public function show($uuid)
{
    $user = User::withUuid($uuid)
        ->with('profile')
        ->with('works')
        ->first();

    if (! $user) {
        abort(404);
    }

    return compact('user');
}

It throw the error mentionned in the title.

But it works well with tinker :

$user = User::withUuid('2094a25a-efde-11e7-8005-f156302fb27e')->with('profile')->with('works')->paginate(10)->first();
[!] Aliasing 'User' to 'App\User' for this Tinker session.
=> App\User {#795
     id: b"\x11çïÞ ”¢Z€\x05ñV0/²~",
     username: "amber.pagac",
     email: "[email protected]",
     firstname: "Kip",
     lastname: "Lindgren",
     group: "e",
     profile_id: 1,
     login_count: 0,
     created_at: "2018-01-02 16:58:16",
     updated_at: "2018-01-02 16:58:16",
     deleted_at: null,
     profile: App\Profile {#808
       id: 1,
       name: "neque",
       description: "Aut mollitia quis amet necessitatibus aliquid consequatur. Cum eum a voluptas incidunt iusto magnam quasi. Quia quis maiores sit ut omnis dolor. Quis doloremque aut totam unde hic sed sunt.",
       created_at: "2018-01-02 16:58:16",
       updated_at: "2018-01-02 16:58:16",
       deleted_at: null,
     },
     works: Illuminate\Database\Eloquent\Collection {#807
       all: [
         App\Work {#805
           id: b"\x11çðg‰EC*–…]`¯!\x11w",
           user_id: b"\x11çïÞ ”¢Z€\x05ñV0/²~",
           workstation_id: b"\x11çïÞ l\x15ˆ©'!¨ÅKÁ‹",
           started_at: "2017-12-12 10:24:41",
           ended_at: "2017-12-12 11:05:37",
           grade_id: 2,
         },
         App\Work {#802
           id: b"\x11þ­gë\DlØ╣몡║:\x7F",
           user_id: b"\x11çïÞ ”¢Z€\x05ñV0/²~",
           workstation_id: b"\x11þ´Ì bsHèØsq─╣óî",
           started_at: "2017-12-13 12:46:31",
           ended_at: "2017-12-17 20:53:32",
           grade_id: 3,
         },
         App\Work {#801
           id: b"\x11çðg‰k³„³³Áâ\x07”˜ˆ",
           user_id: b"\x11çïÞ ”¢Z€\x05ñV0/²~",
           workstation_id: b"""
             \x11çïÞ mTR¢\n
             iF±ïQz
             """,
           started_at: "2017-12-16 03:18:59",
           ended_at: "2017-12-25 22:40:29",
           grade_id: 1,
         },
       ],
     },
   }

If I append the toJson() method on the tinker command line, it throw an error Illuminate\Database\Eloquent\JsonEncodingException with message 'Error encoding model [App\User] with ID [��� ��Z��V0/�~] to JSON: Malformed UTF-8 characters, possibly incorrectly encoded'.

If I append the toArray() method, only the work id is translated to a string.

I guess the problem comes from the json encoding of the user_id and workstation_id in the Work model.

I tried to use the withUuid mentionned in the README, but I don't have a work_id in my users table.

Is there a way to encode the faulty uuid ?

Forget to mention, I work with PHP7.2 and Laravel 5.5

Incorrect string value error when inserting into table

I created a table in MySQL 5.7 using the following migration class:

class CreateGroupsTable extends Migration
{
    public function up()
    {
        Schema::create('groups', function (Blueprint $table) {
            $table->uuid('id')->primary();
            $table->string('name', 50);
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('groups');
    }
}

For MySQL, Laravel creates an id column of type CHAR(36) set as the primary key.

Unfortunately, when attempting to save a model, I received an error:

>>> $group = new Group();
=> App\Group {#744}
>>> $group->name = 'PUG Foo';
=> "PUG Foo"
>>> $group->save();

Illuminate\Database\QueryException with message 'SQLSTATE[HY000]: General error: 1366 Incorrect string value: '\xE7\xCF\xEA\x06\xE2\x0B...' for column 'id' at row 1 (SQL: insert into `groups` (`name`, `id`, `updated_at`, `created_at`) values (PUG Foo, ����
                                                    ��<���v, 2017-11-23 01:02:50, 2017-11-23 01:02:50))'

I tried several Laravel column type definition variations, including binary(), which uses MySQL's BLOB type instead of BINARY or VARBINARY and generates this error when attempting to set it as a primary key:

$ php artisan migrate

  [Illuminate\Database\QueryException]
  SQLSTATE[42000]: Syntax error or access violation: 1170 BLOB/TEXT column 'id' used in key specification without a key length (SQL: alter table `groups` add primary key `groups_id_primary`(`id`))

I was finally able to successfully save the model and use spatie/laravel-binary-uuid only after using raw SQL to create the id column as a BINARY(16) column in MySQL, like this:

class CreateGroupsTable extends Migration
{
    public function up()
    {
        Schema::create('groups', function (Blueprint $table) {
            $table->string('name', 50);
            $table->timestamps();
        });

        DB::statement('ALTER TABLE `groups` ADD COLUMN `id` BINARY(16) NOT NULL PRIMARY KEY FIRST');
    }

    public function down()
    {
        Schema::dropIfExists('groups');
    }
}

Now, my models save properly with binary UUIDs as primary keys.

I don't think there's anything to change in spatie/laravel-binary-uuid to fix this, but maybe the documentation should be updated.

Perhaps adding binary and varbinary types to Illuminate\Database\Schema\Blueprint in laravel/framework would help make this easier to use. Should I recommend this directly on laravel/framework? It appears it's been rejected in the past (laravel/framework#3297), but it seems MySQL deals with binary data differently in CHAR/VARCHAR fields fromBINARY/VARBINARY fields.

Question - Should I still have a fk defined $casts?

I'm converting a schema to use uuids.

In my model I have

    /**
     * Casts
     *
     * @var array
     */
    protected $casts = [
        'country_id' => 'int',
    ];

I see in the readme that I should add

    /**
     * The binary UUID attributes that should be converted to text.
     *
     * @var array
     */
    protected $uuids = [
        'country_uuid' // foreign or related key
    ];

So I'm removing the column country_id and replacing it with country_uuid.

The question is, do I need to define that in any way in $casts or is that replaced for uuids by the $uuids array?

Composite Key Not Working

Just basically doing a sanity check here because I'm assuming the changes were tested.
In the README it states that composite keys are supported, thus steps:

  1. Create a model and make it extend Illuminate\Database\Eloquent\Relations\Pivot
  2. Add use Spatie\BinaryUuid\HasBinaryUuid;
  3. Change the getKeyName method, as stated in README
public function getKeyName()
{
    return ['key_id', 'key_id_two'];
}
  1. Try to create Model with tinker and receive

PHP Notice: Array to string conversion in vendor/spatie/laravel-binary-uuid/src/HasBinaryUuid.php on line 14

Does it support for lumen 5.5?

I made an installation of this package in my project that I'm using lumen 5.5 and I had this error, but I did not find anything in the documentation talking about lumen support.

composer require spatie/laravel-binary-uuid
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Conclusion: don't install spatie/laravel-binary-uuid 1.0.2
    - Conclusion: don't install spatie/laravel-binary-uuid 1.0.1
    - Conclusion: remove illuminate/auth v5.5.17
    - Installation request for spatie/laravel-binary-uuid ^1.0 -> satisfiable by spatie/laravel-binary-uuid[1.0.0, 1.0.1, 1.0.2].
    - Conclusion: don't install illuminate/auth v5.5.17
    - spatie/laravel-binary-uuid 1.0.0 requires laravel/framework ^5.5.22 -> satisfiable by laravel/framework[5.5.x-dev, 5.6.x-dev, v5.5.22].
    - don't install laravel/framework 5.5.x-dev|don't install illuminate/auth v5.5.17
    - don't install laravel/framework 5.6.x-dev|don't install illuminate/auth v5.5.17
    - don't install laravel/framework v5.5.22|don't install illuminate/auth v5.5.17
    - Installation request for illuminate/auth (locked at v5.5.17) -> satisfiable by illuminate/auth[v5.5.17].

Thank you

Relation model saving causes "UTF-8" Error

I'm struggling with relationship models which contains uuid. Trying to save model which has uuid as FK.
Saving model gives "Malformed UTF-8 characters, possibly incorrectly encoded" as a response.

Model looks like:

class Follow extends Model
{
    use HasBinaryUuid;

    protected $uuids = [
        'corporation_uuid'
    ];
    

    public function user(){
        return $this->belongsTo(User::class);
    }

    public function corporation(){
        return $this->belongsTo(Corporation::class);
    }

Controller function looks like:

public function store($corporation)
    {
        $user = auth('api')->user();
        if($user->isFollowing($corporation)){
            return response([
                'message' => 'Already following'], Response::HTTP_OK);
        }

        $corporation = Corporation::withUuid($corporation)->first();

        $follow = new Follow();
        $follow->user_id = $user->id;

        $corporation->follows()->save($follow);
        
        return response([
            'message' => 'Followed'], Response::HTTP_OK);
    }

Is there any workaround saving with uuid as FK?
Thanks.

Making `_text` available to other secondary UUID columns

Currently, the trait offers some goodies on the main uuid field (the PK). Models may have foreign keys to other models according to their UUID. It would be nice to open some of the features on those attributes too.

The _text computed attribute

other UUID attributes should also have a corresponding computed string representation. e.g. relation_uuid_text would return string version of relation_uuid

This could be handled in the same way as dates are handled. Have a protected $uuids = [ 'relation_uuid' ] on the model to let the trait know which attributes are handled as UUIDs.

Would you be ok for a PR about that?

Lack of "ID" in key name results in exception

I'm using a legacy schema that won't be modified. A uuid column named "CreatedBy" does not contain "id" or "uuid", so getRelatedBinaryKeyName doesn't return the attribute with suffix. Commenting out the preg_match line and just returning "{$attribute}{$suffix}" fixes the issue:

    public function getRelatedBinaryKeyName($attribute): string
    {
        $suffix = $this->getUuidSuffix();

      //return preg_match('/(?:uu)?id/i', $attribute) ? "{$attribute}{$suffix}" : $attribute;
        return "{$attribute}{$suffix}";
    }

Since the CreatedBy column is provided in the model's $uuids array, should getRelatedBinaryKeyName really be doing that check for "uuid" or "id"? If so, could this be made to be conditional based on a config setting?

The exception is thrown from here in Illuminate/Http/JsonResponse.php:

if (! $this->hasValidJson(json_last_error())) {
    throw new InvalidArgumentException(json_last_error_msg());
}

Exception:
InvalidArgumentException. Malformed UTF-8 characters, possibly incorrectly encoded

Validation not working?

Laravel 5.6
Laravel Binary Uuid 1.1

I'm sure I'm missing something here (I've been back to Laravel for only the past 6 months).

I have a model that uses the HasBinaryUuid trait. In my Request class, I want to check for uniqueness of one of the attributes, so I did:

    public function rules()
    {
        return [
            'name'                      => [
                'required',
                'max:255',
                'unique:model_table,name,'.$this->model_name->id
            ],

Unfortunately, the following error occurs:

[2018-08-10 15:49:01] local.ERROR: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '`?' at line 1 (SQL: select count(*) as aggregate from `model_table` where `name` = Test and `�\';L�` <> 蜱��)

Any ideas?

General error: 1215 Cannot add foreign key constraint when using relations

Hi,

I am getting this error when running: php artisan migrate:refresh --seed. If I change back to normal ids I do not get this error.

Full error:
Illuminate\Database\QueryException : SQLSTATE[HY000]: General error: 1215 Cannot add foreign key constraint (SQL: alter tableinvoicerowsadd constraintinvoicerows_invoice_id_foreign foreign key (invoice_id) references invoices (id) on delete cascade)

My old migrations:

// Invoices migration
Schema::create( 'invoices', function ( Blueprint $table ) {
	$table->increments( 'id' );
}
// Invoicerows migration
Schema::create( 'invoicerows', function ( Blueprint $table ) {
	$table->increments( 'id' );

	$table->integer( 'invoice_id' )->unsigned()->nullable();
	$table->foreign( 'invoice_id' )->references( 'id' )->on( 'invoices' )->onDelete( 'cascade' );
}

New migration after installing `spatie/laravel-binary-uuid':

// new Invoices migration
Schema::create( 'invoices', function ( Blueprint $table ) {
	$table->uuid('uuid');
        $table->primary('uuid');
}
// New Invoicerows migration
Schema::create( 'invoicerows', function ( Blueprint $table ) {
	$table->uuid('uuid');
        $table->primary('uuid');

	$table->integer( 'invoice_uuid' )->unsigned()->nullable();
	$table->foreign( 'invoice_uuid' )->references( 'uuid' )->on( 'invoices' )->onDelete( 'cascade' );
}

When I now run php artisan migrate:refresh --seed I get that error.

I have tried to add my original id field: $table->increments( 'id' ); but I suppose you do not need an id field anymore when using binary uuids?

UUID is not properly formatted

The article linked in the readme describes how to format uuid v1 for mysql, however it does not appear that this library is correctly optimizing. While storing the UUID as a binary is a massive improvement over storing it as a CHAR/VARCHAR, it is only part of the problem.

A standard UUID v1 will generate a very poor primary key as the first half of the UUID is based on time while the second half is based on MAC address. The UUID needs to be reordered going into the database in order to have an efficient binary tree.

For example:

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.