6

I am building a notification system at the moment, and the notifications get delivered via model events. Some of the notifications are dependent on things happening with the models relationships.

Example: I have a project model that has a one:many relationship with users,

public function projectmanager() {
    return $this->belongsToMany('User', 'project_managers');
}

I am wanting to monitor for changes on this relationship in my project model events. At the moment, I am doing this by doing this following,

$dirtyAttributes = $project->getDirty();
foreach($dirtyAttributes as $attribute => $value) {
   //Do something
}

This is run in the ::updating event of the model but only looks at the models attributes and not any of it's relational data, is it possible get old relational data and new relational data to compare and process?

Udders
  • 6,914
  • 24
  • 102
  • 194
  • Do you find any solution for this? – ekstro Nov 12 '15 at 10:25
  • I don't think it may be possible. Because data with relation has different model for saving (In your case there may be `ProjectManager` Model right?). Though can you post how you trying to save `Project` model? Are you saving it with relations or alone? – Bhaskar Dabhi Nov 24 '16 at 11:43
  • You can always [get the request](https://laravel.com/docs/5.5/helpers#method-request) inside your model event. It feels weird to me but it works. – Charles Wood Oct 03 '19 at 15:10

1 Answers1

6

You should be using an observer class for this.

This has already been covered fairly simply and well by this SO answer, although that answer uses a slightly older method where the class itself needs to call upon its observer. The documentation for the current version (5.3 as of this answer) recommends registering the observer in your application service provider, which in your example would look similar to:

<?php
namespace App\Providers;

use App\Project;
use App\Observers\ProjectObserver;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Project::observe(ProjectObserver::class);
    }
}

For evaluating the differences between the new model values and the old values still in the relational DB, Laravel provides methods for both: getDirty() and getOriginal().

So your observer would look something like:

<?php

namespace App\Observers;

use App\Project;

class ProjectObserver
{
    /**
     * Listen to the Project updating event.
     *
     * @param  Project $project
     * @return void
     */
    public function updating(Project $project)
    {
        $dirtyAttributes = $project->getDirty();
        $originalAttributes = $project->getOriginal();
        // loop over one and compare/process against the other
        foreach ($dirtyAttributes as $attribute => $value) {
            // do something with the equivalent entry in $originalAttributes
        }
    }
}
Leith
  • 3,139
  • 1
  • 27
  • 38
  • 2
    I know it's been like 3 years, but I'm kind of confused why this answer was accepted. It doesn't address the question, which is how to get the dirty/original values for _related_ fields. getDirty and getOriginal don't include any information about relationships (except for, e.g., foreign keys). – Charles Wood Oct 03 '19 at 15:13
  • 2
    @CharlesWood Changes to the relationship itself are determined by the foreign key, which would be a part of the original/dirty values if changed (i.e. which user was changed as the project manager). If you want to detect changes to related models (e.g. you want to know if a user that is a manager changes their name?) then you would have an observer for the User model and do something with the changes there. If you wanted to manipulate the Project to observe changes to related models for some reason, you could use `getRelations()` or `relationsToArray()`. Maybe `getTouchedRelations()`, depends. – Leith Oct 04 '19 at 21:21
  • Ohhhhh, of course! Thanks for spelling it out for me. – Charles Wood Oct 07 '19 at 13:11