Skip to content

Instantly share code, notes, and snippets.

@matthewerskine
Last active September 22, 2019 21:50
Show Gist options
  • Select an option

  • Save matthewerskine/09af2ad5031f9c318e03b7f41bdd59de to your computer and use it in GitHub Desktop.

Select an option

Save matthewerskine/09af2ad5031f9c318e03b7f41bdd59de to your computer and use it in GitHub Desktop.
Laravel 5.5 Request Director
<?php
use App\Http\ResourceDirector;
if (! function_exists('store')) {
/**
* Stores a new resource using the ResourceDirector.
*
* @param string $resource
* @param string|closure $using
* @return \Illuminate\Http\Resources\Json\Resource
*/
function store($resource, $using)
{
return app(ResourceDirector::class)->store($resource, $using);
}
}
<?php
namespace App\Http;
use Illuminate\Http\JsonResponse;
use Illuminate\Validation\ValidationException;
use App\Contracts\Http\Resources\ValidatorResourceContract;
class ResourceDirector
{
/**
* Validates, stores and responds with a new resource.
*
* @param string $resourceClass
* @param string|closure $using
* @param \Illuminate\Http\Request|null $request
* @return \Illuminate\Http\Resources\Json\Resource
*/
public function store($resourceClass, $using, $request = null)
{
$request = $request ?? request();
return tap(new $resourceClass(null), function($resource) use($using, $request) {
$this->validateIfApplicable($resource, $request);
$resource->resource = is_callable($using)
? $using($request)
: (new $using)->create($request->input());
});
}
/**
* Validates a given request if the resource is described to do so.
*
* @param \Illuminate\Http\Resources\Json\Resource $resource
* @param \Illuminate\Http\Request $request
* @return void
*
* @throws \Illuminate\Validation\ValidationException
*/
protected function validateIfApplicable($resource, $request)
{
if (!$resource instanceof ValidatorResourceContract) {
return false;
}
try {
$resource->validate($request, $validator = app('validator'));
} catch (ValidationException $exception) {
throw new ValidationException($validator, new JsonResponse(
$exception->errors(), 422
));
}
}
}
<?php
use App\Http\ResourceDirector;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Resources\Json\Resource;
use Illuminate\Validation\ValidationException;
use App\Contracts\Http\Resources\ValidatorResourceContract;
class ResourceDirectorTest extends TestCase
{
public function setup()
{
parent::setup();
$this->director = new ResourceDirector;
}
public function test_it_validates_the_request_as_directed_by_the_resource()
{
$this->expectException(ValidationException::class);
$resource = $this->director->store(TestResource::class, TestModel::class);
}
public function test_it_responds_with_a_filled_resource()
{
$resource = $this->director->store(TestResource::class, TestModel::class, tap(request(), function($request) {
$request->merge(['foo' => '123']);
}));
$this->assertEquals('bar', $resource->foo);
$this->assertEquals('bar', $resource->toArray(null)['foo']);
}
public function test_a_custom_callback_can_instantiate_the_model()
{
$request = request();
$request->merge(['foo' => 123]);
$resource = $this->director->store(TestResource::class, function($request) {
return (object) ['foo' => '999'];
}, $request);
$this->assertEquals('999', $resource->foo);
}
public function test_it_supports_resources_that_do_not_require_validation()
{
$resource = $this->director->store(TestResourceNoValidation::class, function($request) {
return ['ok'];
});
$this->assertEquals(['ok'], $resource->resource);
}
}
class TestModel
{
public function create()
{
return (object) ['foo' => 'bar'];
}
}
class TestResourceNoValidation extends Resource
{
//
}
class TestResource extends Resource implements ValidatorResourceContract
{
public function validate($request, $validator)
{
return $validator->validate($request->input(), [
'foo' => 'required',
]);
}
public function toArray($request)
{
return ['foo' => $this->foo];
}
}
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\Resource;
use App\Contracts\Http\Resources\ValidatorResourceContract;
class UserResource extends Resource implements ValidatorResourceContract
{
public function validate($request, $validator)
{
$validator->validate($request->input(), [
'first_name' => 'required',
'last_name' => 'required',
]);
}
public function toArray($request)
{
return [
'last_name' => $this->last_name,
'first_name' => $this->first_name,
];
}
}
<?php
namespace App\Contracts\Http\Resources;
interface ValidatorResourceContract
{
/**
* Validates the request.
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Validation\Validator $validator
* @return void
*/
public function validate($request, $validator);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment