Passed
Pull Request — master (#251)
by Anton
03:05
created

Reaction::rate()   A

Complexity

Conditions 4
Paths 1

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 9
c 0
b 0
f 0
dl 0
loc 14
rs 9.9666
cc 4
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
    protected static $unguarded = true;
42
43
    /**
44
     * @var array<string, float>
45
     */
46
    protected $attributes = [
47
        'rate' => self::RATE_DEFAULT,
48
    ];
49
50
    public function id(): Attribute
51
    {
52
        return new Attribute(
53
            get: fn (string | null $value) => $value,
54
            set: fn (string | null $value) => $value,
55
        );
56
    }
57
58
    public function rate(): Attribute
59
    {
60
        return new Attribute(
61
            get: fn (float | null $value) => $value ?? self::RATE_DEFAULT,
62
            set: function (float | null $value) {
63
                if ($value !== null && ($value < self::RATE_MIN || $value > self::RATE_MAX)) {
64
                    throw RateOutOfRange::withValueBetween(
65
                        $value,
66
                        self::RATE_MIN,
67
                        self::RATE_MAX,
68
                    );
69
                }
70
71
                return $value ?? self::RATE_DEFAULT;
72
            },
73
        );
74
    }
75
76
    public function reactant(): BelongsTo
77
    {
78
        return $this->belongsTo(Reactant::class, 'reactant_id');
79
    }
80
81
    public function reacter(): BelongsTo
82
    {
83
        return $this->belongsTo(Reacter::class, 'reacter_id');
84
    }
85
86
    public function type(): BelongsTo
87
    {
88
        return $this->belongsTo(ReactionType::class, 'reaction_type_id');
89
    }
90
91
    public function getId(): string
92
    {
93
        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...
94
    }
95
96
    public function getReactant(): ReactantInterface
97
    {
98
        return $this->getAttribute('reactant');
99
    }
100
101
    public function getReacter(): ReacterInterface
102
    {
103
        return $this->getAttribute('reacter');
104
    }
105
106
    public function getType(): ReactionTypeInterface
107
    {
108
        return $this->getAttribute('type');
109
    }
110
111
    public function getRate(): float
112
    {
113
        return $this->getAttributeValue('rate');
114
    }
115
116
    public function getWeight(): float
117
    {
118
        return $this->getType()->getMass() * $this->getRate();
119
    }
120
121
    public function isOfType(
122
        ReactionTypeInterface $reactionType,
123
    ): bool {
124
        return $this->getType()->isEqualTo($reactionType);
125
    }
126
127
    public function isNotOfType(
128
        ReactionTypeInterface $reactionType,
129
    ): bool {
130
        return $this->getType()->isNotEqualTo($reactionType);
131
    }
132
133
    public function isToReactant(
134
        ReactantInterface $reactant,
135
    ): bool {
136
        return $this->getReactant()->isEqualTo($reactant);
137
    }
138
139
    public function isNotToReactant(
140
        ReactantInterface $reactant,
141
    ): bool {
142
        return !$this->isToReactant($reactant);
143
    }
144
145
    public function isByReacter(
146
        ReacterInterface $reacter,
147
    ): bool {
148
        return $this->getReacter()->isEqualTo($reacter);
149
    }
150
151
    public function isNotByReacter(
152
        ReacterInterface $reacter,
153
    ): bool {
154
        return !$this->isByReacter($reacter);
155
    }
156
157
    public function changeRate(
158
        float $rate,
159
    ): void {
160
        if ($this->getRate() === $rate) {
161
            throw RateInvalid::withSameValue($rate);
162
        }
163
164
        $this->setAttribute('rate', $rate);
165
        $this->save();
166
    }
167
}
168