#custom named uuid primary key

167 messages · Page 1 of 1 (latest)

crimson geyser
#

I'm trying to rename the default id column to uuid, but I'm getting a Field 'uuid' doesn't have a default value when inserting into that model, the eloquent model does make use of HasUuids and uniqueIds which returns an array: ['uuid'] but that still doesn't solve it, does laravel expect the primary key to always be id to properly generate uuids for those columns as default? or is there a way to override that? thanks!

fluid quartz
#

You'd still need to override the models PK property if you aren't using ID as the column name.

public $primaryKey = 'uuid';
cobalt salmon
#

and set $increments = false

crimson geyser
#

oh right; I did that now however it seems to still throw Field 'uuid' doesn't have a default value

fluid quartz
crimson geyser
#
    protected $primaryKey = 'uuid';
    public $incrementing = false;
    protected $keyType = 'string';
#

did that and it still threw the error

cobalt salmon
#

okie doke

fluid quartz
#

Can you show the entire model?

#

And when you create the model, you aren't muting events are you? Common in feature tests

crimson geyser
#
class User extends Authenticatable
{
    use HasUuids, MustVerifyEmail, HasApiTokens, HasFactory, Notifiable;
    protected $primaryKey = 'uuid';
    protected $fillable = [
        'name',
        'email',
        'password',
    ];
    protected $hidden = [
        'password',
        'remember_token',
    ];
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
        Schema::create('users', function (Blueprint $table) {
            $table->uuid('uuid')->primary();
            $table->string('name', 50);
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });

note that I removed the incrementing and keytype since you mentioned the trait handles that, I had it before this

crimson geyser
fluid quartz
#

What about the uniqueIds() method? You still have that on your model right?

crimson geyser
#

yeah had that too, but removed it to test right as you asked me, let me add it back

tawdry belfry
crimson geyser
#

any reason why I cant just use uuids as primary

fluid quartz
tawdry belfry
crimson geyser
#

the docs hinted at this being possible so I'm confused whats missing

crimson geyser
fluid quartz
crimson geyser
#

went through at least 500 pages to make sure I didnt miss something 😄

tawdry belfry
crimson geyser
#

the error did 😛 not saying any of you did

tawdry belfry
#

It’s just better to use integers over UUIDs for your primary and foreign keys.

crimson geyser
#

noted and will switch to that of course, but still want to know why this doesnt work even though primary key is set now too 😄

fluid quartz
#

The error though I'm unsure. If you define the primary Key property, and you return uuid in the method of columns, you should be golden afaik

crimson geyser
#

some kind of cache I should bust maybe?

fluid quartz
#

Usually not with models

cobalt salmon
#

HasUuids does that trait define anything that might be overriding the one you've set?

crimson geyser
#

is there anything else that could be missing, like uuid as fillable so laravel can do its default uuid generation?...

crimson geyser
#

removing it at least still throws the error

cobalt salmon
#

The trait adds a model::creating listener that should fill out the column, if it's not working, chances are something's misconfigured

crimson geyser
#

if I use uniqueIds am I forced to override the uuid method maybe?

cobalt salmon
#

Tbf the docs don't say that you have to define any of those properties above

crimson geyser
#

even if I add it, it still fails anyway meh

tawdry belfry
cobalt salmon
#

so adding the trait expects you to have 'id' as the primary key still

crimson geyser
crimson geyser
cobalt salmon
#

just add the trait, and protected $primaryKey = 'uuid';

#

nothing else

#

try that

crimson geyser
#

remove all else incl fillable etc?

cobalt salmon
#

no remove public $incrementing = false;
protected $keyType = 'string';

#

if they're still in there

fluid quartz
#

I just realized....could fillable stop uuid from being filled? I have all my models unguarded.

cobalt salmon
#

It might be that this use case just isn't accounted for - a custom PK name AND UUIDs

fluid quartz
#

You didn't put uuid in fillable

cobalt salmon
#

The trait doesn't fill it using a mass assignment method anyway

fluid quartz
#

Hmm

crimson geyser
#
class User extends Authenticatable
{
    use HasUuids;
    protected $primaryKey = 'uuid';

    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    protected $hidden = [
        'password',
        'remember_token',
    ];

    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
    
    public function uniqueIds(): array
    {
        return ['uuid'];
    }
}
#

that still throws

#

uuid in fillable doesnt work either

cobalt salmon
#

Which bit of code is throwing the error btw? you running migrations / seeder / hitting a URL or something else?

crimson geyser
#

seeder

cobalt salmon
#

does it give you a stack trace?

#

i.e. does it tell you which line of the seeder is causing it?

crimson geyser
#

all the seeder does:

DB::table('users')->insert([
  'name' => "test",
  'email' => Str::random(6) . "@gmail.com",
  'email_verified_at' => now(),
  'password' => Hash::make('password'),
]);
fluid quartz
#

Ooooooo

#

Whyyyyyy

crimson geyser
#

🤔

fluid quartz
#

You're not using factories

#

No wonder

#

There's no model events to be fired to trigger uuid to be made

#

You're inserting directly

#

User::factory()->create()

#

You made your model factories right?

crimson geyser
#

I didnt create any factories myself, no

#

was I supposed to?

fluid quartz
#

php artisan make:factory UserFactory

crimson geyser
#

does laravel not generate that during creation?

fluid quartz
#

Your model already has the factory trait

#

Not unless you tell it to with the f flag

#

php artisan make:model User -fm

crimson geyser
#

oh there is already a userfactory

fluid quartz
#

Factory + migration

#

Use it then

crimson geyser
#

I think the seeding example is what had this direct insertion example

#

let me check to make sure

fluid quartz
#

General seeding will show both I think

#

But 99% of the time you should be using factories IMO

crimson geyser
#

yeah there

fluid quartz
#

More so when you rely on eloquent events to trigger things

#

Scroll down

#

To the user factory

crimson geyser
#

Of course, manually specifying the attributes for each model seed is cumbersome.

I didnt think it's cumbersome 😛 so didnt read further

fluid quartz
#

Just wait till you have 30 models to seed and feature tests to run

crimson geyser
#

but it seems it's what makes eloquent be able to hook into the process

fluid quartz
#

Yes, uuids relies on the eloquent creating event to intercept and set the keys

crimson geyser
#

I wonder, was there any path in the docs I could have taken that would've explained I need factories to avoid this uuid issue?

#

I think I went through all of them earier

fluid quartz
#

Inserting raw data with the DB facade doesn't trigger anything with eloquent

#

Not sure a direct path. Uuids is more "advanced" in that it sort of assumes you know how that works (relying on eloquent events)

#

Before L9 had that trait, devs had to manually make their own and hook I to the creating event in the models boot

#

I'd follow more the eloquent docs though

#

Not the pure DB ones initially

crimson geyser
#

I think I was just uuid generating on insertion in the old laravel versions yeah

#

no event/hooks/..

#

all examples back then also just implied that anyway

fluid quartz
#

Glad as soon as you showed how you're seeding, the answer screamed out lol. Did you get it workig now?

tawdry belfry
crimson geyser
#

but that just explains the factories themselves not that uuids require that because of some kind of eventing

#

I dont think hasuuids is even explained in the docs

crimson geyser
fluid quartz
#

Because it's off the traveled path so to speak

crimson geyser
#

I also took note to change it to id+uuid instead of uuid only, since it does make sense that it'd be faster

#

but for now I want to see it working 😄

fluid quartz
#

Like many engineering manuals, they hold your hand initially, but it becomes more cryptic as you dive deeper, as it assumes you've encountered and understood some of what they imply.

crimson geyser
#

the hasPosts looks interesting too because the default userfactory never defined that

tawdry belfry
fluid quartz
crimson geyser
fluid quartz
#

That assumes you have a posts() method on your model for a hasMany relation

#

It's hideous

crimson geyser
#

interesting, something to explore just in case in the future I suppose, but not now 😛

#

neat I think the user one worked, let me check database

#

haha yep it did, now to convert it over to id and uuid, incl. the other ones 😄

#

you guys are awesome, thanks!

fluid quartz
#

👍

crimson geyser
fluid quartz
#

Huh? You never need to overwrite the uuid. It's made each time for you, and as the name suggest, it's universally unique.

#

But if you want to set custom names instead of letting faker make them from your model factory definition for example, then sure.

crimson geyser
#

right? 😄

fluid quartz
#

No. Definitions are default values. You don't define the uuid in your definition because it is made for you when you run create.

#

Don't use make() unless you have a reason and also understand make doesn't fire creating event, thus no uuid will be set

tawdry belfry
crimson geyser
#

it was way simpler last time I touched it, please be fair.. 😄

fluid quartz
#

Still as simple, just more options

crimson geyser
#

just trying to wrap my head around how this would work in theory

fluid quartz
#

Pass array of attributes to create method

#

Don't use make. That's no different than newing up a model inline, setting attributes manually, and not persisting to db.

crimson geyser
#
$user = User::factory()->create([
    'name' => 'Abigail',
]);

ahh I see

fluid quartz
#

Yea

crimson geyser
#

oh I thought the earlier seeder made use of ->make too, but that was create

#

make is simply to init those

fluid quartz
#

Yea, which is fine and all (I have reasons to do so) but you must be aware that when you rely on eloquent events, making will not trigger those nor your model observers.

crimson geyser
#

I guess it makes sense it wouldnt, because otherwise youd fire events that didnt actually create a row?

#

or is there another reason

fluid quartz
#

Well yea, but in general, model events are fired when dealing with an actual DB life cycle through the model. Make is none of those.

#

Read these

crimson geyser
#

👍

fluid quartz
crimson geyser
#

I see some of the other models have hasfactory but laravel didnt generate a factory for them

#

the models being ones I created with artisan

#

is this expected behavior?

fluid quartz
#

As I showed earlier, you must use the -f flag when you make a model to also make factory

#

I always use -fm

#

To make model with factory and migration

crimson geyser
#

it'll add the trait regardless of if I actually created a factory then, right?

#

🤔

fluid quartz
#

Yes. Easier to remove two lines in the rare case you don't want a factory

crimson geyser
#

true 😄

tawdry belfry
crimson geyser
#

hahahaha

#

easier to remember this way too!

fluid quartz
#

Lmao

tawdry belfry
#

“Model, motherf—ers!”

crimson geyser
#

again, you guys are truly amazing! 💖