Skip to content

Commit a8e452c

Browse files
committed
chore: refactor AuditLogFormatterFactory
1 parent 6789395 commit a8e452c

File tree

2 files changed

+61
-18
lines changed

2 files changed

+61
-18
lines changed

app/Audit/AuditLogFormatterFactory.php

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
use App\Audit\ConcreteFormatters\EntityDeletionAuditLogFormatter;
1818
use App\Audit\ConcreteFormatters\EntityUpdateAuditLogFormatter;
1919
use App\Audit\Interfaces\IAuditStrategy;
20+
use Doctrine\ORM\PersistentCollection;
21+
use Illuminate\Support\Facades\Log;
2022

2123
class AuditLogFormatterFactory implements IAuditLogFormatterFactory
2224
{
@@ -34,14 +36,50 @@ public function make(AuditContext $ctx, $subject, $eventType): ?IAuditLogFormatt
3436
$formatter = null;
3537
switch ($eventType) {
3638
case IAuditStrategy::EVENT_COLLECTION_UPDATE:
37-
$child_entity = null;
38-
if (count($subject) > 0) {
39-
$child_entity = $subject[0];
40-
}
41-
if (is_null($child_entity) && isset($subject->getSnapshot()[0]) && count($subject->getSnapshot()) > 0) {
42-
$child_entity = $subject->getSnapshot()[0];
39+
Log::debug("AuditLogFormatterFactory::make IAuditStrategy::EVENT_COLLECTION_UPDATE",
40+
["subject" => get_class($subject)]);
41+
42+
$child_entity_formatter = null;
43+
44+
if ($subject instanceof PersistentCollection) {
45+
// KEY: do NOT initialize the collection.
46+
$targetEntity = null;
47+
48+
// Doctrine variants:
49+
if (method_exists($subject, 'getTypeClass')) {
50+
$targetEntity = $subject->getTypeClass();
51+
Log::debug("AuditLogFormatterFactory::make getTypeClass {$targetEntity}");
52+
} elseif (method_exists($subject, 'getMapping')) {
53+
$mapping = $subject->getMapping();
54+
$targetEntity = $mapping['targetEntity'] ?? null;
55+
Log::debug("AuditLogFormatterFactory::make getMapping {$targetEntity}");
56+
} else {
57+
// last-resort: read private association metadata (still no hydration)
58+
$ref = new \ReflectionObject($subject);
59+
foreach (['association', 'mapping', 'associationMapping'] as $propName) {
60+
if ($ref->hasProperty($propName)) {
61+
$prop = $ref->getProperty($propName);
62+
$prop->setAccessible(true);
63+
$mapping = $prop->getValue($subject);
64+
$targetEntity = $mapping['targetEntity'] ?? null;
65+
if ($targetEntity) break;
66+
}
67+
}
68+
}
69+
70+
if ($targetEntity) {
71+
$child_entity_formatter = ChildEntityFormatterFactory::build($targetEntity);
72+
}
73+
74+
} elseif (is_array($subject)) {
75+
$child_entity = $subject[0] ?? null;
76+
$child_entity_formatter = $child_entity ? ChildEntityFormatterFactory::build($child_entity) : null;
77+
} elseif (is_object($subject) && method_exists($subject, 'getSnapshot')) {
78+
$snap = $subject->getSnapshot(); // only once
79+
$child_entity = $snap[0] ?? null;
80+
$child_entity_formatter = $child_entity ? ChildEntityFormatterFactory::build($child_entity) : null;
4381
}
44-
$child_entity_formatter = $child_entity != null ? ChildEntityFormatterFactory::build($child_entity) : null;
82+
4583
$formatter = new EntityCollectionUpdateAuditLogFormatter($child_entity_formatter);
4684
break;
4785
case IAuditStrategy::EVENT_ENTITY_CREATION:
@@ -65,6 +103,7 @@ public function make(AuditContext $ctx, $subject, $eventType): ?IAuditLogFormatt
65103
}
66104
break;
67105
}
106+
if ($formatter === null) return null;
68107
$formatter->setContext($ctx);
69108
return $formatter;
70109
}
@@ -73,7 +112,7 @@ private function getFormatterByContext(object $subject, string $event_type, Audi
73112
{
74113
$class = get_class($subject);
75114
$entity_config = $this->config['entities'][$class] ?? null;
76-
115+
77116
if (!$entity_config) {
78117
return null;
79118
}

app/Audit/ConcreteFormatters/ChildEntityFormatters/ChildEntityFormatterFactory.php

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,29 @@
1616
**/
1717

1818
use Illuminate\Support\Facades\Log;
19-
use ReflectionClass;
19+
use Doctrine\Common\Util\ClassUtils;
2020

2121
/**
2222
* Class ChildEntityFormatterFactory
2323
* @package App\Audit\ConcreteFormatters\ChildEntityFormatter
2424
*/
2525
class ChildEntityFormatterFactory {
2626

27-
public static function build($entity): ?IChildEntityAuditLogFormatter {
27+
public static function build(object|string $entityOrClass): ?IChildEntityAuditLogFormatter {
2828
try {
29-
$class_name = (new ReflectionClass($entity))->getShortName();
30-
$class_name = "App\\Audit\\ConcreteFormatters\\ChildEntityFormatters\\{$class_name}AuditLogFormatter";
31-
if(class_exists($class_name)) {
32-
return new $class_name();
33-
}
34-
return null;
35-
} catch (\ReflectionException $e) {
29+
$fqcn = is_string($entityOrClass)
30+
? ltrim($entityOrClass, '\\')
31+
: (class_exists(ClassUtils::class) ? ClassUtils::getClass($entityOrClass) : get_class($entityOrClass));
32+
33+
$short = substr($fqcn, strrpos($fqcn, '\\') + 1);
34+
35+
Log::debug("Child Entity Formatter " . $short);
36+
$class = "App\\Audit\\ConcreteFormatters\\ChildEntityFormatters\\{$short}AuditLogFormatter";
37+
38+
return class_exists($class) ? new $class() : null;
39+
} catch (\Throwable $e) {
3640
Log::error($e);
3741
return null;
3842
}
3943
}
40-
}
44+
}

0 commit comments

Comments
 (0)