Passed
Push — master ( ea0818...92b4a6 )
by Adrian
01:28
created

BaseAction::getConditions()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 9
c 0
b 0
f 0
dl 0
loc 17
rs 9.9666
cc 4
nc 6
nop 0
1
<?php
2
declare(strict_types=1);
3
4
namespace Sirius\Orm\Action;
5
6
use Sirius\Orm\Entity\EntityInterface;
7
use Sirius\Orm\Exception\FailedActionException;
8
use Sirius\Orm\Mapper;
9
use Sirius\Orm\Relation\Relation;
10
11
abstract class BaseAction implements ActionInterface
12
{
13
    /**
14
     * @var Mapper
15
     */
16
    protected $mapper;
17
18
    /**
19
     * @var EntityInterface
20
     */
21
    protected $entity;
22
23
    /**
24
     * Actions to be executed before the `execute()` method (save parent entities)
25
     * @var array
26
     */
27
    protected $before = [];
28
29
    /**
30
     * Actions to be executed after the `execute()` method (save child entities)
31
     * @var array
32
     */
33
    protected $after = [];
34
35
    protected $hasRun = false;
36
37
    /**
38
     * @var EntityInterface
39
     */
40
    protected $parentEntity;
41
42
    /**
43
     * @var Relation
44
     */
45
    protected $relation;
46
47
    /**
48
     * Contains additional options for the action:
49
     * - relations (relations that will be saved)
50
     *      - true: all related entities will be saved, without limit
51
     *      - false: do not save any related entity
52
     *      - array: list of the related entities to be saved
53
     *              (ex: ['category', 'category.parent', 'images'])
54
     * @var array
55
     */
56
    protected $options;
57
58
    public function __construct(
59
        Mapper $mapper,
60
        EntityInterface $entity,
61
        array $options = []
62
    ) {
63
        $this->mapper  = $mapper;
64
        $this->entity  = $entity;
65
        $this->options = $options;
66
    }
67
68
    public function prepend(ActionInterface $action)
69
    {
70
        $this->before[] = $action;
71
    }
72
73
    /**
74
     * @return Mapper
75
     */
76
    public function getMapper(): Mapper
77
    {
78
        return $this->mapper;
79
    }
80
81
    /**
82
     * @return EntityInterface
83
     */
84
    public function getEntity(): EntityInterface
85
    {
86
        return $this->entity;
87
    }
88
89
    public function getOption($name)
90
    {
91
        return $this->options[$name] ?? null;
92
    }
93
94
    public function append(ActionInterface $action)
95
    {
96
        $this->after[] = $action;
97
    }
98
99
    protected function addActionsForRelatedEntities()
100
    {
101
        if ($this->getOption('relations') === false || ! $this->mapper) {
102
            return;
103
        }
104
105
        foreach ($this->getMapper()->getRelations() as $name) {
106
            if (! $this->mapper->hasRelation($name)) {
107
                continue;
108
            }
109
            $this->mapper->getRelation($name)->addActions($this);
110
        }
111
    }
112
113
    protected function getConditions()
114
    {
115
        $entityPk = (array)$this->mapper->getPrimaryKey();
116
        $conditions = [];
117
        foreach ($entityPk as $col) {
118
            $val = $this->mapper->getEntityAttribute($this->entity, $col);
119
            if ($val) {
120
                $conditions[$col] = $val;
121
            }
122
        }
123
124
        // not enough columns? reset
125
        if (count($conditions) != count($entityPk)) {
126
            return [];
127
        }
128
129
        return $conditions;
130
    }
131
132
    public function run($calledByAnotherAction = false)
133
    {
134
        $executed = [];
135
136
        try {
137
            $this->addActionsForRelatedEntities();
138
139
            foreach ($this->before as $action) {
140
                $action->run(true);
141
                $executed[] = $action;
142
            }
143
            $this->execute();
144
            $executed[]   = $this;
145
            $this->hasRun = true;
146
            foreach ($this->after as $action) {
147
                $action->run(true);
148
                $executed[] = $action;
149
            }
150
        } catch (\Exception $e) {
151
            $this->undo($executed);
152
            throw new FailedActionException(
153
                sprintf("%s failed for mapper %s", get_class($this), $this->mapper->getTableAlias(true)),
154
                (int)$e->getCode(),
155
                $e
156
            );
157
        }
158
159
        /** @var ActionInterface $action */
160
        foreach ($executed as $action) {
161
            // if called by another action, that action will call `onSuccess`
162
            if (! $calledByAnotherAction || $action !== $this) {
163
                $action->onSuccess();
164
            }
165
        }
166
167
        return true;
168
    }
169
170
    public function revert()
171
    {
172
        return; // each action implements it's own logic if necessary
173
    }
174
175
    protected function undo(array $executed)
176
    {
177
        foreach ($executed as $action) {
178
            $action->revert();
179
        }
180
    }
181
182
    public function onSuccess()
183
    {
184
        return;
185
    }
186
187
188
    protected function execute()
189
    {
190
        throw new \BadMethodCallException(sprintf('%s must implement `execute()`', get_class($this)));
191
    }
192
}
193