alsofronie / eloquent-uuid Goto Github PK
View Code? Open in Web Editor NEWAn Eloquent UUID Trait to use with Laravel 5.1 and 5.2
License: MIT License
An Eloquent UUID Trait to use with Laravel 5.1 and 5.2
License: MIT License
Hi,
I'm fixing in my fork this error. Example:
"Médico" are UTF-8 string and mb_detect_encoding return false, like as binary uuid value.
My fix silasrm@57e8bf0
I'm trying to test event dispatching in my application using the PHPUnit tools provided by Laravel.
In a test where Event::fake()
is called before creating a new Model
(a User
for instance) the package's UUID trait's boot method seems to break.
I get MySQL errors relating to field id
not having a default value:
QueryException: SQLSTATE[HY000]: General error: 1364 Field 'id' doesn't have a default value (SQL: insert into ...
I'm using Laravel 5.6 as my application base.
Trimmed example code to reproduce:
<?php
class migration_dis_and_dat
{
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->uuid('id');
$table->string('email')->unique();
$table->string('password');
$table->timestamps();
$table->primary('id');
});
}
}
<?php
class User extends Model {
use UuidModelTrait;
...
}
<?php
class UserTest extends TestCase
{
public function test_user_create_dispatches_event()
{
Event::fake();
User::create(['email' => '[email protected]', 'password' => Hash::make('helloworld'));
// around here the error should appear,
// if you comment `Event::fake()` and `Event::assertDispatched`
// then the user should be created normally
Event::assertDispatched(MyEvent::class);
}
}
Hi
I was just searching for laravel uuid and came across your package.
According to my understanding when using UUID as PK its better to store as a binary field.
Do you have any thoughts about that? Here's a couple of links...
https://www.percona.com/blog/2014/12/19/store-uuid-optimized-way/
http://stackoverflow.com/questions/2365132/uuid-performance-in-mysql/2365176
regards
l.
I was looking forward to use this library since it's the only one I found that takes into consideration the DB storage optimization for UUIDs.
However, the latest release does not include pull request #6 yet, so I have to write some funny code to make the trait work fine with custom boot logic.
I can't use the master branch or the development release, I am required to use normal releases available from packagist. Is there any chance of a newer release including this fix coming in the near future?
Thanks.
I've tried implementing this and it isn't working for me. My model does not have any boot stuff added, so there shouldn't be any conflicts. The methodbootUuidModelTrait() gets called, but I'm getting a SQL error before static::creating() is called.
SQLSTATE[HY000]: General error: 1364 Field 'id' doesn't have a default value (SQL: insert into `sessions` (`user_id`, `updated_at`, `created_at`) values (8, 2016-11-10 21:20:49, 2016-11-10 21:20:49))
<?php
namespace Example\Models;
use Illuminate\Database\Eloquent\Model;
use Alsofronie\Uuid\UuidModelTrait;
class Session extends Model {
use UuidModelTrait;
}
$session = new \Example\Models\Session();
$session->user_id = 8;
$session->save();
CREATE TABLE `sessions` (
`id` char(36) NOT NULL,
`user_id` int(11) NOT NULL,
`expires_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`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 (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
I just discovered a new short type of Uuid. It takes only 22 characters like _id of Mongodb.
There is also a library for php: https://github.com/linkorb/xuid
Do you think it is possible to implement this alternative?
Thank you in advance.
In my model:
Order.php
use Uuid32ModelTrait;
protected static function boot()
{
static::deleting(function ($order) { // before delete() method call this
$order->items()->delete();
});
}
When I save:
$order = new Order();
$order->user()->associate($user);
$order->save();
it always return me "message": "SQLSTATE[HY000]: General error: 1364 Field 'id' doesn't have a default value "
If I remove my boot function it works properly.
I make a PR proposing a quick solution
ErrorException in UuidBinaryModelTrait.php line 94:
preg_match() expects parameter 2 to be string, array given
It indicates it goes wrong here:
at preg_match('//u', array('id' => '6dee1660-b57a-11', 'code' => 'NL', 'name' => 'The Netherlands', 'asset_id' => null, 'created_at' => null, 'updated_at' => null, 'id_string' => '36646565313636302d623537612d3131', 'asset' => null)) in UuidBinaryModelTrait.php line 94
I am currently using Lumen 5.3.
When I comment out these lines in the function toArray()
, it seems to work fine:
foreach ($parentArray as $key => $value) {
if(!preg_match('//u', $parentArray[$key])){//non-valid utf-8
$parentArray[$key] = (property_exists($this, 'uuidOptimization') && $this::$uuidOptimization)
? self::toNormal($parentArray[$key]) : bin2hex($parentArray[$key]);
}
}
Hello, thanks for this great package, however I keep getting this error "General error: Field 'id' doesn't have a default value" as it appears the "creating" event is not firing. Has anyone else seen this issue? Any ideas on how to fix?
Add Laravel 6 support
Although the new release seems to fix the serialization problem (#29), setting a temporarily custom attribute with default PHP syntax is no longer possible.
Unit test:
public function testToArray(){
$model = EloquentBinOptimizedUserModel::create([
'username' => 'Model',
'password' => 'secret'
]);
$customModel = EloquentBinOptimizedPostModel::create(['name' => 'bla']);
$model->cust = $customModel;
$array = $model->toArray();
static::assertNotNull($array);
$json = json_encode($array);
var_dump($json);
static::assertNotNull($json);
}
The test above passes in v1.0.7, but the new version leads to more memory consumption resulting in:
mmap() failed: [12] Cannot allocate memory
causing the server to hang/crash.
anyone has experience using this with laravel 5.4
working ? broken? issues?
Thanks
I love this package make it easier to save my ID.
Found a problem if I have method boot at my model like example:
protected static function boot()
{
parent::boot();
static::addGlobalScope('follows_count', function (Builder $builder) {
$builder->withCount('follows');
});
}
The eloquent-uuid won't run if I create new instance. If I remove the boot method it will work properly.
Any solution?
I was getting the following error:
PHP warning: preg_match() expects parameter 2 to be string, object given in /home/.../vendor/alsofronie/eloquent-uuid/src/UuidBinaryModelTrait.php on line 105
I had to override the method in my model by adding extra check if the value is not object:
private function deepArray($array)
{
foreach ($array as $key => $value) {
$value = $array[$key];
if (is_array($value)) {
$array[$key] = $this->deepArray($value);
} elseif (!is_object($value) && !preg_match('//u', $value)) {
// TODO: drop the preg_match because it's slow and
// what if a binary value in the uuid gets represented
// by valid ASCII or UTF symbols?
$array[$key] = (property_exists($this, 'uuidOptimization') && $this::$uuidOptimization) ?
self::toNormal($value) :
bin2hex($value);
}
}
return $array;
}
I'm using the UuidBinaryModelTrait
for all my models.
Unfortunately the sync
method (provided by Laravel) doesn't seem to generate a binary Uuid.
To be more concrete:
I have a class User
and a class Organisation
. A User
has multiple organisations and an Organisation
has multiple users. This relationship is saved in the database table user_organisations
, which is where the problem occurs. When I call $user->organisations()->sync([$o_id, $o_id2])
to insert a new row, I get an Internal Server Error indicating that the primary key already exists. When I verify the user_organisations
table, I noticed that the only row in the table has a key 0x000000000000000000000000000000
.
In the new 1.0.5 release there is the following error, when attempting to call toArray()
on an object using the UuidBinaryModelTrait and having at least a one-to-many relation:
preg_match() expects parameter 2 to be string, array given
(...)/vendor/alsofronie/eloquent-uuid/src/UuidBinaryModelTrait.php Line 94
Probably the right solution would be to check if $key == $this->primaryKey
instead of checking with a regex. That would ensure the conversion would be done only for the primary key (which is the only key we know of that contains binary data).
Yes, there might be more than one key containing binary data (for example in a one-to-one relation, there could be a reference to another model using the UuidBinaryModelTrait), but I think at that point it's the developer's duty to use $model->$hidden
to hide them (or to provide an accessor method to properly format them). Otherwise, just check that the value is actually a string before using the regex.
If you like my solution I can try to make a pull request during the weekend to fix the bug.
In the meantime I have implemented the following workaround, which disables the UuidBinaryModelTrait::toArray()
method by renaming it and by using the base Model::toArray()
one.
<?php
namespace Blah;
use Alsofronie\Uuid\UuidBinaryModelTrait;
use Illuminate\Database\Eloquent\Model;
trait BinaryUUIDTrait
{
use UuidBinaryModelTrait {
// Rename the trait's toArray method so that the parent one can be used.
UuidBinaryModelTrait::toArray as _brokenToArray;
}
/**
* @see Model::toArray()
* @return array
*/
public function toArray()
{
// Forward the call to the parent method.
return parent::toArray();
}
}
If you copy-paste it to a file, and use this trait instead of the original one, $model->toArray()
works again (although you will see binary junk in the array. :)
During optimisation dashes are removed using regex:
$uuid = preg_replace('/\-/', null, $uuid);
But in the toNormal($uuid)
method removed dashes aren't being added back.
Hi, it's possible to use findOrFail in the same way as find with both the binary version or the string (bin2hex) version???
It always return null for relation. For example. $user = User::with('posts')->find( $user_hex_uuid ); It return empty for posts relation.
Hi this is my first time doing a pull request, the fix is at #6 for your convenience. Here is the background behind the fix:
I'm using a static boot() function in my models and the problem is that the model events for creating() and created() are getting called out of order. This is due to the UuidModelTrait boot() getting called at the incorrect time and registering its creating() event to happen after my created() event. More info:
http://www.archybold.com/blog/post/booting-eloquent-model-traits
The fix for UuidModelTrait
is:
public static function bootUuidModelTrait()
{
//parent::boot();
static::creating(function ($model) {
// This is necessary because on \Illuminate\Database\Eloquent\Model::performInsert
// will not check for $this->getIncrementing() but directly for $this->incrementing
$model->incrementing = false;
$uuidVersion = (!empty($model->uuidVersion) ? $model->uuidVersion : 4); // defaults to 4
$uuid = Uuid::generate($uuidVersion);
$model->attributes[$model->getKeyName()] = $uuid->string;
}, 0);
}
Same goes for bootUuid32ModelTrait()
and bootUuidBinaryModelTrait()
. This new code works when I call parent::boot();
within my model's boot() function.
Can I use this package with Laravel 5.3 without issues?
Hi,
when working with the binary traits, it is currently impossible to to add them to the queue as the models contain malformed UTF-8. A possible solution is to modify the serialization process (see laravel/framework#19252 and laravel/framework#15065).
I currently created a dirty workaround by creating an extra trait
. In my case all the binary fields in the database end with id
, this declares the check in the unserialize
method:
<?php
namespace App;
/**
* Allows serialization and deserialization for models
* //TODO: fix slow deserialization
*/
trait UuidBinaryModelSerialization
{
public function serialize()
{
return json_encode($this->toArray());
}
public function unserialize($stringRepresentationOfObject)
{
$decodedArray = json_decode($stringRepresentationOfObject);
foreach ($decodedArray as $key => $value) {
if(substr($key, -2) === "id"){//convert to binary
$this->$key = $this->toOptimized($value);
}else{
$this->$key = $value;
}
}
}
Another possibility is adding an array to the models containing the names of the fields that are binary. This should decrease the amount of checks in the for
-loop in unserialize
.
Would it be possible to allow an OOTB solution for this serialization process?
Hi,
Have you tested this implementation? https://www.percona.com/blog/2014/12/19/store-uuid-optimized-way/
How do you get the uuid of a newly created model?
The uuid is saved to the database correctly, but isn't being set on the returned model and reloading from the database doesn't work because the keys don't match.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.