Passed
Pull Request — master (#68)
by Anton
05:41 queued 02:10
created

Reactant::getConnectionName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
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 getConnectionName(): ?string
50
    {
51
        return COG_LOVE_DB_CONNECTION ?? $this->connection;
52
    }
53
54
    public function reactable(): MorphTo
55
    {
56
        return $this->morphTo('reactable', 'type', 'id', 'love_reactant_id');
57
    }
58
59
    public function reactions(): HasMany
60
    {
61
        return $this->hasMany(Reaction::class, 'reactant_id');
62
    }
63
64
    public function reactionCounters(): HasMany
65
    {
66
        return $this->hasMany(ReactionCounter::class, 'reactant_id');
67
    }
68
69
    public function reactionTotal(): HasOne
70
    {
71
        return $this->hasOne(ReactionTotal::class, 'reactant_id');
72
    }
73
74
    public function getId(): string
75
    {
76
        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...
77
    }
78
79
    public function getReactable(): ReactableContract
80
    {
81
        $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...
82
83
        if (is_null($reactable)) {
84
            throw new NotAssignedToReactable();
85
        }
86
87
        return $reactable;
88
    }
89
90
    public function getReactions(): iterable
91
    {
92
        return $this->getAttribute('reactions');
93
    }
94
95
    public function getReactionCounters(): iterable
96
    {
97
        return $this->getAttribute('reactionCounters');
98
    }
99
100
    public function getReactionCounterOfType(
101
        ReactionTypeContract $reactionType
102
    ): ReactionCounterContract {
103
        // TODO: Test query count with eager loaded relation
104
        // TODO: Test query count without eager loaded relation
105
        $counter = $this
106
            ->getAttribute('reactionCounters')
107
            ->where('reaction_type_id', $reactionType->getId())
108
            ->first();
109
110
        if (is_null($counter)) {
111
            return new NullReactionCounter($this, $reactionType);
112
        }
113
114
        return $counter;
115
    }
116
117
    public function getReactionTotal(): ReactionTotalContract
118
    {
119
        return $this->getAttribute('reactionTotal')
120
            ?? new NullReactionTotal($this);
121
    }
122
123
    public function isReactedBy(
124
        ReacterContract $reacter
125
    ): bool {
126
        if ($reacter->isNull()) {
127
            return false;
128
        }
129
130
        // TODO: Test if relation was loaded partially
131
        if ($this->relationLoaded('reactions')) {
132
            return $this
133
                ->getAttribute('reactions')
134
                ->contains(function (ReactionContract $reaction) use ($reacter) {
135
                    return $reaction->isByReacter($reacter);
136
                });
137
        }
138
139
        return $this->reactions()->where([
140
            'reacter_id' => $reacter->getId(),
141
        ])->exists();
142
    }
143
144
    public function isNotReactedBy(
145
        ReacterContract $reacter
146
    ): bool {
147
        return !$this->isReactedBy($reacter);
148
    }
149
150
    public function isReactedByWithType(
151
        ReacterContract $reacter,
152
        ReactionTypeContract $reactionType
153
    ): bool {
154
        if ($reacter->isNull()) {
155
            return false;
156
        }
157
158
        // TODO: Test if relation was loaded partially
159
        if ($this->relationLoaded('reactions')) {
160
            return $this
161
                ->getAttribute('reactions')
162
                ->contains(function (ReactionContract $reaction) use ($reacter, $reactionType) {
163
                    return $reaction->isByReacter($reacter)
164
                        && $reaction->isOfType($reactionType);
165
                });
166
        }
167
168
        return $this->reactions()->where([
169
            'reaction_type_id' => $reactionType->getId(),
170
            'reacter_id' => $reacter->getId(),
171
        ])->exists();
172
    }
173
174
    public function isNotReactedByWithType(
175
        ReacterContract $reacter,
176
        ReactionTypeContract $reactionType
177
    ): bool {
178
        return !$this->isReactedByWithType($reacter, $reactionType);
179
    }
180
181
    public function isEqualTo(
182
        ReactantContract $that
183
    ): bool {
184
        return $that->isNotNull()
185
            && $this->getId() === $that->getId();
186
    }
187
188
    public function isNotEqualTo(
189
        ReactantContract $that
190
    ): bool {
191
        return !$this->isEqualTo($that);
192
    }
193
194
    public function isNull(): bool
195
    {
196
        return !$this->exists;
197
    }
198
199
    public function isNotNull(): bool
200
    {
201
        return $this->exists;
202
    }
203
204
    public function createReactionCounterOfType(
205
        ReactionTypeContract $reactionType
206
    ): void {
207
        if ($this->reactionCounters()->where('reaction_type_id', $reactionType->getId())->exists()) {
208
            throw ReactionCounterDuplicate::ofTypeForReactant($reactionType, $this);
209
        }
210
211
        $this->reactionCounters()->create([
212
            'reaction_type_id' => $reactionType->getId(),
213
            'count' => 0,
214
            'weight' => 0,
215
        ]);
216
217
        // Need to reload relation with fresh data
218
        $this->load('reactionCounters');
219
    }
220
221
    public function createReactionTotal(): void
222
    {
223
        if ($this->reactionTotal()->exists()) {
224
            throw ReactionTotalDuplicate::forReactant($this);
225
        }
226
227
        $this->reactionTotal()->create([
228
            'count' => 0,
229
            'weight' => 0,
230
        ]);
231
232
        // Need to reload relation with fresh data
233
        $this->load('reactionTotal');
234
    }
235
}
236