Completed
Push — master ( 874ccb...435ecb )
by Peter
07:24
created

Transformer::toModel()   F

Complexity

Conditions 12
Paths 336

Size

Total Lines 88

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 40
CRAP Score 12.002

Importance

Changes 0
Metric Value
dl 0
loc 88
ccs 40
cts 41
cp 0.9756
rs 3.8084
c 0
b 0
f 0
cc 12
nc 336
nop 5
crap 12.002

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\Transformers;
15
16
use Maslosoft\Addendum\Interfaces\AnnotatedInterface;
17
use Maslosoft\Addendum\Utilities\ClassChecker;
18
use Maslosoft\Mangan\Events\ClassNotFound;
19
use Maslosoft\Mangan\Events\Event;
20
use Maslosoft\Mangan\Events\UnknownDocumentType;
21
use Maslosoft\Mangan\Exceptions\ManganException;
22
use Maslosoft\Mangan\Exceptions\TransformatorException;
23
use Maslosoft\Mangan\Helpers\Decorator\Decorator;
24
use Maslosoft\Mangan\Helpers\Decorator\ModelDecorator;
25
use Maslosoft\Mangan\Helpers\Finalizer\FinalizingManager;
26
use Maslosoft\Mangan\Helpers\NotFoundResolver;
27
use Maslosoft\Mangan\Helpers\PkManager;
28
use Maslosoft\Mangan\Helpers\PropertyFilter\Filter;
29
use Maslosoft\Mangan\Helpers\Sanitizer\Sanitizer;
30
use Maslosoft\Mangan\Helpers\UnknownDocumentTypePanicker;
31
use Maslosoft\Mangan\Meta\DocumentPropertyMeta;
32
use Maslosoft\Mangan\Meta\ManganMeta;
33
34
/**
35
 * Transformer
36
 *
37
 * @author Piotr Maselkowski <pmaselkowski at gmail.com>
38
 */
39
abstract class Transformer
40
{
41
42
	/**
43
	 * Returns the given object as an associative array
44
	 * @param AnnotatedInterface|object $model
45
	 * @param string[] $fields Fields to transform
46
	 * @return array an associative array of the contents of this object
47
	 */
48 152
	public static function fromModel(AnnotatedInterface $model, $fields = [])
49
	{
50 152
		$meta = ManganMeta::create($model);
51 152
		$calledClass = get_called_class();
52 152
		$decorator = new Decorator($model, $calledClass, $meta);
53 152
		$md = new ModelDecorator($model, $calledClass, $meta);
54 152
		$sanitizer = new Sanitizer($model, $calledClass, $meta);
55 152
		$filter = new Filter($model, $calledClass, $meta);
56 152
		$arr = [];
57 152
		foreach ($meta->fields() as $name => $fieldMeta)
58
		{
59 152
			if (!empty($fields) && !in_array($name, $fields))
60
			{
61 115
				continue;
62
			}
63 152
			if (!$filter->fromModel($model, $fieldMeta))
64
			{
65 46
				continue;
66
			}
67
68
			// NOTE: Sanitizers must be ran for all
69
			// fields, as types *might* change between
70
			// transformations.
71
72
			// Set model value for writing, this might
73
			// cause data type to change. This is
74
			// required for decorators.
75 152
			$model->$name = $sanitizer->write($name, $model->$name);
76 152
			$decorator->write($name, $arr);
77
78
			// Sanitize value again to ensure that model
79
			// has value of proper type, defined for
80
			// transformer type.
81 152
			$model->$name = $sanitizer->read($name, $model->$name);
82
		}
83 152
		$md->write($arr);
84 152
		return FinalizingManager::fromModel($arr, static::class, $model);
85
	}
86
87
	/**
88
	 * Create document from array
89
	 *
90
	 * @param mixed[]            $data
91
	 * @param string|object      $className
92
	 * @param AnnotatedInterface $instance
93
	 * @return AnnotatedInterface
94
	 * @throws TransformatorException
95
	 * @throws ManganException
96
	 */
97 106
	public static function toModel($data, $className = null, AnnotatedInterface $instance = null, AnnotatedInterface $parent = null, $parentField = '')
98
	{
99 106
		$data = (array) $data;
100 106
		if (is_object($className))
101
		{
102 84
			$className = get_class($className);
103
		}
104 106
		if (!$className)
105
		{
106 44
			if (array_key_exists('_class', $data))
107
			{
108 42
				$className = $data['_class'];
109
			}
110
			else
111
			{
112 2
				if (null !== $instance)
113
				{
114 2
					$className = get_class($instance);
115
				}
116
				else
117
				{
118
					$className = UnknownDocumentTypePanicker::tryHandle($data, $parent, $parentField);
119
				}
120
			}
121
		}
122 106
		if ($instance)
123
		{
124 8
			$model = $instance;
125
		}
126
		else
127
		{
128 102
			self::ensureClass($className);
129 102
			$model = new $className;
130
		}
131 106
		$data['_class'] = get_class($model);
132 106
		$meta = ManganMeta::create($model);
133 106
		$calledClass = get_called_class();
134 106
		$decorator = new Decorator($model, $calledClass, $meta);
135 106
		$md = new ModelDecorator($model, $calledClass, $meta);
136 106
		$sanitizer = new Sanitizer($model, $calledClass, $meta);
137 106
		$filter = new Filter($model, $calledClass, $meta);
138
139
		// Ensure that primary keys are processed first,
140
		// as in some cases those could be processed *after* related
141
		// document(s), which results in wrong _id (or pk) being passed.
142 106
		$fieldsMeta = (array) $meta->fields();
143 106
		$pks = (array)PkManager::getPkKeys($model);
144 106
		foreach($pks as $key)
145
		{
146 106
			if(!array_key_exists($key, $fieldsMeta))
147
			{
148 17
				continue;
149
			}
150 105
			$pkMeta = $fieldsMeta[$key];
151 105
			unset($fieldsMeta[$key]);
152 105
			$fieldsMeta = array_merge([$key => $pkMeta], $fieldsMeta);
153
		}
154
155 106
		foreach ($fieldsMeta as $name => $fieldMeta)
156
		{
157
			/* @var $fieldMeta DocumentPropertyMeta */
158 106
			if (array_key_exists($name, $data))
159
			{
160
				// Value is available in passed data
161 106
				$value = $data[$name];
162
			}
163 22
			elseif (!empty($instance))
164
			{
165
				// Take value from existing instance
166
				// NOTE: We could `continue` here but value should be sanitized anyway
167 5
				$value = $model->$name;
168
			}
169
			else
170
			{
171
				// As a last resort set to default
172 18
				$value = $fieldMeta->default;
173
			}
174 106
			if (!$filter->toModel($model, $fieldMeta))
175
			{
176 29
				continue;
177
			}
178 106
			$decorator->read($name, $value);
179 106
			$model->$name = $sanitizer->read($name, $model->$name);
180
		}
181 106
		$md->read($data);
182
183 106
		return FinalizingManager::toModel(static::class, $model);
184
	}
185
186
	/**
187
	 * Get metadata for model
188
	 * @deprecated Use ManganMeta::create($model) instead
189
	 * @param AnnotatedInterface $model
190
	 * @return ManganMeta
191
	 */
192
	protected static function getMeta(AnnotatedInterface $model)
193
	{
194
		return ManganMeta::create($model);
195
	}
196
197
	/**
198
	 * Ensure that `$class` exists, will
199
	 * try to use class not found resolver
200
	 * to find replacements if available.
201
	 *
202
	 * @param $class
203
	 * @throws ManganException
204
	 */
205 102
	protected static function ensureClass(&$class)
206
	{
207 102
		if (!ClassChecker::exists($class))
208
		{
209
			$event = new ClassNotFound;
210
			$event->notFound = $class;
211
			if (Event::hasHandler(AnnotatedInterface::class, NotFoundResolver::EventClassNotFound) && Event::handled(AnnotatedInterface::class, NotFoundResolver::EventClassNotFound, $event))
212
			{
213
				$class = $event->replacement;
214
			}
215
			else
216
			{
217
				$params = [
218
					$class,
219
					NotFoundResolver::class,
220
					NotFoundResolver::EventClassNotFound,
221
					AnnotatedInterface::class
222
				];
223
				$msg = vsprintf("Model class `%s` not found. Attach event handler for `%s::%s` event on `%s` if You changed name to handle this case.", $params);
224
				throw new ManganException($msg);
225
			}
226
		}
227 102
	}
228
}
229