Skip to content

Commit 6aaf856

Browse files
committed
Enable the use of extra where clauses
1 parent 5dc8590 commit 6aaf856

File tree

3 files changed

+205
-17
lines changed

3 files changed

+205
-17
lines changed

src/UniqueTranslationRule.php

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,18 @@
22

33
namespace CodeZero\UniqueTranslation;
44

5+
use Illuminate\Validation\Rules\DatabaseRule;
6+
57
class UniqueTranslationRule
68
{
7-
/**
8-
* @var string
9-
*/
10-
protected $rule = 'unique_translation';
9+
use DatabaseRule;
1110

1211
/**
13-
* The table to run the query against.
12+
* The name of the validation rule.
1413
*
1514
* @var string
1615
*/
17-
protected $table;
18-
19-
/**
20-
* The column to check on.
21-
*
22-
* @var string|null
23-
*/
24-
protected $column;
16+
protected $rule = 'unique_translation';
2517

2618
/**
2719
* The value of the the 'ignoreColumn' to ignore.
@@ -85,13 +77,14 @@ public function ignore($value, $column = 'id')
8577
*/
8678
public function __toString()
8779
{
88-
return sprintf(
89-
'%s:%s,%s,%s,%s',
80+
return rtrim(sprintf(
81+
'%s:%s,%s,%s,%s,%s',
9082
$this->rule,
9183
$this->table,
9284
$this->column ?: 'NULL',
9385
$this->ignoreValue ?: 'NULL',
94-
$this->ignoreColumn ?: 'NULL'
95-
);
86+
$this->ignoreColumn ?: 'NULL',
87+
$this->formatWheres()
88+
), ',');
9689
}
9790
}

src/UniqueTranslationValidator.php

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use App;
66
use Config;
77
use DB;
8+
use Illuminate\Support\Str;
89

910
class UniqueTranslationValidator
1011
{
@@ -113,6 +114,7 @@ protected function isUnique($value, $name, $locale, $parameters)
113114

114115
$query = $this->findTranslation($connection, $table, $column, $locale, $value);
115116
$query = $this->ignore($query, $ignoreColumn, $ignoreValue);
117+
$query = $this->addConditions($query, $this->getUniqueExtra($parameters));
116118

117119
$isUnique = $query->count() === 0;
118120

@@ -157,6 +159,85 @@ protected function ignore($query, $column = null, $value = null)
157159
return $query;
158160
}
159161

162+
/**
163+
* Get the extra conditions for a unique rule.
164+
* Taken From: \Illuminate\Validation\Concerns\ValidatesAttributes
165+
*
166+
* @param array $parameters
167+
*
168+
* @return array
169+
*/
170+
protected function getUniqueExtra($parameters)
171+
{
172+
if (isset($parameters[4])) {
173+
return $this->getExtraConditions(array_slice($parameters, 4));
174+
}
175+
176+
return [];
177+
}
178+
179+
/**
180+
* Get the extra conditions for a unique / exists rule.
181+
* Taken from: \Illuminate\Validation\Concerns\ValidatesAttributes
182+
*
183+
* @param array $segments
184+
*
185+
* @return array
186+
*/
187+
protected function getExtraConditions(array $segments)
188+
{
189+
$extra = [];
190+
191+
$count = count($segments);
192+
193+
for ($i = 0; $i < $count; $i += 2) {
194+
$extra[$segments[$i]] = $segments[$i + 1];
195+
}
196+
197+
return $extra;
198+
}
199+
200+
/**
201+
* Add the given conditions to the query.
202+
* Adapted from: \Illuminate\Validation\DatabasePresenceVerifier
203+
*
204+
* @param \Illuminate\Database\Query\Builder $query
205+
* @param array $conditions
206+
*
207+
* @return \Illuminate\Database\Query\Builder
208+
*/
209+
protected function addConditions($query, $conditions)
210+
{
211+
foreach ($conditions as $key => $value) {
212+
$this->addWhere($query, $key, $value);
213+
}
214+
215+
return $query;
216+
}
217+
218+
/**
219+
* Add a "where" clause to the given query.
220+
* Taken from: \Illuminate\Validation\DatabasePresenceVerifier
221+
*
222+
* @param \Illuminate\Database\Query\Builder $query
223+
* @param string $key
224+
* @param string $extraValue
225+
*
226+
* @return void
227+
*/
228+
protected function addWhere($query, $key, $extraValue)
229+
{
230+
if ($extraValue === 'NULL') {
231+
$query->whereNull($key);
232+
} elseif ($extraValue === 'NOT_NULL') {
233+
$query->whereNotNull($key);
234+
} elseif (Str::startsWith($extraValue, '!')) {
235+
$query->where($key, '!=', mb_substr($extraValue, 1));
236+
} else {
237+
$query->where($key, $extraValue);
238+
}
239+
}
240+
160241
/**
161242
* Add error messages to the validator.
162243
*

tests/WhereClauseTest.php

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?php
2+
3+
namespace CodeZero\UniqueTranslation\Tests;
4+
5+
use CodeZero\UniqueTranslation\Tests\Stubs\Model;
6+
use CodeZero\UniqueTranslation\UniqueTranslationRule;
7+
use Validator;
8+
9+
// * * *
10+
// You can use any method defined in the DatabaseRule
11+
// trait, except the whereIn and whereNotIn methods.
12+
//
13+
// https://laravel.com/api/5.8/Illuminate/Validation/Rules/DatabaseRule.html
14+
//
15+
// This is because it uses a closure which cannot be converted into a string.
16+
// We need to convert the rule into a string to use it with the UniqueTranslationValidator.
17+
// The reason we use this kind of validator is because it has access to the Validator instance.
18+
// We need that instance to add custom error messages.
19+
// * * *
20+
21+
class WhereClauseTestTest extends TestCase
22+
{
23+
/** @test */
24+
public function it_accepts_where_clause()
25+
{
26+
Model::create([
27+
'slug' => ['en' => 'existing-slug-en'],
28+
'name' => ['en' => 'existing-name-en'],
29+
'other_field' => 'foobar',
30+
]);
31+
32+
$rules = [
33+
'slug.*' => "{$this->rule}:{$this->table},slug,null,null,other_field,!foobar",
34+
'name.*' => UniqueTranslationRule::for($this->table)->where('other_field', 'not foobar'),
35+
];
36+
37+
$validation = Validator::make([
38+
'slug' => ['en' => 'existing-slug-en'],
39+
'name' => ['en' => 'existing-name-en'],
40+
], $rules);
41+
42+
$this->assertTrue($validation->passes());
43+
$this->assertEmpty($validation->errors()->keys());
44+
}
45+
46+
/** @test */
47+
public function it_accepts_where_not_clauses()
48+
{
49+
Model::create([
50+
'slug' => ['en' => 'existing-slug-en'],
51+
'name' => ['en' => 'existing-name-en'],
52+
'other_field' => 'foobar',
53+
]);
54+
55+
$rules = [
56+
'slug.*' => "{$this->rule}:{$this->table},slug,null,null,other_field,!foobar",
57+
'name.*' => UniqueTranslationRule::for($this->table)->whereNot('other_field', 'foobar'),
58+
];
59+
60+
$validation = Validator::make([
61+
'slug' => ['en' => 'existing-slug-en'],
62+
'name' => ['en' => 'existing-name-en'],
63+
], $rules);
64+
65+
$this->assertTrue($validation->passes());
66+
$this->assertEmpty($validation->errors()->keys());
67+
}
68+
69+
/** @test */
70+
public function it_accepts_where_null_clause()
71+
{
72+
Model::create([
73+
'slug' => ['en' => 'existing-slug-en'],
74+
'name' => ['en' => 'existing-name-en'],
75+
'other_field' => 'foobar',
76+
]);
77+
78+
$rules = [
79+
'slug.*' => "{$this->rule}:{$this->table},slug,null,null,other_field,NULL",
80+
'name.*' => UniqueTranslationRule::for($this->table)->whereNull('other_field'),
81+
];
82+
83+
$validation = Validator::make([
84+
'slug' => ['en' => 'existing-slug-en'],
85+
'name' => ['en' => 'existing-name-en'],
86+
], $rules);
87+
88+
$this->assertTrue($validation->passes());
89+
$this->assertEmpty($validation->errors()->keys());
90+
}
91+
92+
/** @test */
93+
public function it_accepts_where_not_null_clause()
94+
{
95+
Model::create([
96+
'slug' => ['en' => 'existing-slug-en'],
97+
'name' => ['en' => 'existing-name-en'],
98+
'other_field' => null,
99+
]);
100+
101+
$rules = [
102+
'slug.*' => "{$this->rule}:{$this->table},slug,null,null,other_field,NOT_NULL",
103+
'name.*' => UniqueTranslationRule::for($this->table)->whereNotNull('other_field'),
104+
];
105+
106+
$validation = Validator::make([
107+
'slug' => ['en' => 'existing-slug-en'],
108+
'name' => ['en' => 'existing-name-en'],
109+
], $rules);
110+
111+
$this->assertTrue($validation->passes());
112+
$this->assertEmpty($validation->errors()->keys());
113+
}
114+
}

0 commit comments

Comments
 (0)