Laravel populate non-null fields while creating without a default value.
In users migration I added $table->string('username').
What is the best way to auto-populate this?
Using boot/booted method within User model:
protected static function booted () {
if (auth()->user()) {
self::creating(function ($model) {
$model->username = self::generateUsername(self::name);
});
}
}
This doesn't work because the username column is not null and has no default value. I could add nullable or default value as something but that seems like a wrong thing to do.
Ofc, I can add on create as well
User::create($request->validated() + ['username'=>User::generateUsername($request->name)])
But I want to leave it to auto-populate.
Now I am not sure is creating method run on the start of creating or end..
public function setNameAttribute($value)
{
$this->attributes['name'] = $value;
$this->attributes['username'] = $this->generateUsername($value);
}
This solved it :D
Related
I'm trying to give my modal a custom id (no auto increment). So I've overwrite the boot method of my modal. The creating event is used like this:
public static function boot()
{
static::creating(function ($modal) {
$modal->id = $myID;
return true;
});
}
Now when I try to revert the id after saving an entry the id of the new entry is alwas 0.
$modal = new Modal;
$modal->myValue = $myValue;
$modal->save();
dd($modal->id) // This will returns always 0
The strange thing is that the record is successful written to the database with the right id.
What is wrong with my code?
Edit:
It's not returning null. It's returning 0
You need to disable auto increment with setting property $incrementing to false in your model.
public $incrementing = false
I have this route:
Route::get('subscribers/{subscriber}', 'SubscriberController#show');
In my controller's show method, I want it to use the email as a lookup to my table instead of the default id. Is this possible?
public function show(Subscriber $subscriber)
{
// I need this to do like
// $subscriber = Subscriber::findOrFail(<email>);
//
// instead of the default
// $subscriber = Subscriber::finaOrFail(<id>);
return $subscriber;
}
I tried to look for an answer here in StackOverflow but my limited knowledge about Laravel does not seem to allow me to use the right keywords.
Add the following method to your Subscriber Model.
public function getRouteKeyName()
{
return 'email';
}
You can read more about Route Model Binding in the Laravel Documentation.
In my project I'm working on multiple databases and one central one.
I'm using spatie's activity log package to log actions done form control panel to all of that databases.
I have table Items in each of the databases (except for the central) with auto incremented primary key, and another index called hash, which is kind of uuid. Hash is always unique.
Now, when I want to log actions, I can encounter problem as it will save ID of Item, so... in my activity tables I will get two records for subject_id = 1, while one activity happend to Item on one db and another on another, and so on.
How can I change set morphing to use my uuid column instead of id without changing $primaryKey on related model?
Item model relation:
public function activities(): MorphMany
{
$this->morphMany(Activity::class, 'subject', 'subject_id', 'hash');
}
Activity model relation:
public function subject(): MorphTo
{
if (config('activitylog.subject_returns_soft_deleted_models')) {
return $this->morphTo()->withTrashed();
}
return $this->morphTo('activity_log', 'subject_type', 'subject_id', 'hash');
}
Also, I found in ActivityLogger:
public function performedOn(Model $model)
{
$this->getActivity()->subject()->associate($model);
return $this;
}
I ended up with temporary hack.
First of all, I've added a public method to my model:
public function setPrimaryKey(string $columnName)
{
$this->primaryKey = $columnName;
$this->keyType = 'string';
}
Later on I extended ActivityLogger class and implemented my own perfomedOn() method.
public function performedOn(Model $model)
{
if($model instanceof Item::class) {
$model->setPrimaryKey('hash');
}
return parent::performedOn($model);
}
I am aware it is not the best solution but kind of works for now.
Let me preface this post with, I cannot change the method of inserting the primary key. This is being developed on a legacy system and I have no control over the method of retrieving the primary key, I just have to deal with it.
I have found that Laravel will not update the collection primary key when using the create method, with an observer that inserts the primary key value.
Here is my scenario (I have shrunk the models and files for space):
migration file:
Schema::create('forms_maps', function (Blueprint $table) {
$table->integer('id')->unsigned();
$table->string('name');
});
ModelObserver.php:
public function creating(Model $model)
{
$countername = strtolower(class_basename(get_class($model))).'s_id';
$model->id = tap(\App\Models\OCounter::where('countername',$countername)->first())->increment('counterval')->fresh()->counterval;
Log::debug("Creating ". strtolower(class_basename(get_class($model))) . ": " . $model);
}
DatabaseSeeder.php:
$accountApp = \App\Models\FormsMap::create(['name' => 'Account Application']);
Log::debug("Created formsmap: " . $accountApp);
The output log:
Creating formsmap: {"name":"Account Application","id":84}
Created formsmap: {"name":"Account Application","id":0}
As you can see from the log, when the record is created using the create method, inside of the observer, I am getting the proper id; however, that value is not being passed back to the collection in the DatabaseSeeder. Am I looking at this incorrectly? Should I be using something else to insert the values into the tables? I do not want to insert this value manually/inline because every model has to have this information injected.
Thanks!
GRRR!!!! I do this every time! The answer is:
The model needs to have the incrementing turned off.
class FormsMap extends Model
{
public $timestamps = false;
public $incrementing = false;
...
}
Heh!
Suppose I have a Post model by this Attributes :
post_id
title
description
owner //=> type same as user_id of User Model
created_at
updated_at
And now I want to fill out owner field with current (Authenticated) user ID on store action of PostConroller when using Create Model. (owner value not included in $dataArray and should automatically get user_id):
Post::create($dataArray)
Is there a way to do that?
In your model class, add save function override:
public function save(array $options = array())
{
$this->owner = auth()->id();
parent::save($options);
}
That's automatic. Just be carefull if you tend to use save in any other scenario so you don't overwrite owner.
I'm not sure what do you mean by automatically, but you can add user_id to an array:
$dataArray['owner'] = $user_id;
Post::create($dataArray);
But usually you're using collections when creating new model, so:
$dataCollection->put('owner', $user_id);
Post::create($dataCollection);
will add user_id to collection.
Also, do not forget to add owner to $fillable array.
public function save(array $options = []) {
if (!isset($this->user_id) || !(intval($this->user_id)>0) ) {
$this->user_id = auth()->id();
}
return parent::save($options);
}
If your user->id's are number
Remember the return, else save is always false
I#m stuck on this too.
Automatic means that you can provide ALL other data, but when a create is called, the model just knows to add user to equal Auth:id()
like this? (but this isn't working for me
protected static function booting(): void
{
static::creating(function ($product) {
$product->user_id = Auth::id();
});
}