Passed
Push — dev_2x ( fb9ebe...5f9618 )
by Adrian
02:22
created

BaseAction::revert()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
declare(strict_types=1);
3
4
namespace Sirius\Orm\Action;
5
6
use Sirius\Orm\Contract\ActionInterface;
7
use Sirius\Orm\Contract\EntityInterface;
8
use Sirius\Orm\Contract\HydratorInterface;
9
use Sirius\Orm\Exception\FailedActionException;
10
use Sirius\Orm\Mapper;
11
use Sirius\Orm\Relation\Relation;
12
13
abstract class BaseAction implements ActionInterface
14
{
15
    /**
16
     * @var Mapper
17
     */
18
    protected $mapper;
19
20
    /**
21
     * @var EntityInterface
22
     */
23
    protected $parentEntity;
24
25
    /**
26
     * @var EntityInterface
27
     */
28
    protected $entity;
29
30
    /**
31
     * Actions to be executed before the `execute()` method (save parent entities)
32
     * @var array
33
     */
34
    protected $before = [];
35
36
    /**
37
     * Actions to be executed after the `execute()` method (save child entities)
38
     * @var array
39
     */
40
    protected $after = [];
41
42
    /**
43
     * @var bool
44
     */
45
    protected $hasRun = false;
46
47
    /**
48
     * @var Relation
49
     */
50
    protected $relation;
51
52
    /**
53
     * @var HydratorInterface
54
     */
55
    protected $entityHydrator;
56
57
    /**
58
     * Contains additional options for the action:
59
     * - relations (relations that will be saved)
60
     *      - true: all related entities will be saved, without limit
61
     *      - false: do not save any related entity
62
     *      - array: list of the related entities to be saved
63
     *              (ex: ['category', 'category.parent', 'images'])
64
     * @var array
65
     */
66
    protected $options;
67
68
    public function __construct(
69
        Mapper $mapper,
70
        EntityInterface $entity,
71
        array $options = []
72
    ) {
73
        $this->mapper         = $mapper;
74
        $this->entity         = $entity;
75
        $this->options        = $options;
76
        $this->entityHydrator = $mapper->getHydrator();
77
    }
78
79
    /**
80
     * Adds an action to be ran/executed BEFORE this action's execute()
81
     *
82
     * @param ActionInterface $action
83
     */
84
    public function prepend(ActionInterface $action)
85
    {
86
        $this->before[] = $action;
87
    }
88
89
    /**
90
     * Adds an action to be ran/executed AFTER this action's execute()
91
     *
92
     * @param ActionInterface $action
93
     */
94
    public function append(ActionInterface $action)
95
    {
96
        $this->after[] = $action;
97
    }
98
99
    /**
100
     * @return EntityInterface
101
     */
102
    public function getEntity(): EntityInterface
103
    {
104
        return $this->entity;
105
    }
106
107
    /**
108
     * @param $name
109
     *
110
     * @return mixed|null
111
     */
112
    public function getOption($name)
113
    {
114
        return $this->options[$name] ?? null;
115
    }
116
117
    public function includesRelation($relationName)
118
    {
119
        $relations = (array) $this->getOption('relations');
120
121
        return $relations === true || in_array($relationName, $relations);
122
    }
123
124
    /**
125
     * Calls the relations and checks if they have to attach other actions
126
     * Usually used for deep save/delete
127
     */
128
    protected function addActionsForRelatedEntities()
129
    {
130
        if (! $this->mapper) {
131
            return;
132
        }
133
134
        foreach ($this->mapper->getRelations() as $name) {
135
            $this->mapper->getRelation($name)->addActions($this);
136
        }
137
    }
138
139
    /**
140
     * Returns the conditions for the query to be executed
141
     * Usually used by UPDATE/DELETE queries
142
     *
143
     * @return array
144
     */
145
    protected function getConditions()
146
    {
147
        $entityPk   = (array)$this->mapper->getConfig()->getPrimaryKey();
148
        $conditions = [];
149
        foreach ($entityPk as $col) {
150
            $val = $this->entityHydrator->get($this->entity, $col);
151
            if ($val) {
152
                $conditions[$col] = $val;
153
            }
154
        }
155
156
        // not enough columns? reset
157
        if (count($conditions) != count($entityPk)) {
158
            return [];
159
        }
160
161
        return $conditions;
162
    }
163
164
    /**
165
     * Performs the action.
166
     * Runs the prepended actions, executes the main logic, runs the appended actions.
167
     *
168
     * @param false $calledByAnotherAction
169
     *
170
     * @return bool|mixed
171
     */
172
    public function run($calledByAnotherAction = false)
173
    {
174
        $executed = [];
175
176
        try {
177
            $this->addActionsForRelatedEntities();
178
179
            foreach ($this->before as $action) {
180
                $action->run(true);
181
                $executed[] = $action;
182
            }
183
            $this->execute();
184
            $executed[]   = $this;
185
            $this->hasRun = true;
186
            foreach ($this->after as $action) {
187
                $action->run(true);
188
                $executed[] = $action;
189
            }
190
        } catch (\Exception $e) {
191
            $this->undo($executed);
0 ignored issues
show
Bug introduced by
The method undo() does not exist on Sirius\Orm\Action\BaseAction. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

191
            $this->/** @scrutinizer ignore-call */ 
192
                   undo($executed);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
192
            throw new FailedActionException(
193
                sprintf("%s failed for mapper %s", get_class($this), $this->mapper->getConfig()->getTableAlias(true)),
194
                (int)$e->getCode(),
195
                $e
196
            );
197
        }
198
199
        // if called by another action, that action will call `onSuccess`
200
        if ($calledByAnotherAction) {
201
            return true;
202
        }
203
204
        /** @var ActionInterface $action */
205
        foreach ($executed as $action) {
206
            $action->onSuccess();
207
        }
208
209
        return true;
210
    }
211
212
    /**
213
     * @inheritDoc
214
     */
215
    public function onSuccess()
216
    {
217
        return;
218
    }
219
220
    /**
221
     * Contains the code for the main purpose of the action
222
     * Eg: inserting/deleting a row, updating some fields etc
223
     */
224
    protected function execute()
225
    {
226
        throw new \BadMethodCallException(sprintf('%s must implement `execute()`', get_class($this)));
227
    }
228
}
229