Completed
Push — master ( cff413...7f9d4c )
by Peter
07:00
created

Transformer::toModel()   F

Complexity

Conditions 12
Paths 336

Size

Total Lines 87

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 39
CRAP Score 12.0022

Importance

Changes 0
Metric Value
dl 0
loc 87
ccs 39
cts 40
cp 0.975
rs 3.8303
c 0
b 0
f 0
cc 12
nc 336
nop 5
crap 12.0022

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
		$meta = ManganMeta::create($model);
132 106
		$calledClass = get_called_class();
133 106
		$decorator = new Decorator($model, $calledClass, $meta);
134 106
		$md = new ModelDecorator($model, $calledClass, $meta);
135 106
		$sanitizer = new Sanitizer($model, $calledClass, $meta);
136 106
		$filter = new Filter($model, $calledClass, $meta);
137
138
		// Ensure that primary keys are processed first,
139
		// as in some cases those could be processed *after* related
140
		// document(s), which results in wrong _id (or pk) being passed.
141 106
		$fieldsMeta = (array) $meta->fields();
142 106
		$pks = (array)PkManager::getPkKeys($model);
143 106
		foreach($pks as $key)
144
		{
145 106
			if(!array_key_exists($key, $fieldsMeta))
146
			{
147 17
				continue;
148
			}
149 105
			$pkMeta = $fieldsMeta[$key];
150 105
			unset($fieldsMeta[$key]);
151 105
			$fieldsMeta = array_merge([$key => $pkMeta], $fieldsMeta);
152
		}
153
154 106
		foreach ($fieldsMeta as $name => $fieldMeta)
155
		{
156
			/* @var $fieldMeta DocumentPropertyMeta */
157 106
			if (array_key_exists($name, $data))
158
			{
159
				// Value is available in passed data
160 106
				$value = $data[$name];
161
			}
162 24
			elseif (!empty($instance))
163
			{
164
				// Take value from existing instance
165
				// NOTE: We could `continue` here but value should be sanitized anyway
166 5
				$value = $model->$name;
167
			}
168
			else
169
			{
170
				// As a last resort set to default
171 20
				$value = $fieldMeta->default;
172
			}
173 106
			if (!$filter->toModel($model, $fieldMeta))
174
			{
175 29
				continue;
176
			}
177 106
			$decorator->read($name, $value);
178 106
			$model->$name = $sanitizer->read($name, $model->$name);
179
		}
180 106
		$md->read($data);
181
182 106
		return FinalizingManager::toModel(static::class, $model);
183
	}
184
185
	/**
186
	 * Get metadata for model
187
	 * @deprecated Use ManganMeta::create($model) instead
188
	 * @param AnnotatedInterface $model
189
	 * @return ManganMeta
190
	 */
191
	protected static function getMeta(AnnotatedInterface $model)
192
	{
193
		return ManganMeta::create($model);
194
	}
195
196
	/**
197
	 * Ensure that `$class` exists, will
198
	 * try to use class not found resolver
199
	 * to find replacements if available.
200
	 *
201
	 * @param $class
202
	 * @throws ManganException
203
	 */
204 102
	protected static function ensureClass(&$class)
205
	{
206 102
		if (!ClassChecker::exists($class))
207
		{
208
			$event = new ClassNotFound;
209
			$event->notFound = $class;
210
			if (Event::hasHandler(AnnotatedInterface::class, NotFoundResolver::EventClassNotFound) && Event::handled(AnnotatedInterface::class, NotFoundResolver::EventClassNotFound, $event))
211
			{
212
				$class = $event->replacement;
213
			}
214
			else
215
			{
216
				$params = [
217
					$class,
218
					NotFoundResolver::class,
219
					NotFoundResolver::EventClassNotFound,
220
					AnnotatedInterface::class
221
				];
222
				$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);
223
				throw new ManganException($msg);
224
			}
225
		}
226 102
	}
227
}
228