Skip to content

Commit 89fe4f8

Browse files
authored
Merge pull request #57 from sourcetoad/track-deleted-records
Allow Deleted Owners and Entities to be Resolved and Fix `LazyLoadingViolation` When Loading Owners
2 parents 2bfa878 + 1d684aa commit 89fe4f8

File tree

7 files changed

+85
-34
lines changed

7 files changed

+85
-34
lines changed

README.md

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ Recommended action is creating an enum class to describe all models in your syst
7474

7575
This enforces the user to create shorthand notation for all models to cut down on database size. If a numeric morph is not found, the system will fail out. Due to issues with blindly overwriting and applying these morphs globally, they are manually applied. This means that morphs in your application are left untouched.
7676

77-
#### Trackable Trait
77+
#### Trackable Trait and Contract
7878
For models that may contain information that you wish to be notified was accessed or mutated. You may add the `Trackable` trait and contract to these models:
7979

8080
```php
@@ -85,7 +85,13 @@ use Sourcetoad\Logger\Traits\Trackable;
8585
class TrackedModel extends Model implements TrackableContract {
8686
use Trackable;
8787

88-
// Tracked models must implement this function
88+
// Tracked models must implement these functions
89+
90+
public function getOwnerRelationshipName(): ?string
91+
{
92+
//
93+
}
94+
8995
public function trackableOwnerResolver(): ?Model
9096
{
9197
//
@@ -130,7 +136,7 @@ class TrackedModel extends Model implements TrackableContract {
130136
}
131137

132138
/**
133-
* Collection of accesses of this model
139+
* Collection of access logs of this model
134140
*
135141
* @return LoggerMorphMany<AuditModel>
136142
*/
@@ -155,10 +161,31 @@ Schedule::command('logger:audit-resolver')
155161
This will run taking 200 items of both changes and retrieved models. It will identify the owner associated with the model through the `Trackable` contract. The functions for each individual model should be easy.
156162

157163
```php
164+
public function getOwnerRelationshipName(): string
165+
{
166+
return 'object.relation.owner';
167+
}
168+
158169
public function trackableOwnerResolver(): Owner
159170
{
160171
return $this->object->relation->owner;
161172
}
162173
```
163174

164-
As you can see, we have to traverse whatever relation/property we need in order to relate the model at hand to an owner. If there is no match, you probably shouldn't be logging it.
175+
As you can see, we have to traverse whatever relation/property we need in order to relate the model at hand to an owner.
176+
177+
However, there may be cases where a tracked model is also the owner of that model. In those cases, there is no relation/property to traverse.
178+
179+
```php
180+
public function getOwnerRelationshipName(): null
181+
{
182+
return null;
183+
}
184+
185+
public function trackableOwnerResolver(): TrackedModel
186+
{
187+
return $this;
188+
}
189+
```
190+
191+
If neither of these scenarios match, you probably shouldn't be logging it.

src/Commands/AuditModelResolver.php

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
namespace Sourcetoad\Logger\Commands;
44

55
use Illuminate\Console\Command;
6+
use Illuminate\Database\Eloquent\Relations\MorphTo;
67
use Sourcetoad\Logger\Logger;
78
use Sourcetoad\Logger\Helpers\AuditResolver;
9+
use Sourcetoad\Logger\LoggerServiceProvider;
810
use Sourcetoad\Logger\Models\AuditChange;
911
use Sourcetoad\Logger\Models\AuditModel;
1012

@@ -15,28 +17,48 @@ class AuditModelResolver extends Command
1517

1618
public function handle(): void
1719
{
18-
AuditChange::query()->where('processed', false)->with('entity')->chunkById(200, function ($items) {
19-
/** @var AuditChange[] $items */
20-
foreach ($items as $item) {
21-
$owner = AuditResolver::findOwner($item->entity);
22-
23-
$item->processed = true;
24-
$item->owner_id = $owner?->getKey();
25-
$item->owner_type = !is_null($owner) ? Logger::getNumericMorphMap($owner) : null;
26-
$item->saveOrFail();
27-
}
28-
});
29-
30-
AuditModel::query()->where('processed', false)->with('entity')->chunkById(200, function ($items) {
31-
/** @var AuditModel[] $items */
32-
foreach ($items as $item) {
33-
$owner = AuditResolver::findOwner($item->entity);
34-
35-
$item->processed = true;
36-
$item->owner_id = $owner?->getKey();
37-
$item->owner_type = !is_null($owner) ? Logger::getNumericMorphMap($owner) : null;
38-
$item->saveOrFail();
39-
}
40-
});
20+
AuditChange::query()
21+
->where('processed', false)
22+
->with(['entity' => function (MorphTo $morphTo) {
23+
$morphTo->morphWith($this->getOwnerMorphs());
24+
}])
25+
->chunkById(200, function ($items) {
26+
/** @var AuditChange[] $items */
27+
foreach ($items as $item) {
28+
$owner = AuditResolver::findOwner($item->entity);
29+
30+
$item->processed = true;
31+
$item->owner_id = $owner?->getKey();
32+
$item->owner_type = !is_null($owner) ? Logger::getNumericMorphMap($owner) : null;
33+
$item->saveOrFail();
34+
}
35+
});
36+
37+
AuditModel::query()
38+
->where('processed', false)
39+
->with(['entity' => function (MorphTo $morphTo) {
40+
$morphTo->morphWith($this->getOwnerMorphs());
41+
}])
42+
->chunkById(200, function ($items) {
43+
/** @var AuditModel[] $items */
44+
foreach ($items as $item) {
45+
$owner = AuditResolver::findOwner($item->entity);
46+
47+
$item->processed = true;
48+
$item->owner_id = $owner?->getKey();
49+
$item->owner_type = !is_null($owner) ? Logger::getNumericMorphMap($owner) : null;
50+
$item->saveOrFail();
51+
}
52+
});
53+
}
54+
55+
/**
56+
* @return array<class-string, string[]>
57+
*/
58+
private function getOwnerMorphs(): array
59+
{
60+
return collect(LoggerServiceProvider::$morphs)->mapWithKeys(fn(string $class) => [
61+
$class => array_filter([(new $class)->getOwnerRelationshipName()]),
62+
])->toArray();
4163
}
4264
}

src/Contracts/Trackable.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@
88

99
interface Trackable
1010
{
11+
public function getOwnerRelationshipName(): ?string;
1112
public function trackableOwnerResolver(): ?Model;
1213
}

src/Helpers/AuditResolver.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@
99

1010
class AuditResolver
1111
{
12-
public static function findOwner(?Trackable $model): ?Model
12+
public static function findOwner(?Trackable $trackable): ?Model
1313
{
14-
if (empty($model)) {
14+
if (empty($trackable)) {
1515
return null;
1616
}
1717

18-
$owner = $model->trackableOwnerResolver();
18+
$owner = $trackable->trackableOwnerResolver();
1919

2020
if (empty($owner)) {
2121
return null;

src/Models/AuditChange.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class AuditChange extends BaseModel
5353
/** @return MorphTo<Model, self> */
5454
public function owner(): MorphTo
5555
{
56-
return $this->morphTo();
56+
return $this->morphTo()->withTrashed();
5757
}
5858

5959
public function key(): BelongsTo
@@ -64,6 +64,6 @@ public function key(): BelongsTo
6464
/** @return MorphTo<Trackable, self> */
6565
public function entity(): MorphTo
6666
{
67-
return $this->morphTo();
67+
return $this->morphTo()->withTrashed();
6868
}
6969
}

src/Models/AuditModel.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,12 @@ public static function createOrFind(AuditActivity $activity, int $modelType, int
6161
/** @return MorphTo<Model, self> */
6262
public function owner(): MorphTo
6363
{
64-
return $this->morphTo();
64+
return $this->morphTo()->withTrashed();
6565
}
6666

6767
/** @return MorphTo<Trackable, self> */
6868
public function entity(): MorphTo
6969
{
70-
return $this->morphTo();
70+
return $this->morphTo()->withTrashed();
7171
}
7272
}

src/Traits/Trackable.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,6 @@ public static function bootTrackable(): void
2727
});
2828
}
2929

30+
abstract public function getOwnerRelationshipName(): ?string;
3031
abstract public function trackableOwnerResolver(): ?Model;
3132
}

0 commit comments

Comments
 (0)