spatie / laravel-activitylog

Log activity inside your Laravel app
https://docs.spatie.be/laravel-activitylog
MIT License
5.29k stars 712 forks source link

Many to many relations have the same value in old and attributes #1325

Open peirix opened 6 days ago

peirix commented 6 days ago

Describe the bug Basically the same as #1081 but that got closed, so thought I'd create a new one.

We have an Item class that has a many-to-many relationship to Color through the colors():

public function colors()
{
    return $this->belongsToMany(Color::class, 'item_color');
}

And then our getActivitylogOptions is set up as following:

public function getActivitylogOptions(): LogOptions
{
    return LogOptions::defaults()
        ->logFillable()
        ->logOnly(['colors'])
        ->logOnlyDirty();
}

The first thing is that I can't tell it to log ['colors.id'] which I would like to, I then get an error saying id attribute does not exist on collection. But also the old colors array and the attribute colors array are always the same. And I found that if I sync the colors first the arrays will have the new value, and if I save the item before syncing the colors it will have the old value

$item->save(); // in this log the arrays are both the old colors
$item->colors()->sync($request->input('colors'));
$item->save(); //  in this log the arrays are both the new colors

To Reproduce

Schema::create('items', function (Blueprint $table) {
    $table->id();
    $table->string('name')->nullable();
    $table->softDeletes();
    $table->timestamps();
});
Schema::create('colors', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('slug');
    $table->string('hex_code');
    $table->timestamps();
});
Schema::create('item_color', function (Blueprint $table) {
    $table->foreignIdFor(Item::class)->constrained('items')->onUpdate('cascade')->onDelete('cascade');
    $table->foreignIdFor(Color::class)->constrained('colors')->onUpdate('cascade')->onDelete('cascade');

    $table->primary(['item_id', 'color_id']);
});

Expected behavior First off I would expect the arrays to be different, but I would also like to be able to specify what attribute of the related model I would like to be in the array

Versions (please complete the following information)

zenepay commented 2 days ago

I got the same issue. Try to log on changing User roles with: following code, and dd found old and attributes got the same value

public function getActivitylogOptions(): LogOptions
    {
        return LogOptions::defaults()
            ->logOnly(['name', 'email', 'password', 'roles'])
            ->logOnlyDirty()
            ->dontSubmitEmptyLogs();
    }
public function tapActivity(Activity $activity, string $event)
    {
        /** @var Collection $properties */

        if ($properties = $activity->properties) {
            dd($activity->changes());
            if ($properties->has('old')) {
                $old = $properties->get('old');
                if (isset($old['password'])) {
                    $old['password'] = '<secret>';
                }

                if (isset($old['roles'])) {
                    $oldRoleNames = [];
                    if (is_array($old['roles'])) {
                        foreach ($old['roles'] as $oldRole) {
                            $oldRoleNames[] = $oldRole['name'];
                        }
                        $old['roles'] = implode(',', $oldRoleNames);
                    }
                }

                $properties->put('old', $old);
            }

            if ($properties->has('attributes')) {
                $attributes = $properties->get('attributes');
                if (isset($attributes['password'])) {
                    $attributes['password'] = '<new secret>';
                }
                if (isset($attributes['roles'])) {
                    $roleNames = [];
                    if (is_array($attributes['roles'])) {
                        foreach ($attributes['roles'] as $role) {
                            $roleNames[] = $role['name'];
                        }
                        $attributes['roles'] = implode(',', $roleNames);
                    }
                }
                $properties->put('attributes', $attributes);
            }

            $activity->properties = $properties;
        }
    }