Completed
Push — master ( 0bb3f5...c1ad5e )
by Peter
06:13
created

EmbeddedDecorator::write()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 6.0106

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 14
cts 15
cp 0.9333
rs 8.8657
c 0
b 0
f 0
cc 6
nc 5
nop 4
crap 6.0106
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\Decorators;
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\Exceptions\ManganException;
21
use Maslosoft\Mangan\Helpers\DbRefManager;
22
use Maslosoft\Mangan\Helpers\NotFoundResolver;
23
use Maslosoft\Mangan\Helpers\UnknownDocumentTypePanicker;
24
use Maslosoft\Mangan\Interfaces\Decorators\Property\DecoratorInterface;
25
use Maslosoft\Mangan\Interfaces\Transformators\TransformatorInterface;
26
use Maslosoft\Mangan\Meta\DocumentPropertyMeta;
27
use Maslosoft\Mangan\Meta\ManganMeta;
28
29
/**
30
 * EmbeddedDecorator
31
 *
32
 * @author Piotr Maselkowski <pmaselkowski at gmail.com>
33
 */
34
class EmbeddedDecorator implements DecoratorInterface
35
{
36
37 17
	public function read($model, $name, &$dbValue, $transformatorClass = TransformatorInterface::class)
38
	{
39 17
		if (is_object($dbValue) && $dbValue instanceof AnnotatedInterface)
40
		{
41
			$model->$name = $dbValue;
42
			return;
43
		}
44 17
		if($dbValue === null)
45
		{
46 1
			$meta = ManganMeta::create($model)->$name;
47 1
			if($meta->embedded && $meta->embedded->nullable)
48
			{
49 1
				$model->$name = null;
50 1
				return null;
51
			}
52
		}
53 16
		static::ensureClass($model, $name, $dbValue);
54 16
		$instance = null;
55 16
		if ($model->$name instanceof $dbValue['_class'])
56
		{
57 1
			$instance = $model->$name;
58
		}
59 16
		$embedded = $transformatorClass::toModel($dbValue, $instance, $instance);
60
61
		// Field was transformed from DB Ref
62 16
		$embedded = DbRefManager::maybeCreateInstanceFrom($embedded);
63
64 16
		$model->$name = $embedded;
65 16
	}
66
67 25
	public function write($model, $name, &$dbValue, $transformatorClass = TransformatorInterface::class)
68
	{
69 25
		if (null === $model->$name)
70
		{
71 4
			$meta = ManganMeta::create($model)->$name;
72 4
			if($meta->embedded && $meta->embedded->nullable)
73
			{
74 1
				$dbValue[$name] = null;
75 1
				return null;
76
			}
77 3
			$className = static::getClassName($model, $name);
78 3
			if (!is_string($className))
79
			{
80 1
				return null;
81
			}
82
			// This is to prevent infinite loops
83
			// if class name is same as current `$model`, ie not really set.
84
			// @link https://github.com/Maslosoft/Mangan/issues/86
85 2
			if(is_a($model, $className))
86
			{
87
				return null;
88
			}
89 2
			$dbValue[$name] = $transformatorClass::fromModel(new $className);
90 2
			return;
91
		}
92 21
		$dbValue[$name] = $transformatorClass::fromModel($model->$name);
93 21
	}
94
95 21
	public static function ensureClass($model, $name, &$dbValue)
96
	{
97 21
		if (!is_array($dbValue) || !array_key_exists('_class', $dbValue) || empty($dbValue['_class']))
98
		{
99
			$class = static::getClassName($model, $name);
100
		}
101
		else
102
		{
103 21
			$class = $dbValue['_class'];
104
		}
105 21
		if (!ClassChecker::exists($class))
106
		{
107 1
			$event = new ClassNotFound($model);
108 1
			$event->notFound = $class;
109 1
			if (Event::hasHandler($model, NotFoundResolver::EventClassNotFound) && Event::handled($model, NotFoundResolver::EventClassNotFound, $event))
110
			{
111 1
				$class = $event->replacement;
112
			}
113
			else
114
			{
115 1
				throw new ManganException(sprintf("Embedded model class `%s` not found in model `%s` field `%s`", $class, get_class($model), $name));
116
			}
117
		}
118
119
		// Something is very wrong here.
120
		// The `$dbValue` variable must be array for embedded documents
121
		// if it is not, it means that we have wrong data type stored in
122
		// database. There is last resort handling below this condition
123
		// check, however it *might* be risky to use this, as we cannot
124
		// be sure that we can reconstruct proper object from some scalar value.
125 21
		if(!is_array($dbValue) && null !==$dbValue)
126
		{
127
			$data = $dbValue;
128
			$dbValue = [];
129
			$dbValue['data'] = $data;
130
			$class = UnknownDocumentTypePanicker::tryHandle($dbValue, $model, $name);
131
		}
132 21
		$dbValue['_class'] = $class;
133 21
	}
134
135 3
	protected static function getClassName($model, $name)
136
	{
137 3
		$fieldMeta = ManganMeta::create($model)->$name;
138
139
		/* @var $fieldMeta DocumentPropertyMeta */
140 3
		return $fieldMeta->embedded->class;
141
	}
142
143
}
144