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,69 @@ 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+ Log::debug (sprintf ());
53+ $ type = $ subject ->getTypeClass ();
54+ // Your log shows this is ClassMetadata
55+ if ($ type instanceof ClassMetadata) {
56+ // Doctrine supports either getName() or public $name
57+ $ targetEntity = method_exists ($ type , 'getName ' ) ? $ type ->getName () : ($ type ->name ?? null );
58+ } elseif (is_string ($ type )) {
59+ $ targetEntity = $ type ;
60+ }
61+ Log::debug ("AuditLogFormatterFactory::make getTypeClass targetEntity {$ targetEntity }" );
62+ }
63+ elseif (method_exists ($ subject , 'getMapping ' )) {
64+ $ mapping = $ subject ->getMapping ();
65+ $ targetEntity = $ mapping ['targetEntity ' ] ?? null ;
66+ Log::debug ("AuditLogFormatterFactory::make getMapping targetEntity {$ targetEntity }" );
67+ } else {
68+ // last-resort: read private association metadata (still no hydration)
69+ $ ref = new \ReflectionObject ($ subject );
70+ foreach (['association ' , 'mapping ' , 'associationMapping ' ] as $ propName ) {
71+ if ($ ref ->hasProperty ($ propName )) {
72+ $ prop = $ ref ->getProperty ($ propName );
73+ $ prop ->setAccessible (true );
74+ $ mapping = $ prop ->getValue ($ subject );
75+ $ targetEntity = $ mapping ['targetEntity ' ] ?? null ;
76+ if ($ targetEntity ) break ;
77+ }
78+ }
79+ }
80+
81+ if ($ targetEntity ) {
82+ // IMPORTANT: build formatter WITHOUT touching collection items
83+ $ child_entity_formatter = ChildEntityFormatterFactory::build ($ targetEntity );
84+ }
85+ Log::debug
86+ (
87+ sprintf
88+ (
89+ "AuditLogFormatterFactory::make subject is a PersistentCollection isInitialized %b ? ( final ) " ,
90+ $ subject ->isInitialized ()
91+ )
92+ );
93+ } elseif (is_array ($ subject )) {
94+ $ child_entity = $ subject [0 ] ?? null ;
95+ $ child_entity_formatter = $ child_entity ? ChildEntityFormatterFactory::build ($ child_entity ) : null ;
96+ } elseif (is_object ($ subject ) && method_exists ($ subject , 'getSnapshot ' )) {
97+ $ snap = $ subject ->getSnapshot (); // only once
98+ $ child_entity = $ snap [0 ] ?? null ;
99+ $ child_entity_formatter = $ child_entity ? ChildEntityFormatterFactory::build ($ child_entity ) : null ;
43100 }
44- $ child_entity_formatter = $ child_entity != null ? ChildEntityFormatterFactory:: build ( $ child_entity ) : null ;
101+
45102 $ formatter = new EntityCollectionUpdateAuditLogFormatter ($ child_entity_formatter );
46103 break ;
47104 case IAuditStrategy::EVENT_ENTITY_CREATION :
@@ -65,6 +122,7 @@ public function make(AuditContext $ctx, $subject, $eventType): ?IAuditLogFormatt
65122 }
66123 break ;
67124 }
125+ if ($ formatter === null ) return null ;
68126 $ formatter ->setContext ($ ctx );
69127 return $ formatter ;
70128 }
@@ -73,7 +131,7 @@ private function getFormatterByContext(object $subject, string $event_type, Audi
73131 {
74132 $ class = get_class ($ subject );
75133 $ entity_config = $ this ->config ['entities ' ][$ class ] ?? null ;
76-
134+
77135 if (!$ entity_config ) {
78136 return null ;
79137 }
0 commit comments