Skip to content

Commit

Permalink
Merge pull request #26 from TappNetwork/add_custom_presentation
Browse files Browse the repository at this point in the history
Add the ability to customize the presentation
  • Loading branch information
andreia authored Aug 15, 2024
2 parents 57212cc + 32052e3 commit a953b7e
Show file tree
Hide file tree
Showing 5 changed files with 266 additions and 22 deletions.
187 changes: 185 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ composer require tapp/filament-auditing:"^3.0"
> **Note**
> For **Filament 2.x** check the **[2.x](https://github.com//TappNetwork/filament-auditing/tree/2.x)** branch
You can publish the view file with:
You can publish the view files with:

```bash
php artisan vendor:publish --tag="filament-auditing-views"
Expand Down Expand Up @@ -67,6 +67,13 @@ return [
// ],
]

'custom_audits_view' => false,

'custom_view_parameters' => [
],

'mapping' => [
],
];
```

Expand Down Expand Up @@ -117,7 +124,6 @@ return [
];
```


After adding this information in the config, please run this command for changes to take place.

```bash
Expand All @@ -126,6 +132,183 @@ php artisan optimize

As things stand, methods with two required parameters are not supported.

### Custom View Data Formatting

If you want to modify the content of the audit to display the old and new values in a specific way, such as showing the value of a specific column instead of the ID for relationships, or even customize the entire view to display data in a different way than the default table, you can use one of these methods described below (first, make sure the plugin views are published):

#### Show a related column instead of foreign id

To use another field to be displayed for relationships instead of the foreign id, in old and new values, you can add on the `mapping` array, in `filament-auditing.php` config file, the label and field that should be displayed, as well as the related model, using the foreign key as the array key. For example, on an `user` relationship with the `user_id` foreing key, this config will display the user `name` along with the `User` label:

```bash
'mapping' => [
'user_id' => [
'model' => App\Models\User::class,
'field' => 'name',
'label' => 'User',
],
],
```

And you'd like to customize the view, you can do it in the published view `views/vendor/filament-auditing/tables/columns/key-value.blade.php` file.

#### Customizing the Old and New Values

If you need to customize the presentation for other old and new values, besides the related fields, you can add a `formatAuditFieldsForPresentation($field, $record)` method on the model that is auditable, with two parameters:
- the first parameter contains the name of the field (`old_values` or `new_values`).
- the second parameter contains de current audit record

This method must return the formatted audit fields.

For example, let's say you have an `Article` model that is auditable and contains a related user, and you added a `formatAuditFieldsForPresentation($field, $record)` method that returns the related user name instead of the id, and the data formatted with some HTML code:

```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use OwenIt\Auditing\Contracts\Audit;
use OwenIt\Auditing\Contracts\Auditable;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Arr;
use Illuminate\Support\HtmlString;

class Article extends Model implements Auditable
{
use \OwenIt\Auditing\Auditable;

// ...

public function formatAuditFieldsForPresentation($field, Audit $record)
{
$fields = Arr::wrap($record->{$field});

$formattedResult = '<ul>';

foreach ($fields as $key => $value) {
$formattedResult .= '<li>';
$formattedResult .= match ($key) {
'user_id' => '<strong>User</strong>: '.User::find($record->{$field}['user_id'])?->name.'<br />',
'title' => '<strong>Title</strong>: '.(string) str($record->{$field}['title'])->title().'<br />',
'order' => '<strong>Order</strong>: '.$record->{$field}['order'].'<br />',
'content' => '<strong>Content</strong>: '.$record->{$field}['content'].'<br />',
default => ' - ',
};
$formattedResult .= '</li>';
}

$formattedResult .= '</ul>';

return new HtmlString($formattedResult);
}

public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}
```

#### Customizing the Entire View Content

If you'd like to customize the entire view content, you may set the `custom_audits_view` config value to `true` on `config/filament-auditing.php` file:

```php
'custom_audits_view' => true,
```

This modification will allow you to take full control of the display and tailor it to your specific requirements. You can now add your custom content on `resources/views/vendor/filament-auditing/tables/custom-audit-content.blade.php` file.
For example:

```php
@php
$type = (string) str(class_basename($owner))->lower();
@endphp

@if(isset($records))
<x-filament-tables::table>
<x-slot name="header">
@foreach($headers as $header)
<x-filament-tables::header-cell>
{{$header}}
</x-filament-tables::header-cell>
@endforeach
</x-slot>
@foreach($records as $audit)
<x-filament-tables::row>
@foreach ($audit->getModified() as $attribute => $modified)
<x-filament-tables::cell>
@lang($type.'.metadata', $audit->getMetadata())
<br />
@php
$current = $type.'.'.$audit->event.'.modified.'.$attribute;

$modified['new'] = $owner->formatFieldForPresentation($attribute, $modified['new']);

if (isset($modified['old'])) {
$modified['old'] = $owner->formatFieldForPresentation($attribute, $modified['old']);
}
@endphp

@lang($current, $modified)
</x-filament-tables::cell>
@endforeach
</x-filament-tables::row>
@endforeach
</x-filament-tables::table>
@else
<div class="flex items-center justify-center h-32 text-gray-500 dark:text-gray-400">
@lang($type.'.unavailable_audits')
</div>
@endif
```

The owner record is available to this view via `$owner` variable. To pass some additional parameters to the view, you may use the `custom_view_parameters` config:

```php
'custom_view_parameters' => [
'headers' => [
'Audit',
],
],
```

To format a field, you may also add a `formatFieldForPresentation` method on the owner model, with the field name and value as parameters, like in the example above. This method must return a formatted field.

For example, in an `Article` model, to return the name of the related user:

```php
public function formatFieldForPresentation($field, $value)
{
return match($field) {
'user_id' => $value ? optional(User::find($value))->name : $value,
default => $value,
};
}
```

An example of the `article.php` lang file content used in the `custom-audit-content.blade.php` view code above:

```php
<?php

return [
'unavailable_audits' => 'No article audits available',

'metadata' => 'On :audit_created_at, :user_name [:audit_ip_address] :audit_event this record via :audit_url',

'updated' => [
'modified' => [
'order' => 'The Order has been modified from <strong>:old</strong> to <strong>:new</strong>',
'title' => 'The Title has been modified from <strong>:old</strong> to <strong>:new</strong>',
'content' => 'The Content has been modified from <strong>:old</strong> to <strong>:new</strong>',
'user_id' => 'The User has been modified from <strong>:old</strong> to <strong>:new</strong>',
],
],
];
```

### Permissions

Two permissions are registered by default, allowing access to:
Expand Down
8 changes: 8 additions & 0 deletions config/filament-auditing.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,12 @@
// ],
],

'custom_audits_view' => false,

'custom_view_parameters' => [
],

'mapping' => [
],

];
38 changes: 24 additions & 14 deletions resources/views/tables/columns/key-value.blade.php
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
@php
$data = isset($state) ? $state : $getState()
@endphp

<div class="my-2 text-sm font-medium tracking-tight">
@foreach($getState() ?? [] as $key => $value)
<span class="inline-block p-1 mr-1 rounded-md whitespace-normal text-gray-700 dark:text-gray-200 bg-gray-500/10">
{{ $key }}
</span>
@unless(is_array($value))
{{ $value }}
@else
<span class="divide-x divide-solid divide-gray-200 dark:divide-gray-700">
@foreach ($value as $nestedValue)
{{$nestedValue['id']}}
@endforeach
</span>
@endunless
@endforeach
<ul>
@foreach($data ?? [] as $key => $value)
<li>
<span class="inline-block rounded-md whitespace-normal text-gray-700 dark:text-gray-200 bg-gray-500/10">
{{ Str::title($key) }}:
</span>
<span class="font-semibold">
@unless(is_array($value))
{{ $value }}
@else
<span class="divide-x divide-solid divide-gray-200 dark:divide-gray-700">
@foreach ($value as $nestedValue)
{{$nestedValue['id']}}
@endforeach
</span>
@endunless
</span>
</li>
@endforeach
</ul>
</div>
1 change: 1 addition & 0 deletions resources/views/tables/custom-audit-content.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{-- add your custom code here --}}
54 changes: 48 additions & 6 deletions src/RelationManagers/AuditsRelationManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
use Filament\Notifications\Notification;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables;
use Filament\Tables\Columns\Column;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\Arr;
use Illuminate\View\View;
use OwenIt\Auditing\Contracts\Audit;

class AuditsRelationManager extends RelationManager
Expand Down Expand Up @@ -38,8 +40,31 @@ public static function getTitle(Model $ownerRecord, string $pageClass): string

public function table(Table $table): Table
{
$oldValuesColumn =
method_exists($this->getOwnerRecord(), 'formatAuditFieldsForPresentation')
?
Tables\Columns\TextColumn::make('old_values')
->formatStateUsing(fn (Column $column, $record, $state) => method_exists($this->getOwnerRecord(), 'formatAuditFieldsForPresentation') ? $this->getOwnerRecord()->formatAuditFieldsForPresentation($column->getName(), $record) : $state)
->label(trans('filament-auditing::filament-auditing.column.old_values'))
:
Tables\Columns\TextColumn::make('old_values')
->formatStateUsing(fn (Column $column, $record, $state): View => view('filament-auditing::tables.columns.key-value', ['state' => $this->mapRelatedColumns($column->getState(), $record)]))
->label(trans('filament-auditing::filament-auditing.column.old_values'));

$newValuesColumn =
method_exists($this->getOwnerRecord(), 'formatAuditFieldsForPresentation')
?
Tables\Columns\TextColumn::make('new_values')
->formatStateUsing(fn (Column $column, $record, $state) => method_exists($this->getOwnerRecord(), 'formatAuditFieldsForPresentation') ? $this->getOwnerRecord()->formatAuditFieldsForPresentation($column->getName(), $record) : $state)
->label(trans('filament-auditing::filament-auditing.column.new_values'))
:
Tables\Columns\TextColumn::make('new_values')
->formatStateUsing(fn (Column $column, $record, $state): View => view('filament-auditing::tables.columns.key-value', ['state' => $this->mapRelatedColumns($column->getState(), $record)]))
->label(trans('filament-auditing::filament-auditing.column.new_values'));

return $table
->modifyQueryUsing(fn (Builder $query) => $query->with('user')->orderBy(config('filament-auditing.audits_sort.column'), config('filament-auditing.audits_sort.direction')))
->content(fn (): ?View => config('filament-auditing.custom_audits_view') ? view('filament-auditing::tables.custom-audit-content', Arr::add(self::customViewParameters(), 'owner', $this->getOwnerRecord())) : null)
->columns(Arr::flatten([
Tables\Columns\TextColumn::make('user.name')
->label(trans('filament-auditing::filament-auditing.column.user_name')),
Expand All @@ -48,12 +73,8 @@ public function table(Table $table): Table
Tables\Columns\TextColumn::make('created_at')
->since()
->label(trans('filament-auditing::filament-auditing.column.created_at')),
Tables\Columns\ViewColumn::make('old_values')
->view('filament-auditing::tables.columns.key-value')
->label(trans('filament-auditing::filament-auditing.column.old_values')),
Tables\Columns\ViewColumn::make('new_values')
->view('filament-auditing::tables.columns.key-value')
->label(trans('filament-auditing::filament-auditing.column.new_values')),
$oldValuesColumn,
$newValuesColumn,
self::extraColumns(),
]))
->filters([
Expand All @@ -78,6 +99,27 @@ public function table(Table $table): Table
]);
}

protected static function customViewParameters(): array
{
return config('filament-auditing.custom_view_parameters');
}

protected function mapRelatedColumns($state, $record)
{
$relationshipsToUpdate = Arr::wrap(config('filament-auditing.mapping'));

if (count($relationshipsToUpdate) !== 0) {
foreach ($relationshipsToUpdate as $key => $relationship) {
if (array_key_exists($key, $state)) {
$state[$relationship['label']] = $relationship['model']::find($state[$key])?->{$relationship['field']};
unset($state[$key]);
}
}
}

return $state;
}

protected static function extraColumns()
{
return Arr::map(config('filament-auditing.audits_extend'), function ($buildParameters, $columnName) {
Expand Down

0 comments on commit a953b7e

Please sign in to comment.