88use Illuminate \Contracts \Validation \ValidationRule ;
99use Illuminate \Database \Eloquent \Builder ;
1010use Illuminate \Database \Eloquent \Model ;
11+ use Illuminate \Support \Str ;
1112
1213class UniqueEloquent implements ValidationRule
1314{
@@ -63,12 +64,17 @@ class UniqueEloquent implements ValidationRule
6364 */
6465 private bool $ includeSoftDeleted = false ;
6566
67+ /**
68+ * @var bool Whether the ID is a UUID
69+ */
70+ private bool $ isFieldUuid = false ;
71+
6672 /**
6773 * UniqueEloquent constructor.
6874 *
69- * @param class-string<Model> $model Class name of model.
70- * @param string|null $key Relevant key in the model.
71- * @param Closure|null $builderClosure Closure that can extend the eloquent builder
75+ * @param class-string<Model> $model Class name of model.
76+ * @param string|null $key Relevant key in the model.
77+ * @param Closure|null $builderClosure Closure that can extend the eloquent builder
7278 */
7379 public function __construct (string $ model , ?string $ key = null , ?Closure $ builderClosure = null )
7480 {
@@ -77,17 +83,32 @@ public function __construct(string $model, ?string $key = null, ?Closure $builde
7783 $ this ->setBuilderClosure ($ builderClosure );
7884 }
7985
86+ /**
87+ * @param class-string<Model> $model Class name of model.
88+ * @param string|null $key Relevant key in the model.
89+ * @param Closure|null $builderClosure Closure that can extend the eloquent builder
90+ */
91+ public static function make (string $ model , ?string $ key = null , ?Closure $ builderClosure = null ): self
92+ {
93+ return new self ($ model , $ key , $ builderClosure );
94+ }
95+
8096 /**
8197 * Determine if the validation rule passes.
8298 *
83- * @param string $attribute
84- * @param mixed $value
85- * @param Closure $fail
99+ * @param string $attribute
100+ * @param mixed $value
101+ * @param Closure $fail
86102 *
87103 * @return void
88104 */
89105 public function validate (string $ attribute , mixed $ value , Closure $ fail ): void
90106 {
107+ if ($ this ->isFieldUuid ) {
108+ if (!is_string ($ value ) || !Str::isUuid ($ value )) {
109+ return ;
110+ }
111+ }
91112 /** @var Model|Builder $builder */
92113 $ builder = new $ this ->model ();
93114 $ modelKeyName = $ builder ->getKeyName ();
@@ -112,19 +133,20 @@ public function validate(string $attribute, mixed $value, Closure $fail): void
112133 if ($ this ->customMessage !== null ) {
113134 $ fail ($ this ->customMessage );
114135 } else {
115- $ fail ($ this ->customMessageTranslationKey ?? 'modelValidationRules::validation.unique_model ' )->translate ([
116- 'attribute ' => $ attribute ,
117- 'model ' => strtolower (class_basename ($ this ->model )),
118- 'value ' => $ value ,
119- ]);
136+ $ fail ($ this ->customMessageTranslationKey ?? 'modelValidationRules::validation.unique_model ' )
137+ ->translate ([
138+ 'attribute ' => $ attribute ,
139+ 'model ' => strtolower (class_basename ($ this ->model )),
140+ 'value ' => $ value ,
141+ ]);
120142 }
121143 }
122144 }
123145
124146 /**
125147 * Set a custom validation message.
126148 *
127- * @param string $message
149+ * @param string $message
128150 * @return $this
129151 */
130152 public function withMessage (string $ message ): self
@@ -137,7 +159,7 @@ public function withMessage(string $message): self
137159 /**
138160 * Set a translated custom validation message.
139161 *
140- * @param string $translationKey
162+ * @param string $translationKey
141163 * @return $this
142164 */
143165 public function withCustomTranslation (string $ translationKey ): self
@@ -150,15 +172,15 @@ public function withCustomTranslation(string $translationKey): self
150172 /**
151173 * Set a closure that can extend the eloquent builder.
152174 *
153- * @param Closure|null $builderClosure
175+ * @param Closure|null $builderClosure
154176 */
155177 public function setBuilderClosure (?Closure $ builderClosure ): void
156178 {
157179 $ this ->builderClosure = $ builderClosure ;
158180 }
159181
160182 /**
161- * @param Closure $builderClosure
183+ * @param Closure $builderClosure
162184 * @return $this
163185 */
164186 public function query (Closure $ builderClosure ): self
@@ -169,8 +191,8 @@ public function query(Closure $builderClosure): self
169191 }
170192
171193 /**
172- * @param mixed $id
173- * @param string|null $column
194+ * @param mixed $id
195+ * @param string|null $column
174196 */
175197 public function setIgnore (mixed $ id , ?string $ column = null ): void
176198 {
@@ -180,7 +202,7 @@ public function setIgnore(mixed $id, ?string $column = null): void
180202
181203 /**
182204 * @param mixed $id
183- * @param string|null $column
205+ * @param string|null $column
184206 * @return UniqueEloquent
185207 */
186208 public function ignore (mixed $ id , ?string $ column = null ): self
@@ -201,6 +223,20 @@ public function setIncludeSoftDeleted(bool $includeSoftDeleted): void
201223 $ this ->includeSoftDeleted = $ includeSoftDeleted ;
202224 }
203225
226+ /**
227+ * The field has the data type UUID.
228+ * If a value is not a UUID, the validation will be skipped.
229+ * 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.
230+ *
231+ * @return $this
232+ */
233+ public function uuid (): self
234+ {
235+ $ this ->isFieldUuid = true ;
236+
237+ return $ this ;
238+ }
239+
204240 /**
205241 * Activate including soft deleted models in the query.
206242 *
0 commit comments