OneToMany::addActionOnSave()   A
last analyzed

Complexity

Conditions 5
Paths 7

Size

Total Lines 31
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 19
c 1
b 0
f 0
nc 7
nop 1
dl 0
loc 31
rs 9.3222
1
<?php
2
3
namespace Sirius\Orm\Relation;
4
5
use Sirius\Orm\Action\BaseAction;
6
use Sirius\Orm\Collection\Collection;
7
use Sirius\Orm\Entity\EntityInterface;
8
use Sirius\Orm\Entity\StateEnum;
9
use Sirius\Orm\Entity\Tracker;
10
use Sirius\Orm\Helpers\Inflector;
11
use Sirius\Orm\Query;
12
13
class OneToMany extends Relation
14
{
15
    use HasAggregates;
16
17
    protected function applyDefaults(): void
18
    {
19
        $nativeKey = $this->nativeMapper->getPrimaryKey();
20
        if (! isset($this->options[RelationConfig::NATIVE_KEY])) {
21
            $this->options[RelationConfig::NATIVE_KEY] = $nativeKey;
22
        }
23
24
        if (! isset($this->options[RelationConfig::FOREIGN_KEY])) {
25
            $prefix                                     = Inflector::singularize($this->nativeMapper->getTable());
26
            $this->options[RelationConfig::FOREIGN_KEY] = $this->getKeyColumn($prefix, $nativeKey);
27
        }
28
29
        parent::applyDefaults();
30
    }
31
32
    public function getQuery(Tracker $tracker)
33
    {
34
        $nativeKey = $this->options[RelationConfig::NATIVE_KEY];
35
        $nativePks = $tracker->pluck($nativeKey);
36
37
        $query = $this->foreignMapper
38
            ->newQuery()
39
            ->where($this->options[RelationConfig::FOREIGN_KEY], $nativePks);
40
41
        $query = $this->applyQueryCallback($query);
42
43
        $query = $this->applyForeignGuards($query);
44
45
        return $query;
46
    }
47
48
    public function joinSubselect(Query $query, string $reference)
49
    {
50
        $subselect = $query->subSelectForJoinWith()
51
                           ->columns($this->foreignMapper->getTable() . '.*')
52
                           ->from($this->foreignMapper->getTable())
53
                           ->as($reference);
54
55
        $subselect = $this->applyQueryCallback($subselect);
56
57
        $subselect = $this->applyForeignGuards($subselect);
58
59
        return $query->join('INNER', $subselect->getStatement(), $this->getJoinOnForSubselect());
60
    }
61
62
    public function attachMatchesToEntity(EntityInterface $nativeEntity, array $result)
63
    {
64
        // no point in linking entities if the native one is deleted
65
        if ($nativeEntity->getPersistenceState() == StateEnum::DELETED) {
66
            return;
67
        }
68
69
        $nativeId = $this->getEntityId($this->nativeMapper, $nativeEntity, array_keys($this->keyPairs));
70
71
        $found = $result[$nativeId] ?? [];
72
73
        $this->nativeMapper->setEntityAttribute($nativeEntity, $this->name, new Collection($found));
74
    }
75
76
    public function attachEntities(EntityInterface $nativeEntity, EntityInterface $foreignEntity)
77
    {
78
        foreach ($this->keyPairs as $nativeCol => $foreignCol) {
79
            $nativeKeyValue  = $this->nativeMapper->getEntityAttribute($nativeEntity, $nativeCol);
80
            $this->foreignMapper->setEntityAttribute($foreignEntity, $foreignCol, $nativeKeyValue);
81
        }
82
    }
83
84
    public function detachEntities(EntityInterface $nativeEntity, EntityInterface $foreignEntity)
85
    {
86
        $state = $foreignEntity->getPersistenceState();
87
        $foreignEntity->setPersistenceState(StateEnum::SYNCHRONIZED);
88
        foreach ($this->keyPairs as $nativeCol => $foreignCol) {
89
            $this->foreignMapper->setEntityAttribute($foreignEntity, $foreignCol, null);
90
        }
91
        $this->foreignMapper->setEntityAttribute($foreignEntity, $this->name, null);
92
        $foreignEntity->setPersistenceState($state);
93
    }
94
95
    protected function addActionOnDelete(BaseAction $action)
96
    {
97
        $nativeEntity       = $action->getEntity();
98
        $remainingRelations = $this->getRemainingRelations($action->getOption('relations'));
99
100
        // no cascade delete? treat as save so we can process the changes
101
        if (! $this->isCascade()) {
102
            $this->addActionOnSave($action);
103
        } else {
104
            // retrieve them again from the DB since the related collection might not have everything
105
            // for example due to a relation query callback
106
            $foreignEntities = $this->getQuery(new Tracker([$nativeEntity->getArrayCopy()]))
107
                                    ->get();
108
109
            foreach ($foreignEntities as $foreignEntity) {
110
                $deleteAction = $this->foreignMapper
111
                    ->newDeleteAction($foreignEntity, ['relations' => $remainingRelations]);
112
                $action->append($this->newSyncAction($nativeEntity, $foreignEntity, 'delete'));
113
                $action->append($deleteAction);
114
            }
115
        }
116
    }
117
118
    protected function addActionOnSave(BaseAction $action)
119
    {
120
        if (!$this->relationWasChanged($action->getEntity())) {
121
            return;
122
        }
123
124
        $nativeEntity       = $action->getEntity();
125
        $remainingRelations = $this->getRemainingRelations($action->getOption('relations'));
126
127
        /** @var Collection $foreignEntities */
128
        $foreignEntities = $this->nativeMapper->getEntityAttribute($nativeEntity, $this->name);
129
        $changes         = $foreignEntities->getChanges();
130
131
        // save the entities still in the collection
132
        foreach ($foreignEntities as $foreignEntity) {
133
            if (! empty($foreignEntity->getChanges())) {
134
                $saveAction = $this->foreignMapper
135
                    ->newSaveAction($foreignEntity, ['relations' => $remainingRelations]);
136
                $saveAction->addColumns($this->getExtraColumnsForAction());
137
                $action->append($this->newSyncAction($nativeEntity, $foreignEntity, 'save'));
138
                $action->append($saveAction);
139
            }
140
        }
141
142
        // save entities that were removed but NOT deleted
143
        foreach ($changes['removed'] as $foreignEntity) {
144
            $saveAction = $this->foreignMapper
145
                ->newSaveAction($foreignEntity, ['relations' => $remainingRelations]);
146
            $saveAction->addColumns($this->getExtraColumnsForAction());
147
            $action->append($this->newSyncAction($nativeEntity, $foreignEntity, 'delete'));
148
            $action->append($saveAction);
149
        }
150
    }
151
}
152