Laravel Eloquent Model ์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๊ธฐ๋กํ•ด์ฃผ๋Š” laravel-auditing ํŒจํ‚ค์ง€

๊ฐœ์š”

์›น ์„œ๋น„์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋‹ค ๋ณด๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ์‹ค์ˆ˜๋กœ ๋ฐ์ดํƒ€๋ฅผ ์‚ญ์ œํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•˜๋Š” ๊ฒฝ์šฐ์— ๋Œ€๋น„ํ•ด์•ผ ํ•  ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

DBMS ์ฐจ์›์˜ ๋ฐฑ์—…๊ณผ ๋ณต๊ตฌ๋Š” ์‹œ์Šคํ…œ ์žฅ์• ๋‚˜ ํ•ดํ‚น๋“ฑ์˜ ๋น„์ƒ ์ƒํ™ฉ์— ๋Œ€๋น„ํ•˜๋Š” ์ž‘์—…์ด๊ณ  ๊ฐœ๋ณ„ ๋ ˆ์ฝ”๋“œ์˜ ๋ณ€๊ฒฝ์„ ์ถ”์ ํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๋ฐ์—๋Š” ์ ํ•ฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋ฌผ๋ก  DBMS ๋ฅผ archiving mode ๋กœ ์„ค์ •ํ•˜๊ณ  ์šด์˜ํ•˜๋ฉด ๋ ˆ์ฝ”๋“œ ๋‹จ์œ„์˜ ๋ณ€๊ฒฝ์„ ์ถ”์ ํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ ๋น„์ƒ์‹œ ๋ณต๊ตฌ์šฉ์ด๋ผ DBA ์˜ ๋„์›€๊ณผ ์‹œ์Šคํ…œ ์ฐจ์›์˜ ์ž‘์—…์ด ํ•„์š”ํ•˜๋ฏ€๋กœ ๋ ˆ์ฝ”๋“œ ๋‹จ์œ„ ์‹ค์ˆ˜ ๋ฐฉ์ง€๋Š” app ๋ ˆ๋ฒจ์—์„œ ๋Œ€์‘ํ•˜๋Š” ๊ฒŒ ์ ์ ˆํ•ฉ๋‹ˆ๋‹ค.


์‚ญ์ œ ์‹ค์ˆ˜๋Š” ์ผ๋‹จ soft delete ๊ธฐ๋Šฅ์„ ์ ์šฉํ•˜๋ฉด ๋˜์ง€๋งŒ ์‹ค์ˆ˜๋กœ ์ž˜๋ชป๋œ ๋ฐ์ดํ„ฐ๋กœ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ฒƒ์€ ๋ณ€๊ฒฝ ์ด๋ ฅ์„ ๋ณ„๋„๋กœ ์ถ”์ ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฐ ์š”๊ตฌ ์‚ฌํ•ญ์ด ๋‹ค๋ฅธ ๊ณณ์—๋„ ๋งŽ์€์ง€ ๋ผ๋ผ๋ฒจ์—์„œ ์‰ฝ๊ฒŒ ๋ชจ๋ธ์˜ ๋ณ€๊ฒฝ ์ด๋ ฅ์„ ๊ด€๋ฆฌํ•ด์ฃผ๋Š” ย Revisionableย ์ด๋ผ๋Š” ํŒจํ‚ค์ง€์™€ย ย laravel-auditing ์ด๋ผ๋Š” ํŒจํ‚ค์ง€๊ฐ€ github ์— ์˜ฌ๋ผ์™€ ์žˆ๊ณ  ์ƒ๋‹นํžˆ ๋งŽ์€ star ๋ฅผ ๋ฐ›๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๋ณ„์ ์€ ย Revisionableย ์ด ์ข€ ๋” ๋งŽ์ง€๋งŒ ์ €๋Š” ๋งค๋‰ด์–ผ์ด ์ฒด๊ณ„์ ์œผ๋กœ ๋˜์–ด ์žˆ๊ณ  ํ•œ ๋ฒˆ์— ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ปฌ๋Ÿผ์ด ๋ณ€๊ฒฝ๋˜๋„ ํ•œ ๊ฑด์˜ ์ด๋ ฅ ๋ ˆ์ฝ”๋“œ๋งŒ ์ƒ๊ธฐ๋Š” ์žฅ์ ์ด ์žˆ๋Š”ย ย laravel-auditingย ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”๋ฐ ๊ฐ„๋žตํ•œ ์‚ฌ์šฉ๋ฒ•์„ ๊ณต์œ ํ•ด ๋ด…๋‹ˆ๋‹ค.

์ดํ˜„์„๋‹˜์ด ๋ผ๋ผ๋ฒจ ์ผ์ผ ์ผ์‹์—ย ๋ชจ๋ธ ๋ณ€๊ฒฝ ์ด๋ ฅ์„ ์ž๋™์„ ์ €์žฅํ•ด์ฃผ๋Š” ํŒจํ‚ค์ง€ Revisionable ๋ผ๋Š” ์œ ์šฉํ•œ ๊ธ€์„ ์˜ฌ๋ ค์ฃผ์…จ์œผ๋‹ˆ ์ฐธ๊ณ ํ•˜์„ธ์š”.


์„ค์น˜

composer ๋กœ ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

composer require owen-it/laravel-auditing


config/app.php ์— ํ”„๋กœ๋ฐ”์ด๋”๋ฅผ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.

'providers' => [
    // ...

    OwenIt\Auditing\AuditingServiceProvider::class,

    // ...
],


vendor ํŒŒ์ผ์„ ํผ๋ธ”๋ฆฌ์‹ฑํ•ฉ๋‹ˆ๋‹ค.

php artisan vendor:publish --provider "OwenIt\Auditing\AuditingServiceProvider" --tag="config"


config/audit.phpย ์—์„œ auditing ํŒจํ‚ค์ง€์˜ ๋™์ž‘์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

<?php

return [
	// auditing ๋™์ž‘ ์—ฌ๋ถ€๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    'enabled' => env('AUDITING_ENABLED', true),

	// User, IP Address ์ •๋ณด๋“ฑ์„ ๊ฐ€์ ธ์˜ฌ ๋ฆฌ์กธ๋ฒ„๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
   'resolver' => [
        'user'       => OwenIt\Auditing\Resolvers\UserResolver::class,
        'ip_address' => OwenIt\Auditing\Resolvers\IpAddressResolver::class,
        'user_agent' => OwenIt\Auditing\Resolvers\UserAgentResolver::class,
        'url'        => OwenIt\Auditing\Resolvers\UrlResolver::class,
    ],
	// Audit ์„ ํ•  event ๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ์ƒ์„ฑ, ๊ฐฑ์‹ , ์‚ญ์ œ, ๋ณต๊ตฌ์‹œ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. 
	'events' => [
	    'created',
        'updated',
        'deleted',
        'restored',
    ],

DB migration ํŒŒ์ผ์„ ํผ๋ธ”๋ฆฌ์‹ฑํ•ฉ๋‹ˆ๋‹ค.

php artisan vendor:publish --provider "OwenIt\Auditing\AuditingServiceProvider" --tag="migrations"

audit ์ปฌ๋Ÿผ์„ text ์—์„œ json ์œผ๋กœ ๋ฐ”๊พธ๋ฉด JSON Where ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ƒ์„ฑ๋œ migration ํŒŒ์ผ(๋‚ ์ž_create_audits_table.php)์—์„œ ๋‹ค์Œ ๋‚ด์šฉ์„ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.

create_audits_table.php
// $table->text('old_values')->nullable();
// $table->text('new_values')->nullable();

$table->json('old_values')->nullable();
$table->json('new_values')->nullable();

DB migration ์„ ์‹คํ–‰ํ•˜๋ฉด Model ์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๊ธฐ๋กํ•˜๋Š” audits ํ…Œ์ด๋ธ”์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

php artisan migrate

Model ์„ค์ •


๋ณ€๊ฒฝ์„ ์ถ”์ ํ•˜๋ ค๋Š” Eloquent Model ์—ย implements \OwenIt\Auditing\Contracts\Auditableย  ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ ย Auditable trait ์„ ์‚ฌ์šฉํ•˜๋„๋ก ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

์•„๋ž˜์˜ ์˜ˆ์ œ๋Š” ๊ณ ๊ฐ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” Customer ๋ชจ๋ธ์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค.

class Customer extends Model implements \OwenIt\Auditing\Contracts\Auditable
{
    use \OwenIt\Auditing\Auditable;

์ด์ œ Customer model ์„ ์ƒ์„ฑ/์ˆ˜์ •/์‚ญ์ œ/๋ณต๊ตฌ ํ•˜๋Š” ๋ชจ๋“  event ๋งˆ๋‹ค audits ํ…Œ์ด๋ธ”์— ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ๊ธฐ๋ก๋ฉ๋‹ˆ๋‹ค.

Audit ๋ ˆ์ฝ”๋“œ ์‚ฌ์šฉ

๋ ˆ์ฝ”๋“œ ๊ฐ€์ ธ์˜ค๊ธฐ

audit ๋ ˆ์ฝ”๋“œ์— ์ ‘๊ทผํ•˜๋ ค๋ฉด ๋จผ์ € audit ์ด ์ €์žฅ๋œ ๋ชจ๋ธ์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

// Customer id
$c = Customer::find(1);

// ํ•ด๋‹น ๋ชจ๋ธ์˜ ๋ชจ๋“  ์ด๋ ฅ๋“ค
$audits = $c->audits;

// ์ฒซ ๋ฒˆ์งธ ์ด๋ ฅ
$f = $audits->first();

// ๋งˆ์ง€๋ง‰ ์ด๋ ฅ
$f = $c->audits()->latest()->first();

// id ๋กœ ๊ฐ€์ ธ์˜ค๊ธฐ
$f = $c->audits()->find(2);


eager loading ์œผ๋กœ User ๋ชจ๋ธ ๊ฐ€์ ธ์˜ค๊ธฐ

laravel-auditing ์€ ์ž๋™์œผ๋กœ ๋ชจ๋ธ์„ ๋ณ€๊ฒฝํ•œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋ ˆ์ฝ”๋“œ์— ์ถ”๊ฐ€ํ•˜๋ฏ€๋กœ ์•„๋ž˜์™€ ๊ฐ™์ด ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// audit ์ด ์ €์žฅ๋œ ๊ณ ๊ฐ id
$c = Customer::find(1);

// ํ•ด๋‹น ๋ชจ๋ธ์˜ ๋ชจ๋“  ์ด๋ ฅ๋“ค
$a = $c->audits->first();

// ์ˆ˜์ •ํ•œ ์‚ฌ์šฉ์ž ์ •๋ณด ๊ฐ€์ ธ์˜ด.
$a->user;


ํ•˜์ง€๋งŒ $aโ†’user ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์‹œ์ ์— ๋‹ค์‹œ DB fetch ๊ฐ€ ์ผ์–ด๋‚˜๋ฏ€๋กœ N + 1 ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ  ์„ฑ๋Šฅ์— ์•…์˜ํ–ฅ์„ ์ฃผ๊ฒŒ ๋˜๋ฏ€๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด with() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ด๊ฑฐ ๋กœ๋”ฉ์œผ๋กœ ์ˆ˜์ •ํ•œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๊ฐ™์ด ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

$c = Customer::find(1);

$audits = $c->audits()->with('user')->get();


audit ๋ฉ”ํƒ€ ๋ฐ์ดํƒ€ ๊ฐ€์ ธ์˜ค๊ธฐ

audit ๊ณผ ๊ด€๋ จํ•œ ๋ฉ”ํƒ€ ๋ฐ์ดํƒ€๋ฅผ array ๋กœ ๊ฐ€์ ธ์˜ค๋ ค๋ฉด audit ๋ชจ๋ธ์˜ย getMetadata ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

$audits = Customer::find(1)->audits;

$f = $audits->first();

var_dump($f->getMetadata());


๋ณ€๊ฒฝ๋œ properties ๋งŒ ๊ฐ€์ ธ์˜ค๊ธฐ

audit ๋ชจ๋ธ์˜ getModified ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด audit ํ…Œ์ด๋ธ”์˜ old_values ์™€ new_values ๋งŒ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

$audits = Customer::find(1)->audits;
$f = $audits->first();
var_dump($f->getModified ());


Ref