Completed
Push — master ( bae40e...a4cb2c )
by James
19:36 queued 09:47
created

RuleRepository::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
1
<?php
2
/**
3
 * RuleRepository.php
4
 * Copyright (c) 2017 [email protected]
5
 *
6
 * This file is part of Firefly III.
7
 *
8
 * Firefly III is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * Firefly III is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
20
 */
21
declare(strict_types=1);
22
23
namespace FireflyIII\Repositories\Rule;
24
25
use FireflyIII\Exceptions\FireflyException;
26
use FireflyIII\Models\Rule;
27
use FireflyIII\Models\RuleAction;
28
use FireflyIII\Models\RuleGroup;
29
use FireflyIII\Models\RuleTrigger;
30
use FireflyIII\User;
31
use Illuminate\Support\Collection;
32
use Log;
33
34
/**
35
 * Class RuleRepository.
36
 *
37
 * @SuppressWarnings(PHPMD.TooManyPublicMethods)
38
 */
39
class RuleRepository implements RuleRepositoryInterface
40
{
41
    /** @var User */
42
    private $user;
43
44
    /**
45
     * Constructor.
46
     */
47
    public function __construct()
48
    {
49
        if ('testing' === env('APP_ENV')) {
50
            Log::warning(sprintf('%s should not be instantiated in the TEST environment!', \get_class($this)));
51
        }
52
    }
53
54
    /**
55
     * @return int
56
     */
57
    public function count(): int
58
    {
59
        return $this->user->rules()->count();
60
    }
61
62
    /**
63
     * @param Rule $rule
64
     *
65
     * @return bool
66
     * @throws \Exception
67
     */
68
    public function destroy(Rule $rule): bool
69
    {
70
        foreach ($rule->ruleTriggers as $trigger) {
71
            $trigger->delete();
72
        }
73
        foreach ($rule->ruleActions as $action) {
74
            $action->delete();
75
        }
76
        $rule->delete();
77
78
        return true;
79
    }
80
81
    /**
82
     * @param int $ruleId
83
     *
84
     * @return Rule|null
85
     */
86
    public function find(int $ruleId): ?Rule
87
    {
88
        $rule = $this->user->rules()->find($ruleId);
89
        if (null === $rule) {
90
            return null;
91
        }
92
93
        return $rule;
94
    }
95
96
    /**
97
     * Get all the users rules.
98
     *
99
     * @return Collection
100
     */
101
    public function getAll(): Collection
102
    {
103
        return $this->user->rules()->with(['ruleGroup'])->get();
104
    }
105
106
    /**
107
     * FIxXME can return null.
108
     *
109
     * @return RuleGroup
110
     */
111
    public function getFirstRuleGroup(): RuleGroup
112
    {
113
        return $this->user->ruleGroups()->first();
114
    }
115
116
    /**
117
     * Get the rules for a user tailored to the import process.
118
     *
119
     * @return Collection
120
     */
121
    public function getForImport(): Collection
122
    {
123
        return Rule::distinct()
124
                   ->where('rules.user_id', $this->user->id)
125
                   ->leftJoin('rule_groups', 'rule_groups.id', '=', 'rules.rule_group_id')
126
                   ->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
127
                   ->where('rule_groups.active', 1)
128
                   ->where('rule_triggers.trigger_type', 'user_action')
129
                   ->where('rule_triggers.trigger_value', 'store-journal')
130
                   ->where('rules.active', 1)
131
                   ->orderBy('rule_groups.order', 'ASC')
132
                   ->orderBy('rules.order', 'ASC')
133
                   ->get(['rules.*', 'rule_groups.order']);
134
    }
135
136
    /**
137
     * @param RuleGroup $ruleGroup
138
     *
139
     * @return int
140
     */
141
    public function getHighestOrderInRuleGroup(RuleGroup $ruleGroup): int
142
    {
143
        return (int)$ruleGroup->rules()->max('order');
144
    }
145
146
    /**
147
     * @param Rule $rule
148
     *
149
     * @return string
150
     *
151
     * @throws FireflyException
152
     */
153
    public function getPrimaryTrigger(Rule $rule): string
154
    {
155
        $count = $rule->ruleTriggers()->count();
156
        if (0 === $count) {
157
            throw new FireflyException('Rules should have more than zero triggers, rule #' . $rule->id . ' has none!');
158
        }
159
160
        return $rule->ruleTriggers()->where('trigger_type', 'user_action')->first()->trigger_value;
161
    }
162
163
    /**
164
     * @param Rule $rule
165
     *
166
     * @return bool
167
     */
168
    public function moveDown(Rule $rule): bool
169
    {
170
        $order = $rule->order;
171
172
        // find the rule with order+1 and give it order-1
173
        $other = $rule->ruleGroup->rules()->where('order', $order + 1)->first();
174
        if ($other) {
175
            --$other->order;
176
            $other->save();
177
        }
178
179
        ++$rule->order;
180
        $rule->save();
181
        $this->resetRulesInGroupOrder($rule->ruleGroup);
182
183
        return true;
184
    }
185
186
    /**
187
     * @param Rule $rule
188
     *
189
     * @return bool
190
     */
191
    public function moveUp(Rule $rule): bool
192
    {
193
        $order = $rule->order;
194
195
        // find the rule with order-1 and give it order+1
196
        $other = $rule->ruleGroup->rules()->where('order', $order - 1)->first();
197
        if ($other) {
198
            ++$other->order;
199
            $other->save();
200
        }
201
202
        --$rule->order;
203
        $rule->save();
204
        $this->resetRulesInGroupOrder($rule->ruleGroup);
205
206
        return true;
207
    }
208
209
    /**
210
     * @param Rule  $rule
211
     * @param array $ids
212
     *
213
     * @return bool
214
     */
215
    public function reorderRuleActions(Rule $rule, array $ids): bool
216
    {
217
        $order = 1;
218
        foreach ($ids as $actionId) {
219
            /** @var RuleTrigger $trigger */
220
            $action = $rule->ruleActions()->find($actionId);
221
            if (null !== $action) {
222
                $action->order = $order;
223
                $action->save();
224
                ++$order;
225
            }
226
        }
227
228
        return true;
229
    }
230
231
    /**
232
     * @param Rule  $rule
233
     * @param array $ids
234
     *
235
     * @return bool
236
     */
237
    public function reorderRuleTriggers(Rule $rule, array $ids): bool
238
    {
239
        $order = 1;
240
        foreach ($ids as $triggerId) {
241
            /** @var RuleTrigger $trigger */
242
            $trigger = $rule->ruleTriggers()->find($triggerId);
243
            if (null !== $trigger) {
244
                $trigger->order = $order;
245
                $trigger->save();
246
                ++$order;
247
            }
248
        }
249
250
        return true;
251
    }
252
253
    /**
254
     * @param RuleGroup $ruleGroup
255
     *
256
     * @return bool
257
     */
258
    public function resetRulesInGroupOrder(RuleGroup $ruleGroup): bool
259
    {
260
        $ruleGroup->rules()->whereNotNull('deleted_at')->update(['order' => 0]);
261
262
        $set   = $ruleGroup->rules()
263
                           ->orderBy('order', 'ASC')
264
                           ->orderBy('updated_at', 'DESC')
265
                           ->get();
266
        $count = 1;
267
        /** @var Rule $entry */
268
        foreach ($set as $entry) {
269
            $entry->order = $count;
270
            $entry->save();
271
            ++$count;
272
        }
273
274
        return true;
275
    }
276
277
    /**
278
     * @param User $user
279
     */
280
    public function setUser(User $user): void
281
    {
282
        $this->user = $user;
283
    }
284
285
    /**
286
     * @param array $data
287
     *
288
     * @return Rule
289
     */
290
    public function store(array $data): Rule
291
    {
292
        /** @var RuleGroup $ruleGroup */
293
        $ruleGroup = $this->user->ruleGroups()->find($data['rule_group_id']);
294
295
        // get max order:
296
        $order = $this->getHighestOrderInRuleGroup($ruleGroup);
297
298
        // start by creating a new rule:
299
        $rule = new Rule;
300
        $rule->user()->associate($this->user->id);
301
302
        $rule->rule_group_id   = $data['rule_group_id'];
303
        $rule->order           = ($order + 1);
304
        $rule->active          = true;
305
        $rule->strict          = $data['strict'] ?? false;
306
        $rule->stop_processing = 1 === (int)$data['stop_processing'];
307
        $rule->title           = $data['title'];
308
        $rule->description     = \strlen($data['description']) > 0 ? $data['description'] : null;
309
310
        $rule->save();
311
312
        // start storing triggers:
313
        $this->storeTriggers($rule, $data);
314
315
        // same for actions.
316
        $this->storeActions($rule, $data);
317
318
        return $rule;
319
    }
320
321
    /**
322
     * @param Rule  $rule
323
     * @param array $values
324
     *
325
     * @return RuleAction
326
     */
327
    public function storeAction(Rule $rule, array $values): RuleAction
328
    {
329
        $ruleAction = new RuleAction;
330
        $ruleAction->rule()->associate($rule);
331
        $ruleAction->order           = $values['order'];
332
        $ruleAction->active          = true;
333
        $ruleAction->stop_processing = $values['stop_processing'];
334
        $ruleAction->action_type     = $values['action'];
335
        $ruleAction->action_value    = $values['value'] ?? '';
336
        $ruleAction->save();
337
338
        return $ruleAction;
339
    }
340
341
    /**
342
     * @param Rule  $rule
343
     * @param array $values
344
     *
345
     * @return RuleTrigger
346
     */
347
    public function storeTrigger(Rule $rule, array $values): RuleTrigger
348
    {
349
        $ruleTrigger = new RuleTrigger;
350
        $ruleTrigger->rule()->associate($rule);
351
        $ruleTrigger->order           = $values['order'];
352
        $ruleTrigger->active          = true;
353
        $ruleTrigger->stop_processing = $values['stop_processing'];
354
        $ruleTrigger->trigger_type    = $values['action'];
355
        $ruleTrigger->trigger_value   = $values['value'] ?? '';
356
        $ruleTrigger->save();
357
358
        return $ruleTrigger;
359
    }
360
361
    /**
362
     * @param Rule  $rule
363
     * @param array $data
364
     *
365
     * @return Rule
366
     */
367
    public function update(Rule $rule, array $data): Rule
368
    {
369
        // update rule:
370
        $rule->rule_group_id   = $data['rule_group_id'];
371
        $rule->active          = $data['active'];
372
        $rule->stop_processing = $data['stop_processing'];
373
        $rule->title           = $data['title'];
374
        $rule->strict          = $data['strict'] ?? false;
375
        $rule->description     = $data['description'];
376
        $rule->save();
377
378
        // delete triggers:
379
        $rule->ruleTriggers()->delete();
380
381
        // delete actions:
382
        $rule->ruleActions()->delete();
383
384
        // recreate triggers:
385
        $this->storeTriggers($rule, $data);
386
387
        // recreate actions:
388
        $this->storeActions($rule, $data);
389
390
        return $rule;
391
    }
392
393
    /**
394
     * @param Rule  $rule
395
     * @param array $data
396
     *
397
     * @return bool
398
     */
399
    private function storeActions(Rule $rule, array $data): bool
400
    {
401
        $order = 1;
402
        foreach ($data['rule_actions'] as $action) {
403
            $value          = $action['value'] ?? '';
404
            $stopProcessing = $action['stop_processing'] ?? false;
405
406
            $actionValues = [
407
                'action'          => $action['name'],
408
                'value'           => $value,
409
                'stop_processing' => $stopProcessing,
410
                'order'           => $order,
411
            ];
412
413
            $this->storeAction($rule, $actionValues);
414
        }
415
416
        return true;
417
    }
418
419
    /**
420
     * @param Rule  $rule
421
     * @param array $data
422
     *
423
     * @return bool
424
     */
425
    private function storeTriggers(Rule $rule, array $data): bool
426
    {
427
        $order          = 1;
428
        $stopProcessing = false;
429
430
        $triggerValues = [
431
            'action'          => 'user_action',
432
            'value'           => $data['trigger'],
433
            'stop_processing' => $stopProcessing,
434
            'order'           => $order,
435
        ];
436
437
        $this->storeTrigger($rule, $triggerValues);
438
        foreach ($data['rule_triggers'] as $trigger) {
439
            $value          = $trigger['value'] ?? '';
440
            $stopProcessing = $trigger['stop_processing'] ?? false;
441
442
            $triggerValues = [
443
                'action'          => $trigger['name'],
444
                'value'           => $value,
445
                'stop_processing' => $stopProcessing,
446
                'order'           => $order,
447
            ];
448
449
            $this->storeTrigger($rule, $triggerValues);
450
            ++$order;
451
        }
452
453
        return true;
454
    }
455
}
456