Passed
Push — dev_2x ( fedccd...fb9ebe )
by Adrian
02:16
created

Aggregate::attachAggregateToEntity()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 5
c 1
b 0
f 1
dl 0
loc 11
rs 10
cc 3
nc 3
nop 2
1
<?php
2
declare(strict_types=1);
3
4
namespace Sirius\Orm\Relation;
5
6
use Sirius\Orm\Contract\EntityInterface;
7
use Sirius\Orm\Contract\HydratorInterface;
8
use Sirius\Orm\Entity\LazyAggregate;
9
use Sirius\Orm\Entity\LazyValue;
10
use Sirius\Orm\Entity\Tracker;
11
use Sirius\Orm\Query;
12
13
class Aggregate
14
{
15
    /**
16
     * @var string
17
     */
18
    protected $name;
19
20
    /**
21
     * @var Relation
22
     */
23
    protected $relation;
24
25
    /**
26
     * @var array
27
     */
28
    protected $options;
29
30
    /**
31
     * @var HydratorInterface
32
     */
33
    protected $entityHydrator;
34
35
    /**
36
     * @var HydratorInterface
37
     */
38
    protected $foreignEntityHydrator;
39
40
    public function __construct(string $name, Relation $relation, array $options)
41
    {
42
        $this->name           = $name;
43
        $this->relation       = $relation;
44
        $this->options        = $options;
45
        $this->entityHydrator = $relation->getNativeMapper()->getHydrator();
46
    }
47
48
    public function getQuery(Tracker $tracker)
49
    {
50
        $keys = $this->relation->getKeyPairs();
51
52
        /** @var Query $query */
53
        $query = $this->relation->getQuery($tracker);
54
        $query->resetColumns();
55
        $query->columns(...array_values($keys));
56
        $query->columns(sprintf(
57
            '%s as %s',
58
            $this->options[RelationConfig::AGG_FUNCTION],
59
            $this->name
60
        ));
61
62
        $callback = $this->options[RelationConfig::AGG_CALLBACK] ?? null;
63
        if (is_callable($callback)) {
64
            $query = $callback($query);
65
        }
66
67
        $query->groupBy(...array_values($keys));
68
69
        /**
70
         * the query callback for the relation or for the aggregate might implement ORDER_BY
71
         * and that would cause issues with MySQL's sql_mode=ONLY_FULL_GROUP_BY
72
         */
73
        $query->resetOrderBy();
74
75
        /**
76
         * just in case a query callback is setting limits
77
         */
78
        $query->resetLimit();
79
80
        return $query;
81
    }
82
83
    public function attachLazyAggregateToEntity(EntityInterface $entity, Tracker $tracker)
84
    {
85
        $valueLoader = $tracker->getLazyAggregate($this);
86
        $this->entityHydrator->set($entity, $this->name, $valueLoader);
87
    }
88
89
    public function attachAggregateToEntity(EntityInterface $entity, array $results)
90
    {
91
        $value = null;
92
        foreach ($results as $row) {
93
            if ($this->entityMatchesRow($entity, $row)) {
94
                $value = $row[$this->name] ?? null;
95
            }
96
        }
97
        // we have to do this because some properties may be read only (ie: there is no setter)
98
        // using a LazyValue is the only way for the hydrator to inject a value
99
        $this->entityHydrator->set($entity, $this->name, new LazyValue($value));
100
    }
101
102
    public function isLazyLoad()
103
    {
104
        return ! isset($this->options[RelationConfig::LOAD_STRATEGY]) ||
105
               $this->options[RelationConfig::LOAD_STRATEGY] == RelationConfig::LOAD_LAZY;
106
    }
107
108
    public function isEagerLoad()
109
    {
110
        return isset($this->options[RelationConfig::LOAD_STRATEGY]) &&
111
               $this->options[RelationConfig::LOAD_STRATEGY] == RelationConfig::LOAD_EAGER;
112
    }
113
114
    public function getName()
115
    {
116
        return $this->name;
117
    }
118
119
    private function entityMatchesRow(EntityInterface $entity, $row)
120
    {
121
        $keys = $this->relation->getKeyPairs();
122
        foreach ($keys as $nativeCol => $foreignCol) {
123
            $entityValue = $this->entityHydrator->get($entity, $nativeCol);
124
            $rowValue    = $row[$foreignCol];
125
            // if both native and foreign key values are present (not unlinked entities) they must be the same
126
            // otherwise we assume that the entities can be linked together
127
            if ($entityValue && $rowValue && $entityValue != $rowValue) {
128
                return false;
129
            }
130
        }
131
132
        return true;
133
    }
134
}
135