Laravel-auditing: Event duplicated in database if trying to override a method

Created on 26 Aug 2017  路  3Comments  路  Source: owen-it/laravel-auditing

| Q | A
| ----------------- | ---
| Bug? |yes
| New Feature? | no
| Framework | Laravel
| Framework version | 5.4
| Package version | 4.1.2
| PHP version | 7.1.0

Actual Behaviour

After overriding one of the methods the Audit Event is duplicated (written twice) in the database.

Expected Behaviour

The Audit event should only be written once to the database.

Steps to Reproduce

I'm running a multi-auth application with both users and admins models, I extended the Audit Model by adding an additional column 'admin_id' and then to get the Audit Event to write the admin_id I override the toAudit() method from the OwenIt\Auditing\Auditable Trait to add the extra data to the $data array.

Here's the weird behavior though: even if I use the exact same code in the override it still writes the event twice; the act of overriding the method changes the underlying codes behavior, so ignore what I'm trying to do and just override the method with no other changes and the event is duplicated in the database. The override method works perfectly otherwise, writing user_id when its the user and admin_id when its the admin.

My further extended model (uses App\Auditable below and this duplicates the event.)

namespace App;

use Illuminate\Database\Eloquent\Model as Eloquent;
use OwenIt\Auditing\Contracts\Auditable as AuditableContract;

abstract class MyAuditedModel extends Eloquent implements AuditableContract {

    use Auditable;
    protected $guarded = [];
}

My override Method:

namespace App;

use OwenIt\Auditing\Auditable as OwenAuditable;
use Illuminate\Support\Str;
use RuntimeException;

trait Auditable {

use OwenAuditable;

    public function toAudit()
    {
        if (!$this->readyForAuditing()) {
            throw new RuntimeException('A valid audit event has not been set');
        }

        $method = 'audit'.Str::studly($this->auditEvent).'Attributes';

        if (!method_exists($this, $method)) {
            throw new RuntimeException(sprintf(
                'Unable to handle "%s" event, %s() method missing',
                $this->auditEvent,
                $method
            ));
        }

        $this->updateAuditExclusions();

        $old = [];
        $new = [];

        $this->{$method}($old, $new);

        $foreignKey = Config::get('audit.user.foreign_key', 'user_id');

        return $this->transformAudit([
            'old_values'     => $old,
            'new_values'     => $new,
            'event'          => $this->auditEvent,
            'auditable_id'   => $this->getKey(),
            'auditable_type' => $this->getMorphClass(),
            $foreignKey      => $this->resolveUserId(),
            'url'            => $this->resolveUrl(),
            'ip_address'     => $this->resolveIpAddress(),
            'user_agent'     => $this->resolveUserAgent(),
        'admin_id'    => auth()->guard('admin')->id(),  //this is the only change to the method
        ]);
    }

Possible Solutions

I tried debugging it, it looks like the Audit Events are firing twice but why that is I'm just not experienced enough to answer.

Most helpful comment

Hi @rbuckingham,

I'm glad it worked out for you. Let me just suggest one change that is gonna make your life easier.

Instead of overriding the whole toAudit() method, you can just override the transformAudit() in your MyAuditedModel class.

Take a look here, for how it's done.

All 3 comments

I just renamed my override to MyAuditable and its works now. Well I'm glad I fixed it, just not sure why/how that is.

Thanks for sharing this project, I wrote my own until I discovered this one.

Hi @rbuckingham,

I'm glad it worked out for you. Let me just suggest one change that is gonna make your life easier.

Instead of overriding the whole toAudit() method, you can just override the transformAudit() in your MyAuditedModel class.

Take a look here, for how it's done.

We had to do the same kind of thing. Since the _hidden_ attribute in conjunction with _auditStrict_ actually completely hides the fields that were updated, I use the fields in _hidden_ within my overridden transformAudit() to check what fields to hide. This way, with my overridden Model class, I can have one transformAudit() that gets called by all classes, that will cycle through the _hidden_ array fields; allowing the hidden attributes to be dynamic for each class Audit is in.

Was this page helpful?
0 / 5 - 0 ratings