Completed
Push — master ( 1d5331...4dc992 )
by Peter
05:05
created

I18NAbleTrait::setLang()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 26
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 5.0592

Importance

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

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
273
		}
274 67
	}
275
276
}
277