Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unchanged columns logged when selecting a subset of columns #1095

Open
KKSzymanowski opened this issue Sep 28, 2022 · 5 comments
Open

Unchanged columns logged when selecting a subset of columns #1095

KKSzymanowski opened this issue Sep 28, 2022 · 5 comments
Labels
enhancement hacktoberfest https://hacktoberfest.digitalocean.com help wanted

Comments

@KKSzymanowski
Copy link

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
@Gummibeer Gummibeer added enhancement help wanted hacktoberfest https://hacktoberfest.digitalocean.com and removed bug labels Oct 1, 2022
@vishalw89
Copy link

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.

@n41tik
Copy link

n41tik commented Mar 28, 2023

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

@KKSzymanowski
Copy link
Author

@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

@junnysolano
Copy link

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,

@junnysolano
Copy link

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement hacktoberfest https://hacktoberfest.digitalocean.com help wanted
Projects
None yet
Development

No branches or pull requests

5 participants