1717use App \Audit \ConcreteFormatters \EntityDeletionAuditLogFormatter ;
1818use App \Audit \ConcreteFormatters \EntityUpdateAuditLogFormatter ;
1919use App \Audit \Interfaces \IAuditStrategy ;
20-
20+ use Doctrine \ORM \PersistentCollection ;
21+ use Illuminate \Support \Facades \Log ;
22+ use Doctrine \ORM \Mapping \ClassMetadata ;
2123class AuditLogFormatterFactory implements IAuditLogFormatterFactory
2224{
2325
@@ -34,14 +36,68 @@ 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+ $ child_entity_formatter = null ;
40+
41+ if ($ subject instanceof PersistentCollection) {
42+ $ targetEntity = null ;
43+ Log::debug
44+ (
45+ sprintf
46+ (
47+ "AuditLogFormatterFactory::make subject is a PersistentCollection isInitialized %b ? " ,
48+ $ subject ->isInitialized ()
49+ )
50+ );
51+ if (method_exists ($ subject , 'getTypeClass ' )) {
52+ $ type = $ subject ->getTypeClass ();
53+ // Your log shows this is ClassMetadata
54+ if ($ type instanceof ClassMetadata) {
55+ // Doctrine supports either getName() or public $name
56+ $ targetEntity = method_exists ($ type , 'getName ' ) ? $ type ->getName () : ($ type ->name ?? null );
57+ } elseif (is_string ($ type )) {
58+ $ targetEntity = $ type ;
59+ }
60+ Log::debug ("AuditLogFormatterFactory::make getTypeClass targetEntity {$ targetEntity }" );
61+ }
62+ elseif (method_exists ($ subject , 'getMapping ' )) {
63+ $ mapping = $ subject ->getMapping ();
64+ $ targetEntity = $ mapping ['targetEntity ' ] ?? null ;
65+ Log::debug ("AuditLogFormatterFactory::make getMapping targetEntity {$ targetEntity }" );
66+ } else {
67+ // last-resort: read private association metadata (still no hydration)
68+ $ ref = new \ReflectionObject ($ subject );
69+ foreach (['association ' , 'mapping ' , 'associationMapping ' ] as $ propName ) {
70+ if ($ ref ->hasProperty ($ propName )) {
71+ $ prop = $ ref ->getProperty ($ propName );
72+ $ prop ->setAccessible (true );
73+ $ mapping = $ prop ->getValue ($ subject );
74+ $ targetEntity = $ mapping ['targetEntity ' ] ?? null ;
75+ if ($ targetEntity ) break ;
76+ }
77+ }
78+ }
79+
80+ if ($ targetEntity ) {
81+ // IMPORTANT: build formatter WITHOUT touching collection items
82+ $ child_entity_formatter = ChildEntityFormatterFactory::build ($ targetEntity );
83+ }
84+ Log::debug
85+ (
86+ sprintf
87+ (
88+ "AuditLogFormatterFactory::make subject is a PersistentCollection isInitialized %b ? ( final ) " ,
89+ $ subject ->isInitialized ()
90+ )
91+ );
92+ } elseif (is_array ($ subject )) {
93+ $ child_entity = $ subject [0 ] ?? null ;
94+ $ child_entity_formatter = $ child_entity ? ChildEntityFormatterFactory::build ($ child_entity ) : null ;
95+ } elseif (is_object ($ subject ) && method_exists ($ subject , 'getSnapshot ' )) {
96+ $ snap = $ subject ->getSnapshot (); // only once
97+ $ child_entity = $ snap [0 ] ?? null ;
98+ $ child_entity_formatter = $ child_entity ? ChildEntityFormatterFactory::build ($ child_entity ) : null ;
4399 }
44- $ child_entity_formatter = $ child_entity != null ? ChildEntityFormatterFactory:: build ( $ child_entity ) : null ;
100+
45101 $ formatter = new EntityCollectionUpdateAuditLogFormatter ($ child_entity_formatter );
46102 break ;
47103 case IAuditStrategy::EVENT_ENTITY_CREATION :
@@ -65,6 +121,7 @@ public function make(AuditContext $ctx, $subject, $eventType): ?IAuditLogFormatt
65121 }
66122 break ;
67123 }
124+ if ($ formatter === null ) return null ;
68125 $ formatter ->setContext ($ ctx );
69126 return $ formatter ;
70127 }
@@ -73,7 +130,7 @@ private function getFormatterByContext(object $subject, string $event_type, Audi
73130 {
74131 $ class = get_class ($ subject );
75132 $ entity_config = $ this ->config ['entities ' ][$ class ] ?? null ;
76-
133+
77134 if (!$ entity_config ) {
78135 return null ;
79136 }
0 commit comments