BaseModel   A
last analyzed

Complexity

Total Complexity 13

Size/Duplication

Total Lines 101
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 36
c 1
b 0
f 0
dl 0
loc 101
rs 10
wmc 13

3 Methods

Rating   Name   Duplication   Size   Complexity  
A validate() 0 12 3
A camelize() 0 6 1
B __construct() 0 39 9
1
<?php
2
3
namespace alekciy\ofd;
4
5
use Exception;
6
use InvalidArgumentException;
7
use Valitron\Validator;
8
9
/**
10
 * Базовый класс для моделей ответов от API провайдера.
11
 * Объект класса при создании проходит проверку, поэтому если создание прошло без генерации исключения, значит объект валиден.
12
 */
13
abstract class BaseModel
14
{
15
	/**
16
	 * @param array $init Массив значений свойств инициализации.
17
	 * @throws Exception
18
	 */
19
	public function __construct(array $init)
20
	{
21
		$propertyMap = $this->getPropertyInitMap();
22
		foreach ($init as $propertyName => $propertyValue) {
23
			// Если задано преобразование имени свойства (или трансформация)
24
			if (isset($propertyMap[$propertyName])) {
25
				$propertyMapValue = $propertyMap[$propertyName];
26
				if (is_array($propertyMapValue)) {
27
					// Задаем имя свойства модели
28
					if (isset($propertyMapValue[0])) {
29
						$propertyName = $propertyMapValue[0];
30
					}
31
					// Преобразуем значение вызвав заданный метод модели
32
					if (isset($propertyMapValue['conv'])) {
33
						if (!method_exists($this, $propertyMapValue['conv'])) {
34
							throw new Exception(sprintf('В объекте %s отсутствует метод %s(), задайте корректное имя в getPropertyInitMap(): [%s => [conv => МЕТОД]]',
35
								get_class($this),
36
								$propertyMapValue['conv'],
37
								$propertyName
38
							));
39
						}
40
						$propertyValue = $this->{$propertyMapValue['conv']}($propertyValue);
41
					}
42
				} else {
43
					$propertyName = $propertyMap[$propertyName];
44
				}
45
			}
46
47
			if (property_exists($this, $propertyName)) {
48
				$this->$propertyName = $propertyValue;
49
			} else {
50
				$propertyName = self::camelize($propertyName);
51
				if (property_exists($this, $propertyName)) {
52
					$this->$propertyName = $propertyValue;
53
				}
54
			}
55
		}
56
		// Проверяем корректность свойств модели
57
		$this->validate();
58
	}
59
60
	/**
61
	 * В потомках метод должен возращать карту преобразований имен при инициализации (ключ) и свойств объекта (значение).
62
	 * Если значение это массив, то при наличие ключа conv к инициализационному значению будет применена функция заданная
63
	 * значением этого ключа. Например: ['totalSum' => ['sum', 'conv'=> 'RubToKop']], значение ключа totalSum из массива
64
	 * инициализации будет через вызов метода RubToKop($value), конвертация рублей в копейки, сохранено в свойство sum
65
	 * модели.
66
	 *
67
	 * @return array
68
	 */
69
	abstract protected function getPropertyInitMap(): array;
70
71
	/**
72
	 * Конвертировать строку $str (представленную в utf8 кодировке) в camelCase.
73
	 *
74
	 * @param $str
75
	 * @return string
76
	 */
77
	static public function camelize($str): string
78
	{
79
		$str = mb_convert_case($str, MB_CASE_TITLE, 'utf8');
80
		$str = preg_replace('~[\-_\s]~ui', '', $str);
81
		$firstLetter = mb_substr($str, 0, 1, 'utf8');
82
		return mb_convert_case($firstLetter, MB_CASE_LOWER, 'utf8') . mb_substr($str, 1, mb_strlen($str)-1, 'utf8');
83
	}
84
85
	/**
86
	 * Правила валидации в виде массива: ключ - имя правила, значение - массив с именами свойств для которых правило будет
87
	 * проверяться. Например, свойство method является обязательным для заполнения: ['method', ['required']]
88
	 *
89
	 * @see https://github.com/vlucas/valitron#built-in-validation-rules
90
	 * @return array
91
	 */
92
	abstract protected function getRuleList(): array;
93
94
	/**
95
	 * Выполнить проверку корректности свойств модели.
96
	 *
97
	 * @param string $lang Язык на котором выводятся сообщения с ошибками валидации.
98
	 * @return void
99
	 * @throws Exception
100
	 * @throws InvalidArgumentException
101
	 */
102
	public function validate($lang = 'ru')
103
	{
104
		Validator::lang($lang);
105
		$validator = new Validator(get_object_vars($this));
106
		// Правила валидации дочерних классов
107
		$validator->mapFieldsRules($this->getRuleList());
108
		if (!$validator->validate()) {
109
			$messageList = [];
110
			foreach ($validator->errors() as  $propertyName => $errorList) {
111
				$messageList[] = $propertyName . ': ' . implode(', ', $errorList);
112
			}
113
			throw new Exception(implode('. ', $messageList));
114
		}
115
	}
116
}