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

Feature/log json attributes #601

Merged
merged 11 commits into from
Oct 6, 2019
27 changes: 27 additions & 0 deletions docs/advanced-usage/logging-model-events.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,33 @@ class NewsItem extends Model
Changing only `name` means only the `name` attribute will be logged in the activity, and `text` will be left out.



## Logging only the specific JSON attributes sub-key
ReeceM marked this conversation as resolved.
Show resolved Hide resolved

If you would like to log only the changes to a specific JSON objects sub-keys. You can use the same method for logging specific columns with the difference of choosing the json key to log.

```php
use Illuminate\Database\Eloquent\Model;
use Spatie\Activitylog\Traits\LogsActivity;

class NewsItem extends Model
{
use LogsActivity;

protected $fillable = ['preferences', 'name'];

protected static $logAttributes = ['preferences->notifications->status', 'preferences->avatar_url'];

protected $casts = [
'preferences' => 'collection' // casting the JSON database column
];
}
```

Changing only `preferences->notifications->status` or `preferences->avatar_url` means only the `preferences->notifications->status` or `preferences->avatar_url` attribute will be logged in the activity, and everything else `preferences` will be left out.

The result in the log entry key for the attribute will be what is in the `$logAttributes`.
Gummibeer marked this conversation as resolved.
Show resolved Hide resolved

## Prevent save logs items that have no changed attribute

Setting `$submitEmptyLogs` to `false` prevents the package from storing empty logs. Storing empty logs can happen when you only want to log a certain attribute but only another changes.
Expand Down
12 changes: 12 additions & 0 deletions src/Traits/DetectsChanges.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Spatie\Activitylog\Traits;

use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Database\Eloquent\Model;
use Spatie\Activitylog\Exceptions\CouldNotLogChanges;
Expand Down Expand Up @@ -125,6 +126,8 @@ public static function logChanges(Model $model): array
foreach ($attributes as $attribute) {
if (Str::contains($attribute, '.')) {
$changes += self::getRelatedModelAttributeValue($model, $attribute);
} elseif (preg_match('/(->)/', $attribute)) {
ReeceM marked this conversation as resolved.
Show resolved Hide resolved
$changes[$attribute] = static::jsonLogAttribute($model, $attribute);
} else {
$changes[$attribute] = $model->getAttribute($attribute);

Expand Down Expand Up @@ -154,4 +157,13 @@ protected static function getRelatedModelAttributeValue(Model $model, string $at

return ["{$relatedModelName}.{$relatedAttribute}" => $relatedModel->$relatedAttribute ?? null];
}

protected static function jsonLogAttribute(Model $model, string $attribute)
ReeceM marked this conversation as resolved.
Show resolved Hide resolved
{
$path = preg_split('/(->)/', $attribute);
ReeceM marked this conversation as resolved.
Show resolved Hide resolved
$modelAttribute = array_shift($path);
$modelAttribute = collect($model->getAttribute($modelAttribute));

return Arr::get($modelAttribute->toArray() ?? [], implode('.', $path));
ReeceM marked this conversation as resolved.
Show resolved Hide resolved
}
}
66 changes: 66 additions & 0 deletions tests/DetectsChangesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1075,6 +1075,72 @@ public function it_can_use_nullable_date_as_loggable_attributes()
$this->assertEquals($expectedChanges, $this->getLastActivity()->changes()->toArray());
}

/** @test */
public function it_can_store_the_changes_of_json_attributes()
Gummibeer marked this conversation as resolved.
Show resolved Hide resolved
{
$articleClass = new class() extends Article {
protected static $logAttributes = ['name', 'json->data'];
public static $logOnlyDirty = true;
protected $casts = [
'json' => 'collection',
];

use LogsActivity;
};

$article = new $articleClass();
$article->json = ['data' => 'test'];
$article->name = 'I am JSON';
$article->save();

$expectedChanges = [
'attributes' => [
'name' => 'I am JSON',
'json->data' => 'test',
],
];

$changes = $this->getLastActivity()->changes()->toArray();

$this->assertSame($expectedChanges, $changes);
}

/** @test */
public function it_will_not_store_changes_to_untracked_json()
{
$articleClass = new class() extends Article {
protected static $logAttributes = ['name', 'json->data'];
public static $logOnlyDirty = true;
protected $casts = [
'json' => 'collection',
];

use LogsActivity;
};

$article = new $articleClass();
$article->json = ['unTracked' => 'test'];
$article->name = 'a name';
$article->save();

$article->name = 'I am JSON';
$article->json = ['unTracked' => 'different string'];
$article->save();

$expectedChanges = [
'attributes' => [
'name' => 'I am JSON',
],
'old' => [
'name' => 'a name',
],
];

$changes = $this->getLastActivity()->changes()->toArray();

$this->assertSame($expectedChanges, $changes);
}

protected function createArticle(): Article
{
$article = new $this->article();
Expand Down
47 changes: 47 additions & 0 deletions tests/LogsActivityTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,53 @@ public function it_will_not_submit_log_when_there_is_no_changes()
$this->assertCount(1, Activity::all());
}

/** @test */
public function it_will_submit_a_log_with_json_changes()
{
$model = new class() extends Article {
use LogsActivity;

protected static $submitEmptyLogs = false;
protected static $logAttributes = ['text', 'json->data'];
public static $logOnlyDirty = true;
protected $casts = [
'json' => 'collection',
];
};

$entity = new $model([
'text' => 'test',
'json' => [
'data' => 'oldish',
],
]);

$entity->save();

$this->assertCount(1, Activity::all());

$entity->json = [
'data' => 'chips',
'irrelevant' => 'should not be',
];

$entity->save();

$expectedChanges = [
'attributes' => [
'json->data' => 'chips',
],
'old' => [
'json->data' => 'oldish',
],
];

$changes = $this->getLastActivity()->changes()->toArray();

$this->assertCount(2, Activity::all());
$this->assertSame($expectedChanges, $changes);
}

public function loginWithFakeUser()
{
$user = new $this->user();
Expand Down