I have some questions relating to soft delete in laravel. I have search up on what it does and what it means and the most understandable part about soft delete is from this sentence
"When models are soft deleted, they are not actually removed from your
database. Instead, a deleted_at attribute is set on the model and
inserted into the database. If a model has a non-null deleted_at
value, the model has been soft deleted. To enable soft deletes for a
model, use the Illuminate\Database\Eloquent\SoftDeletes trait on the
model and add the deleted_at column to your $dates property:"
So here are my questions based from that sentence:
Q1:
So when I use soft delete in my code, and when I try to delete some
data, does the data in the view page(blade.php) disappear while the
database still contain those data?
Q2:
I saw some people using something called static::deleting, I don't
really quite get how this work? Could you explain what it does? Thank
you
Q3:
How do you delete data using soft delete? I saw people just putting
some stuff into their model instead of using button, so does that mean
you can only delete it manually inside the model instead of just
clicking the delete button in the view page?
Question 1
By default: Yes.
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.
Question 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
Question 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
Q1: So when I use soft delete in my code, and when I try to delete
some data, does the data in the view page(blade.php) disappear while
the database still contain those data?
Yes. The soft delete fill the deleted_at column in the database. Since that, Eloquent will not retrieve these data (except if you ask for). If you use custom SQL request, you'll need to add a WHERE deleted_at IS NULL
Q2: I saw some people using something called static::deleting, I don't
really quite get how this work? Could you explain what it does? Thank
you
I'm not using that day to day, but it's an event you can call (see here ) to automatically delete content related (for example, if you remove an user, you can also remove all his post. It's kind of cascading delete)
Q3: How do you delete data using soft delete? I saw people just
putting some stuff into their model instead of using button, so does
that mean you can only delete it manually inside the model instead of
just clicking the delete button in the view page?
To use the soft delete, you just $object->detroy($id) or $myEloquentRequest->where(...)->delete()
If you want to force a real delete (so the entries will be definitly removed from the database), you can use $flight->forceDelete();
See here for more.
You can do the delete wherever you want. The click on a button bring the user to the delete() method in your controller. You can delete there or call a method inside the model to trigger the delete (and maybe some more complex deleting like event ... )
Soft delete means not delete records in database.So we handle one flag
for manage records is deleted or not.
Let's i explain more with examples :
In our records many user so we add one fields delete_at into database and defaults it's value null so it's records is not deleted.
Now when we fetch all user data we write query like
Select * from user where delete_at = null
So this query return all user data which is not deleted.
Now we delete this user so when we click on delete button we create custom query and update this user delete_at fields with current datetime
Update delete_at=date() where user_id = 1
so now this records is soft delete.
Now i answer your question:
Q.1)No data not displaying after soft delete because when we fetch data it's check delete_at fields null or not.
clarifying your points.
1. So when we use soft delete in code, deleted_at will be updated to present timestamp from null, so while querying data from that particular table from anywhere in your project the eloquent model will automatically return data whose deleted_at is set NULL i.e., it is not soft deleted.
people using something called static::deleting.
you might have seen somewhat like this example if I am understanding your question in right way:
class X-Model extends Eloquent
{
public function xy()
{
return $this->has_many('XY_Model');
}
// this is a recommended way to declare event handlers
protected static function boot() {
parent::boot();
static::deleting(function($x) {
// before delete() method call this
$user->xy()->delete();
// do the rest of the cleanup...
});
}
}
This is a use-case for Eloquent events to delete a record which will use the "deleting" event to do the cleanup.
delete data using soft delete
now after calling this, you can soft delete the data from the table and also u can soft delete the data from other dependent tables if relationships are maintained properly in models. this is actually known as cascading effect. example for you to understand.
Model structure:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Utilities\Uuids;
use Illuminate\Database\Eloquent\SoftDeletes;
use Iatstuti\Database\Support\CascadeSoftDeletes;
class XFolder extends Model
{
use SoftDeletes, CascadeSoftDeletes;
use Uuids;
protected $connection = 'XDB';
protected $table = 'x_folder';
protected $dates = ['deleted_at'];
public $incrementing = false;
protected $cascadeDeletes = ['XReference']; //related table with foreign keys
protected $visible = [ 'id', 'x_id', 'xz_id', 'at_id', 'title', 'description', 'description', 'status', 'created_on' , 'is_active'];
protected $fillable = [
'x_id',
'xz_id',
'at_id',
'title',
'description',
'status',
'created_on',
'is_active'
];
public function XReference()
{
return $this->hasMany('App\Models\XReference', 'x_id');
}
}
now in the controller you can call like this:
public function deleteData($id)
{
$results = $this->getModel()->where('id', $id)->deleteData();
return $results;
}
this will delete data (soft delete in cascading format).
Q1: So when I use soft delete in my code, and when I try to delete some data, does the data in the view page(blade.php) disappear while the database still contain those data?
Yes, if your model use softDelete, than the search query will be like this: Select * from table_name where delete_at = null
Q2: I saw some people using something called static::deleting, I don't really quite get how this work? Could you explain what it does?
You can do it with: Modelname::destroy($id) static action
It destroys the model object where the $id is set. On destroying I mean: it updates the deleted_at column, so the aplication will see it as a soft deleted object
Q3: How do you delete data using soft delete? I saw people just putting some stuff into their model instead of using button, so does that mean you can only delete it manually inside the model instead of just clicking the delete button in the view page?
Example of one of my project:
I have a delete button at the partner screen what routes to partner/{{ id }}/delete
at the routes: Route::get('/partner/{id}/delete', 'PartnerController#deletePartner');
What goes to this action:
public function deletePartner($partnerId = 0){
if ($partnerId > 0){
Partner::destroy($partnerId);
}
return redirect("/partner");
}
So: If I click to delete button it check, that the ID is set and then "destroys it" (soft delete). After deletion it redirects back to the partner
EDIT:
For the example given in the question 3, when you delete the data, does the database data disappear or only the view?
It will dissapear only from the view. In the database it will be stored as:
id name ... created_at updated_at deleted_at
1 foo ... 2017-10-01 00:00 2017-10-01 00:00 NULL
2 bar ... 2017-10-01 00:00 2017-10-01 00:00 2017-10-25 16:00
The first one is a non-deleted the second one is a soft-deleted object and the view only will show the first one
Related
I have a table named data and I am using soft delete to delete the record.i have one more field in my data table which is also updated when a record is deleted. but when I restore that soft deleted record, it updates my deleted_at field but doesn't update the other field.
I use this code
Data::withTrashed()->find($id)->restore();
How can I do this?
You could write an observer that hooks into the restored event:
<?php
namespace App\Observers;
class DataObserver
{
public function restored($data)
{
// Update field here
}
}
And of course register it a boot() method of a service provider:
Data::observe(DataObserver::class);
Note: Depending on the timing, you may prefer the restoring event instead
Data::withTrashed()->find($id)->restore();
meant this only for restoring which is set the deleted_at column to null
so if you want update the other field, just do the usual things for updating fields after you restore it
like this one
Data::withTrashed()->find($id)->restore();
Data::find($id)->update($array);
To achieve this you can create function in your Data model which will do that for you.
public function restoreItem()
{
$this->deleted_at = null;
$this->other_field = null;
$this->save();
}
Now you need to call this in your controller:-
Data::withTrashed()->find($id)->restoreItem();
That's it.
This will work
Data::withTrashed()->whereId($id)->update([
'deleted_at'=>null,
'other_column'=>$value,
]);
is it possible to disable toSearchableArray when doing updates to a record or is there any way to only update specific fields in a record in my search index?
Eg:
public function toSearchableArray()
{
$item = $this->toArray();
$item['title'] = $this->title;
...
...
...
$item['category'] = $this->category->category_name;
$item['uploaded_at'] = Carbon::now('America/Montreal')->timestamp;
}
The only problem now is each time I update a record it also resets its uploaded_at timestamp and re-loads the relationship which is one more query I dont need since it already has it set when I created the item.
So is there any way I can temporary disable toSearchableArray ? I only need to update a few fields in in the row in my index so there is no need to rerun everything in toSearchableArray
Like bellow only update the title and then update the title in my algolia index without reseting uploaded_at or loading the category relation again
$order = App\Order::find(1);
$order->title = 'a new title'
$order->save();
You can use, unsearchable function available in laravel scout.
$modal->unsearchable();
//etc.....
//Finally save the modal
$modal->save()
This way when you save or update it's won't sync to algolia.
If You again want to sync the model to algolia you may call searchable method as shown below.
$modal->searchable();
I have a flight class and this flight has a custom view field like so:
This represents a belongs to many relationship which stores website_id / flight_id and pricing as pivot data in a pivot table.
The custom view uses JS to send this data back to the controller in this format:
{"1":{"price_adult":"434","price_child":"545"},"2":{"price_adult":"323","price_child":"324"},"3":{"price_adult":"434","price_child":"43"}}
Trying to send this data with the request doesn't create the relations fields, and because I do not have a flight ID at the point of creating this within the controller I can not loop this JSON to make the relations manually.
Can anyone point out what the best course of action is or if there is support for this? I took a look at the docs but they are woefully short and patchy in terms of being much help.
EDIT:
I should have said I can probably make this work using a custom name attribute on the model for the relation, then add a set mutator to loop this data and update the prices relation but I don't want to go down this route if there is support for this I am missing out of the box in backpack.
EDIT2:
Someone asked about the relation:
$this->belongsToMany(Website::class, 'website_pricing')->withPivot('price_adult', 'price_child');
This is working fine its not a problem with the relation working its how can I get backpack to store the data as a relation when the flight has no ID yet, or how can I pass the data I posted above in such a way that the backpack crud controller can handle it?
You may need to create a flight first, if no flight id is being provided. Can you explain the database relational structure more?
Basically thought I should post what I did because no one could provide an answer to this.
So basically you have to copy the store / update functions from the parent, changing a few lines.
$this->crud->hasAccessOrFail('create');
// fallback to global request instance
if (is_null($request)) {
$request = \Request::instance();
}
// replace empty values with NULL, so that it will work with MySQL strict mode on
foreach ($request->input() as $key => $value) {
if (empty($value) && $value !== '0') {
$request->request->set($key, null);
}
}
// insert item in the db
$item = $this->crud->create($request->except(['save_action', '_token', '_method']));
$this->data['entry'] = $this->crud->entry = $item;
// show a success message
\Alert::success(trans('backpack::crud.insert_success'))->flash();
// save the redirect choice for next time
parent::setSaveAction();
return parent::performSaveAction($item->getKey());
Basically any line which references a function in the parent class using $this->method needs to be changed to parent::
This line is what I used to submit the relations JSON string passed to the controller as relations $item->prices()->sync(json_decode($request->input('prices'), true));
This is done after the line containing $item = $this->crud->create as the item id that just got stored will be available at that point.
I use multiple domains for the same site structure, so almost all tables have the domain_id column. So, to NOT define the domain_id condition every time I am using the following approach (not sure if this is the best one)
I created BaseModel that other models are extending from and have this in boot method of that BaseModel
parent::boot();
static::addGlobalScope(new DomainScope());
and here is the apply method's content according to docs
if (Schema::hasColumn($model->getTable(), 'domain_id')) {
$builder->where('domain_id', '=', DOMAIN_ID);
}
This works great, however if I have e.g. 5 find queries on the same page, to the same table (just as an example) in debug panel I see 5 queries like this
select column_name from information_schema.columns where table_schema = 'my_db_name' and table_name = 'my_table_name'
Now, I perfectly understand that to check whether the column exists in the table it gets the information from information schema, but why it is making the same query for the same table over and over again. I would presume It should make one request and then cache it, and for subsequent requests just read from cache.
q1) Does laravel internally cache this query ? I am thinking maybe because debug is enabled, that is why each time its making a query ? but cant find any verification of this
q2) and if it does not cache, can I cache it manually. I checked the laravel docs for adding a cache, but the problem here that query is not done by me, so I cant figure out to simply use Cache::remember
Thanks
Well, I would suggest not to fire a query for checking if domain_id exists in the table every time. You can do this by simply adding a variable to your model that defines whether the model is a multi-domain model (has a domain_id column) or single domain model (no domain_id column) like so:
// In BaseModel.php
public $multiDomain = true; // override to false in your child model class if it's a single domain model
// In DomainScope apply method
if ($model->multiDomain)) {
$builder->where('domain_id', '=', DOMAIN_ID);
}
If you don't want to use the above, you can cache like so:
if(Cache::remember($model->getTable() . "_domain", $minutes, function() use($model) {
return Schema::hasColumn($model->getTable(), 'domain_id');
}) {
$builder->where('domain_id', '=', DOMAIN_ID);
}
How do I determine if a model uses soft deletes in Laravel 4.2?
In the Laravel API I found the function isSoftDeleting(), but apparently that was removed from Laravel 4.2 now that it uses the SoftDeletingTrait.
How would I go about determining if a model uses soft deletes now?
If you want to check programatically whether a Model uses soft deletes you can use the PHP function class_uses to determine if your model uses the SoftDeletingTrait
// You can use a string of the class name
$traits = class_uses('Model');
// Or you can pass an instance
$traits = class_uses($instanceOfModel);
if (in_array('SoftDeletingTrait', $traits))
{
// Model uses soft deletes
}
// You could inline this a bit
if (in_array('SoftDeletingTrait', class_uses('Model')))
{
// Model uses soft deletes
}
I needed to detect soft deletion on a model where the trait had been included in a parent class, so class_uses() did not work for me. Instead, I checked for the bootSoftDeletingTrait() method. Something along the lines of:
// Class Name
$usesSoftDeletes = method_exists('User', 'bootSoftDeletingTrait');
or
// Model Instance
$usesSoftDeletes = method_exists($model, 'bootSoftDeletingTrait');
should work.
For Laravel 5.x only
If checking on main model:
//class_uses retrieves a list of traits from the object passed into an array
//Hence in_array check for name of trait (in string format)
//#param bool $usesSoftDeletes
$usesSoftDeletes = in_array('Illuminate\Database\Eloquent\SoftDeletes', class_uses($model));
If you are checking on the main model's relationship, use the following:
//Replace `myRelationshipName` by the name of the relationship you are checking on.
//getRelated() function fetches the class of the relationship.
//#param bool $relatedUsesSoftDeletes
$relatedModel = $model->myRelationshipName()->getRelated();
$relatedUsesSoftDeletes = in_array('Illuminate\Database\Eloquent\SoftDeletes', class_uses($relatedModel));
There's basically not a direct approach on knowing if a model soft deletes by calling a function since the method isSoftDeleting() has been removed from 4.2. You know if a model is using soft delete if the use SoftDeletingTrait; is present in your Model's class and when the deleted_at column exists in your database table.
You can basically trust Laravel on removing a record from your database using soft delete when you have defined the use SoftDeletingTrait in your model's class, and have the deleted_at (is a TIMESTAMP) in your model's database table.
Well, I figured out out a good enough solution for my needs.
First I make this call:
$traits = class_uses($model);
Then I check for the the softdeletingtrait
$usesSoftDeletes = in_array('Illuminate\Database\Eloquent\SoftDeletingTrait', $traits);
At least this way I avoid having to call the database for each model I test. Though it'll break if they change the SoftDeletingTrait name or location later on...
This is the best way
$model = 'App\\Models\\ModelName';
$uses_soft_delete = in_array('Illuminate\Database\Eloquent\SoftDeletes', class_uses($model));
if($uses_soft_delete) {
// write code...
}