Completed
Push — master ( a6387c...f7252d )
by André
23:52 queued 09:38
created

TranslationHelper::getTranslatedContentName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 7
rs 9.4285
1
<?php
2
3
/**
4
 * File containing the ContentHelper class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 */
9
namespace eZ\Publish\Core\Helper;
10
11
use eZ\Publish\API\Repository\ContentService;
12
use eZ\Publish\API\Repository\Values\Content\Content;
13
use eZ\Publish\API\Repository\Values\Content\ContentInfo;
14
use eZ\Publish\API\Repository\Values\Content\Field;
15
use eZ\Publish\API\Repository\Values\Content\VersionInfo;
16
use eZ\Publish\API\Repository\Values\ContentType\ContentType;
17
use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition;
18
use eZ\Publish\API\Repository\Values\ValueObject;
19
use eZ\Publish\Core\MVC\ConfigResolverInterface;
20
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
21
use Psr\Log\LoggerInterface;
22
23
/**
24
 * Helper class for translation.
25
 */
26
class TranslationHelper
27
{
28
    /**
29
     * @var \eZ\Publish\Core\MVC\ConfigResolverInterface
30
     */
31
    protected $configResolver;
32
33
    /**
34
     * @var \eZ\Publish\API\Repository\ContentService
35
     */
36
    protected $contentService;
37
38
    /**
39
     * @var array
40
     */
41
    private $siteAccessesByLanguage;
42
43
    /**
44
     * @var \Psr\Log\LoggerInterface
45
     */
46
    private $logger;
47
48
    public function __construct(ConfigResolverInterface $configResolver, ContentService $contentService, array $siteAccessesByLanguage, LoggerInterface $logger = null)
49
    {
50
        $this->configResolver = $configResolver;
51
        $this->contentService = $contentService;
52
        $this->siteAccessesByLanguage = $siteAccessesByLanguage;
53
        $this->logger = $logger;
54
    }
55
56
    /**
57
     * Returns content name, translated.
58
     * By default this method uses prioritized languages, unless $forcedLanguage is provided.
59
     *
60
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
61
     * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale)
62
     *
63
     * @return string
64
     */
65
    public function getTranslatedContentName(Content $content, $forcedLanguage = null)
66
    {
67
        return $this->getTranslatedContentNameByVersionInfo(
68
            $content->getVersionInfo(),
69
            $forcedLanguage
70
        );
71
    }
72
73
    /**
74
     * Returns content name, translated, from a VersionInfo object.
75
     * By default this method uses prioritized languages, unless $forcedLanguage is provided.
76
     *
77
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
78
     * @param string $forcedLanguage
79
     *
80
     * @return string
81
     */
82
    private function getTranslatedContentNameByVersionInfo(VersionInfo $versionInfo, $forcedLanguage = null)
83
    {
84
        foreach ($this->getLanguages($forcedLanguage) as $lang) {
85
            $translatedName = $versionInfo->getName($lang);
86
            if ($translatedName !== null) {
87
                return $translatedName;
88
            }
89
        }
90
91
        return '';
92
    }
93
94
    /**
95
     * Returns content name, translated, from a ContentInfo object.
96
     * By default this method uses prioritized languages, unless $forcedLanguage is provided.
97
     *
98
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
99
     * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale)
100
     *
101
     * @todo Remove ContentService usage when translated names are available in ContentInfo (see https://jira.ez.no/browse/EZP-21755)
102
     *
103
     * @return string
104
     */
105
    public function getTranslatedContentNameByContentInfo(ContentInfo $contentInfo, $forcedLanguage = null)
106
    {
107
        if (isset($forcedLanguage) && $forcedLanguage === $contentInfo->mainLanguageCode) {
108
            return $contentInfo->name;
109
        }
110
111
        return $this->getTranslatedContentNameByVersionInfo(
112
            $this->contentService->loadVersionInfo($contentInfo),
113
            $forcedLanguage
114
        );
115
    }
116
117
    /**
118
     * Returns Field object in the appropriate language for a given content.
119
     * By default, this method will return the field in current language if translation is present. If not, main language will be used.
120
     * If $forcedLanguage is provided, will return the field in this language, if translation is present.
121
     *
122
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
123
     * @param string $fieldDefIdentifier Field definition identifier.
124
     * @param string $forcedLanguage Locale we want the field translation in (e.g. "fre-FR"). Null by default (takes current locale)
125
     *
126
     * @return \eZ\Publish\API\Repository\Values\Content\Field|null
127
     */
128
    public function getTranslatedField(Content $content, $fieldDefIdentifier, $forcedLanguage = null)
129
    {
130
        // Loop over prioritized languages to get the appropriate translated field.
131
        foreach ($this->getLanguages($forcedLanguage) as $lang) {
132
            $field = $content->getField($fieldDefIdentifier, $lang);
133
            if ($field instanceof Field) {
134
                return $field;
135
            }
136
        }
137
    }
138
139
    /**
140
     * Returns Field definition name in the appropriate language for a given content.
141
     *
142
     * By default, this method will return the field definition name in current language if translation is present. If not, main language will be used.
143
     * If $forcedLanguage is provided, will return the field definition name in this language, if translation is present.
144
     *
145
     * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType
146
     * @param string $fieldDefIdentifier Field Definition identifier
147
     * @param string $property Specifies if 'name' or 'description' should be used
148
     * @param string $forcedLanguage Locale we want the field definition name translated in in (e.g. "fre-FR"). Null by default (takes current locale)
149
     *
150
     * @throws InvalidArgumentException
151
     *
152
     * @return string|null
153
     */
154
    public function getTranslatedFieldDefinitionProperty(
155
        ContentType $contentType,
156
        $fieldDefIdentifier,
157
        $property = 'name',
158
        $forcedLanguage = null
159
    ) {
160
        $fieldDefinition = $contentType->getFieldDefinition($fieldDefIdentifier);
161
        if (!$fieldDefinition instanceof FieldDefinition) {
162
            throw new InvalidArgumentException(
163
                '$fieldDefIdentifier',
164
                "Field '{$fieldDefIdentifier}' not found on {$contentType->identifier}"
165
            );
166
        }
167
168
        $method = 'get' . $property;
169
        if (!method_exists($fieldDefinition, $method)) {
170
            throw new InvalidArgumentException('$property', "Method get'{$property}'() not found on FieldDefinition");
171
        }
172
173
        // Loop over prioritized languages to get the appropriate translated field definition name
174
        // Should ideally have used array_unique, but in that case the loop should ideally never reach last item
175
        foreach ($this->getLanguages($forcedLanguage, $contentType->mainLanguageCode) as $lang) {
176
            if ($name = $fieldDefinition->$method($lang)) {
177
                return $name;
178
            }
179
        }
180
    }
181
182
    /**
183
     * Gets translated property generic helper.
184
     *
185
     * For generic use, expects array property as-is on value object, typically $object->$property[$language]
186
     *
187
     * Languages will consist of either forced language or current languages list, in addition helper will check if for
188
     * mainLanguage property and append that to languages if alwaysAvailable property is true or non-existing.
189
     *
190
     * @param \eZ\Publish\API\Repository\Values\ValueObject $object  Can be any kid of Value object which directly holds the translated property
191
     * @param string $property Property name, example 'names', 'descriptions'
192
     * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale)
193
     *
194
     * @throws InvalidArgumentException
195
     *
196
     * @return string|null
197
     */
198
    public function getTranslatedByProperty(ValueObject $object, $property, $forcedLanguage = null)
199
    {
200
        if (!isset($object->$property)) {
201
            throw new InvalidArgumentException('$property', "Property '{$property}' not found on " . get_class($object));
202
        }
203
204
        // Always force main language as fallback, if defined and if either alwaysAvailable is true or not defined
205
        // if language is already is set on array we still do this as ideally the loop will never
206
        if (isset($object->mainLanguageCode) && (!isset($object->alwaysAvailable) || $object->alwaysAvailable)) {
0 ignored issues
show
Documentation introduced by
The property alwaysAvailable does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
207
            $languages = $this->getLanguages($forcedLanguage, $object->mainLanguageCode);
0 ignored issues
show
Documentation introduced by
The property mainLanguageCode does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
208
        } else {
209
            $languages = $this->getLanguages($forcedLanguage);
210
        }
211
212
        // Get property value first in case it is magic (__isset and __get) property
213
        $propertyValue = $object->$property;
214
        foreach ($languages as $lang) {
215
            if (isset($propertyValue[$lang])) {
216
                return $propertyValue[$lang];
217
            }
218
        }
219
    }
220
221
    /**
222
     * Gets translated method generic helper.
223
     *
224
     * For generic use, expects method exposing translated property as-is on value object, typically $object->$method($language)
225
     *
226
     * Languages will consist of either forced language or current languages list, in addition helper will append null
227
     * to list of languages so method may fallback to main/initial language if supported by domain.
228
     *
229
     * @param \eZ\Publish\API\Repository\Values\ValueObject $object  Can be any kind of Value object which directly holds the methods that provides translated value.
230
     * @param string $method Method name, example 'getName', 'description'
231
     * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale)
232
     *
233
     * @throws InvalidArgumentException
234
     *
235
     * @return string|null
236
     */
237
    public function getTranslatedByMethod(ValueObject $object, $method, $forcedLanguage = null)
238
    {
239
        if (!method_exists($object, $method)) {
240
            throw new InvalidArgumentException('$method', "Method '{$method}' not found on " . get_class($object));
241
        }
242
243
        foreach ($this->getLanguages($forcedLanguage) as $lang) {
244
            if ($value = $object->$method($lang)) {
245
                return $value;
246
            }
247
        }
248
    }
249
250
    /**
251
     * Returns a SiteAccess name for translation in $languageCode.
252
     * This is used for LanguageSwitcher feature (generate links for current content in a different language if available).
253
     * Will use configured translation_siteaccesses if any. Otherwise will use related siteaccesses (e.g. same repository, same rootLocationId).
254
     *
255
     * Will return null if no translation SiteAccess can be found.
256
     *
257
     * @param string $languageCode Translation language code.
258
     *
259
     * @return string|null
260
     */
261
    public function getTranslationSiteAccess($languageCode)
262
    {
263
        $translationSiteAccesses = $this->configResolver->getParameter('translation_siteaccesses');
264
        $relatedSiteAccesses = $this->configResolver->getParameter('related_siteaccesses');
265
266
        if (!isset($this->siteAccessesByLanguage[$languageCode])) {
267
            if ($this->logger) {
268
                $this->logger->error("Couldn't find any SiteAccess with '$languageCode' as main language.");
269
            }
270
271
            return null;
272
        }
273
274
        $relatedSiteAccesses = $translationSiteAccesses ?: $relatedSiteAccesses;
275
        $translationSiteAccesses = array_intersect($this->siteAccessesByLanguage[$languageCode], $relatedSiteAccesses);
276
277
        return array_shift($translationSiteAccesses);
278
    }
279
280
    /**
281
     * Returns the list of all available languages, including the ones configured in related SiteAccesses.
282
     *
283
     * @return array
284
     */
285
    public function getAvailableLanguages()
286
    {
287
        $translationSiteAccesses = $this->configResolver->getParameter('translation_siteaccesses');
288
        $relatedSiteAccesses = $translationSiteAccesses ?: $this->configResolver->getParameter('related_siteaccesses');
289
        $availableLanguages = array();
290
        $currentLanguages = $this->configResolver->getParameter('languages');
291
        $availableLanguages[] = array_shift($currentLanguages);
292
293
        foreach ($relatedSiteAccesses as $sa) {
294
            $languages = $this->configResolver->getParameter('languages', null, $sa);
295
            $availableLanguages[] = array_shift($languages);
296
        }
297
298
        sort($availableLanguages);
299
300
        return array_unique($availableLanguages);
301
    }
302
303
    /**
304
     * @param string|null $forcedLanguage
305
     * @param string|null $fallbackLanguage
306
     *
307
     * @return array|mixed
308
     */
309
    private function getLanguages($forcedLanguage = null, $fallbackLanguage = null)
310
    {
311
        if ($forcedLanguage !== null) {
312
            $languages = array($forcedLanguage);
313
        } else {
314
            $languages = $this->configResolver->getParameter('languages');
315
        }
316
317
        // Always add $fallbackLanguage, even if null, as last entry so that we can fallback to
318
        // main/initial language if domain supports it.
319
        $languages[] = $fallbackLanguage;
320
321
        return $languages;
322
    }
323
}
324