Completed
Push — test-fieldrelation-location-se... ( 448993...a417bd )
by
unknown
13:37
created

TranslationHelper::getTranslatedContentName()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 2
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
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
 * @version //autogentag//
10
 */
11
namespace eZ\Publish\Core\Helper;
12
13
use eZ\Publish\API\Repository\ContentService;
14
use eZ\Publish\API\Repository\Values\Content\Content;
15
use eZ\Publish\API\Repository\Values\Content\ContentInfo;
16
use eZ\Publish\API\Repository\Values\Content\Field;
17
use eZ\Publish\API\Repository\Values\ContentType\ContentType;
18
use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition;
19
use eZ\Publish\API\Repository\Values\ValueObject;
20
use eZ\Publish\Core\MVC\ConfigResolverInterface;
21
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
22
use Psr\Log\LoggerInterface;
23
24
/**
25
 * Helper class for translation.
26
 */
27
class TranslationHelper
28
{
29
    /**
30
     * @var \eZ\Publish\Core\MVC\ConfigResolverInterface
31
     */
32
    protected $configResolver;
33
34
    /**
35
     * @var \eZ\Publish\API\Repository\ContentService
36
     */
37
    protected $contentService;
38
39
    /**
40
     * @var array
41
     */
42
    private $siteAccessesByLanguage;
43
44
    /**
45
     * @var \Psr\Log\LoggerInterface
46
     */
47
    private $logger;
48
49
    public function __construct(ConfigResolverInterface $configResolver, ContentService $contentService, array $siteAccessesByLanguage, LoggerInterface $logger = null)
50
    {
51
        $this->configResolver = $configResolver;
52
        $this->contentService = $contentService;
53
        $this->siteAccessesByLanguage = $siteAccessesByLanguage;
54
        $this->logger = $logger;
55
    }
56
57
    /**
58
     * Returns content name, translated.
59
     * By default this method uses prioritized languages, unless $forcedLanguage is provided.
60
     *
61
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
62
     * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale)
63
     *
64
     * @return string
65
     */
66
    public function getTranslatedContentName(Content $content, $forcedLanguage = null)
67
    {
68
        foreach ($this->getLanguages($forcedLanguage) as $lang) {
69
            $translatedName = $content->getVersionInfo()->getName($lang);
70
            if ($translatedName !== null) {
71
                return $translatedName;
72
            }
73
        }
74
    }
75
76
    /**
77
     * Returns content name, translated, from a ContentInfo object.
78
     * By default this method uses prioritized languages, unless $forcedLanguage is provided.
79
     *
80
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
81
     * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale)
82
     *
83
     * @todo Remove ContentService usage when translated names are available in ContentInfo (see https://jira.ez.no/browse/EZP-21755)
84
     *
85
     * @return string
86
     */
87
    public function getTranslatedContentNameByContentInfo(ContentInfo $contentInfo, $forcedLanguage = null)
88
    {
89
        if (isset($forcedLanguage) && $forcedLanguage === $contentInfo->mainLanguageCode) {
90
            return $contentInfo->name;
91
        }
92
93
        return $this->getTranslatedContentName(
94
            $this->contentService->loadContentByContentInfo($contentInfo),
95
            $forcedLanguage
96
        );
97
    }
98
99
    /**
100
     * Returns Field object in the appropriate language for a given content.
101
     * By default, this method will return the field in current language if translation is present. If not, main language will be used.
102
     * If $forcedLanguage is provided, will return the field in this language, if translation is present.
103
     *
104
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
105
     * @param string $fieldDefIdentifier Field definition identifier.
106
     * @param string $forcedLanguage Locale we want the field translation in (e.g. "fre-FR"). Null by default (takes current locale)
107
     *
108
     * @return \eZ\Publish\API\Repository\Values\Content\Field|null
109
     */
110
    public function getTranslatedField(Content $content, $fieldDefIdentifier, $forcedLanguage = null)
111
    {
112
        // Loop over prioritized languages to get the appropriate translated field.
113
        foreach ($this->getLanguages($forcedLanguage) as $lang) {
114
            $field = $content->getField($fieldDefIdentifier, $lang);
115
            if ($field instanceof Field) {
116
                return $field;
117
            }
118
        }
119
    }
120
121
    /**
122
     * Returns Field definition name in the appropriate language for a given content.
123
     *
124
     * By default, this method will return the field definition name in current language if translation is present. If not, main language will be used.
125
     * If $forcedLanguage is provided, will return the field definition name in this language, if translation is present.
126
     *
127
     * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType
128
     * @param string $fieldDefIdentifier Field Definition identifier
129
     * @param string $property Specifies if 'name' or 'description' should be used
130
     * @param string $forcedLanguage Locale we want the field definition name translated in in (e.g. "fre-FR"). Null by default (takes current locale)
131
     *
132
     * @throws InvalidArgumentException
133
     *
134
     * @return string|null
135
     */
136
    public function getTranslatedFieldDefinitionProperty(
137
        ContentType $contentType,
138
        $fieldDefIdentifier,
139
        $property = 'name',
140
        $forcedLanguage = null
141
    ) {
142
        $fieldDefinition = $contentType->getFieldDefinition($fieldDefIdentifier);
143
        if (!$fieldDefinition instanceof FieldDefinition) {
144
            throw new InvalidArgumentException(
145
                '$fieldDefIdentifier',
146
                "Field '{$fieldDefIdentifier}' not found on {$contentType->identifier}"
147
            );
148
        }
149
150
        $method = 'get' . $property;
151
        if (!method_exists($fieldDefinition, $method)) {
152
            throw new InvalidArgumentException('$property', "Method get'{$property}'() not found on FieldDefinition");
153
        }
154
155
        // Loop over prioritized languages to get the appropriate translated field definition name
156
        // Should ideally have used array_unique, but in that case the loop should ideally never reach last item
157
        foreach ($this->getLanguages($forcedLanguage, $contentType->mainLanguageCode) as $lang) {
158
            if ($name = $fieldDefinition->$method($lang)) {
159
                return $name;
160
            }
161
        }
162
    }
163
164
    /**
165
     * Gets translated property generic helper.
166
     *
167
     * For generic use, expects array property as-is on value object, typically $object->$property[$language]
168
     *
169
     * Languages will consist of either forced language or current languages list, in addition helper will check if for
170
     * mainLanguage property and append that to languages if alwaysAvailable property is true or non-existing.
171
     *
172
     * @param \eZ\Publish\API\Repository\Values\ValueObject $object  Can be any kid of Value object which directly holds the translated property
173
     * @param string $property Property name, example 'names', 'descriptions'
174
     * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale)
175
     *
176
     * @throws InvalidArgumentException
177
     *
178
     * @return string|null
179
     */
180
    public function getTranslatedByProperty(ValueObject $object, $property, $forcedLanguage = null)
181
    {
182
        if (!isset($object->$property)) {
183
            throw new InvalidArgumentException('$property', "Property '{$property}' not found on " . get_class($object));
184
        }
185
186
        // Always force main language as fallback, if defined and if either alwaysAvailable is true or not defined
187
        // if language is already is set on array we still do this as ideally the loop will never
188
        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...
189
            $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...
190
        } else {
191
            $languages = $this->getLanguages($forcedLanguage);
192
        }
193
194
        // Get property value first in case it is magic (__isset and __get) property
195
        $propertyValue = $object->$property;
196
        foreach ($languages as $lang) {
197
            if (isset($propertyValue[$lang])) {
198
                return $propertyValue[$lang];
199
            }
200
        }
201
    }
202
203
    /**
204
     * Gets translated method generic helper.
205
     *
206
     * For generic use, expects method exposing translated property as-is on value object, typically $object->$method($language)
207
     *
208
     * Languages will consist of either forced language or current languages list, in addition helper will append null
209
     * to list of languages so method may fallback to main/initial language if supported by domain.
210
     *
211
     * @param \eZ\Publish\API\Repository\Values\ValueObject $object  Can be any kind of Value object which directly holds the methods that provides translated value.
212
     * @param string $method Method name, example 'getName', 'description'
213
     * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale)
214
     *
215
     * @throws InvalidArgumentException
216
     *
217
     * @return string|null
218
     */
219
    public function getTranslatedByMethod(ValueObject $object, $method, $forcedLanguage = null)
220
    {
221
        if (!method_exists($object, $method)) {
222
            throw new InvalidArgumentException('$method', "Method '{$method}' not found on " . get_class($object));
223
        }
224
225
        foreach ($this->getLanguages($forcedLanguage) as $lang) {
226
            if ($value = $object->$method($lang)) {
227
                return $value;
228
            }
229
        }
230
    }
231
232
    /**
233
     * Returns a SiteAccess name for translation in $languageCode.
234
     * This is used for LanguageSwitcher feature (generate links for current content in a different language if available).
235
     * Will use configured translation_siteaccesses if any. Otherwise will use related siteaccesses (e.g. same repository, same rootLocationId).
236
     *
237
     * Will return null if no translation SiteAccess can be found.
238
     *
239
     * @param string $languageCode Translation language code.
240
     *
241
     * @return string|null
242
     */
243
    public function getTranslationSiteAccess($languageCode)
244
    {
245
        $translationSiteAccesses = $this->configResolver->getParameter('translation_siteaccesses');
246
        $relatedSiteAccesses = $this->configResolver->getParameter('related_siteaccesses');
247
248
        if (!isset($this->siteAccessesByLanguage[$languageCode])) {
249
            if ($this->logger) {
250
                $this->logger->error("Couldn't find any SiteAccess with '$languageCode' as main language.");
251
            }
252
253
            return null;
254
        }
255
256
        $relatedSiteAccesses = $translationSiteAccesses ?: $relatedSiteAccesses;
257
        $translationSiteAccesses = array_intersect($this->siteAccessesByLanguage[$languageCode], $relatedSiteAccesses);
258
259
        return array_shift($translationSiteAccesses);
260
    }
261
262
    /**
263
     * Returns the list of all available languages, including the ones configured in related SiteAccesses.
264
     *
265
     * @return array
266
     */
267
    public function getAvailableLanguages()
268
    {
269
        $translationSiteAccesses = $this->configResolver->getParameter('translation_siteaccesses');
270
        $relatedSiteAccesses = $translationSiteAccesses ?: $this->configResolver->getParameter('related_siteaccesses');
271
        $availableLanguages = array();
272
        $currentLanguages = $this->configResolver->getParameter('languages');
273
        $availableLanguages[] = array_shift($currentLanguages);
274
275
        foreach ($relatedSiteAccesses as $sa) {
276
            $languages = $this->configResolver->getParameter('languages', null, $sa);
277
            $availableLanguages[] = array_shift($languages);
278
        }
279
280
        sort($availableLanguages);
281
282
        return array_unique($availableLanguages);
283
    }
284
285
    /**
286
     * @param string|null $forcedLanguage
287
     * @param string|null $fallbackLanguage
288
     *
289
     * @return array|mixed
290
     */
291
    private function getLanguages($forcedLanguage = null, $fallbackLanguage = null)
292
    {
293
        if ($forcedLanguage !== null) {
294
            $languages = array($forcedLanguage);
295
        } else {
296
            $languages = $this->configResolver->getParameter('languages');
297
        }
298
299
        // Always add $fallbackLanguage, even if null, as last entry so that we can fallback to
300
        // main/initial language if domain supports it.
301
        $languages[] = $fallbackLanguage;
302
303
        return $languages;
304
    }
305
}
306