Completed
Push — master ( c8f19a...ddbfd2 )
by Peter
72:09 queued 69:12
created

Validator::validate()   C

Complexity

Conditions 13
Paths 46

Size

Total Lines 77
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 52
CRAP Score 13.001

Importance

Changes 2
Bugs 0 Features 0
Metric Value
dl 0
loc 77
ccs 52
cts 53
cp 0.9811
rs 5.2202
c 2
b 0
f 0
cc 13
eloc 37
nc 46
nop 1
crap 13.001

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