HasMany   A
last analyzed

Complexity

Total Complexity 12

Size/Duplication

Total Lines 89
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 27
dl 0
loc 89
rs 10
c 0
b 0
f 0
wmc 12

5 Methods

Rating   Name   Duplication   Size   Complexity  
A shouldAdd() 0 7 2
A in() 0 5 2
A load() 0 18 4
A __construct() 0 4 1
A makeCollections() 0 16 3
1
<?php
2
declare(strict_types=1);
3
4
namespace Stratadox\TableLoader\Loader;
5
6
use function in_array;
7
use Stratadox\Hydrator\ArrayHydrator;
8
use Stratadox\Hydrator\CannotHydrate;
9
use Stratadox\Hydrator\Hydrates;
10
11
/**
12
 * Defines a has-many relationship.
13
 *
14
 * @author Stratadox
15
 */
16
final class HasMany implements MakesConnections
17
{
18
    private $property;
19
    private $collection;
20
21
    private function __construct(string $property, Hydrates $collection)
22
    {
23
        $this->property = $property;
24
        $this->collection = $collection;
25
    }
26
27
    /**
28
     * Makes a connector for a has-many type relationship.
29
     *
30
     * @param string        $property   The property to map.
31
     * @param Hydrates|null $collection The collection hydrator to use.
32
     * @return MakesConnections         The relationship connector.
33
     */
34
    public static function in(
35
        string $property,
36
        Hydrates $collection = null
37
    ): MakesConnections {
38
        return new self($property, $collection ?: ArrayHydrator::create());
39
    }
40
41
    /** @inheritdoc */
42
    public function load(
43
        KnowsWhereToLook $from,
44
        array $data,
45
        KnowsWhereToLookTo $to,
46
        ContainsResultingObjects $objects
47
    ): array {
48
        $related = [];
49
        foreach ($data as $row) {
50
            if ($to->ignoreThe($row)) {
51
                continue;
52
            }
53
            $object = $objects[$to->label()][$to->this($row)];
54
            if ($this->shouldAdd($object, $from->this($row), $related)) {
55
                $related[$from->this($row)][] = $object;
56
            }
57
        }
58
        return [
59
            $this->property => $this->makeCollections($from->label(), $related)
60
        ];
61
    }
62
63
    /**
64
     * Checks if we should add the object to the relations collection.
65
     *
66
     * @param object $relation The related object.
67
     * @param string $from     The id of the object that holds the relationship.
68
     * @param array  $existing The map of existing relationships.
69
     * @return bool            Whether the object is new.
70
     */
71
    private function shouldAdd(
72
        object $relation,
73
        string $from,
74
        array $existing
75
    ): bool {
76
        return !isset($existing[$from])
77
            || !in_array($relation, $existing[$from], true);
78
    }
79
80
    /**
81
     * Makes collections for the relationship.
82
     *
83
     * @param string  $from The label of the entity we connect from.
84
     * @param array[] $many The objects to connect with.
85
     *
86
     * @return array        The collections as [string id => iterable]
87
     * @throws CannotLoadTable
88
     */
89
    private function makeCollections(string $from, array $many): array
90
    {
91
        $collections = [];
92
        foreach ($many as $id => $relatedObjects) {
93
            try {
94
                $collections[$id] = $this->collection->fromArray($relatedObjects);
95
            } catch (CannotHydrate $exception) {
96
                throw UnmappableRelationship::encountered(
97
                    $exception,
98
                    $this->property,
99
                    $from,
100
                    $id
101
                );
102
            }
103
        }
104
        return $collections;
105
    }
106
}
107