Skip to content

Commit b435700

Browse files
authored
IBX-9415: Added support for ContentAwareInterface in ibexa_* Twig functions (#467)
1 parent 10fcc63 commit b435700

21 files changed

+558
-38
lines changed

src/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtension.php

Lines changed: 91 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
use Ibexa\Contracts\Core\Repository\Repository;
1010
use Ibexa\Contracts\Core\Repository\Values\Content\Content;
11+
use Ibexa\Contracts\Core\Repository\Values\Content\ContentAwareInterface;
1112
use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo;
1213
use Ibexa\Contracts\Core\Repository\Values\Content\Field;
1314
use Ibexa\Contracts\Core\Repository\Values\ValueObject;
@@ -137,69 +138,70 @@ public function getFunctions(): array
137138
}
138139

139140
/**
140-
* @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $content Must be a valid Content or ContentInfo object.
141+
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Content|\Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo|\Ibexa\Contracts\Core\Repository\Values\Content\ContentAwareInterface $data Must be a valid Content, ContentInfo, or ContentAwareInterface object.
141142
* @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale)
142143
*
143-
* @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType When $content is not a valid Content or ContentInfo object.
144+
* @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType When $content is not a valid Content, ContentInfo, or ContentAwareInterface object.
144145
*
145146
* @return string
146147
*/
147-
public function getTranslatedContentName(ValueObject $content, $forcedLanguage = null)
148+
public function getTranslatedContentName(object $data, $forcedLanguage = null)
148149
{
150+
$content = $this->resolveData($data);
149151
if ($content instanceof Content) {
150152
return $this->translationHelper->getTranslatedContentName($content, $forcedLanguage);
151153
} elseif ($content instanceof ContentInfo) {
152154
return $this->translationHelper->getTranslatedContentNameByContentInfo($content, $forcedLanguage);
153155
}
154156

155157
throw new InvalidArgumentType(
156-
'$content',
157-
sprintf('%s or %s', Content::class, ContentInfo::class),
158-
$content
158+
'$data',
159+
sprintf('%s or %s or %s', Content::class, ContentInfo::class, ContentAwareInterface::class),
160+
$data
159161
);
160162
}
161163

162164
/**
163165
* Returns the translated field, very similar to getTranslatedFieldValue but this returns the whole field.
164166
* To be used with ibexa_image_alias for example, which requires the whole field.
165167
*
166-
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content
168+
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Content|\Ibexa\Contracts\Core\Repository\Values\Content\ContentAwareInterface $data
167169
* @param string $fieldDefIdentifier Identifier for the field we want to get.
168170
* @param string $forcedLanguage Locale we want the field in (e.g. "cro-HR"). Null by default (takes current locale).
169171
*
170172
* @return \Ibexa\Contracts\Core\Repository\Values\Content\Field
171173
*/
172-
public function getTranslatedField(Content $content, $fieldDefIdentifier, $forcedLanguage = null)
174+
public function getTranslatedField(object $data, $fieldDefIdentifier, $forcedLanguage = null)
173175
{
174-
return $this->translationHelper->getTranslatedField($content, $fieldDefIdentifier, $forcedLanguage);
176+
return $this->translationHelper->getTranslatedField($this->getContent($data), $fieldDefIdentifier, $forcedLanguage);
175177
}
176178

177179
/**
178-
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content
180+
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Content|\Ibexa\Contracts\Core\Repository\Values\Content\ContentAwareInterface $data
179181
* @param string $fieldDefIdentifier Identifier for the field we want to get the value from.
180182
* @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale).
181183
*
182184
* @return mixed A primitive type or a field type Value object depending on the field type.
183185
*/
184-
public function getTranslatedFieldValue(Content $content, $fieldDefIdentifier, $forcedLanguage = null)
186+
public function getTranslatedFieldValue(object $data, $fieldDefIdentifier, $forcedLanguage = null)
185187
{
186-
return $this->translationHelper->getTranslatedField($content, $fieldDefIdentifier, $forcedLanguage)->value;
188+
return $this->translationHelper->getTranslatedField($this->getContent($data), $fieldDefIdentifier, $forcedLanguage)->value;
187189
}
188190

189191
/**
190-
* Gets name of a FieldDefinition name by loading ContentType based on Content/ContentInfo object.
192+
* Gets name of a FieldDefinition name by loading ContentType based on Content/ContentInfo/ContentAwareInterface object.
191193
*
192-
* @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $content Must be Content or ContentInfo object
194+
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Content|\Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo|\Ibexa\Contracts\Core\Repository\Values\Content\ContentAwareInterface $data Must be Content, ContentInfo, or ContentAwareInterface object
193195
* @param string $fieldDefIdentifier Identifier for the field we want to get the name from
194196
* @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale)
195197
*
196-
* @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType When $content is not a valid Content object.
198+
* @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType When $content is not a valid Content, ContentInfo, or ContentAwareInterface object.
197199
*
198200
* @return string|null
199201
*/
200-
public function getTranslatedFieldDefinitionName(ValueObject $content, $fieldDefIdentifier, $forcedLanguage = null)
202+
public function getTranslatedFieldDefinitionName(object $data, $fieldDefIdentifier, $forcedLanguage = null)
201203
{
202-
if ($contentType = $this->getContentType($content)) {
204+
if ($contentType = $this->getContentType($this->resolveData($data))) {
203205
return $this->translationHelper->getTranslatedFieldDefinitionProperty(
204206
$contentType,
205207
$fieldDefIdentifier,
@@ -208,23 +210,25 @@ public function getTranslatedFieldDefinitionName(ValueObject $content, $fieldDef
208210
);
209211
}
210212

211-
throw new InvalidArgumentType('$content', 'Content|ContentInfo', $content);
213+
throw new InvalidArgumentType(
214+
'$data',
215+
sprintf('%s or %s or %s', Content::class, ContentInfo::class, ContentAwareInterface::class),
216+
$data
217+
);
212218
}
213219

214220
/**
215-
* Gets name of a FieldDefinition description by loading ContentType based on Content/ContentInfo object.
221+
* Gets name of a FieldDefinition description by loading ContentType based on Content/ContentInfo/ContentAwareInterface object.
216222
*
217-
* @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $content Must be Content or ContentInfo object
223+
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Content|\Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo|\Ibexa\Contracts\Core\Repository\Values\Content\ContentAwareInterface $data Must be Content, ContentInfo, or ContentAwareInterface object
218224
* @param string $fieldDefIdentifier Identifier for the field we want to get the name from
219225
* @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale)
220226
*
221-
* @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType When $content is not a valid Content object.
222-
*
223227
* @return string|null
224228
*/
225-
public function getTranslatedFieldDefinitionDescription(ValueObject $content, $fieldDefIdentifier, $forcedLanguage = null)
229+
public function getTranslatedFieldDefinitionDescription(object $data, $fieldDefIdentifier, $forcedLanguage = null)
226230
{
227-
if ($contentType = $this->getContentType($content)) {
231+
if ($contentType = $this->getContentType($this->resolveData($data))) {
228232
return $this->translationHelper->getTranslatedFieldDefinitionProperty(
229233
$contentType,
230234
$fieldDefIdentifier,
@@ -233,11 +237,20 @@ public function getTranslatedFieldDefinitionDescription(ValueObject $content, $f
233237
);
234238
}
235239

236-
throw new InvalidArgumentType('$content', 'Content|ContentInfo', $content);
240+
throw new InvalidArgumentType(
241+
'$data',
242+
sprintf('%s or %s or %s', Content::class, ContentInfo::class, ContentAwareInterface::class),
243+
$data
244+
);
237245
}
238246

239-
public function hasField(Content $content, string $fieldDefIdentifier): bool
247+
/**
248+
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Content|\Ibexa\Contracts\Core\Repository\Values\Content\ContentAwareInterface $data
249+
*/
250+
public function hasField(object $data, string $fieldDefIdentifier): bool
240251
{
252+
$content = $this->getContent($data);
253+
241254
return $content->getContentType()->hasFieldDefinition($fieldDefIdentifier);
242255
}
243256

@@ -250,21 +263,21 @@ public function getFieldGroupName(string $identifier): ?string
250263
* Checks if a given field is considered empty.
251264
* This method accepts field as Objects or by identifiers.
252265
*
253-
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content
266+
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Content|\Ibexa\Contracts\Core\Repository\Values\Content\ContentAwareInterface $data
254267
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Field|string $fieldDefIdentifier Field or Field Identifier to
255268
* get the value from.
256269
* @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR").
257270
* Null by default (takes current locale).
258271
*
259272
* @return bool
260273
*/
261-
public function isFieldEmpty(Content $content, $fieldDefIdentifier, $forcedLanguage = null)
274+
public function isFieldEmpty(object $data, $fieldDefIdentifier, $forcedLanguage = null)
262275
{
263276
if ($fieldDefIdentifier instanceof Field) {
264277
$fieldDefIdentifier = $fieldDefIdentifier->fieldDefIdentifier;
265278
}
266279

267-
return $this->fieldHelper->isFieldEmpty($content, $fieldDefIdentifier, $forcedLanguage);
280+
return $this->fieldHelper->isFieldEmpty($this->getContent($data), $fieldDefIdentifier, $forcedLanguage);
268281
}
269282

270283
/**
@@ -285,8 +298,12 @@ private function getContentType(ValueObject $content)
285298
}
286299
}
287300

288-
public function getFirstFilledImageFieldIdentifier(Content $content)
301+
/**
302+
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Content|\Ibexa\Contracts\Core\Repository\Values\Content\ContentAwareInterface $data
303+
*/
304+
public function getFirstFilledImageFieldIdentifier(object $data)
289305
{
306+
$content = $this->getContent($data);
290307
foreach ($content->getFieldsByLanguage() as $field) {
291308
$fieldTypeIdentifier = $content->getContentType()
292309
->getFieldDefinition($field->fieldDefIdentifier)
@@ -305,6 +322,50 @@ public function getFirstFilledImageFieldIdentifier(Content $content)
305322

306323
return null;
307324
}
325+
326+
/**
327+
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Content|\Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo|\Ibexa\Contracts\Core\Repository\Values\Content\ContentAwareInterface $data
328+
*
329+
* @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType
330+
*/
331+
private function resolveData(object $data): ValueObject
332+
{
333+
if ($data instanceof Content || $data instanceof ContentInfo) {
334+
return $data;
335+
}
336+
337+
if ($data instanceof ContentAwareInterface) {
338+
return $data->getContent();
339+
}
340+
341+
throw new InvalidArgumentType(
342+
'$content',
343+
sprintf('%s or %s or %s', Content::class, ContentInfo::class, ContentAwareInterface::class),
344+
$data,
345+
);
346+
}
347+
348+
/**
349+
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Content|\Ibexa\Contracts\Core\Repository\Values\Content\ContentAwareInterface $data
350+
*
351+
* @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType
352+
*/
353+
private function getContent(object $data): Content
354+
{
355+
if ($data instanceof Content) {
356+
return $data;
357+
}
358+
359+
if ($data instanceof ContentAwareInterface) {
360+
return $data->getContent();
361+
}
362+
363+
throw new InvalidArgumentType(
364+
'$content',
365+
sprintf('%s or %s', Content::class, ContentAwareInterface::class),
366+
$data,
367+
);
368+
}
308369
}
309370

310371
class_alias(ContentExtension::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\ContentExtension');

src/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtension.php

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
namespace Ibexa\Core\MVC\Symfony\Templating\Twig\Extension;
88

99
use Ibexa\Contracts\Core\Repository\Values\Content\Content;
10+
use Ibexa\Contracts\Core\Repository\Values\Content\ContentAwareInterface;
1011
use Ibexa\Contracts\Core\Repository\Values\Content\Field;
1112
use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition;
1213
use Ibexa\Core\Base\Exceptions\InvalidArgumentException;
14+
use Ibexa\Core\Base\Exceptions\InvalidArgumentType;
1315
use Ibexa\Core\Helper\TranslationHelper;
1416
use Ibexa\Core\MVC\Symfony\FieldType\View\ParameterProviderRegistryInterface;
1517
use Ibexa\Core\MVC\Symfony\Templating\FieldBlockRendererInterface;
@@ -52,17 +54,22 @@ public function __construct(
5254

5355
public function getFunctions()
5456
{
55-
$renderFieldCallable = function (Environment $environment, Content $content, $fieldIdentifier, array $params = []) {
57+
$renderFieldCallable = function (Environment $environment, $data, $fieldIdentifier, array $params = []) {
5658
$this->fieldBlockRenderer->setTwig($environment);
5759

58-
return $this->renderField($content, $fieldIdentifier, $params);
60+
return $this->renderField($this->getContent($data), $fieldIdentifier, $params);
5961
};
6062

61-
$renderFieldDefinitionSettingsCallable = function (Environment $environment, FieldDefinition $fieldDefinition, array $params = []) {
62-
$this->fieldBlockRenderer->setTwig($environment);
63+
$renderFieldDefinitionSettingsCallable =
64+
function (
65+
Environment $environment,
66+
FieldDefinition $fieldDefinition,
67+
array $params = []
68+
) {
69+
$this->fieldBlockRenderer->setTwig($environment);
6370

64-
return $this->renderFieldDefinitionSettings($fieldDefinition, $params);
65-
};
71+
return $this->renderFieldDefinitionSettings($fieldDefinition, $params);
72+
};
6673

6774
return [
6875
new TwigFunction(
@@ -210,6 +217,27 @@ private function getFieldTypeIdentifier(Content $content, Field $field)
210217

211218
return $this->fieldTypeIdentifiers[$key];
212219
}
220+
221+
/**
222+
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Content|\Ibexa\Contracts\Core\Repository\Values\Content\ContentAwareInterface $content
223+
*
224+
* @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType
225+
*/
226+
private function getContent(object $content): Content
227+
{
228+
if ($content instanceof Content) {
229+
return $content;
230+
}
231+
if ($content instanceof ContentAwareInterface) {
232+
return $content->getContent();
233+
}
234+
235+
throw new InvalidArgumentType(
236+
'$content',
237+
sprintf('%s or %s', Content::class, ContentAwareInterface::class),
238+
$content,
239+
);
240+
}
213241
}
214242

215243
class_alias(FieldRenderingExtension::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\FieldRenderingExtension');

src/lib/MVC/Symfony/Templating/Twig/Extension/RenderContentExtension.php

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
namespace Ibexa\Core\MVC\Symfony\Templating\Twig\Extension;
1010

1111
use Ibexa\Contracts\Core\Repository\Values\Content\Content;
12+
use Ibexa\Contracts\Core\Repository\Values\Content\ContentAwareInterface;
13+
use Ibexa\Core\Base\Exceptions\InvalidArgumentType;
1214
use Ibexa\Core\MVC\Symfony\Event\ResolveRenderOptionsEvent;
1315
use Ibexa\Core\MVC\Symfony\Templating\RenderContentStrategy;
1416
use Ibexa\Core\MVC\Symfony\Templating\RenderOptions;
@@ -58,14 +60,39 @@ public function getFunctions(): array
5860
];
5961
}
6062

61-
public function renderContent(Content $content, array $options = []): string
63+
/**
64+
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Content|\Ibexa\Contracts\Core\Repository\Values\Content\ContentAwareInterface $data
65+
*/
66+
public function renderContent(object $data, array $options = []): string
6267
{
6368
$renderOptions = new RenderOptions($options);
6469
$event = $this->eventDispatcher->dispatch(
6570
new ResolveRenderOptionsEvent($renderOptions)
6671
);
6772

68-
return $this->renderContentStrategy->render($content, $event->getRenderOptions());
73+
return $this->renderContentStrategy->render($this->getContent($data), $event->getRenderOptions());
74+
}
75+
76+
/**
77+
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Content|\Ibexa\Contracts\Core\Repository\Values\Content\ContentAwareInterface $data
78+
*
79+
* @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType
80+
*/
81+
private function getContent(object $data): Content
82+
{
83+
if ($data instanceof Content) {
84+
return $data;
85+
}
86+
87+
if ($data instanceof ContentAwareInterface) {
88+
return $data->getContent();
89+
}
90+
91+
throw new InvalidArgumentType(
92+
'$content',
93+
sprintf('%s or %s', Content::class, ContentAwareInterface::class),
94+
$data,
95+
);
6996
}
7097
}
7198

tests/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtensionTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Ibexa\Contracts\Core\Repository\ContentService;
1010
use Ibexa\Contracts\Core\Repository\ContentTypeService;
1111
use Ibexa\Contracts\Core\Repository\Repository;
12+
use Ibexa\Contracts\Core\Repository\Values\Content\ContentAwareInterface;
1213
use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo;
1314
use Ibexa\Contracts\Core\Repository\Values\Content\Field;
1415
use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface;
@@ -126,6 +127,20 @@ protected function getContent(string $contentTypeIdentifier, array $fieldsData,
126127
return $content;
127128
}
128129

130+
/**
131+
* @param array<string, mixed> $fieldsData
132+
* @param array<mixed> $namesData
133+
*/
134+
protected function getContentAwareObject(string $contentTypeIdentifier, array $fieldsData, array $namesData = []): ContentAwareInterface
135+
{
136+
$content = $this->getContent($contentTypeIdentifier, $fieldsData, $namesData);
137+
138+
$mock = $this->createMock(ContentAwareInterface::class);
139+
$mock->method('getContent')->willReturn($content);
140+
141+
return $mock;
142+
}
143+
129144
private function getConfigResolverMock()
130145
{
131146
$mock = $this->createMock(ConfigResolverInterface::class);

0 commit comments

Comments
 (0)