-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathExistsEloquent.php
221 lines (193 loc) · 5.59 KB
/
ExistsEloquent.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
<?php
declare(strict_types=1);
namespace Korridor\LaravelModelValidationRules\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class ExistsEloquent implements ValidationRule
{
/**
* Class name of model.
*
* @var class-string<Model>
*/
private string $model;
/**
* Relevant key in the model.
*
* @var string|null
*/
private ?string $key;
/**
* Closure that can extend the eloquent builder.
*
* @var Closure|null
*/
private ?Closure $builderClosure;
/**
* Custom validation message.
*
* @var string|null
*/
private ?string $customMessage = null;
/**
* Custom translation key for message.
*
* @var string|null
*/
private ?string $customMessageTranslationKey = null;
/**
* Include soft deleted models in the query.
*
* @var bool
*/
private bool $includeSoftDeleted = false;
/**
* @var bool Whether the key field is of type UUID
*/
private bool $isFieldUuid = false;
/**
* Create a new rule instance.
*
* @param class-string<Model> $model Class name of model
* @param string|null $key Relevant key in the model
* @param Closure|null $builderClosure Closure that can extend the eloquent builder
*/
public function __construct(string $model, ?string $key = null, ?Closure $builderClosure = null)
{
$this->model = $model;
$this->key = $key;
$this->setBuilderClosure($builderClosure);
}
/**
* Create a new rule instance.
*
* @param class-string<Model> $model Class name of model
* @param string|null $key Relevant key in the model
* @param Closure|null $builderClosure Closure that can extend the eloquent builder
*/
public static function make(string $model, ?string $key = null, ?Closure $builderClosure = null): self
{
return new self($model, $key, $builderClosure);
}
/**
* Set a custom validation message.
*
* @param string $message
* @return $this
*/
public function withMessage(string $message): self
{
$this->customMessage = $message;
return $this;
}
/**
* Set a translated custom validation message.
*
* @param string $translationKey
* @return $this
*/
public function withCustomTranslation(string $translationKey): self
{
$this->customMessageTranslationKey = $translationKey;
return $this;
}
/**
* The field has the data type UUID.
* If the field is not a UUID, the validation will fail, before the query is executed.
* This is useful for example for Postgres databases where queries fail if a field with UUID data type is queried with a non-UUID value.
*
* @return $this
*/
public function uuid(): self
{
$this->isFieldUuid = true;
return $this;
}
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @param Closure $fail
*
* @return void
*/
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if ($this->isFieldUuid) {
if (!is_string($value) || !Str::isUuid($value)) {
$this->fail($attribute, $value, $fail);
return;
}
}
/** @var Model|Builder $builder */
$builder = new $this->model();
$modelKeyName = $builder->getKeyName();
if (null === $this->key) {
$builder = $builder->where($modelKeyName, $value);
} else {
$builder = $builder->where($this->key, $value);
}
if (null !== $this->builderClosure) {
$builderClosure = $this->builderClosure;
$builder = $builderClosure($builder);
}
if ($this->includeSoftDeleted) {
$builder = $builder->withTrashed();
}
if ($builder->doesntExist()) {
$this->fail($attribute, $value, $fail);
return;
}
}
private function fail(string $attribute, mixed $value, Closure $fail): void
{
if ($this->customMessage !== null) {
$fail($this->customMessage);
} else {
$fail($this->customMessageTranslationKey ?? 'modelValidationRules::validation.exists_model')->translate([
'attribute' => $attribute,
'model' => strtolower(class_basename($this->model)),
'value' => $value,
]);
}
}
/**
* @param Closure|null $builderClosure
*/
public function setBuilderClosure(?Closure $builderClosure): void
{
$this->builderClosure = $builderClosure;
}
/**
* @param Closure $builderClosure
* @return $this
*/
public function query(Closure $builderClosure): self
{
$this->setBuilderClosure($builderClosure);
return $this;
}
/**
* Activate or deactivate including soft deleted models in the query.
*
* @param bool $includeSoftDeleted
* @return void
*/
public function setIncludeSoftDeleted(bool $includeSoftDeleted): void
{
$this->includeSoftDeleted = $includeSoftDeleted;
}
/**
* Activate including soft deleted models in the query.
* @return $this
*/
public function includeSoftDeleted(): self
{
$this->setIncludeSoftDeleted(true);
return $this;
}
}