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

CompositionIterator::skip()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3.3332

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 4
cts 6
cp 0.6667
rs 9.7998
c 0
b 0
f 0
cc 3
nc 3
nop 1
crap 3.3332
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 60
	public function __construct(AnnotatedInterface $model)
43
	{
44 60
		$this->model = $model;
45 60
	}
46
47
	/**
48
	 * Limit results to only direct descendants.
49
	 * @return $this
50
	 */
51 6
	public function direct()
52
	{
53 6
		$this->direct = true;
54 6
		return $this;
55
	}
56
57
	/**
58
	 * Limit results to only to the type provided.
59
	 *
60
	 * The `$type` should be class or interface name
61
	 * or object instance.
62
	 *
63
	 * Repeated calls will add types uniquely.
64
	 *
65
	 * @param $type string|object
66
	 * @param $include boolean Whether to include this type or skip
67
	 * @return $this
68
	 */
69 58
	public function ofType($type, $include = true)
70
	{
71 58
		if (is_object($type))
72
		{
73
			$type = get_class($type);
74
		}
75 58
		assert(is_string($type));
76 58
		assert(ClassChecker::exists($type));
77 58
		$this->types[$type] = $include;
78 58
		return $this;
79
	}
80
81 60
	private function init()
82
	{
83 60
		if (null === $this->models)
84
		{
85 60
			$this->models = [];
86 60
			$this->iterate($this->model);
87
		}
88 60
	}
89
90 60
	private function iterate($model)
91
	{
92 60
		foreach (ManganMeta::create($model)->fields() as $name => $meta)
93
		{
94
			// Not one of:
95
			// * Embedded(Array)
96
			// * DbRef(Array)
97
			// * Related(Array)
98 60
			if(!$meta->owned)
99
			{
100 60
				continue;
101
			}
102 18
			if (is_array($model->$name))
103
			{
104 11
				foreach ($model->$name as $child)
105
				{
106 11
					if ($this->skip($child))
107
					{
108
						continue;
109
					}
110 11
					if ($this->doInclude($child))
111
					{
112 7
						$this->models[] = $child;
113
					}
114 11
					if($this->recurse())
115
					{
116 11
						$this->iterate($child);
117
					}
118
				}
119 11
				continue;
120
			}
121 15
			if ($this->skip($model->$name))
122
			{
123
				continue;
124
			}
125 15
			if ($this->doInclude($model->$name))
126
			{
127 12
				$this->models[] = $model->$name;
128
			}
129 15
			if($this->recurse())
130
			{
131 15
				$this->iterate($model->$name);
132
			}
133
		}
134 60
	}
135
136 18
	private function skip($model)
137
	{
138
		// Non-object
139 18
		if (!is_object($model))
140
		{
141
			return true;
142
		}
143
		// Skip if not annotated
144 18
		if (!$model instanceof AnnotatedInterface)
145
		{
146
			return true;
147
		}
148 18
		return false;
149
	}
150
151
	/**
152
	 * Whether to include `$model` in result
153
	 * @param $model
154
	 * @return bool
155
	 */
156 18
	private function doInclude($model)
157
	{
158
		// Don't skip if no types
159 18
		if (empty($this->types))
160
		{
161 6
			return true;
162
		}
163
164
		// Include if is_a type
165 16
		foreach ($this->types as $type => $include)
166
		{
167 16
			if (is_a($model, $type))
168
			{
169 16
				return $include;
170
			}
171
		}
172 5
		return false;
173
	}
174
175 18
	private function recurse()
176
	{
177 18
		return !$this->direct;
178
	}
179
180 14
	public function current()
181
	{
182 14
		$this->init();
183 14
		return $this->models[$this->pointer];
184
	}
185
186 14
	public function next()
187
	{
188 14
		$this->init();
189 14
		++$this->pointer;
190 14
	}
191
192
	public function key()
193
	{
194
		$this->init();
195
		return $this->pointer;
196
	}
197
198 60
	public function valid()
199
	{
200 60
		$this->init();
201 60
		return isset($this->models[$this->pointer]);
202
	}
203
204 60
	public function rewind()
205
	{
206 60
		$this->init();
207 60
		$this->pointer = 0;
208 60
	}
209
210 5
	public function count()
211
	{
212 5
		$this->init();
213 5
		return count($this->models);
214
	}
215
216
217
}