Completed
Push — master ( c72de8...38327d )
by Stefano
20s queued 13s
created

I18nHelper::newLangUrl()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 3
nop 3
dl 0
loc 13
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * BEdita, API-first content management framework
4
 * Copyright 2018 ChannelWeb Srl, Chialab Srl
5
 *
6
 * This file is part of BEdita: you can redistribute it and/or modify
7
 * it under the terms of the GNU Lesser General Public License as published
8
 * by the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * See LICENSE.LGPL or <http://gnu.org/licenses/lgpl-3.0.html> for more details.
12
 */
13
namespace BEdita\I18n\View\Helper;
14
15
use BEdita\I18n\Core\I18nTrait;
16
use Cake\Core\Configure;
17
use Cake\I18n\I18n;
18
use Cake\Routing\Router;
19
use Cake\Utility\Hash;
20
use Cake\View\Helper;
21
22
/**
23
 * Helper to handle i18n things in view.
24
 */
25
class I18nHelper extends Helper
26
{
27
    use I18nTrait;
28
29
    /**
30
     * Translation data per object and lang (internal cache).
31
     * If `null` no cache has been created, if empty array no translations
32
     * have been found.
33
     *
34
     * Structure:
35
     *
36
     *   translation[<object ID>][<lang>][<field>] = <value>.
37
     *
38
     *
39
     * @var array|null
40
     */
41
    protected $translation = null;
42
43
    /**
44
     * Return the current URL replacing current lang with new lang passed.
45
     *
46
     * @param string $newLang The new lang you want in URL.
47
     * @param string $switchUrl The switch lang URL defined for this app, if any.
48
     * @return string
49
     */
50
    public function changeUrlLang($newLang, $switchUrl = null) : string
51
    {
52
        $request = Router::getRequest(true);
53
        if (empty($request)) {
54
            return '';
55
        }
56
        $path = $request->getUri()->getPath();
57
        $query = $request->getUri()->getQuery();
58
59
        $newLangUrl = $this->newLangUrl($newLang, $path, $query);
60
        if ($newLangUrl !== null) {
61
            return $newLangUrl;
62
        }
63
64
        if (!empty($switchUrl)) {
65
            return sprintf('%s?new=%s', $switchUrl, $newLang);
66
        }
67
68
        if (!empty($query)) {
69
            $path .= sprintf('?%s', $query);
70
        }
71
72
        return $path;
73
    }
74
75
    /**
76
     * Try to create a new language URL from current path using lang prefix.
77
     *
78
     * @param string $newLang The new lang you want in URL.
79
     * @param string $path The current URL path.
80
     * @param string $query The current URL query.
81
     * @return string|null The new lang url or null if no lang prefix was found
82
     */
83
    protected function newLangUrl($newLang, $path, $query) : ?string
84
    {
85
        $prefix = sprintf('/%s', $this->getLang());
86
        if (stripos($path, $prefix . '/') === 0 || $path === $prefix) {
87
            $url = sprintf('/%s', $newLang) . substr($path, strlen($prefix));
88
            if ($query) {
89
                $url .= '?' . $query;
90
            }
91
92
            return $url;
93
        }
94
95
        return null;
96
    }
97
98
    /**
99
     * Translate object field
100
     * Return translation (by response object and included data, field and language)
101
     *
102
     * @param array $object The object to translate
103
     * @param string $attribute The attribute name
104
     * @param string|null $lang The lang (2 chars string)
105
     * @param bool $defaultNull Pass true when you want null as default, on missing translation
106
     * @param array $included The included translations data
107
     * @return string|null
108
     */
109
    public function field(array $object, string $attribute, ?string $lang = null, bool $defaultNull = false, array &$included = []) : ?string
110
    {
111
        $defaultValue = null;
112
        if (!$defaultNull) {
113
            $defaultValue = Hash::get($object, sprintf('attributes.%s', $attribute), Hash::get($object, sprintf('%s', $attribute)));
114
        }
115
        if (empty($included) && !empty($this->getView()->viewVars['included'])) {
116
            $included = $this->getView()->viewVars['included'];
117
        }
118
        if (empty($lang)) {
119
            $lang = Configure::read('I18n.lang', '');
120
        }
121
        $returnValue = $this->getTranslatedField($object, $attribute, $lang, $included);
122
        if ($returnValue === null) {
123
            return $defaultValue;
124
        }
125
126
        return $returnValue;
127
    }
128
129
    /**
130
     * Verify that object has translation for the specified attribute and lang
131
     *
132
     * @param array $object The object to translate
133
     * @param string $attribute The attribute name
134
     * @param string|null $lang The lang (2 chars string
135
     * @param array $included The included translations data)
136
     * @return bool
137
     */
138
    public function exists(array $object, string $attribute, ?string $lang = null, array &$included = []) : bool
139
    {
140
        if (empty($included) && !empty($this->getView()->viewVars['included'])) {
141
            $included = $this->getView()->viewVars['included'];
142
        }
143
        if (empty($lang)) {
144
            $lang = Configure::read('I18n.lang', '');
145
        }
146
        $val = $this->getTranslatedField($object, $attribute, $lang, $included);
147
148
        return ($val !== null);
149
    }
150
151
    /**
152
     * Reset internal translation cache.
153
     * To use when `included` array has changed.
154
     *
155
     * @return void
156
     */
157
    public function reset() : void
158
    {
159
        $this->translation = null;
160
    }
161
162
    /**
163
     * Return translated field per response object and included, attribute and lang. Null on missing translation.
164
     * First time that it's called per response object and included, it fills $this->translation data.
165
     * I.e.:
166
     *
167
     *     $this->translation[100]['en'] = ['title' => 'Example', 'description' => 'This is an example']
168
     *     $this->translation[100]['it'] = ['title' => 'Esempio', 'description' => 'Questo è un esempio']
169
     *     $this->translation[100]['sp'] = ['title' => 'Ejemplo', 'description' => 'Este es un ejemplo']
170
     *
171
     * @param array $object The object to translate
172
     * @param string $attribute The attribute name
173
     * @param string $lang The lang (2 chars string)
174
     * @param array $included The included translations data
175
     * @return string|null The translation of attribute field per object response and lang
176
     */
177
    private function getTranslatedField(array $object, string $attribute, string $lang, array &$included) : ?string
178
    {
179
        if (empty($object['id'])) {
180
            return null;
181
        }
182
183
        $id = $object['id'];
184
185
        if ($this->translation === null) {
186
            $translations = Hash::combine($included, '{n}.id', '{n}.attributes', '{n}.type');
187
            $this->translation = Hash::combine(
188
                $translations,
189
                'translations.{n}.lang',
190
                'translations.{n}.translated_fields',
191
                'translations.{n}.object_id'
192
            );
193
        }
194
195
        $path = sprintf('%s.%s.%s', $id, $lang, $attribute);
196
197
        return Hash::get($this->translation, $path);
198
    }
199
}
200