Skip to content

Commit d986d8c

Browse files
committed
Remote Model Validation Rule
1 parent 76e069d commit d986d8c

File tree

3 files changed

+159
-0
lines changed

3 files changed

+159
-0
lines changed

src/Providers/MicroserviceServiceProvider.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Illuminate\Support\Facades\Auth;
88
use Illuminate\Support\Facades\Gate;
99
use Illuminate\Support\Facades\Http;
10+
use Illuminate\Support\Facades\Validator;
1011
use Illuminate\Support\ServiceProvider;
1112
use Kroderdev\LaravelMicroserviceCore\Auth\GatewayGuard;
1213
use Kroderdev\LaravelMicroserviceCore\Contracts\AccessUserInterface;
@@ -18,6 +19,7 @@
1819
use Kroderdev\LaravelMicroserviceCore\Http\Middleware\PermissionMiddleware;
1920
use Kroderdev\LaravelMicroserviceCore\Http\Middleware\RoleMiddleware;
2021
use Kroderdev\LaravelMicroserviceCore\Http\Middleware\ValidateJwt;
22+
use Kroderdev\LaravelMicroserviceCore\Interfaces\ApiModelContract;
2123
use Kroderdev\LaravelMicroserviceCore\Services\ApiGatewayClient;
2224
use Kroderdev\LaravelMicroserviceCore\Services\ApiGatewayClientFactory;
2325
use Kroderdev\LaravelMicroserviceCore\Services\AuthServiceClient;
@@ -189,6 +191,24 @@ public function boot(Router $router): void
189191
->timeout(5);
190192
});
191193

194+
// Validation
195+
Validator::extend('exists_remote', function ($attribute, $value, $parameters) {
196+
$model = $parameters[0] ?? null;
197+
$column = $parameters[1] ?? 'id';
198+
if (! $model || ! is_subclass_of($model, ApiModelContract::class)) {
199+
return false;
200+
}
201+
202+
$values = is_array($value) ? $value : [$value];
203+
foreach ($values as $v) {
204+
if (! $model::find($v)) {
205+
return false;
206+
}
207+
}
208+
209+
return true;
210+
});
211+
192212
// Exceptions
193213
$handler = $this->app->make(ExceptionHandler::class);
194214

src/Rules/ExistsRemote.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace Kroderdev\LaravelMicroserviceCore\Rules;
4+
5+
use Illuminate\Contracts\Validation\Rule;
6+
7+
class ExistsRemote implements Rule
8+
{
9+
protected string $model;
10+
protected string $column;
11+
12+
public function __construct(string $model, string $column = 'id')
13+
{
14+
$this->model = $model;
15+
$this->column = $column;
16+
}
17+
18+
public function passes($attribute, $value): bool
19+
{
20+
$values = is_array($value) ? $value : [$value];
21+
22+
foreach ($values as $v) {
23+
if (! $this->model::find($v)) {
24+
return false;
25+
}
26+
}
27+
28+
return true;
29+
}
30+
31+
public function message(): string
32+
{
33+
return trans('validation.exists');
34+
}
35+
}

tests/Rules/ExistsRemoteTest.php

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<?php
2+
3+
namespace Tests\Rules;
4+
5+
use Illuminate\Support\Facades\Validator;
6+
use Orchestra\Testbench\TestCase;
7+
use Kroderdev\LaravelMicroserviceCore\Contracts\ApiGatewayClientInterface;
8+
use Kroderdev\LaravelMicroserviceCore\Models\Model as ApiModel;
9+
use Kroderdev\LaravelMicroserviceCore\Providers\MicroserviceServiceProvider;
10+
use Tests\Services\FakeGatewayClient;
11+
12+
require_once __DIR__.'/../Services/FakeGatewayClient.php';
13+
14+
class RemoteRole extends ApiModel
15+
{
16+
protected static string $endpoint = '/roles';
17+
18+
protected $fillable = ['id'];
19+
}
20+
21+
class ExistsRemoteTest extends TestCase
22+
{
23+
protected FakeGatewayClient $gateway;
24+
25+
protected function getPackageProviders($app)
26+
{
27+
return [MicroserviceServiceProvider::class];
28+
}
29+
30+
protected function setUp(): void
31+
{
32+
parent::setUp();
33+
$this->gateway = new FakeGatewayClient();
34+
$this->app->bind(ApiGatewayClientInterface::class, fn () => $this->gateway);
35+
}
36+
37+
/** @test */
38+
public function passes_when_all_ids_exist()
39+
{
40+
$this->gateway = new class () extends FakeGatewayClient {
41+
public function get(string $uri, array $query = [])
42+
{
43+
parent::get($uri, $query);
44+
if (in_array($uri, ['/roles/1', '/roles/2'])) {
45+
$id = (int) substr($uri, 7);
46+
return ['data' => ['id' => $id]];
47+
}
48+
49+
return null;
50+
}
51+
};
52+
$this->app->bind(ApiGatewayClientInterface::class, fn () => $this->gateway);
53+
54+
$validator = Validator::make(
55+
['roles' => [1, 2]],
56+
['roles.*' => ['exists_remote:' . RemoteRole::class . ',id']]
57+
);
58+
59+
$this->assertTrue($validator->passes());
60+
}
61+
62+
/** @test */
63+
public function rule_object_works_the_same()
64+
{
65+
$this->gateway = new class () extends FakeGatewayClient {
66+
public function get(string $uri, array $query = [])
67+
{
68+
parent::get($uri, $query);
69+
return ['data' => ['id' => (int) substr($uri, 7)]];
70+
}
71+
};
72+
$this->app->bind(ApiGatewayClientInterface::class, fn () => $this->gateway);
73+
74+
$rule = new \Kroderdev\LaravelMicroserviceCore\Rules\ExistsRemote(RemoteRole::class);
75+
76+
$validator = Validator::make(['role' => 1], ['role' => [$rule]]);
77+
78+
$this->assertTrue($validator->passes());
79+
}
80+
81+
/** @test */
82+
public function fails_when_any_id_is_missing()
83+
{
84+
$this->gateway = new class () extends FakeGatewayClient {
85+
public function get(string $uri, array $query = [])
86+
{
87+
parent::get($uri, $query);
88+
if ($uri === '/roles/1') {
89+
return ['data' => ['id' => 1]];
90+
}
91+
92+
return null;
93+
}
94+
};
95+
$this->app->bind(ApiGatewayClientInterface::class, fn () => $this->gateway);
96+
97+
$validator = Validator::make(
98+
['roles' => [1, 2]],
99+
['roles.*' => ['exists_remote:' . RemoteRole::class . ',id']]
100+
);
101+
102+
$this->assertFalse($validator->passes());
103+
}
104+
}

0 commit comments

Comments
 (0)