i'm using UserObserver and it's working only if i updated My in this way
User::find(1)->update(['name'=> 'vich']);
but when i try to do
public function update_user_profile(UpdateProfileRequest $request)
{
$user = User::find(1)->update($request->validated());
}
then the observer will not fire update event but the tow ways are updating without any errors
My Observer
public function updating(User $user)
{
dd('99');
}
I think the updated values are not 'dirty' meaning they have the same value as in the database and thus not firing any query. Not sure though, but you can try using different values will it fire then?
Related
public function destroy(
SubmissionType $submissionType,
int $id
) {
return response([], 200);
}
This code triggers
select * from table where id = 1 and deleted_at is null limit 1
Why is this happening? I don't want it to run the select query automatically. I want to inject a dummy model and write a test this automatic behavior is throwing 500 internal server error.
In Laravl you have dependency injection and model binding. In your case it means you can inject models into your control and it will automatically fetch it from the Database, this is quite nice in most cases.
So imagine your route.
submissiontypes/{submissionTypeId}
If you define your destroy method as follows nothing will be triggered in the database.
public function destroy(int $submissionTypeId) {
}
If you want to do model binding, you do as follows. This will tell Laravel to fetch your submission type on the submissionTypeId and automatically load your SubmissionType model.
public function destroy(SubmissionType $submissionType) {
}
When i tried to use Eloquent event (update) it works fine to me , but i noticed that event doesn't fired when i wrote this query
Samples::where('id', $id)->update($inputs);
but it works when i wrote this
Samples::find($id)->update($inputs);
and this is my update event
public static function boot()
{
static::updating(function ($model) {
dd('it works !');
parent::boot();
}
Your first code will directly generate an UPDATE query and send it to the database. It will never load any model.
The second code will first execute a SELECT query to fill a model and then it will do an UPDATE query using the received data.
This is why the first query will never fire your update event.
I am explaining my issue with an example.
My model class is User and observer class is UserObserver.
I have added some code in the updated method of UserObserver that will run everytime the User model update function is used. For example updated method in theUserObserver(below) should get called whenever an update happen in User record.
class UserObserver{
function updated($userModel)
{
//Send mail code
}
}
The code in the UserObserver works when User data update like shown below:
User::find(2)->update(['name'=>'Update Name']);
However, the code in the UserObserver won't run when User data is updated in the following way:
User::where('id', 2)->update(['name'=>'Update Name']);
When I debug I can understand that User::find(2) return User model object and User::where('id', 2) will return a Builder object. So, how can I make use of our observer class method regardless of whether it is updated using User Model object or Builder object?
The issue is, I do have an existing application, some of the models are updating like User::where('id', 2)->update(['name'=>'Update Name']);. It is a difficult task to modify update statement to User::find(2)->update(['name'=>'Update Name']);.
Try as below:
User::where('id', 2)->first()->update(['name'=>'Update Name']);
The one problem with this code is that if you don't have the user with the id = 2 in your database then you will get null and you will get and exception may be as calling update on undefined entity.
So you can avoid this as below:
$user = User::where('id', 2)->first();
if(!empty($user)) {
$user->getModel()->update(['name'=>'Update Name']);
}
Dear Observer not depend on Model object or Builder Object.
It works on both statements...
User::find(2)
and
User::where('id', 2)
I am facing the same problem yesterday, but i install last Monday revisionable package and when i explore that package, its work on my code "User::where('id', 2)".
Actually i am placing my code in updating() method, but when i place the same code in saving() method its work for me...
User model class should have a fillable variable.
class User extends Model
{
protected $fillable = ['name'];
}
Try to add the first() method after where clause.
User::where('id', 2)->first()->update(['name'=>'Update Name']);
Or twist where clause to get User model object.
$user = User::where('id', 2)->first();
if(!$user) {
$user->update(['name'=>'Update Name']);
}
I'm developing a Laravel web app using Laravel 5.2. My question is very simple... How do I listen to a forceDelete event in order to forceDelete model relations?
I've been looking around the web and S.O. for a few but all the questions/answers I've found where releted to the delete method, and also in the API documentation I haven't found very much...
In my case I have a Registry model and a RegistryDetail model
Registry table
|id|name|surname|....
RegistryDetail table
|id|id_registry|....
I've created for both this boot function:
protected static function boot()
{
parent::boot();
static::deleted(function($registry) {
// Delete registry_detail
$registry->registryDetail->delete();
});
static::restored(function($registry) {
// Restore registry_detail
$registry->registrydetail()->withTrashed()->restore();
});
}
Since both models have SoftDeletes the static::deleted function is called only when the delete() method is called. if I call a forceDelete() method the related model won't be deleted from the database.
If you need more informations let me know.
Thanks in advance
The deleted event should still fire when calling forceDelete(). Inside the deleted() event method, you can check the the forceDeleting protected property via isForceDeleting() to see if you're in a regular delete or a forced delete.
static::deleted(function($registry) {
// Delete registry_detail
if ($registry->isForceDeleting()) {
$registry->registryDetail->forceDelete();
} else {
$registry->registryDetail->delete();
}
});
We are trying to detect the changes in Laravel related models at attribute level, as we have to keep audit trail of all the changes which are made via the application.
We can track the changes via isDirty method on the Eloquent model for single model that is not related to any other model, but there is no way that we can track the changes on the related eloquent models. isDirty doesn't work on related models attributes. Can some one please help us on this?
Update to original question:
Actually we are trying to track changes on the pivot table that has extra attributes as well defined on it. IsDirty method doesn't work on those extra attributes which are defined in the pivot table.
Thanks
As much I understand your question, It's can achieve through Model Event and some sort of extra code with current and relation model.
Laravel Model Events
If you dont want to use any additional stuff, you can just use the Laravel Model Events (that in fact Ardent is wrapping in the hooks). Look into the docs http://laravel.com/docs/5.1/eloquent#events
Eloquent models fire several events, allowing you to hook into various
points in the model's lifecycle using the following methods: creating,
created, updating, updated, saving, saved, deleting, deleted,
restoring, restored.
Whenever a new item is saved for the first time, the creating and
created events will fire. If an item is not new and the save method is
called, the updating / updated events will fire. In both cases, the
saving / saved events will fire.
If false is returned from the creating, updating, saving, or deleting
events, the action will be cancelled:
Finally, reffering to your question you can utilize the above approaches in numerous ways but most obviously you can combine it (or not) with the Eloquent Models' getDirty() api docs here method and getRelation() api docs here method
It will work for example with the saving event.
Model::saving(function($model){
foreach($model->getDirty() as $attribute => $value){
$original= $model->getOriginal($attribute);
echo "Changed";
}
$relations = $model->getRelations();
foreach($relations as $relation){
$relation_model = getRelation($relation);
foreach($relation_model->getDirty() as $attribute => $value){
$original= $relation_model->getOriginal($attribute);
echo "Relation Changed";
}
}
return true; //if false the model wont save!
});
Another Thought might help you. when you saving
save() will check if something in the model has changed. If it hasn't it won't run a db query.
Here's the relevant part of code in Illuminate\Database\Eloquent\Model#performUpdate:
protected function performUpdate(Builder $query, array $options = [])
{
$dirty = $this->getDirty();
if (count($dirty) > 0)
{
// runs update query
}
return true;
}
The getDirty() method simply compares the current attributes with a copy saved in original when the model is created. This is done in the syncOriginal() method:
public function __construct(array $attributes = array())
{
$this->bootIfNotBooted();
$this->syncOriginal();
$this->fill($attributes);
}
public function syncOriginal()
{
$this->original = $this->attributes;
return $this;
}
check model is dirty isDirty():
if($user->isDirty()){
// changes have been made
}
Or check certain attribute:
if($user->isDirty('price')){
// price has changed
}
I did not check this code but hopeful to use as your answer by thoughts, if you have any confusion to deal such requirement or something need to optimize or change please let me know.