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

Reactant::isReactedByWithType()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 12
dl 0
loc 22
rs 9.8666
c 0
b 0
f 0
cc 4
nc 3
nop 2
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\Reactant\Models;
15
16
use Cog\Contracts\Love\Reactable\Models\Reactable as ReactableContract;
17
use Cog\Contracts\Love\Reactant\Exceptions\NotAssignedToReactable;
18
use Cog\Contracts\Love\Reactant\Models\Reactant as ReactantContract;
19
use Cog\Contracts\Love\Reactant\ReactionCounter\Exceptions\ReactionCounterDuplicate;
20
use Cog\Contracts\Love\Reactant\ReactionCounter\Models\ReactionCounter as ReactionCounterContract;
21
use Cog\Contracts\Love\Reactant\ReactionTotal\Exceptions\ReactionTotalDuplicate;
22
use Cog\Contracts\Love\Reactant\ReactionTotal\Models\ReactionTotal as ReactionTotalContract;
23
use Cog\Contracts\Love\Reacter\Models\Reacter as ReacterContract;
24
use Cog\Contracts\Love\Reaction\Models\Reaction as ReactionContract;
25
use Cog\Contracts\Love\ReactionType\Models\ReactionType as ReactionTypeContract;
26
use Cog\Laravel\Love\Reactant\ReactionCounter\Models\NullReactionCounter;
27
use Cog\Laravel\Love\Reactant\ReactionCounter\Models\ReactionCounter;
28
use Cog\Laravel\Love\Reactant\ReactionTotal\Models\NullReactionTotal;
29
use Cog\Laravel\Love\Reactant\ReactionTotal\Models\ReactionTotal;
30
use Cog\Laravel\Love\Reaction\Models\Reaction;
31
use Illuminate\Database\Eloquent\Model;
32
use Illuminate\Database\Eloquent\Relations\HasMany;
33
use Illuminate\Database\Eloquent\Relations\HasOne;
34
use Illuminate\Database\Eloquent\Relations\MorphTo;
35
36
final class Reactant extends Model implements
37
    ReactantContract
38
{
39
    protected $table = 'love_reactants';
40
41
    protected $fillable = [
42
        'type',
43
    ];
44
45
    protected $casts = [
46
        'id' => 'string',
47
    ];
48
49
    public function reactable(): MorphTo
50
    {
51
        return $this->morphTo('reactable', 'type', 'id', 'love_reactant_id');
52
    }
53
54
    public function reactions(): HasMany
55
    {
56
        return $this->hasMany(Reaction::class, 'reactant_id');
57
    }
58
59
    public function reactionCounters(): HasMany
60
    {
61
        return $this->hasMany(ReactionCounter::class, 'reactant_id');
62
    }
63
64
    public function reactionTotal(): HasOne
65
    {
66
        return $this->hasOne(ReactionTotal::class, 'reactant_id');
67
    }
68
69
    public function getId(): string
70
    {
71
        return $this->getAttributeValue('id');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getAttributeValue('id') could return the type boolean|null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
72
    }
73
74
    public function getReactable(): ReactableContract
75
    {
76
        $reactable = $this->getAttribute('reactable');
0 ignored issues
show
Bug introduced by
The attribute reactable does not seem to exist on Cog\Laravel\Love\Reactant\Models\Reactant. Are you maybe missing a database migration?

If used attribute cannot be found there are two main reasons: 1. there is a creating database migration missing or 2. there is a major typo in either the declaration or the usage.

Loading history...
77
78
        if (is_null($reactable)) {
79
            throw new NotAssignedToReactable();
80
        }
81
82
        return $reactable;
83
    }
84
85
    public function getReactions(): iterable
86
    {
87
        return $this->getAttribute('reactions');
88
    }
89
90
    public function getReactionCounters(): iterable
91
    {
92
        return $this->getAttribute('reactionCounters');
93
    }
94
95
    public function getReactionCounterOfType(
96
        ReactionTypeContract $reactionType
97
    ): ReactionCounterContract {
98
        // TODO: Test query count with eager loaded relation
99
        // TODO: Test query count without eager loaded relation
100
        $counter = $this
101
            ->getAttribute('reactionCounters')
102
            ->where('reaction_type_id', $reactionType->getId())
103
            ->first();
104
105
        if (is_null($counter)) {
106
            return new NullReactionCounter($this, $reactionType);
107
        }
108
109
        return $counter;
110
    }
111
112
    public function getReactionTotal(): ReactionTotalContract
113
    {
114
        return $this->getAttribute('reactionTotal')
115
            ?? new NullReactionTotal($this);
116
    }
117
118
    public function isReactedBy(
119
        ReacterContract $reacter
120
    ): bool {
121
        if ($reacter->isNull()) {
122
            return false;
123
        }
124
125
        // TODO: Test if relation was loaded partially
126
        if ($this->relationLoaded('reactions')) {
127
            return $this
128
                ->getAttribute('reactions')
129
                ->contains(function (ReactionContract $reaction) use ($reacter) {
130
                    return $reaction->isByReacter($reacter);
131
                });
132
        }
133
134
        return $this->reactions()->where([
135
            'reacter_id' => $reacter->getId(),
136
        ])->exists();
137
    }
138
139
    public function isNotReactedBy(
140
        ReacterContract $reacter
141
    ): bool {
142
        return !$this->isReactedBy($reacter);
143
    }
144
145
    public function isReactedByWithType(
146
        ReacterContract $reacter,
147
        ReactionTypeContract $reactionType
148
    ): bool {
149
        if ($reacter->isNull()) {
150
            return false;
151
        }
152
153
        // TODO: Test if relation was loaded partially
154
        if ($this->relationLoaded('reactions')) {
155
            return $this
156
                ->getAttribute('reactions')
157
                ->contains(function (ReactionContract $reaction) use ($reacter, $reactionType) {
158
                    return $reaction->isByReacter($reacter)
159
                        && $reaction->isOfType($reactionType);
160
                });
161
        }
162
163
        return $this->reactions()->where([
164
            'reaction_type_id' => $reactionType->getId(),
165
            'reacter_id' => $reacter->getId(),
166
        ])->exists();
167
    }
168
169
    public function isNotReactedByWithType(
170
        ReacterContract $reacter,
171
        ReactionTypeContract $reactionType
172
    ): bool {
173
        return !$this->isReactedByWithType($reacter, $reactionType);
174
    }
175
176
    public function isEqualTo(
177
        ReactantContract $that
178
    ): bool {
179
        return $that->isNotNull()
180
            && $this->getId() === $that->getId();
181
    }
182
183
    public function isNotEqualTo(
184
        ReactantContract $that
185
    ): bool {
186
        return !$this->isEqualTo($that);
187
    }
188
189
    public function isNull(): bool
190
    {
191
        return !$this->exists;
192
    }
193
194
    public function isNotNull(): bool
195
    {
196
        return $this->exists;
197
    }
198
199
    public function createReactionCounterOfType(
200
        ReactionTypeContract $reactionType
201
    ): void {
202
        if ($this->reactionCounters()->where('reaction_type_id', $reactionType->getId())->exists()) {
203
            throw ReactionCounterDuplicate::ofTypeForReactant($reactionType, $this);
204
        }
205
206
        $this->reactionCounters()->create([
207
            'reaction_type_id' => $reactionType->getId(),
208
            'count' => 0,
209
            'weight' => 0,
210
        ]);
211
212
        // Need to reload relation with fresh data
213
        $this->load('reactionCounters');
214
    }
215
216
    public function createReactionTotal(): void
217
    {
218
        if ($this->reactionTotal()->exists()) {
219
            throw ReactionTotalDuplicate::forReactant($this);
220
        }
221
222
        $this->reactionTotal()->create([
223
            'count' => 0,
224
            'weight' => 0,
225
        ]);
226
227
        // Need to reload relation with fresh data
228
        $this->load('reactionTotal');
229
    }
230
}
231