CompositionIterator   A
last analyzed

Complexity

Total Complexity 33

Size/Duplication

Total Lines 210
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 87.34%

Importance

Changes 0
Metric Value
wmc 33
lcom 1
cbo 2
dl 0
loc 210
ccs 69
cts 79
cp 0.8734
rs 9.76
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A direct() 0 5 1
A ofType() 0 11 2
A getCurrentField() 0 8 2
A init() 0 8 2
A doInclude() 0 18 4
A recurse() 0 4 1
A current() 0 5 1
A next() 0 5 1
A key() 0 5 1
A valid() 0 5 1
A rewind() 0 5 1
A count() 0 5 1
B iterate() 0 47 11
A skip() 0 14 3
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: peter
5
 * Date: 07.03.18
6
 * Time: 17:46
7
 */
8
9
namespace Maslosoft\Mangan\Helpers;
10
11
12
use Countable;
13
use Iterator;
14
use Maslosoft\Addendum\Interfaces\AnnotatedInterface;
15
use Maslosoft\Addendum\Utilities\ClassChecker;
16
use Maslosoft\Mangan\Mangan;
17
use Maslosoft\Mangan\Meta\ManganMeta;
18
19
/**
20
 * Iterate over composition of documents.
21
 *
22
 * NOTE: This will include only AnnotatedInterface instances.
23
 *
24
 * @package Maslosoft\Mangan\Helpers
25
 */
26
class CompositionIterator implements Iterator, Countable
27
{
28
	private $model = null;
29
30
	private $direct = false;
31
32
	private $types = [];
33
34
	/**
35
	 * Models holder
36
	 * @var null|AnnotatedInterface[]
37
	 */
38
	private $models = null;
39
40
	private $pointer = 0;
41
42
	private $fieldNames = [];
43
44 86
	public function __construct(AnnotatedInterface $model)
45
	{
46 86
		$this->model = $model;
47 86
	}
48
49
	/**
50
	 * Limit results to only direct descendants.
51
	 * @return $this
52
	 */
53 6
	public function direct()
54
	{
55 6
		$this->direct = true;
56 6
		return $this;
57
	}
58
59
	/**
60
	 * Limit results to only to the type provided.
61
	 *
62
	 * The `$type` should be class or interface name
63
	 * or object instance.
64
	 *
65
	 * Repeated calls will add types uniquely.
66
	 *
67
	 * @param $type string|object
68
	 * @param $include boolean Whether to include this type or skip
69
	 * @return $this
70
	 */
71 84
	public function ofType($type, $include = true)
72
	{
73 84
		if (is_object($type))
74
		{
75
			$type = get_class($type);
76
		}
77 84
		assert(is_string($type));
78 84
		assert(ClassChecker::exists($type));
79 84
		$this->types[$type] = $include;
80 84
		return $this;
81
	}
82
83
	/**
84
	 * Get currently iterated over field name,
85
	 * which have models
86
	 * @return string
87
	 */
88
	public function getCurrentField()
89
	{
90
		if(isset($this->fieldNames[$this->pointer]))
91
		{
92
			return $this->fieldNames[$this->pointer];
93
		}
94
		return '';
95
	}
96
97 86
	private function init()
98
	{
99 86
		if (null === $this->models)
100
		{
101 86
			$this->models = [];
102 86
			$this->iterate($this->model);
103
		}
104 86
	}
105
106 86
	private function iterate($model)
107
	{
108 86
		foreach (ManganMeta::create($model)->fields() as $name => $meta)
109
		{
110
			// Not one of:
111
			// * Embedded(Array)
112
			// * DbRef(Array)
113
			// * Related(Array)
114 86
			if(!$meta->owned)
115
			{
116 86
				continue;
117
			}
118 25
			if (is_array($model->$name))
119
			{
120 15
				foreach ($model->$name as $child)
121
				{
122 15
					if ($this->skip($child))
123
					{
124
						continue;
125
					}
126 15
					if ($this->doInclude($child))
127
					{
128 9
						$this->fieldNames[] = $name;
129 9
						$this->models[] = $child;
130
					}
131 15
					if($this->recurse())
132
					{
133 12
						$this->iterate($child);
134
					}
135
				}
136 15
				continue;
137
			}
138 20
			if ($this->skip($model->$name))
139
			{
140 6
				continue;
141
			}
142 17
			if ($this->doInclude($model->$name))
143
			{
144 13
				$this->fieldNames[] = $name;
145 13
				$this->models[] = $model->$name;
146
			}
147 17
			if($this->recurse())
148
			{
149 17
				$this->iterate($model->$name);
150
			}
151
		}
152 86
	}
153
154 25
	private function skip($model)
155
	{
156
		// Non-object
157 25
		if (!is_object($model))
158
		{
159 6
			return true;
160
		}
161
		// Skip if not annotated
162 23
		if (!$model instanceof AnnotatedInterface)
163
		{
164
			return true;
165
		}
166 23
		return false;
167
	}
168
169
	/**
170
	 * Whether to include `$model` in result
171
	 * @param $model
172
	 * @return bool
173
	 */
174 23
	private function doInclude($model)
175
	{
176
		// Don't skip if no types
177 23
		if (empty($this->types))
178
		{
179 6
			return true;
180
		}
181
182
		// Include if is_a type
183 21
		foreach ($this->types as $type => $include)
184
		{
185 21
			if (is_a($model, $type))
186
			{
187 15
				return $include;
188
			}
189
		}
190 7
		return false;
191
	}
192
193 23
	private function recurse()
194
	{
195 23
		return !$this->direct;
196
	}
197
198 17
	public function current()
199
	{
200 17
		$this->init();
201 17
		return $this->models[$this->pointer];
202
	}
203
204 17
	public function next()
205
	{
206 17
		$this->init();
207 17
		++$this->pointer;
208 17
	}
209
210
	public function key()
211
	{
212
		$this->init();
213
		return $this->pointer;
214
	}
215
216 86
	public function valid()
217
	{
218 86
		$this->init();
219 86
		return isset($this->models[$this->pointer]);
220
	}
221
222 86
	public function rewind()
223
	{
224 86
		$this->init();
225 86
		$this->pointer = 0;
226 86
	}
227
228 5
	public function count()
229
	{
230 5
		$this->init();
231 5
		return count($this->models);
232
	}
233
234
235
}