I18NAbleTrait::getLanguages()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
/**
4
 * This software package is licensed under AGPL or Commercial license.
5
 *
6
 * @package maslosoft/mangan
7
 * @licence AGPL or Commercial
8
 * @copyright Copyright (c) Piotr Masełkowski <[email protected]>
9
 * @copyright Copyright (c) Maslosoft
10
 * @copyright Copyright (c) Others as mentioned in code
11
 * @link https://maslosoft.com/mangan/
12
 */
13
14
namespace Maslosoft\Mangan\Traits;
15
16
use Maslosoft\Mangan\Events\Event;
17
use Maslosoft\Mangan\Events\ModelEvent;
18
use Maslosoft\Mangan\Helpers\CompositionIterator;
19
use Maslosoft\Mangan\Interfaces\InternationalInterface;
20
use Maslosoft\Mangan\Meta\ManganMeta;
21
22
/**
23
 * I18N-able trait contains basic implementation of I18N feature. This covers
24
 * methods from `InternationalInterface`.
25
 *
26
 * Use this trait to provide internationalized columns for models.
27
 *
28
 * @see InternationalInterface
29
 * @author Piotr Maselkowski <pmaselkowski at gmail.com>
30
 */
31
trait I18NAbleTrait
32
{
33
34
	private $_lang = 'en';
35
	private $_rawI18N = [];
36
	private $_languages = ['en'];
37
	private $_defaultLanguage = 'en';
38
39
	/**
40
	 * Get current working language code
41
	 * @return string Language code
42
	 * @Ignored
43
	 */
44 80
	public function getLang()
45
	{
46 80
		return $this->_lang? : $this->getDefaultLanguage();
47
	}
48
49
	/**
50
	 * Get available languages
51
	 * @return string[]
52
	 * @Ignored
53
	 */
54 39
	public function getLanguages()
55
	{
56 39
		return $this->_languages;
57
	}
58
59
	/**
60
	 * Get i18n values with all languages.
61
	 * This returns all language values of all class fields. Class field names are
62
	 * keys for arrays of language values, with language codes as a keys.
63
	 *
64
	 * Example returned variable:
65
	 * ```php
66
	 * [
67
	 * 		'name' => [
68
	 * 			'en' => 'January',
69
	 * 			'pl' => 'Styczeń'
70
	 * 		],
71
	 * 		'description' => [
72
	 * 			'en' => 'First mothn of a year'
73
	 * 			'pl' => 'Pierwszy miesiąc roku'
74
	 * 		]
75
	 * ]
76
	 * ```
77
	 * @return mixed[] Associative array of language values
78
	 * @Ignored
79
	 */
80 31
	public function getRawI18N()
81
	{
82 31
		$meta = ManganMeta::create($this);
83 31
		$fields = $meta->properties('i18n');
84
85
		// Set current model value for current language for each property
86 31
		foreach ($fields as $name => $i18n)
87
		{
88
			// This is to keep rawi18n value in sync with current model value.
89 31
			$this->_rawI18N[$name][$this->getLang()] = $this->$name;
90
		}
91 31
		return $this->_rawI18N;
92
	}
93
94
	/**
95
	 * Set language code. This changes current model language.
96
	 * After setting language model attributes will store values in different locale.
97
	 *
98
	 * Language code must be previously set by `setLanguages`.
99
	 * When trying to set undefined language code, method will do nothing.
100
	 * When setting already choosen language code, method will also ignore this call.
101
	 * Example method calls:
102
	 * ```php
103
	 * // Set available languages
104
	 * $model->setLanguages(['en', 'pl']);
105
	 *
106
	 * // Will ignore as en is by default
107
	 * $model->setLang('en');
108
	 *
109
	 * // Will set pl as language
110
	 * $model->setLang('pl');
111
	 *
112
	 * // Will ignore as ru is not available
113
	 * $model->setLang('ru')
114
	 * ```
115
	 *
116
	 * For initial call, when there are no data set yet, `$triggetEvents`
117
	 * can be set to false to improve performance.
118
	 *
119
	 * @param string $code
120
	 * @param boolean $triggerEvents
121
	 * @Ignored
122
	 */
123 97
	public function setLang($code, $triggerEvents = true)
124
	{
125
		// Ensure that calling with empty code will not set language
126
		// to empty value
127 97
		if(empty($code))
128
		{
129 84
			return false;
130
		}
131 26
		if ($this->_lang === $code)
132
		{
133 23
			return false;
134
		}
135 15
		if (!in_array($code, $this->getLanguages()))
136
		{
137 11
			$this->_languages[] = $code;
138
		}
139 15
		if(!$triggerEvents)
140
		{
141 4
			$this->_lang = $code;
142 4
			return true;
143
		}
144
145 11
		$it = new CompositionIterator($this);
146
		// Need a deep scan, as some objects
147
		// might have proxy documents that do not
148
		// implement InternationalInterface::class,
149
		// so those would be omitted. The setLang will
150
		// anyway not trigger if language is same as
151
		// currently set.
152 11
		$it->ofType(InternationalInterface::class);
153 11
		foreach($it as $model)
154
		{
155
			/* @var $model InternationalInterface */
156 2
			$model->setLang($code);
157
		}
158
159 11
		$event = new ModelEvent($this);
160 11
		$event->data = $code;
161 11
		if (!Event::valid($this, InternationalInterface::EventBeforeLangChange, $event))
162
		{
163
			return false;
164
		}
165 11
		$this->_changeAttributesLang($this->_lang, $code);
166 11
		$this->_lang = $code;
167 11
		Event::trigger($this, InternationalInterface::EventAfterLangChange, $event);
168 11
		return true;
169
	}
170
171
	/**
172
	 * Set available languages. This method accepts one parameter,
173
	 * array contaning language codes prefably in short ISO form.
174
	 *
175
	 * Example valid array and method calls:
176
	 *
177
	 * ```php
178
	 * $languages = ['en', 'pl', 'ru'];
179
	 * $model->setLanguages($languages);
180
	 * $model2->setLanguages(['en']);
181
	 * ```
182
	 *
183
	 * For initial call, when there are no data set yet, `$triggetEvents`
184
	 * can be set to false to improve performance.
185
	 *
186
	 * @param string[] $languages
187
	 * @param boolean $triggerEvents
188
	 * @Ignored
189
	 */
190 80
	public function setLanguages($languages, $triggerEvents = true)
191
	{
192 80
		if($triggerEvents)
193
		{
194 80
			$event = new ModelEvent($this);
195 80
			$event->data = $languages;
196 80
			if (!Event::valid($this, InternationalInterface::EventBeforeLanguagesSet, $event))
197
			{
198
				return;
199
			}
200
		}
201 80
		$this->_languages = $languages;
202 80
		if($triggerEvents)
203
		{
204 80
			Event::trigger($this, InternationalInterface::EventAfterLanguagesSet, $event);
205
		}
206 80
	}
207
208
	/**
209
	 * Set i18n values in all languages.
210
	 * This method must keep `$values` for further use, by method `getRawI18N`.
211
	 * @param mixed[] $values
212
	 * @Ignored
213
	 */
214 15
	public function setRawI18N($values)
215
	{
216 15
		$this->_rawI18N = $values;
217 15
	}
218
219
	/**
220
	 * Get default language used for I18N operations.
221
	 *
222
	 * If not previously set, will fall back to `en`.
223
	 *
224
	 * @return string
225
	 * @Ignored
226
	 */
227 11
	public function getDefaultLanguage()
228
	{
229 11
		return $this->_defaultLanguage? : 'en';
230
	}
231
232
	/**
233
	 * Set default language used for I18N operations. This language
234
	 * will be used if the `setLang` method was not called.
235
	 *
236
	 * The value should be language code, for example `en`
237
	 *
238
	 * @param string $language
239
	 * @Ignored
240
	 */
241 1
	public function setDefaultLanguage($language)
242
	{
243 1
		$this->_defaultLanguage = $language;
244 1
	}
245
246
	/**
247
	 * Change i18n attributes values to appropriate language
248
	 * @param string $fromCode
249
	 * @param string $toCode
250
	 */
251 11
	private function _changeAttributesLang($fromCode, $toCode)
252
	{
253 11
		$meta = ManganMeta::create($this);
254 11
		$fields = $meta->properties('i18n');
255 11
		$defaultLang = $this->getDefaultLanguage();
256 11
		foreach ($fields as $name => $i18n)
257
		{
258 11
			$current = $this->$name;
259 11
			if (isset($this->_rawI18N[$name]) && array_key_exists($toCode, $this->_rawI18N[$name]))
260
			{
261 4
				$new = $this->_rawI18N[$name][$toCode];
262
			}
263
			else
264
			{
265 11
				$i18n = $meta->field($name)->i18n;
266
267 11
				if($i18n->allowDefault && isset($this->_rawI18N[$name]) && array_key_exists($defaultLang, $this->_rawI18N[$name]))
268
				{
269 1
					$new = $this->_rawI18N[$name][$defaultLang];
270
				}
271 11
				elseif($i18n->allowAny && !empty($this->_rawI18N[$name]))
272
				{
273
					$wasFound = false;
274
					foreach($this->getLanguages() as $code)
275
					{
276
						if(!empty($this->_rawI18N[$name][$code]))
277
						{
278
							$new = $this->_rawI18N[$name][$code];
279
							$wasFound = true;
280
							break;
281
						}
282
					}
283
					if(!$wasFound)
284
					{
285
						$new = $meta->$name->default;
286
					}
287
				}
288
				else
289
				{
290 11
					$new = $meta->$name->default;
291
				}
292
			}
293 11
			$this->_rawI18N[$name][$fromCode] = $current;
294 11
			$this->$name = $new;
295
		}
296 11
	}
297
298
}
299