Completed
Push — master ( 513b92...79c3ce )
by Richard van
15s queued 11s
created

Item::getFirstItem()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
/**
4
 * micrometa
5
 *
6
 * @category   Jkphl
7
 * @package    Jkphl\Micrometa
8
 * @subpackage Jkphl\Micrometa\Ports
9
 * @author     Joschi Kuphal <[email protected]> / @jkphl
10
 * @copyright  Copyright © 2018 Joschi Kuphal <[email protected]> / @jkphl
11
 * @license    http://opensource.org/licenses/MIT The MIT License (MIT)
12
 */
13
14
/***********************************************************************************
15
 *  The MIT License (MIT)
16
 *
17
 *  Copyright © 2018 Joschi Kuphal <[email protected]> / @jkphl
18
 *
19
 *  Permission is hereby granted, free of charge, to any person obtaining a copy of
20
 *  this software and associated documentation files (the "Software"), to deal in
21
 *  the Software without restriction, including without limitation the rights to
22
 *  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
23
 *  the Software, and to permit persons to whom the Software is furnished to do so,
24
 *  subject to the following conditions:
25
 *
26
 *  The above copyright notice and this permission notice shall be included in all
27
 *  copies or substantial portions of the Software.
28
 *
29
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
31
 *  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
32
 *  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
33
 *  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
34
 *  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35
 ***********************************************************************************/
36
37
namespace Jkphl\Micrometa\Ports\Item;
38
39
use Jkphl\Micrometa\Application\Contract\ValueInterface;
40
use Jkphl\Micrometa\Application\Factory\AliasFactory;
41
use Jkphl\Micrometa\Application\Factory\PropertyListFactory;
42
use Jkphl\Micrometa\Application\Item\ItemInterface as ApplicationItemInterface;
43
use Jkphl\Micrometa\Application\Item\PropertyListInterface;
44
use Jkphl\Micrometa\Domain\Exceptions\OutOfBoundsException as DomainOutOfBoundsException;
45
use Jkphl\Micrometa\Domain\Item\Iri;
46
use Jkphl\Micrometa\Infrastructure\Factory\ItemFactory;
47
use Jkphl\Micrometa\Infrastructure\Factory\ProfiledNamesFactory;
48
use Jkphl\Micrometa\Infrastructure\Parser\ProfiledNamesList;
49
use Jkphl\Micrometa\Ports\Exceptions\InvalidArgumentException;
50
use Jkphl\Micrometa\Ports\Exceptions\OutOfBoundsException;
51
52
/**
53
 * Micro information item
54
 *
55
 * @package    Jkphl\Micrometa
56
 * @subpackage Jkphl\Micrometa\Ports
57
 */
58
class Item extends Collection implements ItemInterface
59
{
60
    /**
61
     * Application item
62
     *
63
     * @var ApplicationItemInterface
64
     */
65
    protected $item;
66
67
    /**
68
     * @var ItemList
69
     */
70
    private $itemList;
71
72
    /**
73
     * Item constructor
74
     *
75
     * @param ApplicationItemInterface $item Application item
76
     */
77 22
    public function __construct(ApplicationItemInterface $item)
78
    {
79 22
        $this->item = $item;
80 22
        $this->itemList = new ItemList(ItemFactory::createFromApplicationItems($this->item->getChildren()));
81 22
    }
82
83
    /**
84
     * Get the first value of an item property
85
     *
86
     * @param string $name Item property name
87
     *
88
     * @return ValueInterface|ValueInterface[]|array|ItemInterface First value of an item property
89
     * @api
90
     */
91 3
    public function __get($name)
92
    {
93 3
        return $this->getProperty($name, null, 0);
94
    }
95
96
    /**
97
     * Get a single property (value)
98
     *
99
     * @param string|\stdClass|Iri $name Property name
100
     * @param string $profile            Property profile
101
     * @param int|null $index            Property value index
102
     *
103
     * @return ValueInterface|ValueInterface[]|array|ItemInterface Property value(s)
104
     * @throws OutOfBoundsException If the property name is unknown
105
     * @throws OutOfBoundsException If the property value index is out of bounds
106
     * @api
107
     */
108 8
    public function getProperty($name, $profile = null, $index = null)
109
    {
110
        try {
111 8
            $propertyValues = $this->item->getProperty($name, $profile);
112 4
        } catch (DomainOutOfBoundsException $exception) {
113 4
            throw new OutOfBoundsException($exception->getMessage(), $exception->getCode());
114
        }
115
116
        // Return the value(s)
117 8
        return ($index === null) ?
118 8
            array_map([$this, 'getPropertyValue'], $propertyValues) : $this->getPropertyIndex($propertyValues, $index);
119
    }
120
121
    /**
122
     * Return a particular property index
123
     *
124
     * @param ValueInterface[] $propertyValues Property values
125
     * @param int $index                       Property value index
126
     *
127
     * @return ValueInterface|ItemInterface
128
     */
129 7
    protected function getPropertyIndex(array $propertyValues, $index)
130
    {
131
        // If the property value index is out of bounds
132 7
        if (!isset($propertyValues[$index])) {
133 1
            throw new OutOfBoundsException(
134 1
                sprintf(OutOfBoundsException::INVALID_PROPERTY_VALUE_INDEX_STR, $index),
135 1
                OutOfBoundsException::INVALID_PROPERTY_VALUE_INDEX
136
            );
137
        }
138
139 6
        return $this->getPropertyValue($propertyValues[$index]);
140
    }
141
142
    /**
143
     * Prepare a property value for returning it
144
     *
145
     * @param ValueInterface $value Property value
146
     *
147
     * @return ValueInterface|ItemInterface Returnable property value
148
     */
149 8
    protected function getPropertyValue(ValueInterface $value)
150
    {
151 8
        return ($value instanceof ApplicationItemInterface) ?
152 8
            ItemFactory::createFromApplicationItem($value) : $value;
153
    }
154
155
    /**
156
     * Return whether the item is of a particular type (or contained in a list of types)
157
     *
158
     * The item type(s) can be specified in a variety of ways, @see ProfiledNamesFactory::createFromArguments()
159
     *
160
     * @param array ...$types Item types
161
     *
162
     * @return boolean Item type is contained in the list of types
163
     * @api
164
     */
165 9
    public function isOfType(...$types)
166
    {
167
        /** @var ProfiledNamesList $profiledTypes */
168 9
        $profiledTypes = ProfiledNamesFactory::createFromArguments($types);
169 9
        $aliasFactory  = new AliasFactory();
170
171
        // Run through all item types
172
        /** @var \stdClass $itemType */
173 9
        foreach ($this->item->getType() as $itemType) {
174 9
            $itemTypeNames = $aliasFactory->createAliases($itemType->name);
175 9
            if ($this->isOfProfiledTypes($itemType->profile, $itemTypeNames, $profiledTypes)) {
176 7
                return true;
177
            }
178
        }
179
180 4
        return false;
181
    }
182
183
    /**
184
     * Return whether an aliased item type is contained in a set of query types
185
     *
186
     * @param string $profile          Type profile
187
     * @param array $names             Aliased type names
188
     * @param ProfiledNamesList $types Query types
189
     *
190
     * @return bool Item type is contained in the set of query types
191
     */
192 9
    protected function isOfProfiledTypes($profile, array $names, ProfiledNamesList $types)
193
    {
194
        // Run through all query types
195
        /** @var \stdClass $queryType */
196 9
        foreach ($types as $queryType) {
197 9
            if ($this->isTypeInNames($queryType, $profile, $names)) {
198 7
                return true;
199
            }
200
        }
201
202 4
        return false;
203
    }
204
205
    /**
206
     * Test whether a type is contained in a list of names
207
     *
208
     * @param \stdClass $type Type
209
     * @param string $profile Type profile
210
     * @param array $names    Aliased type names
211
     *
212
     * @return bool Type is contained in names list
213
     */
214 9
    protected function isTypeInNames($type, $profile, array $names)
215
    {
216 9
        return in_array($type->name, $names) &&
217 9
               (($type->profile === null) ? true : ($type->profile == $profile));
218
    }
219
220
    /**
221
     * Get all values of the first available property in a stack
222
     *
223
     * The property stack can be specified in a variety of ways, @see ProfiledNamesFactory::createFromArguments()
224
     *
225
     * @param array $properties Properties
226
     *
227
     * @return ValueInterface[]|array Property values
228
     * @throws InvalidArgumentException If no property name was given
229
     * @throws OutOfBoundsException If none of the requested properties is known
230
     * @api
231
     */
232 2
    public function getFirstProperty(...$properties)
233
    {
234
        /** @var ProfiledNamesList $properties */
235 2
        $properties = ProfiledNamesFactory::createFromArguments(func_get_args());
236
237
        // Prepare a default exception
238 2
        $exception = new OutOfBoundsException(
239 2
            OutOfBoundsException::NO_MATCHING_PROPERTIES_STR,
240 2
            OutOfBoundsException::NO_MATCHING_PROPERTIES
241
        );
242
243
        // Run through all properties
244 2
        foreach ($properties as $property) {
245
            try {
246 2
                return (array)$this->getProperty($property->name, $property->profile);
247 1
            } catch (OutOfBoundsException $exception) {
248 1
                continue;
249
            }
250
        }
251
252 1
        throw $exception;
253
    }
254
255
    /**
256
     * Return all properties
257
     *
258
     * @return PropertyListInterface Properties
259
     * @api
260
     */
261 1
    public function getProperties()
262
    {
263 1
        $propertyList = (new PropertyListFactory())->create();
264 1
        foreach ($this->item->getProperties() as $propertyName => $propertyValues) {
265 1
            $propertyList[$propertyName] = array_map([$this, 'getPropertyValue'], $propertyValues);
266
        }
267
268 1
        return $propertyList;
269
    }
270
271
    /**
272
     * Return an object representation of the item
273
     *
274
     * @return \stdClass Micro information item
275
     * @api
276
     */
277 2
    public function toObject()
278
    {
279 2
        return $this->item->export();
280
    }
281
282
    /**
283
     * Get the item type
284
     *
285
     * @return \stdClass[] Item type
286
     * @api
287
     */
288 1
    public function getType()
289
    {
290 1
        return $this->item->getType();
291
    }
292
293
    /**
294
     * Get the item format
295
     *
296
     * @return int Item format
297
     * @api
298
     */
299 1
    public function getFormat()
300
    {
301 1
        return $this->item->getFormat();
302
    }
303
304
    /**
305
     * Get the item ID
306
     *
307
     * @return string Item ID
308
     * @api
309
     */
310 1
    public function getId()
311
    {
312 1
        return $this->item->getId();
313
    }
314
315
    /**
316
     * Get the item language
317
     *
318
     * @return string Item language
319
     * @api
320
     */
321 1
    public function getLanguage()
322
    {
323 1
        return $this->item->getLanguage();
324
    }
325
326
    /**
327
     * Return the item value
328
     *
329
     * @return string Item value
330
     * @api
331
     */
332 1
    public function getValue()
333
    {
334 1
        return $this->item->getValue();
335
    }
336
337
    /**
338
     * Filter the items by item type(s).
339
     *
340
     * @param array ...$types Item types
341
     *
342
     * @return ItemInterface[] Items matching the requested types
343
     */
344 5
    public function getItems(...$types)
345
    {
346 5
        return $this->itemList->getItems(...$types);
347
    }
348
349
    /**
350
     * Return the first item, optionally of particular types.
351
     *
352
     * @param array ...$types Item types
353
     *
354
     * @return ItemInterface Item
355
     */
356 2
    public function getFirstItem(...$types)
357
    {
358 2
        return $this->itemList->getFirstItem(...$types);
359
    }
360
361
    /**
362
     * Count the elements in the item list.
363
     *
364
     * @return int
365
     */
366 1
    public function count()
367
    {
368 1
        return $this->itemList->count();
369
    }
370
}
371