2727use TYPO3 \CMS \Core \Collection \LazyRecordCollection ;
2828use TYPO3 \CMS \Core \Domain \Record ;
2929use TYPO3 \CMS \Core \Domain \RecordInterface ;
30+ use TYPO3 \CMS \Core \Domain \RecordPropertyClosure ;
31+ use TYPO3 \CMS \Core \Resource \Collection \LazyFileReferenceCollection ;
3032
3133/**
3234 * @internal Not part of TYPO3's public API.
@@ -46,7 +48,6 @@ public function __construct(
4648 public function setRequest (ServerRequestInterface $ request ): void
4749 {
4850 $ this ->request = $ request ;
49- $ this ->contentObjectProcessor ->setRequest ($ request );
5051 }
5152
5253 public function decorate (RecordInterface $ resolvedRecord , ?PageLayoutContext $ context = null ): ContentBlockData
@@ -69,8 +70,6 @@ public function decorate(RecordInterface $resolvedRecord, ?PageLayoutContext $co
6970 $ context ,
7071 );
7172 $ this ->contentBlockDataDecoratorSession ->setContentBlockData ($ identifier , $ contentBlockData );
72- $ this ->gridProcessor ->process ();
73- $ this ->contentObjectProcessor ->process ();
7473 return $ contentBlockData ;
7574 }
7675
@@ -89,144 +88,169 @@ private function buildContentBlockDataObjectRecursive(
8988 if (SpecialFieldType::tryFrom ($ fieldType ->getName ()) !== null ) {
9089 continue ;
9190 }
92- // TCA type "passthrough" is not available in the record, and it won't fall back to raw record value.
93- if ($ fieldType ->getTcaType () === 'passthrough ' ) {
94- $ resolvedField = $ resolvedRelation ->record ->getRawRecord ()->get ($ tcaFieldDefinition ->uniqueIdentifier );
95- } else {
96- $ resolvedField = $ resolvedRelation ->record ->get ($ tcaFieldDefinition ->uniqueIdentifier );
97- }
98- if ($ this ->isRelationField ($ resolvedField )) {
99- $ resolvedField = $ this ->handleRelation (
100- $ resolvedField ,
101- $ depth ,
102- $ context ,
103- );
104- $ grids = $ this ->handleGrids ($ grids , $ context , $ resolvedField , $ tcaFieldDefinition );
91+ $ loadedField = $ this ->loadField ($ resolvedRelation , $ tcaFieldDefinition , $ depth , $ context );
92+ $ processedContentBlockData [$ tcaFieldDefinition ->identifier ] = $ loadedField ;
93+ // Exclude file relations from grids.
94+ if ($ loadedField instanceof RecordPropertyClosure && $ fieldType ->getTcaType () !== 'file ' ) {
95+ $ grids [$ tcaFieldDefinition ->identifier ] = $ this ->handleGrids ($ context , $ loadedField , $ tcaFieldDefinition );
10596 }
106- $ processedContentBlockData [$ tcaFieldDefinition ->identifier ] = $ resolvedField ;
10797 }
10898 $ resolvedRelation ->resolved = $ processedContentBlockData ;
99+ $ gridData = new ContentBlockGridData ($ grids );
109100 $ contentBlockDataObject = $ this ->buildContentBlockDataObject (
110101 $ resolvedRelation ,
111102 $ contentTypeDefinition ->getName (),
112- $ grids ,
103+ $ gridData ,
113104 );
114105 return $ contentBlockDataObject ;
115106 }
116107
108+ private function loadField (
109+ ResolvedContentBlockDataRelation $ resolvedRelation ,
110+ TcaFieldDefinition $ tcaFieldDefinition ,
111+ int $ depth ,
112+ ?PageLayoutContext $ context
113+ ): mixed {
114+ $ fieldType = $ tcaFieldDefinition ->fieldType ;
115+ // TCA type "passthrough" is not available in the record, and it won't fall back to raw record value.
116+ if ($ fieldType ->getTcaType () === 'passthrough ' ) {
117+ $ resolvedField = $ resolvedRelation ->record ->getRawRecord ()->get ($ tcaFieldDefinition ->uniqueIdentifier );
118+ return $ resolvedField ;
119+ }
120+ // Simple field type, load eagerly.
121+ if ($ this ->isRelationField ($ tcaFieldDefinition ) === false ) {
122+ $ resolvedField = $ resolvedRelation ->record ->get ($ tcaFieldDefinition ->uniqueIdentifier );
123+ return $ resolvedField ;
124+ }
125+ // Relation field type, load lazily.
126+ $ recordPropertyClosure = new RecordPropertyClosure (
127+ function () use ($ resolvedRelation , $ tcaFieldDefinition , $ depth , $ context ): ContentBlockData |LazyRecordCollection |LazyFileReferenceCollection |null {
128+ $ resolvedField = $ resolvedRelation ->record ->get ($ tcaFieldDefinition ->uniqueIdentifier );
129+ $ resolvedField = $ this ->handleRelation (
130+ $ resolvedField ,
131+ $ depth ,
132+ $ context ,
133+ );
134+ return $ resolvedField ;
135+ }
136+ );
137+ return $ recordPropertyClosure ;
138+ }
139+
117140 /**
118- * @param array<string, RelationGrid>|array<string, RenderedGridItem[]> $grids
119- * @return array<string, RelationGrid>|array<string, RenderedGridItem[]>
141+ * @return LazyRecordCollection<RenderedGridItem>|RecordPropertyClosure
120142 */
121143 private function handleGrids (
122- array $ grids ,
123144 ?PageLayoutContext $ context ,
124- mixed $ resolvedField ,
145+ RecordPropertyClosure $ recordPropertyClosure ,
125146 TcaFieldDefinition $ tcaFieldDefinition
126- ): array {
127- if ($ context === null && $ this ->request !== null ) {
128- $ renderedGridItemDataObjects = $ resolvedField ;
129- if (!is_iterable ($ renderedGridItemDataObjects )) {
130- $ renderedGridItemDataObjects = [$ renderedGridItemDataObjects ];
147+ ): LazyRecordCollection |RecordPropertyClosure {
148+ if ($ context === null ) {
149+ if ($ this ->request === null ) {
150+ throw new \InvalidArgumentException (
151+ 'ContentBlockDataDecorator is missing the request object. ' ,
152+ 1756397952
153+ );
131154 }
132- foreach ($ renderedGridItemDataObjects as $ contentBlockDataObject ) {
133- $ renderedGridItem = new RenderedGridItem ();
134- $ grids [$ tcaFieldDefinition ->identifier ][] = $ renderedGridItem ;
135- $ callback = function () use ($ contentBlockDataObject , $ renderedGridItem ): void {
155+ $ this ->contentObjectProcessor ->setRequest ($ this ->request );
156+ $ initialization = function () use ($ recordPropertyClosure ): array {
157+ $ renderedGridItems = [];
158+ $ renderedGridItemDataObjects = $ recordPropertyClosure ->instantiate ();
159+ if (!is_iterable ($ renderedGridItemDataObjects )) {
160+ $ renderedGridItemDataObjects = [$ renderedGridItemDataObjects ];
161+ }
162+ foreach ($ renderedGridItemDataObjects as $ contentBlockDataObject ) {
163+ $ renderedGridItem = new RenderedGridItem ();
164+ $ renderedGridItems [] = $ renderedGridItem ;
136165 $ this ->contentObjectProcessor ->processContentObject (
137166 $ contentBlockDataObject ,
138167 $ renderedGridItem
139168 );
140- };
141- $ this ->contentObjectProcessor ->addInstruction ($ callback );
142- }
143- }
144- if ($ context !== null ) {
145- $ relationGrid = new RelationGrid ();
146- $ grids [$ tcaFieldDefinition ->identifier ] = $ relationGrid ;
147- $ callback = function () use ($ grids , $ tcaFieldDefinition , $ resolvedField , $ context ): void {
148- $ relationGrid = $ grids [$ tcaFieldDefinition ->identifier ];
149- $ this ->gridProcessor ->processGrid (
150- $ relationGrid ,
151- $ context ,
152- $ tcaFieldDefinition ,
153- $ resolvedField
154- );
169+ }
170+ return $ renderedGridItems ;
155171 };
156- $ this -> gridProcessor -> addInstruction ( $ callback );
172+ return new LazyRecordCollection ( '' , $ initialization );
157173 }
158- return $ grids ;
174+ $ initialization = function () use ($ tcaFieldDefinition , $ recordPropertyClosure , $ context ): ?RelationGrid {
175+ $ resolvedField = $ recordPropertyClosure ->instantiate ();
176+ if ($ resolvedField === null ) {
177+ return null ;
178+ }
179+ $ relationGrid = $ this ->gridProcessor ->processGrid (
180+ $ context ,
181+ $ tcaFieldDefinition ,
182+ $ resolvedField
183+ );
184+ return $ relationGrid ;
185+ };
186+ return new RecordPropertyClosure ($ initialization );
159187 }
160188
161189 private function handleRelation (
162- RecordInterface |LazyRecordCollection $ resolvedField ,
190+ RecordInterface |LazyRecordCollection | LazyFileReferenceCollection | null $ resolvedField ,
163191 int $ depth ,
164192 ?PageLayoutContext $ context = null ,
165- ): ContentBlockData |LazyRecordCollection {
166- if ($ resolvedField instanceof LazyRecordCollection) {
167- $ resolvedField = $ this ->transformMultipleRelation (
168- $ resolvedField ,
169- $ depth ,
170- $ context ,
171- );
193+ ): ContentBlockData |LazyRecordCollection |LazyFileReferenceCollection |null {
194+ if ($ resolvedField === null ) {
195+ return null ;
196+ }
197+ if ($ resolvedField instanceof LazyFileReferenceCollection) {
172198 return $ resolvedField ;
173199 }
174- $ resolvedField = $ this ->transformSelectRelation (
200+ if ($ resolvedField instanceof LazyRecordCollection) {
201+ $ initialization = function () use ($ resolvedField , $ depth , $ context ): array {
202+ $ resolvedField = $ this ->transformMultipleRelation (
203+ $ resolvedField ,
204+ $ depth ,
205+ $ context ,
206+ );
207+ return $ resolvedField ;
208+ };
209+ return new LazyRecordCollection ((string )$ resolvedField , $ initialization );
210+ }
211+ $ resolvedField = $ this ->transformSingleRelation (
175212 $ resolvedField ,
176213 $ depth ,
177214 $ context ,
178215 );
179216 return $ resolvedField ;
180217 }
181218
182- private function isRelationField (mixed $ resolvedField ): bool
219+ private function isRelationField (TcaFieldDefinition $ tcaFieldDefinition ): bool
183220 {
184- if ($ resolvedField instanceof Record) {
221+ $ tcaType = $ tcaFieldDefinition ->fieldType ->getTcaType ();
222+ $ fieldConfig = $ tcaFieldDefinition ->getTca ()['config ' ] ?? [];
223+ if (in_array ($ tcaType , ['category ' , 'inline ' , 'file ' ])) {
185224 return true ;
186225 }
187- if ($ resolvedField instanceof LazyRecordCollection) {
226+ $ allowed = $ fieldConfig ['allowed ' ] ?? '' ;
227+ if ($ tcaType === 'group ' && $ allowed !== '' ) {
188228 return true ;
189229 }
190- return false ;
191- }
192-
193- private function transformSelectRelation (
194- LazyRecordCollection |RecordInterface $ processedField ,
195- int $ depth ,
196- ?PageLayoutContext $ context = null ,
197- ): LazyRecordCollection |ContentBlockData {
198- if ($ processedField instanceof Record) {
199- $ processedField = $ this ->transformSingleRelation (
200- $ processedField ,
201- $ depth ,
202- $ context ,
203- );
204- return $ processedField ;
230+ $ foreignTable = $ fieldConfig ['foreign_table ' ] ?? '' ;
231+ if ($ tcaType === 'select ' && $ foreignTable !== '' ) {
232+ return true ;
205233 }
206- $ processedField = $ this ->transformMultipleRelation (
207- $ processedField ,
208- $ depth ,
209- $ context ,
210- );
211- return $ processedField ;
234+ return false ;
212235 }
213236
214237 /**
215- * @return LazyRecordCollection <ContentBlockData>
238+ * @return array <ContentBlockData>
216239 */
217240 private function transformMultipleRelation (
218241 LazyRecordCollection $ processedField ,
219242 int $ depth ,
220243 ?PageLayoutContext $ context = null ,
221- ): LazyRecordCollection {
244+ ): array {
245+ $ items = [];
222246 foreach ($ processedField as $ key => $ processedFieldItem ) {
223- $ processedField [$ key ] = $ this ->transformSingleRelation (
247+ $ items [$ key ] = $ this ->transformSingleRelation (
224248 $ processedFieldItem ,
225249 $ depth ,
226250 $ context ,
227251 );
228252 }
229- return $ processedField ;
253+ return $ items ;
230254 }
231255
232256 private function transformSingleRelation (
@@ -268,19 +292,16 @@ private function transformSingleRelation(
268292 return $ contentBlockData ;
269293 }
270294
271- /**
272- * @param array<string, RelationGrid>|array<string, RenderedGridItem[]> $grids
273- */
274295 private function buildContentBlockDataObject (
275296 ResolvedContentBlockDataRelation $ resolvedRelation ,
276297 string $ name = '' ,
277- array $ grids = [] ,
298+ ? ContentBlockGridData $ gridData = null ,
278299 ): ContentBlockData {
279300 $ resolvedData = $ resolvedRelation ->resolved ;
280301 if ($ resolvedRelation ->record instanceof Record === false ) {
281302 throw new \RuntimeException ('Resolved record is not a record instance ' , 1728587332 );
282303 }
283- $ contentBlockData = new ContentBlockData ($ resolvedRelation ->record , $ name , $ grids , $ resolvedData );
304+ $ contentBlockData = new ContentBlockData ($ resolvedRelation ->record , $ name , $ gridData , $ resolvedData );
284305 return $ contentBlockData ;
285306 }
286307
0 commit comments