5

I want to track (record) the changes made to each database row. This means, saving a log of each action (insert, update, delete) made to each record of each table.

This issue is solved for models, as they extend from a BaseModel and I'm using model events. However, I cannot seem to find a way to record the changed from pivot tables.

Given the following tables users, profiles and profile_user(profile_id, user_id), I have the following code:

class User extends BaseModel {
    public function profiles() {
        return $this->belongsToMany('Profile');
    }
}

class Profile extends BaseModel {
    public function users() {
        return $this->belongsToMany('User');
    }
}

abstract class BaseModel extends Model {
    public static function boot() {
        parent::boot();

        static::created(function($model) {
            return LogTracker::saveRowCreatedOrUpdated($model, true);
        });

        static::updated(function($model) {
            return LogTracker::saveRowCreatedOrUpdated($model, false);
        });

        static::deleted(function($model) {
            return LogTracker::saveRowDeleted($model);
        });
    }
}

This allows me to record the changes from user and profile but not from profile_user.

I've tried to create a ProfileUser model that extends from Pivot (Illuminate\Database\Eloquent\Relations\Pivot) where I defined the model events but that didn't work.

I'm guessing that's because I never create a new instance of this model. So, I've added the following to my User model (and similar code to Profile):

class User extends BaseModel {
    // (...)
    public function newPivot(Eloquent $parent, array $attributes, $table, $exists) {
        if ($parent instanceof Profile) {
            return new ProfileUser($parent, $attributes, $table, $exists);
        }
        return parent::newPivot($parent, $attributes, $table, $exists);
    }
    // (...)
}

Still, the events are never fired (actually this method is never executed).

I am updating the relationship through sync():

$user->profiles()->sync(explode(',', $values["profiles"]));

I'm looking for a solution that doesn't involve firing a custom event (as this means that I would have to do this for each pivot table in database).

How can I use model events in pivot tables?

Luís Cruz
  • 14,780
  • 16
  • 68
  • 100
  • You can't do that with Eloquent events, since `attach` method relies on `Query\Builder` and simple insert (it is called behind `sync`). And btw what's up with these relations? `Post belongsTo Subcategory` and `Subcategory belongsToMany Post` ?? This is wrong, unless you just made a typo.. – Jarek Tkaczyk Nov 28 '14 at 23:43
  • @JarekTkaczyk Sorry about that, I had unused relationships and picked the wrong example. I've updated the code with a correct example. Are you aware of any way to do this? – Luís Cruz Nov 29 '14 at 00:53
  • you need this package https://github.com/fico7489/laravel-pivot – fico7489 Dec 03 '17 at 22:25

1 Answers1

3

I am aware you don't want a custom event situation but I cannot find any non-custom solution.

You can however do a very simple custom one (pretty much pulled right from Laravel docs):

DB::listen(function($sql, $bindings, $time)
{
    //regex matching for tables in SQL query
    //as well as firing your event handling code
});

http://laravel.com/docs/4.2/database#running-queries

  • 2
    Using `illuminate.query` might be a last resource solution. The advantage of model events, as I see it, is that you get the model and have the ability to use `$model->getTable()` and `$model->isDirty()`. With this solution, I would have to parse the `$sql` to determine if it's an insert, update or delete, parse the tablename and process the bindings. – Luís Cruz Nov 25 '14 at 10:52