laravel-activitylog
laravel-activitylog copied to clipboard
Unchanged columns logged when selecting a subset of columns
Describe the bug
I have a table which has an order
column. When I reorder the elements I update each model with the new order.
Because the table has some large JSON columns I select only id
and order
columns for efficiency. I set the new order and save the model.
foreach (Faq::query()->get(['id', 'order']) as $faq) {
$faq->order = $newOrder[$faq->id];
$faq->save();
}
The diffs created in the activity_log
table look like this:
{
"old": {
"order": 1,
"answer": null,
"question": null
},
"attributes": {
"order": 2,
"answer": "long JSON content",
"question": "long JSON content"
}
}
From what I see laravel-activitylog fetches the model with all columns from the database before creating a log entry which kind of defeats the purpose of my "select list optimization" and produces an incorrect diff.
Generally I want these large JSON columns to be logged in case a user changes them but in this case I only change the order
column so I would like to see only the order
column in the diff.
I have tried running these updates straight off the Eloquent builder:
Faq::query()->where('id', $id)->update(['order' => $newOrder[$id]]);
but in this case nothing is logged.
I have the following activitylog configuration for my models:
public function getActivitylogOptions(): LogOptions
{
return LogOptions::defaults()
->logAll()
->logOnlyDirty()
->logExcept([
'id',
'created_at',
'updated_at',
]);
}
To Reproduce Select a subset of columns from the database, change these columns, save the model and see that other (not initially selected) columns are present in the diff.
Expected behavior Only columns that've actually been changed should be present in the diff.
Versions
- PHP: 8.1
- Database: MySQL 8
- Laravel: 9.31.0
- Package: 4.6.0
In your model you can define it like bellow:
protected $logAttributes = ['name'];
public function getActivitylogOptions(): LogOptions
{
$loggableAttributes = array_intersect(array_keys($this->attributes), $this->logAttributes);
return LogOptions::defaults()
->logOnly($loggableAttributes)
->logOnlyDirty()
->dontSubmitEmptyLogs();
}
This worked for me even when we updating fields which are not in the select statement.
Hi @KKSzymanowski,
When you call Faq::query()->get(['id', 'order'])
you are running selecting all the columns from the database and only using id and order. To fetch only id and order you should use Faq::select(['id', 'order'])->get()
this will only fetch id and order from the database
@n41tik not true:
DB::listen(function($q) {
dump($q->sql);
});
Faq::query()->get(['id', 'order']);
"select `id`, `order` from `faqs` where `faqs`.`deleted_at` is null order by `order` asc" // routes/console.php:19
Hello everyone,
I am presenting the same situation that @KKSzymanowski indicates, is there any way to resolve it?
I tried @vishalw89 idea but it didn't work for me.
Greetings,
Hello everyone again,
Considering this part
$properties['attributes'] = static::logChanges(
// if the current event is retrieved, get the model itself
// else get the fresh default properties from database
// as wouldn't be part of the saved model instance.
$processingEvent == 'retrieved'
? $this
: (
$this->exists
? $this->fresh() ?? $this
: $this
)
);
I have done the following in the method that returns the options
public function getActivitylogOptions(): LogOptions
{
$this->exists = false;
return LogOptions::defaults()
->logOnly([
'name',
'last_name'
])
->useLogName('Person')
->logOnlyDirty()
->dontSubmitEmptyLogs();
}
Do you think this could affect the general functioning of the project?