Completed
Push — master ( 918599...2eadc8 )
by Peter
05:33
created

Validator::validateEntity()   C

Complexity

Conditions 12
Paths 74

Size

Total Lines 61
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 29
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 61
c 1
b 0
f 0
ccs 29
cts 29
cp 1
rs 6.2855
cc 12
eloc 30
nc 74
nop 2
crap 12

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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;
15
16
use InvalidArgumentException;
17
use Maslosoft\Addendum\Interfaces\AnnotatedInterface;
18
use Maslosoft\Mangan\Helpers\Validator\Factory;
19
use Maslosoft\Mangan\Interfaces\ValidatableInterface;
20
use Maslosoft\Mangan\Meta\ManganMeta;
21
22
/**
23
 * Validator
24
 *
25
 * @author Piotr Maselkowski <pmaselkowski at gmail.com>
26
 */
27
class Validator implements ValidatableInterface
28
{
29
30
	/**
31
	 * Model instance
32
	 * @var AnnotatedInterface
33
	 */
34
	private $model = null;
35
36
	/**
37
	 * Metadata for model
38
	 * @var ManganMeta
39
	 */
40
	private $meta = null;
41
42
	/**
43
	 * Array of error messages.
44
	 * Keys are field names, secondary keys are numeric
45
	 * @var string[][]
46
	 */
47
	private $errors = [];
48
49 139
	public function __construct(AnnotatedInterface $model)
50
	{
51 139
		$this->model = $model;
52 139
		$this->meta = ManganMeta::create($this->model);
53
54
		// Ensure that errors array is initialized - even if does not have validators
55 139
		foreach (array_keys($this->meta->fields()) as $name)
56
		{
57 139
			$this->errors[$name] = [];
58
		}
59 139
	}
60
61
	/**
62
	 * Validate model, optionally only selected fields
63
	 * @param string[] $fields
64
	 * @return boolean
65
	 */
66 103
	public function validate($fields = [])
67
	{
68 103
		$valid = [];
69 103
		if (empty($fields))
70
		{
71 103
			$fields = array_keys($this->meta->fields());
72
		}
73
74
		// Model validators
75 103
		$typeValidators = $this->meta->type()->validators;
76 103
		if (!empty($typeValidators))
77
		{
78 1
			$typeName = $this->meta->type()->name;
79
			// Reset errors
80 1
			$this->errors[$typeName] = [];
81 1
			$valid[] = (int)$this->validateEntity($typeName, $typeValidators);
82
		}
83
84 103
		foreach ($fields as $name)
85
		{
86 103
			$fieldMeta = $this->meta->field($name);
87
88
			// Reset errors
89 103
			$this->errors[$name] = [];
90
91
			// Check if meta for field exists
92 103
			if (empty($fieldMeta))
93
			{
94
				throw new InvalidArgumentException(sprintf("Unknown field `%s` in model `%s`", $name, get_class($this->model)));
95
			}
96
97
			// Validate field if has any validator
98 103
			if (!empty($fieldMeta->validators))
99
			{
100 21
				$valid[] = (int)$this->validateEntity($name, $fieldMeta->validators);
101
			}
102
103
			// Validate sub documents
104
			// Skip fields that are not updatable
105 103
			if ($fieldMeta->owned && $fieldMeta->updatable)
106
			{
107 26
				if (is_array($this->model->$name))
108
				{
109
					// Handle arrays of documents
110 11
					foreach ($this->model->$name as $fieldIndex => $model)
111
					{
112 11
						$validator = new Validator($model);
113 11
						$isValid = $validator->validate();
114 11
						$valid[] = (int)$isValid;
115 11
						if (!$isValid)
116
						{
117
							$errors = [
118
								$name => [
119 3
									$fieldIndex => $validator->getErrors()
120
								]
121
							];
122 11
							$this->setErrors($errors);
123
						}
124
					}
125 19
				} elseif (!empty($this->model->$name))
126
				{
127
					// Handle single documents
128 18
					$validator = new Validator($this->model->$name);
129 18
					$isValid = $validator->validate();
130 18
					$valid[] = (int)$isValid;
131 18
					if (!$isValid)
132
					{
133
						$errors = [
134 3
							$name => $validator->getErrors()
135
						];
136 103
						$this->setErrors($errors);
137
					}
138
				}
139
			}
140
		}
141
142 103
		$areAllValid = count($valid) === array_sum($valid);
143
144
		// For easier debug
145 103
		if($areAllValid)
146
		{
147 100
			return true;
148
		}
149 22
		return false;
150
	}
151
152 22
	private function validateEntity($name, $validators)
153
	{
154 22
		$valid = [];
155 22
		foreach ($validators as $validatorMeta)
156
		{
157
			// Filter out validators based on scenarios
158 22
			if (!empty($validatorMeta->on))
159
			{
160 2
				$on = (array)$validatorMeta->on;
161 2
				$enabled = false;
162 2
				foreach ($on as $scenario)
163
				{
164 2
					if ($scenario === ScenarioManager::getScenario($this->model))
165
					{
166 2
						$enabled = true;
167 2
						break;
168
					}
169
				}
170 2
				if (!$enabled)
171
				{
172 2
					continue;
173
				}
174
			}
175 22
			if (!empty($validatorMeta->except))
176
			{
177 1
				$except = (array)$validatorMeta->except;
178 1
				$enabled = true;
179 1
				foreach ($except as $scenario)
180
				{
181 1
					if ($scenario === ScenarioManager::getScenario($this->model))
182
					{
183 1
						$enabled = false;
184 1
						break;
185
					}
186
				}
187 1
				if (!$enabled)
188
				{
189 1
					continue;
190
				}
191
			}
192
193
194
			// Create validator and validate
195 22
			$validator = Factory::create($this->model, $validatorMeta, $name);
196 22
			if ($validator->isValid($this->model, $name))
197
			{
198 18
				$valid[] = true;
199
			} else
200
			{
201 22
				$valid[] = false;
202 22
				$this->errors[$name] = array_merge($this->errors[$name], $validator->getErrors());
203
204
				// Set errors to model instance if it implements ValidatableInterface
205 22
				if ($this->model instanceof ValidatableInterface)
206
				{
207 22
					$this->model->setErrors($this->errors);
208
				}
209
			}
210
		}
211 22
		return count($valid) === array_sum($valid);
212
	}
213
214 10
	public function getErrors()
215
	{
216 10
		return $this->errors;
217
	}
218
219 12
	public function setErrors($errors)
220
	{
221 12
		foreach ($errors as $field => $errors)
222
		{
223 12
			$this->errors[$field] = $errors;
224
		}
225 12
	}
226
227
}
228