Ensure 'user_id' is set in the Accommodation model's $fillable array
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'user_id' cannot be null
I'm testing a seeder class to import some data from another database. I have a table for users and a table for accommodations. I defined the relative models, I wrote the query to select data from the other database and wrote the code to create a user if it's not in the new database yet and to create its accommodation(s).
The creation of the user works, after User::firstOrCreate() I check the newly created user id and it's there (I dd it). After that I try to create the relative accommodation. I used Accommodation::firstOrCreate() or
$acc = new Accommodation;
$acc->name = $query->name;
$acc->user_id = $userid;
...
$acc->save();
where $query is the query on the old database, user_id is the foreign key that relates users and their accommodations (not nullable) and $userid is the id of the newly created user related to the accommodation.
I get the error in the post title, the user id is missing from the query, I read all other posts about this error but none of the suggested solutions seems to work for me. I added user_id to the $fillable property, I tried with $guarded = [] to no avail.
What else can I check or try?
thank you
maxx
@arifthefinix As I wrote, I already tried that. I tried again now and I got the same error.
Show the full code, including where $userid comes from.
Si haces un debug o dd() e intentas mostrar $userId te da nulo? Podrías enviar la $query? Y podrías enviar como obtienes $userId?
AccommodationSeeder.php
class AccommodationSeeder extends Seeder
{
public function run(): void
{
$accommodationsOld = DB::connection('oldDb')
->table('accommodations', 'acc')
->select('acc.user', 'acc.name' , 'acc.email', 'acc.user_id', ...)
->get();
foreach($accommodationsOld as $accommodationOld)
{
$user = User::firstOrCreate(
['email' => $accommodationOld->email],
[
'id' => $accommodationOld->user_id,
'name' => $accommodationOld->user_name,
]
);
// dd($user); // if I uncomment this line the output is the $user model data.
$accommodation = new Accommodation;
$accommodation->name = $accommodationOld->name;
$accommodation->user_id = $user->id;
...
$accommodation->save(); //error here
}
}
}
Accommodation.php
class Accommodation extends Model
{
use HasFactory;
// I tried both $guarded and $fillable, one at a time!
protected $guarded = [];
protected $fillable = [
'name',
'user_id',
...
];
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}
As a workaround I created a function firstOrCreate that uses the builder methods to emulate the eloquent function. But I'd like to know why I can't save a model using eloquent...
I tried to debug Laravel code to undestand where user_id becomes null.
In Illuminate/Database/Eloquent/Model.php there is the following code:
/**
* Perform a model insert operation.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return bool
*/
protected function performInsert(Builder $query)
{
if ($this->usesUniqueIds()) {
$this->setUniqueIds();
}
if ($this->fireModelEvent('creating') === false) {
return false;
}
...
If I check the value of user_id before and after the line with fireModelEvent, before it is an Int and after it is null (I check $this->attributes). I'm not sure why the value of an attribute would change in a method that fires an event. Or it is just a coincidence.
Any clue?
@emmexx Is the id property fillable (and not guarded) in your User model? Usually it's a guarded property, but you're assigning it in User::firstOrCreate().
@JussiMannisto I already answered to that question, sorry.
Yes it is fillable or unguarded.
I have the same problem with other models that I'm trying to save in this seeder class. If I try to save the user_id I get the Integrity constraint violation error. So it's not a problem (error) in a single model but something else.
@JussiMannisto Perhaps I misunderstood your suggestion. I don't have any error while creating the user but when I create other models with a user foreign key (user_id).
@emmexx You've shown that user_id is fillable in the Accommodation model, but that's a different issue. I'm talking about the id in the User model.
@JussiMannisto Ok, but that's not where the error arises. When I try to save Accommodation or other models, user_id becomes null. If I check the user model after creating it, it is there (I can dump it), I checked the id of the user model (it is an int, not a null, of course), I even assigned it to another variable and used this one to assign the value to user_id in the other models. And when debugging the Laravel code the user id is an int until it becomes a null. :-(
The following is the output of some dump commands I added to some of the laravel files:
array:6 [ // vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:1287
"destinatario" => "L'Antica Sosta Agriturismo"
"localita" => "Loc. Mondo Novo"
"cap" => "58014"
"posizione" => Illuminate\Database\Query\Expression^ {#3950
#value: "ST_GeomFromText('11.5339 42.5912')"
}
"town_id" => 4467
"user_id" => 8744
]
"FIREModelEvent" // vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasEvents.php:174
"FIREModelEvent" // vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasEvents.php:187
array:6 [ // vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:1291
"destinatario" => "L'Antica Sosta Agriturismo"
"localita" => "Loc. Mondo Novo"
"cap" => "58014"
"posizione" => Illuminate\Database\Query\Expression^ {#3950
#value: "ST_GeomFromText('11.5339 42.5912')"
}
"town_id" => 4467
"user_id" => null
]
As you can see user_id before is 8744 and after is null. And this is dumping of code from laravel, Eloquent/Builder.php save() method. There's no code from me in between...
@emmexx The issue is somehow related to the user model's ID since you're assigning user_id like so:
$accommodation->user_id = $user->id;
You're trying to mass-assign the user's id in your User::firstOrCreate() call. If the id property in User is not mass-assignable, then it's not going to set the correct ID for the user. This is an obvious bug and you should fix it. Other than that it's hard to say what's wrong since some of the code is cut off (the firstOrCreate call doesn't have a closing bracket).
@JussiMannisto As for the User::firstOrCreate missing bracket, I edited the post with the example code. Sorry. I had to simplify a little bit the code, but I simply removed unnecessary lines with field assignments not linked to the error. And translated to english for readers convenience.
The logic of the code is that I create a user, or use an existing one, and after that I create an accommodation trying to assign it to the aforementioned user.
I copy and paste here from my original code.
$utente = User::firstOrCreate(
['email' => $adesione->utente_email],
[
'id' => $adesione->utente_id,
'name' => $adesione->utente_name,
'password' => Str::random(40),
'created_at'=> $adesione->utente_registerDate,
]
);
$uid = $utente->id;
$strutturaDati = [
'name' => $adesione->struttura_nome,
'user_id' => $utente->id,
... other field assignments here, no other user_id assignment! ;-)
];
$struttura = Accommodation::firstOrCreate(
['id' => $adesione->struttura_id],
$strutturaDati
);
@emmexx I don't notice anything wrong with this code. Does the Accommodation model have any event handlers or observers that might tamper with the data being saved? Something like what is shown here: https://laravel.com/docs/10.x/eloquent#events
@JussiMannisto Not that I remember of.
The App/Events folder is empty.
The App/Providers/EventServiceProvider file listens only to SendEmailVerificationNotification::class and shouldDiscoverEvents returns false.
Could it be some interference from Jetstreams, Filament, some other external package? But what package would interfere in such a way with a user_id field?
@emmexx I don't see how could 3rd party packages could interfere with your model, unless you're using some traits from those packages in your models.
I looked at the run() method and didn't notice anything clearly wrong. Does the Accommodation model contain any code other than what you posted above?
@JussiMannisto I found out the problem. :-( It's my fault obviously.
The accommodation model and another model that was having the same problem has a trait that I created (and forgot) .
static::creating(function ($model) {
$model->user_id = auth()->id();
});
Since I'm running the seeder from the command line auth()->id() is null and Accommodation->user_id is set to null when the model is being created. My bad.
@emmexx Hidden control flow strikes again!
And a youtube tutorial that you don't fully understand when learning something new but you don't listen to the bad feelings that come with it.
@emmexx One thing to learn here is that you should post the real code when you have a problem with it. The Accommodation class you showed is so simple that anyone looking at it could rule it out as being the problem. If it showed custom traits, that would've raised questions and the problem might've been solved quickly.
What I'm saying is that if the id property in your User model is guarded (or not listed in the $fillable array if that's used), this line won't work the way you want:
'id' => $accommodationOld->user_id,
Your user model won't get its id from the old accommodation.
@JussiMannisto You're right but that's another problem.
Since I'm testing the seeder using a specific record from the old database, the user being searched or created is always the same. That user is already in the users table, so the user_id I get from User::firstOrCreate is the one from an existing record. So I'm very sure that when I try to create an accommodation, user_id has a valid value, not null.
@emmexx Can you post your whole run() method? Because if the user model is valid, then there shouldn't be any issues. But something must be wrong.
The following is the unredacted run code of the seeder. It's a mess, sorry but I left the code for my various attempts to solve the error.
public function run(): void
{
// ripulisce
DB::table('accommodations')->truncate();
DB::table('al_memberships')->truncate();
DB::table('payments')->truncate();
DB::table('photos')->truncate();
DB::table('paths')->truncate();
// seleziona id delle strutture che hanno aderito negli ultimi 5 anni
$colonne = [
'utente.email as utente_email',
'utente.id as utente_id',
'utente.name as utente_name',
'utente.registerDate as utente_registerDate',
'struttura.id as struttura_id',
'struttura.status as struttura_status',
'struttura.long as struttura_long',
'struttura.lat as struttura_lat',
'struttura.cf_uid as struttura_cf_uid',
'struttura.cf_created as cf_created',
'struttura.cf_user_id as cf_user_id',
'struttura.nome as struttura_nome',
'struttura.categoria as struttura_categoria',
'struttura.regione as struttura_regione',
'struttura.provincia as struttura_provincia',
'struttura.comune as struttura_comune',
'struttura.cap as struttura_cap',
'struttura.indirizzo as struttura_indirizzo',
'struttura.telefono as struttura_telefono',
//'struttura.status as struttura_fax',
'struttura.email as struttura_email',
'struttura.sito as struttura_sito',
'struttura.stazione as struttura_stazione',
'struttura.km as struttura_km',
'struttura.responsabile as struttura_responsabile',
'struttura.mezpen as struttura_mezpen',
'struttura.numcam as struttura_numcam',
'struttura.costoalta as struttura_costoalta',
'struttura.costobassa as struttura_costobassa',
'struttura.costi as struttura_costi',
'struttura.dom1 as struttura_dom1', // obbligatoria
'struttura.dom2 as struttura_dom2',
'struttura.dom3 as struttura_dom3',
'struttura.dom8 as struttura_dom8',
'struttura.dom9 as struttura_dom9',
'struttura.dom10 as struttura_dom10',
'struttura.dom12 as struttura_dom12', // obbligatoria
'struttura.dom15a as struttura_dom15a', // obbligatoria
'struttura.dom17 as struttura_dom17',
'struttura.descrizione as struttura_descrizione',
/* 'struttura_team',
struttura_note_admin */
'struttura.domT as struttura_domT',
'struttura.telefono2 as struttura_telefono2',
//struttura_evidenza
//struttura_redazione
'struttura.network as struttura_network',
'struttura.cir as struttura_cir',
'adesione.societa as targa_societa',
'adesione.forma as targa_forma_giuridica',
'adesione.p_iva as targa_p_iva',
'adesione.cf as targa_cf',
'adesione.pec as targa_pec',
'adesione.cd as targa_cd',
'adesione.indirizzo_fiscale as targa_indirizzo_fiscale',
'adesione.cap_fiscale as targa_cap_fiscale',
'adesione.comune_fiscale as targa_comune_fiscale',
'adesione.provincia_fiscale as targa_provincia_fiscale',
'adesione.indirizzo_spedizione as targa_indirizzo_spedizione',
'adesione.cap_spedizione as targa_cap_spedizione',
'adesione.comune_spedizione as targa_comune_spedizione',
'adesione.provincia_spedizione as targa_provincia_spedizione',
'adesione.email as targa_email',
'adesione.telefono as targa_telefono',
'adesione.note as targa_note',
'adesione.cf_created as targa_cf_created',
'adesione.anno as targa_anno',
'adesione.status as targa_status',
'adesione.note_admin as targa_note_admin',
'adesione.importo as targa_importo',
'adesione.fattura as targa_fattura',
'adesione.titolare as targa_titolare',
'adesione.destinatario_spedizione as targa_destinatario_spedizione',
'adesione.base as targa_base',
'adesione.materiale as targa_materiale',
'adesione.email_fattura as targa_email_fattura',
'adesione.coupon as targa_coupon',
];
$adesioni = DB::connection('vps3')
->table('alberb2_chronoforms_data_struttura', 'struttura')
->join('alberb2_chronoforms_data_targa AS adesione', 'struttura.id', '=', 'adesione.id')
->join('a2018_users AS utente', 'struttura.cf_user_id', '=', 'utente.id')
->select($colonne)
->whereIn('adesione.status', [2 , 3])
->where('adesione.anno', '>=', '2019')
->where('struttura.language', 'it-IT')
->where('struttura.cf_id', 353) //per i test, rimuovere id=380
->get();
//dd($adesioni);
$indice = 0;
foreach($adesioni as $adesione)
{
echo $indice++ . PHP_EOL;
DB::beginTransaction();
try
{
//dd($adesione);
//dd($adesione->utente_email);
//creo l'utente, primo parametro contiene i campi da cercare nella tabella
$utente = User::firstOrCreate(
['email' => $adesione->utente_email],
[
'id' => $adesione->utente_id,
'name' => $adesione->utente_name,
'password' => Str::random(40),
'created_at'=> $adesione->utente_registerDate,
]
);
$uid = $utente->id;
//$utente->save();
//dd($utente);
//$utente->refresh();
/* $struttura = new Accommodation;
$struttura->name = $adesione->struttura_nome;
$struttura->user_id = $utente->id;
$struttura->descrizione = $adesione->struttura_descrizione;
$struttura->camere = $adesione->struttura_numcam;
$struttura->telefono = $adesione->struttura_telefono;
$struttura->telefono2 = $adesione->struttura_telefono2;
$struttura->email = $adesione->struttura_email;
$struttura->web = $adesione->struttura_sito;
$struttura->cir = $adesione->struttura_cir;
$struttura->costo_minimo = $adesione->struttura_costobassa;
$struttura->costo_massimo = $adesione->struttura_costoalta;
$struttura->sconto_soci = $adesione->struttura_dom2;
$struttura->sconto_gruppi = $adesione->struttura_dom3;
$struttura->sconto_trenitalia = $adesione->struttura_domT;
$struttura->info = $adesione->struttura_dom8;
$struttura->noleggio_bici = $adesione->struttura_dom9;
$struttura->guide_turistiche = $adesione->struttura_dom10;
$struttura->trasporto_bagagli = $adesione->struttura_dom17;
$struttura->distanza_stazione = $adesione->struttura_km;
$struttura->singola_notte = $adesione->struttura_dom1; // obbligatoria
$struttura->parcheggio_bici = $adesione->struttura_dom12; // obbligatoria
$struttura->colazione_rinforzata = $adesione->struttura_dom15a; // obbligatoria
$struttura->status = AccommodationStatus::oldToNew($adesione->struttura_status);
$struttura->save();
dd($struttura); */
$strutturaDati = [
'name' => $adesione->struttura_nome,
'user_id' => $utente->id,
'descrizione' => $adesione->struttura_descrizione,
'camere' => $adesione->struttura_numcam,
'telefono' => $adesione->struttura_telefono,
'telefono2' => $adesione->struttura_telefono2,
'email' => $adesione->struttura_email,
'web' => $adesione->struttura_sito,
'cir' => $adesione->struttura_cir,
'costo_minimo' => $adesione->struttura_costobassa,
'costo_massimo' => $adesione->struttura_costoalta,
'sconto_soci' => $adesione->struttura_dom2,
'sconto_gruppi' => $adesione->struttura_dom3,
'sconto_trenitalia' => $adesione->struttura_domT,
'info' => $adesione->struttura_dom8,
'noleggio_bici' => $adesione->struttura_dom9,
'guide_turistiche' => $adesione->struttura_dom10,
'trasporto_bagagli' => $adesione->struttura_dom17,
'distanza_stazione' => $adesione->struttura_km,
'singola_notte' => $adesione->struttura_dom1, // obbligatoria
'parcheggio_bici' => $adesione->struttura_dom12, // obbligatoria
'colazione_rinforzata' => $adesione->struttura_dom15a, // obbligatoria
'status' => AccommodationStatus::oldToNew($adesione->struttura_status),
];
//dd($strutturaDati);
//creo la struttura
$struttura = Accommodation::firstOrCreate(
['id' => $adesione->struttura_id],
$strutturaDati
);
DB::commit();
} catch(\Exception $e)
{
DB::rollBack();
echo $e->getFile() . ' - ' . $e->getLine() . PHP_EOL;
echo $e->getMessage() . PHP_EOL;
echo $e->getTraceAsString() . PHP_EOL;
}
}
This simply means that you are not getting the user_id, so the database receives a null value.
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'user_id' cannot be null
Make sure that the user_id passed into your method is set properly.
Please or to participate in this conversation.