Completed
Pull Request — master (#24)
by Anton
02:57 queued 39s
created

Recount::recountTotal()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 10
dl 0
loc 16
rs 9.9332
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
3
/*
4
 * This file is part of Laravel Love.
5
 *
6
 * (c) Anton Komarev <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Cog\Laravel\Love\Console\Commands;
15
16
use Cog\Contracts\Love\Reactable\Exceptions\ReactableInvalid;
17
use Cog\Contracts\Love\Reactable\Models\Reactable as ReactableContract;
18
use Cog\Contracts\Love\Reactant\Models\Reactant as ReactantContract;
19
use Cog\Laravel\Love\Reactant\Models\Reactant;
20
use Cog\Laravel\Love\Reactant\ReactionCounter\Services\ReactionCounterService;
21
use Cog\Laravel\Love\ReactionType\Models\ReactionType;
22
use Illuminate\Console\Command;
23
use Illuminate\Contracts\Events\Dispatcher;
24
use Illuminate\Database\Eloquent\Relations\Relation;
25
26
final class Recount extends Command
27
{
28
    /**
29
     * The name and signature of the console command.
30
     *
31
     * @var string
32
     */
33
    protected $signature = 'love:recount {reactableType?} {type?}';
34
35
    /**
36
     * The console command description.
37
     *
38
     * @var string
39
     */
40
    protected $description = 'Recount reactions of the reactable models';
41
42
    /**
43
     * Execute the console command.
44
     *
45
     * @param \Illuminate\Contracts\Events\Dispatcher $events
46
     * @return void
47
     *
48
     * @throws \Cog\Contracts\Love\Reactable\Exceptions\ReactableInvalid
49
     */
50
    public function handle(
51
        Dispatcher $events
0 ignored issues
show
Unused Code introduced by
The parameter $events is not used and could be removed. ( Ignorable by Annotation )

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

51
        /** @scrutinizer ignore-unused */ Dispatcher $events

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
52
    ): void {
53
        if ($reactableType = $this->argument('reactableType')) {
54
            $reactableType = $this->normalizeReactableModelType($reactableType);
0 ignored issues
show
Bug introduced by
It seems like $reactableType can also be of type string[]; however, parameter $modelType of Cog\Laravel\Love\Console...izeReactableModelType() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

54
            $reactableType = $this->normalizeReactableModelType(/** @scrutinizer ignore-type */ $reactableType);
Loading history...
55
        }
56
57
        if ($reactionType = $this->argument('type')) {
58
            $reactionType = ReactionType::fromName($reactionType);
0 ignored issues
show
Bug introduced by
It seems like $reactionType can also be of type string[]; however, parameter $name of Cog\Laravel\Love\Reactio...eactionType::fromName() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

58
            $reactionType = ReactionType::fromName(/** @scrutinizer ignore-type */ $reactionType);
Loading history...
59
        }
60
61
        $reactantsQuery = Reactant::query();
62
63
        if ($reactableType) {
64
            $reactantsQuery->where('type', $reactableType);
65
        }
66
67
        $reactants = $reactantsQuery->get();
68
        foreach ($reactants as $reactant) {
69
            /** @var \Illuminate\Database\Eloquent\Builder $query */
70
            $query = $reactant->reactions();
71
72
            if ($reactionType) {
73
                $query->where('reaction_type_id', $reactionType->getId());
74
            }
75
76
            $counters = $reactant->getReactionCounters();
77
78
            /** @var \Cog\Laravel\Love\Reactant\ReactionCounter\Models\ReactionCounter $counter */
79
            foreach ($counters as $counter) {
80
                if ($reactionType && !$counter->isReactionOfType($reactionType)) {
81
                    continue;
82
                }
83
84
                $counter->update([
85
                    'count' => 0,
86
                    'weight' => 0,
87
                ]);
88
            }
89
90
            $reactions = $query->get();
91
            $this->recountCounters($reactant, $reactions);
92
            $this->recountTotal($reactant);
93
        }
94
    }
95
96
    /**
97
     * Normalize reactable model type.
98
     *
99
     * @param string $modelType
100
     * @return string
101
     *
102
     * @throws \Cog\Contracts\Love\Reactable\Exceptions\ReactableInvalid
103
     */
104
    private function normalizeReactableModelType(
105
        string $modelType
106
    ): string {
107
        return $this
108
            ->reactableModelFromType($modelType)
109
            ->getMorphClass();
0 ignored issues
show
Bug introduced by
The method getMorphClass() does not exist on Cog\Contracts\Love\Reactable\Models\Reactable. Since it exists in all sub-types, consider adding an abstract or default implementation to Cog\Contracts\Love\Reactable\Models\Reactable. ( Ignorable by Annotation )

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

109
            ->/** @scrutinizer ignore-call */ getMorphClass();
Loading history...
110
    }
111
112
    /**
113
     * Instantiate model from type or morph map value.
114
     *
115
     * @param string $modelType
116
     * @return \Cog\Contracts\Love\Reactable\Models\Reactable|\Illuminate\Database\Eloquent\Model
117
     *
118
     * @throws \Cog\Contracts\Love\Reactable\Exceptions\ReactableInvalid
119
     */
120
    private function reactableModelFromType(
121
        string $modelType
122
    ): ReactableContract {
123
        if (!class_exists($modelType)) {
124
            $modelType = $this->findModelTypeInMorphMap($modelType);
125
        }
126
127
        $model = new $modelType;
128
129
        if (!$model instanceof ReactableContract) {
130
            throw ReactableInvalid::notImplementInterface($modelType);
131
        }
132
133
        return $model;
134
    }
135
136
    /**
137
     * Find model type in morph mappings registry.
138
     *
139
     * @param string $modelType
140
     * @return string
141
     *
142
     * @throws \Cog\Contracts\Love\Reactable\Exceptions\ReactableInvalid
143
     */
144
    private function findModelTypeInMorphMap(
145
        string $modelType
146
    ): string {
147
        $morphMap = Relation::morphMap();
148
149
        if (!isset($morphMap[$modelType])) {
150
            throw ReactableInvalid::classNotExists($modelType);
151
        }
152
153
        return $morphMap[$modelType];
154
    }
155
156
    private function recountTotal(
157
        ReactantContract $reactant
158
    ): void {
159
        $counters = $reactant->getReactionCounters();
160
        $totalCount = 0;
161
        $totalWeight = 0;
162
        foreach ($counters as $counter) {
163
            $totalCount += $counter->getCount();
164
            $totalWeight += $counter->getWeight();
165
        }
166
167
        /** @var \Cog\Laravel\Love\Reactant\ReactionTotal\Models\ReactionTotal $total */
168
        $total = $reactant->getReactionTotal();
169
        $total->update([
170
            'count' => $totalCount,
171
            'weight' => $totalWeight,
172
        ]);
173
    }
174
175
    private function recountCounters(
176
        ReactantContract $reactant,
177
        iterable $reactions
178
    ): void {
179
        $service = new ReactionCounterService($reactant);
180
181
        foreach ($reactions as $reaction) {
182
            $service->addReaction($reaction);
183
        }
184
    }
185
}
186