I have created a polling system and in the backend (CMS area) I want the ability for admins to be able to remove polls. When an admin removes a poll, it should soft delete the poll. This is working as intended, however I also want to have the ability for the admin to be able to restore a poll. To do this I am displaying all of the polls (including the soft deleted polls) in the admin area.
PollController index() to get all polls
$polls = Poll::withTrashed()->get();
In the blade I want to have two different buttons for each poll. One of restoring and one for deleting but I only want to display 1 button for each poll depending on whether it can be restored or deleted.
To do this, I have put this inside the foreach in the blade:
#if($poll->trashed())
// Restore button
#else
// Delete button
#endif
However the issue is, trashed() keeps returning true for all the polls when only 1 out of the 3 polls I have are actually soft deleted. I am unsure as to why trashed() returns all of these are true?
How would I get this method working correctly? Thanks.
PART 1
It depends on your query. When using soft deletes, Laravel will query all models that are not soft-deleted by default. When you also want to get the soft-deleted models, you need to call the withTrashed() method on your query. Read more here:
https://laravel.com/docs/5.5/eloquent#querying-soft-deleted-models
To understand what withTrashed() does, you need to understand how soft-deleting works. Soft-deleting models works by adding a new column to your database tables called deleted_at. It's value defaults to null. When you soft-delete a model, Laravel will put the current timestamp into that column. Therefore, this field doesn't contain a null value anymore.
When querying models when using soft-deletes, Laravel appends a deleted_at is null condition to the query. Calling the withTrashed() method, removes that condition from the query.
Have a look on the source of the default query modifier and the withTrashed method.
PART 2
That are events. You can call that to tell Laravel, that it should execute that specific closure when this event happens. In your example, it is listening for the "deleting" event. See more on that here:
https://laravel.com/docs/5.5/eloquent#events
PART 3
You can entirely delete soft-deletable models with the forceDelete() method. See "Permanently Deleting Models" here:
https://laravel.com/docs/5.5/eloquent#querying-soft-deleted-models
FOR EXAMPLE
$items = App\Model::withTrashed()->get();
If you want to restore a single item, just find it by ID:
$item = App\Model::find($id);
Related
I have a call to getActivitylogOptions() in a model.
But I need the data saved in the log to be different in the case of insert/update/delete.
For example, when inserting a new line, I only want to save one or two info fields. But in case of an update, I need to save all the fields that have been modified to know what exactly the user modified.
If I leave it like the following code, the update is correct, but when inserting, it saves all the fields which I don't need.
public function getActivitylogOptions(): LogOptions
{
return LogOptions::defaults()
->logAll()
->logOnlyDirty();
}
Is there any way to change the log according to the action?
I have a structure as follows:
course
week
A given week has foreign key relation to course table.
In my Model for course and week I have actions to perform if there is a delete. This is done with this sort of approach in the Model:
public static function boot() {
parent::boot();
static::deleted(function($course) {
// do some stuff;
});
}
When I delete a course in my backend with this line:
\App\Course::find($id)->delete();
it triggers the deleted event and my code goes to the above deleted function.
Through a cascade delete mechanism, the week entries for the course are also deleted. I confirm this in my database that week entries go away when a course is deleted.
However... the week Model event for deleted is not triggered.
Edit: I since learned that cascade deletes from database won't trigger the model events in Laravel. So I tried doing the delete of week items manually in the course model as
static::deleted(function($course) {
// override any cascade deletes and do it manually
// first delete weeks of this course:
\App\Week::where('course_id', $course->id)->get()->each(function($week) {
$week->delete();
});
});
That deletes the week items just fine but the deleted event for the week Model is still not being triggered, which I find unexpected..
Ideas?
Thanks,
Brian
This is because the models are never actually retrieved when executing the delete statement. This is well explained in the Laravel documentation.
When executing a mass delete statement via Eloquent, the deleting and deleted model events will not be fired for the deleted models. This is because the models are never actually retrieved when executing the delete statement.
The cascade happens on the DB level so you will need to manually trigger the event whenever you delete a course in order for the week observer to be notified, a bit of painful job, but this can do it I think:
// I don't know your relationship, but this is the template that you should use
// Change App\Week with your model with full namespace, and pass the Week instance
// as a parameter
event('eloquent.deleted: App\Week', $course->week);
Based on your edit, you will need to do this in order to work:
\App\Week::where('course_id', $course->id)->get()->each(function($week) {
$week->delete();
});
I cannot test this but I am sure it should work, as the events fire on an Eloquent instance not on QueryBuilder as you do.
In my web app I am trying to update one of many notes that are on one work order.
If I write query like this (Case 1):
// NOTE UPDATE WORKING FINE
Model::where('RN_key', $RN_key)->where('anNo', $anNo)->update(['acNote' => $request['note']]);
I get what I want, it's working fine - only one note on work order is updated as it should be.
If I write query update using first() and save() functions like this (Case 2):
// NOTE UPDATE NOT WORKING - Updates EVERY note on one work order but should update ONLY one
$getWorkSpecification = __vVX_ServisniNalog_Radovi::where('RN_key', $RN_key)->where('anNo', $anNo)->first();
$getWorkSpecification->acNote = $request['note'];
$getWorkSpecification->save();
Then it updates and overwrites every note on that same order thus every other note on order is lost. Why is that happening? I need to make it work with first() and save() functions because then it fires event so Laravel Auditing can save audit so I can have logs on every model change any user make.
With Case 1, every note updates fine but no audits are logged and thats the problem.
Using soft-deletes in Laravel 5.4. When I try to create an record that already has been created but soft-deleted I get the message "The xxx has already been taken". What is the best way to be able handle the event when
a user attempts to save a record that has already has been soft deleted?
I have been trying to capture this event in the store method of the controller but the store method does not get called if the record already exists - AFAIK.
In my use case I have radios that can be associated to only one user at a time but can be unassigned e.g. soft-deleted and then reassigned to another user in the future.
You can restore() to restore a soft deleted model:
App\User::withTrashed()->where('id', $user_id)->restore();
Restoring Soft Deleted Models
I have implemented Aloglia for my Movies table with actors as relational table and it works fine.
Problem:
When I update any movie its also updating algolia index (its good). But how can I update index if I made any change in relational table (for example update an actor of movie).
How to push a specific record manually with laravel scout.
Thanks
The issue itself lies in laravel's events. Whats happening is scout is listening for an 'updated' event which only occurs in laravel when the model object is saved and is dirty (aka value differ from that in the db).
There are two ways you can do this.
The bad lazy way would simply be to add ->touch() to the model prior to save - this will force the updated_at field to update and ultimately trigger the updated event. This is bad as you're wasting a DB query.
The second and preferable way is to register another observer on 'saved' which triggers regardless of whether or not the object is dirty. Likely you either want to check if the model is dirty and only index when its not (to prevent double indexing from the updated event) or just de-register the 'updated' listener that comes in Scout.