Completed
Push — master ( e9b8ec...943b9d )
by Peter
55:00
created

I18NAbleTrait   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 224
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 90%

Importance

Changes 0
Metric Value
wmc 27
lcom 1
cbo 5
dl 0
loc 224
ccs 45
cts 50
cp 0.9
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A getLang() 0 4 2
A getLanguages() 0 4 1
A setLanguages() 0 11 2
A setRawI18N() 0 4 1
A getRawI18N() 0 13 2
A setLang() 0 21 4
A getDefaultLanguage() 0 4 2
A setDefaultLanguage() 0 4 1
C _changeAttributesLang() 0 46 12
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 15
	public function getLang()
44
	{
45 15
		return $this->_lang? : $this->getDefaultLanguage();
46
	}
47
48
	/**
49
	 * Get available languages
50
	 * @return string[]
51
	 * @Ignored
52
	 */
53 66
	public function getLanguages()
54
	{
55 66
		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 15
	public function getRawI18N()
80
	{
81 15
		$meta = ManganMeta::create($this);
82 15
		$fields = $meta->properties('i18n');
83
84
		// Set current model value for current language for each property
85 15
		foreach ($fields as $name => $i18n)
86
		{
87
			// This is to keep rawi18n value in sync with current model value.
88 15
			$this->_rawI18N[$name][$this->getLang()] = $this->$name;
89
		}
90 15
		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
	 * @param string $code
115
	 * @Ignored
116
	 */
117 67
	public function setLang($code)
118
	{
119 67
		if ($this->_lang === $code)
120
		{
121 7
			return false;
122
		}
123 66
		if (!in_array($code, $this->getLanguages()))
124
		{
125 65
			$this->_languages[] = $code;
126
		}
127 66
		$event = new ModelEvent($this);
128 66
		$event->data = $code;
129 66
		if (!Event::valid($this, InternationalInterface::EventBeforeLangChange, $event))
130
		{
131
			return false;
132
		}
133 66
		$this->_changeAttributesLang($this->_lang, $code);
134 66
		$this->_lang = $code;
135 66
		Event::trigger($this, InternationalInterface::EventAfterLangChange, $event);
136 66
		return true;
137
	}
138
139
	/**
140
	 * Set available languages. This method accepts one parameter,
141
	 * array contaning language codes prefably in short ISO form.
142
	 * Example valid array and method calls:
143
	 * ```php
144
	 * $languages = ['en', 'pl', 'ru'];
145
	 * $model->setLanguages($languages);
146
	 * $model2->setLanguages(['en']);
147
	 * ```
148
	 * @param string[] $languages
149
	 * @Ignored
150
	 */
151 7
	public function setLanguages($languages)
152
	{
153 7
		$event = new ModelEvent($this);
154 7
		$event->data = $languages;
155 7
		if (!Event::valid($this, InternationalInterface::EventBeforeLanguagesSet, $event))
156
		{
157
			return;
158
		}
159 7
		$this->_languages = $languages;
160 7
		Event::trigger($this, InternationalInterface::EventAfterLanguagesSet, $event);
161 7
	}
162
163
	/**
164
	 * Set i18n values in all languages.
165
	 * This method must keep `$values` for further use, by method `getRawI18N`.
166
	 * @param mixed[] $values
167
	 * @Ignored
168
	 */
169 8
	public function setRawI18N($values)
170
	{
171 8
		$this->_rawI18N = $values;
172 8
	}
173
174
	/**
175
	 * Get default language used for I18N operations.
176
	 *
177
	 * If not previously set, will fall back to `en`.
178
	 *
179
	 * @return string
180
	 * @Ignored
181
	 */
182 2
	public function getDefaultLanguage()
183
	{
184 2
		return $this->_defaultLanguage? : 'en';
185
	}
186
187
	/**
188
	 * Set default language used for I18N operations. This language
189
	 * will be used if the `setLang` method was not called.
190
	 *
191
	 * The value should be language code, for example `en`
192
	 *
193
	 * @param string $language
194
	 * @Ignored
195
	 */
196
	public function setDefaultLanguage($language)
197
	{
198
		$this->_defaultLanguage = $language;
199
	}
200
201
	/**
202
	 * Change i18n attributes values to appropriate language
203
	 * @param string $fromCode
204
	 * @param string $toCode
205
	 */
206 66
	private function _changeAttributesLang($fromCode, $toCode)
207
	{
208 66
		$meta = ManganMeta::create($this);
209 66
		$fields = $meta->properties('i18n');
210 66
		$defaultLang = $this->getDefaultLanguage();
211
		foreach ($fields as $name => $i18n)
212 9
		{
213 9
			$current = $this->$name;
214
			if (isset($this->_rawI18N[$name]) && array_key_exists($toCode, $this->_rawI18N[$name]))
215 5
			{
216
				$new = $this->_rawI18N[$name][$toCode];
217
			}
218
			else
219 9
			{
220
				$i18n = $meta->field($name)->i18n;
221 9
222 9
				if($i18n->allowDefault && isset($this->_rawI18N[$name]) && array_key_exists($defaultLang, $this->_rawI18N[$name]))
223
				{
224 66
					$new = $this->_rawI18N[$name][$defaultLang];
225
				}
226
				elseif($i18n->allowAny && !empty($this->_rawI18N[$name]))
227
				{
228
					$wasFound = false;
229
					foreach($this->getLanguages() as $code)
230
					{
231
						if(!empty($this->_rawI18N[$name][$code]))
232
						{
233
							$new = $this->_rawI18N[$name][$code];
234
							$wasFound = true;
235
							break;
236
						}
237
					}
238
					if(!$wasFound)
239
					{
240
						$new = $meta->$name->default;
241
					}
242
				}
243
				else
244
				{
245
					$new = $meta->$name->default;
246
				}
247
			}
248
			$this->_rawI18N[$name][$fromCode] = $current;
249
			$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...
250
		}
251
	}
252
253
}
254