I am Trying to Using Observer for Deleting With Relationship But Problem Is When i DD in Created Function Its Working Fine But When i DD In Deleted Function It Shows Nothing (POSTMAN) Means Neither Working Nor Error With Same Everything
Here Is Api:
$api->post('store','App\Http\Controllers\CustomerController#store');
$api->delete('delete/{id}','App\Http\Controllers\CustomerController#destroy');
Here Is Observer file made by artisan
namespace App\Observers;
use App\Customer;
class CustomerObserver
{
public function created(Customer $customer)
{
dd($customer);
}
public function deleted(Customer $customer)
{
dd($customer);
}
}
Here is Customer Controller
class CustomerController extends Controller
{
public function store(Request $request)
{
return Customer::store($request->person);
}
public function destroy($id)
{
$delete = Customer::where('person_id',$id);
$delete->delete();
}
}
Here Is Customer Model File.
class Customer extends Model
{
//Relationship Start From Here
public function person()
{
return $this->belongsTo(Person::class);
}
//End Here
public static function store($request)
{
//Call to Person Model And Save User
$user = Person::store($request);
//Create object of Customer Model
$customer = new Customer();
$customer->fill($request['customers']);
$customer->person()->associate($user)->save();
//return customer
return $customer;
}
}
I know this might be a late reply and not sure if you are still looking for the answer. I think the issue is about how you delete your Customer model.
When you do something like
$delete = Customer::where('person_id',$id);
$delete->delete();
You are executing a mass delete statement. As stated in laravel document, mass deletes will not fire any model events for the models that are deleted This is the reason your deleted observer event didn't fire.
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
Now look at how you create a Customer. You create a model one at a time. Therefore your created observer does get run.
//Create object of Customer Model
$customer = new Customer();
$customer->fill($request['customers']);
$customer->person()->associate($user)->save();
To solve your problem, the easiest way is to retrieve all the models and delete one by one, so that you can trigger the event.
foreach (Customer::where('person_id',$id)->get() as $delete) {
$delete->delete();
}
can you do all things
1 add line in Customer::observe(CustomerObserver::class); in CustomerServiceProvider in boot method
add CustomerServiceProvider in app.php file in provider array
composer dump-autoload
php artisan config:cache
I was also having this problem, however I was calling the delete method from the repository, which prevents the Observer's deleted event from being triggered. When I used the delete from the mode it worked normally.
Only these Methods Works
$customer=Customer::where('id',$id)->first();
if($customer){
$customer->delete();
}
Or
$customer=Customer::find($id)->delete();
Related
I have created an observer called OfficeUserObserver and it's registered in my provider as well. I want a situation whereby when a user is attached to an office. A mail is automatically sent.
So my observer looks thus:
class OfficeUserObserver
{
public function __construct()
{
dd('hi');
}
public function created(OfficeUser $officeUser)
{
dd('hi');
}
public function saving(OfficeUser $officeUser)
{
dd('hi');
}
}
My Controller looks thus:
public function assignUsers(MyRequest $request, Office $office)
{
...
$office->users()->syncWithoutDetaching($users);
}
When I however try to attach a user to an office. The process is successful but nothing ever gets dumped(dd()) on my view despite using dd() in my construct. What am I doing wrong please?
Here is the code in my AppServiceProvider
public function boot()
{
OfficeUser::observe(OfficeUserObserver::class);
}
If you want to use UserObserver you should use pivot models for relations.
https://laravel.com/docs/7.x/eloquent-relationships#defining-custom-intermediate-table-models
class UserOffice extends Model {...}
And observers will work for the pivot model, but not for User or Office.
Or use package for relation events:
https://github.com/chelout/laravel-relationship-events
P.S.: Sorry for disinformation in the previous edit.
I have a model in laravel and I want to do something after the first time which an object of my model is created. the simplest way is to add a static boot method inside my model's class like the code below:
class modelName extends Model
{
public static function boot()
{
parent::boot();
self::created(function ($model) {
//the model created for the first time and saved
//do something
//code here
});
}
}
so far so good! the problem is: the ONLY parameter that created method accepts is the model object itself(according to the documentation) :
Each of these methods receives the model as their only argument.
https://laravel.com/docs/5.5/eloquent#events
I need more arguments to work with after model creation. how can I do that?
Or is there any other way to do something while it's guaranteed that the model has been created?
laravel version is 5.5.
You're close. What I would probably do would be to dispatch an event right after you actually create the model in your controller. Something like this.
class WhateverController
{
public function create()
{
$model = Whatever::create($request->all());
$anotherModel = Another::findOrFail($request->another_id);
if (!$model) {
// The model was not created.
return response()->json(null, 500);
}
event(new WhateverEvent($model, $anotherModel));
}
}
I solved the issue using static property in eloquent model class:
class modelName extends Model
{
public static $extraArguments;
public function __construct(array $attributes = [],$data = [])
{
parent::__construct($attributes);
self::$extraArguments = $data ;
public static function boot()
{
parent::boot();
self::created(function ($model) {
//the model created for the first time and saved
//do something
//code here
self::$extraArguments; // is available in here
});
}
}
It works! but I don't know if it may cause any other misbehavior in the application.
Using laravel events is also a better and cleaner way to do that in SOME cases.but the problem with event solution is you can't know if the model has been created for sure and it's time to call the event or it's still in creating status ( and not created status).
I am currently writing a class that caches model data for a select field.
Now obviously, if any model that is affecting this select field gets inserted, updated or deleted, the cache must be refreshed.
To handle this, I'd like to use the model events of Yii2. For example, if EVENT_AFTER_INSERT is triggered in the model Album, I want to execute the code to refresh the cache of the album select data.
Now I could do this the classical way and add an event to the model Album like this:
class Album extends ActiveRecord {
public function init(){
$this->on(self::EVENT_AFTER_INSERT, [$this, 'refresh_cache']);
$this->on(self::EVENT_AFTER_UPDATE, [$this, 'refresh_cache']);
$this->on(self::EVENT_AFTER_DELETE, [$this, 'refresh_cache']);
}
// ...
}
That would work, yes. Problem is, I'd need to include this code in any model I'd like to create a select field from at any point of development. It's not such a big deal, but you can easily forget it while coding and if the behavior needs to change at some point, you need to update a whole bunch of models.
Now here is my question: Is there any possibility to add events to a model from another component? My idea would be to create a component, that knows about all used select data caches and adds the necessary model events accordingly. any idea how to achieve this or something similar?
you just need create a behaviour and attach it to your various models. see the basic guide and speciffically the Behavior::events() use case
so i went ahead and wrote an example
class RefreshCacheBehavior extends \yii\base\Behavior
{
public function events() {
return [
\yii\db\ActiveRecord::EVENT_AFTER_INSERT => 'refreshCache',
\yii\db\ActiveRecord::EVENT_AFTER_UPDATE => 'refreshCache',
\yii\db\ActiveRecord::EVENT_AFTER_DELETE => 'refreshCache',
];
}
/**
* event handler
* #param \yii\base\Event $event
*/
public function refreshCache($event) {
// model that triggered the event will be $this->owner
// do things with Yii::$app->cache
}
}
class Album extends ActiveRecord {
public function behaviors() {
return [
['class' => RefreshCacheBehavior::className()],
];
}
// ...
}
Is there any possibility to add events to a model from another component?
Yes! You can use class level event handlers. The line of code below shows how to do that.
Event::on(ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
Yii::debug(get_class($event->sender) . ' is inserted');
});
You can use same code in your init method and bind it to your class method instead of that closure.
I would create a class implementing BootstrapInterface and add it to config. Then I would handle those class level events there!
Do yourself a favour and read about events in the Guide as well as the API Documentation
on() is a public method, so you can always attach event to already instantiated object. This may be useful if you're using some kind of factory do build your objects:
public function createModel($id) {
$model = Album::findOne($id);
if ($model === null) {
// some magic
}
$model->on(Album::EVENT_AFTER_INSERT, [$this, 'refresh_cache']);
$model->on(Album::EVENT_AFTER_UPDATE, [$this, 'refresh_cache']);
$model->on(Album::EVENT_AFTER_DELETE, [$this, 'refresh_cache']);
return $model;
}
I have a laravel project, and I need to make some calculations immediately after I save a model and attach some data to it.
Is there any event that is triggered in laravel after calling attach (or detach/sync)?
No, there are no relation events in Eloquent. But you can easily do it yourself (Given for example Ticket belongsToMany Component relation):
// Ticket model
use App\Events\Relations\Attached;
use App\Events\Relations\Detached;
use App\Events\Relations\Syncing;
// ...
public function syncComponents($ids, $detaching = true)
{
static::$dispatcher->fire(new Syncing($this, $ids, $detaching));
$result = $this->components()->sync($ids, $detaching);
if ($detached = $result['detached'])
{
static::$dispatcher->fire(new Detached($this, $detached));
}
if ($attached = $result['attached'])
{
static::$dispatcher->fire(new Attached($this, $attached));
}
}
event object as simple as this:
<?php namespace App\Events\Relations;
use Illuminate\Database\Eloquent\Model;
class Attached {
protected $parent;
protected $related;
public function __construct(Model $parent, array $related)
{
$this->parent = $parent;
$this->related = $related;
}
public function getParent()
{
return $this->parent;
}
public function getRelated()
{
return $this->related;
}
}
then a basic listener as a sensible example:
// eg. AppServiceProvider::boot()
$this->app['events']->listen('App\Events\Relations\Detached', function ($event) {
echo PHP_EOL.'detached: '.join(',',$event->getRelated());
});
$this->app['events']->listen('App\Events\Relations\Attached', function ($event) {
echo PHP_EOL.'attached: '.join(',',$event->getRelated());
});
and usage:
$ php artisan tinker
>>> $t = Ticket::find(1);
=> <App\Models\Ticket>
>>> $t->syncComponents([1,3]);
detached: 4
attached: 1,3
=> null
Of course you could do it without creating Event objects, but this way is more convenient, flexible and simply better.
Steps to solve your problem:
Create custom BelongsToMany relation
In BelongsToMany custom relation override attach, detach, sync and updateExistingPivot methods
In overriden method dispatch desired events.
Override belongsToMany() method in Model and return your custom relation not default relation
and that's it. I created package that already doing that: https://github.com/fico7489/laravel-pivot
Laravel 5.8 now fires events on ->attach()
Check out: https://laravel.com/docs/5.8/releases
And search for: Intermediate Table / Pivot Model Events
https://laracasts.com/discuss/channels/eloquent/eloquent-attach-which-event-is-fired?page=1
Update:
From Laravel 5.8 Pivot Model Events are dispatched like normal model.
https://laravel.com/docs/5.8/releases#laravel-5.8
You just need to add using(PivotModel::class) to your relation and events will work on the PivotModel.
Attach($id) will dispatch Created and Creating
Detach($id) will dispatch Deleting and Deleted,
Sync($ids) will dispatch the needed events too [Created,Creating,Deleting,Deleted]
Only dispatch() with out id doesn't dispatch any event until now.
I'm attempting to make cascading soft deletes work by overriding each model's delete() function, which I think should cascade down from Project to Version to Link, but the problem is it doesn't seem to do that at all. The idea would be that deleting a project would also delete all the versions, which would delete all the links and clear their cached versions, but using $this->versions()->delete(); doesn't seem to actually call the delete() method in the Version model.
Any ideas on how to get this working as I expect it to?
class Project extends Eloquent {
protected $softDelete = true;
public function versions()
{
return $this->hasMany('Version');
}
public function delete()
{
$this->versions()->delete();
return parent::delete();
}
}
class Version extends Eloquent {
protected $softDelete = true;
public function project()
{
return $this->belongsTo('Project', 'project_id');
}
public function links()
{
return $this->hasMany('Link');
}
public function delete()
{
$this->links()->delete();
return parent::delete();
}
}
class Link extends Eloquent {
protected $softDelete = true;
public function version()
{
return $this->belongsTo('Version', 'version_id');
}
public function delete()
{
Cache::forget($this->id);
return parent::delete();
}
}
You want to use the models events instead of overriding the core functions. To quote the docs:
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.
What you want to do is hook into these. You can do this a couple of ways (see the docs here). Here's an example by setting a model boot method:
public static function boot()
{
parent::boot();
/**
* Deleting
* - Called before delete()
* - Passes the instance in as param
*/
static::deleting(function($project){
// Get the project versions ids as an array
$ids = $project->versions()->lists('id');
// Delete the versions
Version::whereIn('id', $ids)->delete();
});
}
You can then do the same in your other models. Hope that helps!
$this->versions()->delete(); as well as Version::whereIn('id', $ids)->delete(); does a delete() call on a query builder, not the eloquent models, which means model events don't get fired. Instead, you should do:
foreach($this->versions as $version) { $version->delete(); }