Completed
Push — master ( 755174...9d7326 )
by Joschi
10:05
created

Collection   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 236
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 98.33%

Importance

Changes 12
Bugs 0 Features 2
Metric Value
c 12
b 0
f 2
dl 0
loc 236
ccs 59
cts 60
cp 0.9833
rs 10
wmc 21
lcom 1
cbo 7

15 Methods

Rating   Name   Duplication   Size   Complexity  
A add() 0 6 1
A remove() 0 14 3
A loadObject() 0 10 2
A current() 0 4 1
A next() 0 4 1
A key() 0 4 1
A valid() 0 4 1
A rewind() 0 4 1
A offsetExists() 0 4 1
A offsetGet() 0 4 1
A offsetUnset() 0 7 1
A count() 0 4 1
A append() 0 5 1
B __construct() 0 22 4
A offsetSet() 0 11 1
1
<?php
2
3
/**
4
 * apparat-object
5
 *
6
 * @category    Apparat
7
 * @package     Apparat\Object\Domain
8
 * @author      Joschi Kuphal <[email protected]> / @jkphl
9
 * @copyright   Copyright © 2016 Joschi Kuphal <[email protected]> / @jkphl
10
 * @license     http://opensource.org/licenses/MIT The MIT License (MIT)
11
 */
12
13
/***********************************************************************************
14
 *  The MIT License (MIT)
15
 *
16
 *  Copyright © 2016 Joschi Kuphal <[email protected]> / @jkphl
17
 *
18
 *  Permission is hereby granted, free of charge, to any person obtaining a copy of
19
 *  this software and associated documentation files (the "Software"), to deal in
20
 *  the Software without restriction, including without limitation the rights to
21
 *  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
22
 *  the Software, and to permit persons to whom the Software is furnished to do so,
23
 *  subject to the following conditions:
24
 *
25
 *  The above copyright notice and this permission notice shall be included in all
26
 *  copies or substantial portions of the Software.
27
 *
28
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
30
 *  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
31
 *  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
32
 *  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
33
 *  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34
 ***********************************************************************************/
35
36
namespace Apparat\Object\Domain\Model\Object;
37
38
use Apparat\Object\Domain\Model\Uri\RepositoryLocator;
39
use Apparat\Object\Domain\Model\Uri\RepositoryLocatorInterface;
40
41
/**
42
 * Lazy loading object collection
43
 *
44
 * @package Apparat\Object
45
 * @subpackage Apparat\Object\Domain
46
 */
47
class Collection implements CollectionInterface
48
{
49
    /**
50
     * Objects
51
     *
52
     * @var RepositoryLocator[]|ObjectInterface[]
53
     */
54
    protected $objects = array();
55
    /**
56
     * Object IDs
57
     *
58
     * @var array
59
     */
60
    protected $objectIds = array();
61
    /**
62
     * Internal object pointer
63
     *
64
     * @var int
65
     */
66
    protected $pointer = 0;
67
68
    /*******************************************************************************
69
     * PUBLIC METHODS
70
     *******************************************************************************/
71
72
    /**
73
     * Collection constructor
74
     *
75
     * @param array $objects Collection objects
76
     * @throws InvalidArgumentException If an invalid object or locator is provided
77
     */
78 8
    public function __construct(array $objects = [])
79
    {
80 8
        foreach ($objects as $object) {
81
            // If it's an object
82 8
            if ($object instanceof ObjectInterface) {
83 3
                $this->objects[$object->getId()->getId()] = $object;
84 3
                continue;
85
86
                // Else if it's an object locator
87
            } elseif ($object instanceof RepositoryLocatorInterface) {
88 8
                $this->objects[$object->getId()->getId()] = $object;
89 8
                continue;
90
            }
91
92 1
            throw new InvalidArgumentException(
93 1
                'Invalid collection object or path',
94 1
                InvalidArgumentException::INVALID_COLLECTION_OBJECT_OR_LOCATOR
95
            );
96
        }
97
98 8
        $this->objectIds = array_keys($this->objects);
99 8
    }
100
101
    /**
102
     * Return the current object
103
     *
104
     * @return ObjectInterface Current object
105
     */
106 4
    public function current()
107
    {
108 4
        return $this->loadObject($this->objectIds[$this->pointer]);
109
    }
110
111
    /**
112
     * Load and return an object by ID
113
     *
114
     * @param int $objectId Object ID
115
     * @return ObjectInterface Object
116
     */
117 4
    protected function loadObject($objectId)
118
    {
119
        // Lazy-load the object once
120 4
        $object = $this->objects[$objectId];
121 4
        if ($object instanceof RepositoryLocatorInterface) {
122 4
            $object = $this->objects[$objectId] = $object->getRepository()->loadObject($object);
123
        }
124
125 4
        return $object;
126
    }
127
128
    /**
129
     * Move forward to next object
130
     *
131
     * @return void
132
     */
133 2
    public function next()
134
    {
135 2
        ++$this->pointer;
136 2
    }
137
138
    /**
139
     * Return the ID of the current object
140
     *
141
     * @return int Object ID
142
     */
143 1
    public function key()
144
    {
145 1
        return $this->objectIds[$this->pointer];
146
    }
147
148
    /**
149
     * Checks if current position is valid
150
     *
151
     * @return boolean The current position is valid
152
     */
153 4
    public function valid()
154
    {
155 4
        return isset($this->objectIds[$this->pointer]);
156
    }
157
158
    /**
159
     * Rewind the Iterator to the first object
160
     *
161
     * @return void
162
     */
163 4
    public function rewind()
164
    {
165 4
        $this->pointer = 0;
166 4
    }
167
168
    /**
169
     * Whether an object ID exists
170
     *
171
     * @param int $offset Object ID
172
     * @return boolean Whether the object ID exists
173
     */
174 1
    public function offsetExists($offset)
175
    {
176 1
        return isset($this->objects[$offset]);
177
    }
178
179
    /**
180
     * Get an object with a particular ID
181
     *
182
     * @param int $offset Object ID
183
     * @return RepositoryLocator|ObjectInterface Object
184
     */
185 2
    public function offsetGet($offset)
186
    {
187 2
        return $this->objects[$offset];
188
    }
189
190
    /**
191
     * Set an object by ID
192
     *
193
     * @param int $offset Object ID
194
     * @param ObjectInterface $value Object
195
     * @throws RuntimeException When an object should be set by ID
196
     */
197 1
    public function offsetSet($offset, $value)
198
    {
199 1
        throw new RuntimeException(
200
            sprintf(
201 1
                'Cannot modify collection by index (%s / %s). Use add() / remove() instead',
202
                $offset,
203
                gettype($value)
204
            ),
205 1
            RuntimeException::CANNOT_MODIFY_COLLECTION_BY_INDEX
206
        );
207
    }
208
209
    /**
210
     * Unset an object by ID
211
     *
212
     * @param int $offset Object ID
213
     * @throws RuntimeException When an object should be set by ID
214
     */
215 1
    public function offsetUnset($offset)
216
    {
217 1
        throw new RuntimeException(
218 1
            sprintf('Cannot modify collection by index (%s). Use add() / remove() instead', $offset),
219 1
            RuntimeException::CANNOT_MODIFY_COLLECTION_BY_INDEX
220
        );
221
    }
222
223
    /**
224
     * Add an object to the collection
225
     *
226
     * @param string|ObjectInterface $object Object or object URL
227
     * @return Collection Modified object collection
228
     */
229 2
    public function add($object)
230
    {
231 2
        $objects = $this->objects;
232 2
        $objects[] = $object;
233 2
        return new self(array_values($objects));
234
    }
235
236
    /**
237
     * Remove an object out of this collection
238
     *
239
     * @param string|ObjectInterface $object Object or object ID
240
     * @return Collection Modified object collection
241
     */
242 1
    public function remove($object)
243
    {
244 1
        $object = ($object instanceof ObjectInterface) ? $object->getId()->getId() : intval($object);
245 1
        if (empty($this->objects[$object])) {
246 1
            throw new InvalidArgumentException(
247 1
                sprintf('Unknown object ID "%s"', $object),
248 1
                InvalidArgumentException::UNKNOWN_OBJECT_ID
249
            );
250
        }
251
252 1
        $objects = $this->objects;
253 1
        unset($objects[$object]);
254 1
        return new self(array_values($objects));
255
    }
256
257
    /**
258
     * Count objects in this collection
259
     *
260
     * @return int The number of objects in this collection
261
     */
262 6
    public function count()
263
    {
264 6
        return count($this->objects);
265
    }
266
267
    /*******************************************************************************
268
     * PRIVATE METHODS
269
     *******************************************************************************/
270
271
    /**
272
     * Append another collection
273
     *
274
     * @param Collection $collection Collection
275
     * @return Collection Combined collections
276
     */
277 1
    public function append(Collection $collection)
278
    {
279 1
        $objects = array_merge($this->objects, $collection->objects);
280 1
        return new self(array_values($objects));
281
    }
282
}
283