Passed
Pull Request — master (#251)
by Anton
03:49 queued 50s
created

Reaction::getId()   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
eloc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
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\Reaction\Models;
15
16
use Cog\Contracts\Love\Reactant\Models\Reactant as ReactantInterface;
17
use Cog\Contracts\Love\Reacter\Models\Reacter as ReacterInterface;
18
use Cog\Contracts\Love\Reaction\Exceptions\RateInvalid;
19
use Cog\Contracts\Love\Reaction\Exceptions\RateOutOfRange;
20
use Cog\Contracts\Love\Reaction\Models\Reaction as ReactionInterface;
21
use Cog\Contracts\Love\ReactionType\Models\ReactionType as ReactionTypeInterface;
22
use Cog\Laravel\Love\Reactant\Models\Reactant;
23
use Cog\Laravel\Love\Reacter\Models\Reacter;
24
use Cog\Laravel\Love\ReactionType\Models\ReactionType;
25
use Cog\Laravel\Love\Support\Database\Eloquent\Model;
26
use Illuminate\Database\Eloquent\Casts\Attribute;
27
use Illuminate\Database\Eloquent\Factories\HasFactory;
28
use Illuminate\Database\Eloquent\Relations\BelongsTo;
29
30
final class Reaction extends Model implements
31
    ReactionInterface
32
{
33
    use HasFactory;
34
35
    public const RATE_DEFAULT = 1.0;
36
    public const RATE_MIN = 0.01;
37
    public const RATE_MAX = 99.99;
38
39
    protected $table = 'love_reactions';
40
41
    /**
42
     * @var array<string, float>
43
     */
44
    protected $attributes = [
45
        'rate' => self::RATE_DEFAULT,
46
    ];
47
48
    /**
49
     * @var array<int, string>
50
     */
51
    protected $fillable = [
52
        'reactant_id',
53
        'reaction_type_id',
54
        'rate',
55
    ];
56
57
    /**
58
     * @var array<string, string>
59
     */
60
    protected $casts = [
61
        'id' => 'string',
62
        'rate' => 'float',
63
    ];
64
65
    public function rate(): Attribute
66
    {
67
        return new Attribute(
68
            set: function (float | null $value) {
69
                if ($value !== null && ($value < self::RATE_MIN || $value > self::RATE_MAX)) {
70
                    throw RateOutOfRange::withValueBetween(
71
                        $value,
72
                        self::RATE_MIN,
73
                        self::RATE_MAX,
74
                    );
75
                }
76
77
                return $value ?? self::RATE_DEFAULT;
78
            },
79
        );
80
    }
81
82
    public function reactant(): BelongsTo
83
    {
84
        return $this->belongsTo(Reactant::class, 'reactant_id');
85
    }
86
87
    public function reacter(): BelongsTo
88
    {
89
        return $this->belongsTo(Reacter::class, 'reacter_id');
90
    }
91
92
    public function type(): BelongsTo
93
    {
94
        return $this->belongsTo(ReactionType::class, 'reaction_type_id');
95
    }
96
97
    public function getId(): string
98
    {
99
        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...
100
    }
101
102
    public function getReactant(): ReactantInterface
103
    {
104
        return $this->getAttribute('reactant');
105
    }
106
107
    public function getReacter(): ReacterInterface
108
    {
109
        return $this->getAttribute('reacter');
110
    }
111
112
    public function getType(): ReactionTypeInterface
113
    {
114
        return $this->getAttribute('type');
115
    }
116
117
    public function getRate(): float
118
    {
119
        return $this->getAttributeValue('rate');
120
    }
121
122
    public function getWeight(): float
123
    {
124
        return $this->getType()->getMass() * $this->getRate();
125
    }
126
127
    public function isOfType(
128
        ReactionTypeInterface $reactionType,
129
    ): bool {
130
        return $this->getType()->isEqualTo($reactionType);
131
    }
132
133
    public function isNotOfType(
134
        ReactionTypeInterface $reactionType,
135
    ): bool {
136
        return $this->getType()->isNotEqualTo($reactionType);
137
    }
138
139
    public function isToReactant(
140
        ReactantInterface $reactant,
141
    ): bool {
142
        return $this->getReactant()->isEqualTo($reactant);
143
    }
144
145
    public function isNotToReactant(
146
        ReactantInterface $reactant,
147
    ): bool {
148
        return !$this->isToReactant($reactant);
149
    }
150
151
    public function isByReacter(
152
        ReacterInterface $reacter,
153
    ): bool {
154
        return $this->getReacter()->isEqualTo($reacter);
155
    }
156
157
    public function isNotByReacter(
158
        ReacterInterface $reacter,
159
    ): bool {
160
        return !$this->isByReacter($reacter);
161
    }
162
163
    public function changeRate(
164
        float $rate,
165
    ): void {
166
        if ($this->getRate() === $rate) {
167
            throw RateInvalid::withSameValue($rate);
168
        }
169
170
        $this->setAttribute('rate', $rate);
171
        $this->save();
172
    }
173
}
174