diff --git a/src/Controllers/RestResponse.php b/src/Controllers/RestResponse.php index be073c2e5..4cc9aa3ea 100644 --- a/src/Controllers/RestResponse.php +++ b/src/Controllers/RestResponse.php @@ -10,6 +10,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Http\JsonResponse; use Illuminate\Support\Arr; +use Illuminate\Support\Facades\App; /** * Class RestResponse. @@ -580,7 +581,7 @@ public function getErrors() return $this->errors instanceof Arrayable ? $this->errors->toArray() : $this->errors; } - public function debug(\Exception $exception, $condition) + public function dump(\Exception $exception, $condition) { if ($condition) { $this->debug = true; @@ -595,6 +596,17 @@ public function debug(\Exception $exception, $condition) return $this; } + /** + * Debug the log if the environment is local. + * + * @param \Exception $exception + * @return $this + */ + public function dumpLocal(\Exception $exception) + { + return $this->dump($exception, App::environment('production') === false); + } + /** * Set the JSON:API format for a single resource. * diff --git a/src/Exceptions/RestifyHandler.php b/src/Exceptions/RestifyHandler.php index 98d759073..4e5351121 100644 --- a/src/Exceptions/RestifyHandler.php +++ b/src/Exceptions/RestifyHandler.php @@ -109,7 +109,7 @@ public function render($request, Exception $exception) if (App::environment('production') === true) { $response->addError(__('messages.something_went_wrong')); } else { - $response->debug($exception, true); + $response->dump($exception, true); } $response->error(); } diff --git a/src/Fields/Field.php b/src/Fields/Field.php index 1a43c325e..8bf3f2aae 100644 --- a/src/Fields/Field.php +++ b/src/Fields/Field.php @@ -118,8 +118,8 @@ public function fillAttribute(RestifyRequest $request, $model) */ protected function fillAttributeFromRequest(RestifyRequest $request, $model, $attribute) { - if ($request->exists($attribute)) { - $value = $request[$attribute]; + if ($request->exists($attribute) || $request->get($attribute)) { + $value = $request[$attribute] ?? $request->get($attribute); $model->{$attribute} = is_callable($this->storeCallback) ? call_user_func($this->storeCallback, $value, $request, $model) : $value; } diff --git a/src/Http/Controllers/RepositoryIndexController.php b/src/Http/Controllers/RepositoryIndexController.php index 221fb2ac8..c186a3a98 100644 --- a/src/Http/Controllers/RepositoryIndexController.php +++ b/src/Http/Controllers/RepositoryIndexController.php @@ -28,11 +28,11 @@ public function handle(RestifyRequest $request) } catch (EntityNotFoundException $e) { return $this->response()->notFound() ->addError($e->getMessage()) - ->debug($e, $request->isDev()); + ->dump($e, $request->isDev()); } catch (UnauthorizedException $e) { - return $this->response()->forbidden()->addError($e->getMessage())->debug($e, $request->isDev()); + return $this->response()->forbidden()->addError($e->getMessage())->dump($e, $request->isDev()); } catch (InstanceOfException | \Throwable $e) { - return $this->response()->error()->debug($e, $request->isDev()); + return $this->response()->error()->dump($e, $request->isDev()); } } } diff --git a/src/Http/Requests/InteractWithRepositories.php b/src/Http/Requests/InteractWithRepositories.php index c426bfc1c..18a4df474 100644 --- a/src/Http/Requests/InteractWithRepositories.php +++ b/src/Http/Requests/InteractWithRepositories.php @@ -31,13 +31,12 @@ public function authorize() /** * Get the class name of the repository being requested. * + * @param null $key * @return Repository - * @throws EntityNotFoundException - * @throws UnauthorizedException */ - public function repository() + public function repository($key = null) { - return tap(Restify::repositoryForKey($this->route('repository')), function ($repository) { + return tap(Restify::repositoryForKey($key ?? $this->route('repository')), function ($repository) { if (is_null($repository)) { throw new EntityNotFoundException(__('Repository :name not found.', [ 'name' => $repository, @@ -109,11 +108,12 @@ public function isResolvedByRestify() * As a model it could accept either a model instance, a collection or even paginated collection. * * @param $model + * @param null $uriKey * @return Repository */ - public function newRepositoryWith($model) + public function newRepositoryWith($model, $uriKey = null) { - $repository = $this->repository(); + $repository = $this->repository($uriKey); return $repository::resolveWith($model); } @@ -121,25 +121,27 @@ public function newRepositoryWith($model) /** * Get a new, scopeless query builder for the underlying model. * + * @param null $uriKey * @return \Illuminate\Database\Eloquent\Builder * @throws EntityNotFoundException * @throws UnauthorizedException */ - public function newQueryWithoutScopes() + public function newQueryWithoutScopes($uriKey = null) { - return $this->model()->newQueryWithoutScopes(); + return $this->model($uriKey)->newQueryWithoutScopes(); } /** * Get a new instance of the underlying model. * + * @param null $uriKey * @return \Illuminate\Database\Eloquent\Model * @throws EntityNotFoundException * @throws UnauthorizedException */ - public function model() + public function model($uriKey = null) { - $repository = $this->repository(); + $repository = $this->repository($uriKey); return $repository::newModel(); } @@ -147,12 +149,15 @@ public function model() /** * Get the query to find the model instance for the request. * - * @param mixed|null $repositoryId + * @param mixed|null $repositoryId + * @param null $uriKey * @return \Illuminate\Database\Eloquent\Builder + * @throws EntityNotFoundException + * @throws UnauthorizedException */ - public function findModelQuery($repositoryId = null) + public function findModelQuery($repositoryId = null, $uriKey = null) { - return $this->newQueryWithoutScopes()->whereKey( + return $this->newQueryWithoutScopes($uriKey)->whereKey( $repositoryId ?? request('repositoryId') ); } diff --git a/src/Repositories/Crudable.php b/src/Repositories/Crudable.php index 3f53c80a4..8b555a852 100644 --- a/src/Repositories/Crudable.php +++ b/src/Repositories/Crudable.php @@ -5,6 +5,9 @@ use Binaryk\LaravelRestify\Contracts\RestifySearchable; use Binaryk\LaravelRestify\Controllers\RestResponse; use Binaryk\LaravelRestify\Exceptions\UnauthorizedException; +use Binaryk\LaravelRestify\Http\Requests\RepositoryDestroyRequest; +use Binaryk\LaravelRestify\Http\Requests\RepositoryStoreRequest; +use Binaryk\LaravelRestify\Http\Requests\RepositoryUpdateRequest; use Binaryk\LaravelRestify\Http\Requests\RestifyRequest; use Binaryk\LaravelRestify\Restify; use Binaryk\LaravelRestify\Services\Search\SearchService; @@ -21,7 +24,7 @@ trait Crudable { /** - * @param RestifyRequest $request + * @param RestifyRequest $request * @return JsonResponse * @throws \Binaryk\LaravelRestify\Exceptions\InstanceOfException * @throws \Throwable @@ -62,18 +65,16 @@ public function index(RestifyRequest $request) } /** - * @param RestifyRequest $request + * @param RestifyRequest $request + * @param $repositoryId * @return JsonResponse + * @throws AuthorizationException + * @throws UnauthorizedException + * @throws \Binaryk\LaravelRestify\Exceptions\Eloquent\EntityNotFoundException */ public function show(RestifyRequest $request, $repositoryId) { - /** - * Dive into the Search service to attach relations. - */ - $this->withResource(tap($this->resource, function ($query) use ($request) { - static::detailQuery($request, $query); - })->firstOrFail()); - + $this->resource = static::showPlain($repositoryId); try { $this->allowToShow($request); } catch (AuthorizationException $e) { @@ -84,8 +85,10 @@ public function show(RestifyRequest $request, $repositoryId) } /** - * @param RestifyRequest $request + * @param RestifyRequest $request * @return JsonResponse + * @throws AuthorizationException + * @throws ValidationException */ public function store(RestifyRequest $request) { @@ -98,62 +101,57 @@ public function store(RestifyRequest $request) ->code(RestResponse::REST_RESPONSE_INVALID_CODE); } - $model = DB::transaction(function () use ($request) { - $model = self::fillWhenStore( - $request, self::newModel() - ); + $this->resource = static::storePlain($request->toArray()); - $model->save(); - - return $model; - }); + static::stored($this->resource); return $this->response('', RestResponse::REST_RESPONSE_CREATED_CODE) - ->model($model) - ->header('Location', Restify::path().'/'.static::uriKey().'/'.$model->id); + ->model($this->resource) + ->header('Location', Restify::path().'/'.static::uriKey().'/'.$this->resource->id); } /** - * @param RestifyRequest $request - * @param $model + * @param RestifyRequest $request + * @param $repositoryId * @return JsonResponse - * @throws \Illuminate\Auth\Access\AuthorizationException + * @throws AuthorizationException + * @throws UnauthorizedException * @throws ValidationException + * @throws \Binaryk\LaravelRestify\Exceptions\Eloquent\EntityNotFoundException */ public function update(RestifyRequest $request, $repositoryId) { $this->allowToUpdate($request); - DB::transaction(function () use ($request) { - $model = static::fillWhenUpdate($request, $this->resource); + $this->resource = static::updatePlain($request->all(), $repositoryId); - $model->save(); - - return $this; - }); + static::updated($this->resource); return response()->json($this->jsonSerialize(), RestResponse::REST_RESPONSE_UPDATED_CODE); } /** - * @param RestifyRequest $request + * @param RestifyRequest $request + * @param $repositoryId * @return JsonResponse - * @throws \Illuminate\Auth\Access\AuthorizationException + * @throws AuthorizationException + * @throws UnauthorizedException + * @throws \Binaryk\LaravelRestify\Exceptions\Eloquent\EntityNotFoundException */ public function destroy(RestifyRequest $request, $repositoryId) { $this->allowToDestroy($request); - DB::transaction(function () use ($request) { - return $this->resource->delete(); - }); + $status = static::destroyPlain($repositoryId); + + static::deleted($status); return $this->response() ->setStatusCode(RestResponse::REST_RESPONSE_DELETED_CODE); } /** - * @param RestifyRequest $request + * @param RestifyRequest $request * @return mixed * @throws \Illuminate\Auth\Access\AuthorizationException * @throws ValidationException @@ -168,7 +166,7 @@ public function allowToUpdate(RestifyRequest $request) } /** - * @param RestifyRequest $request + * @param RestifyRequest $request * @return mixed * @throws \Illuminate\Auth\Access\AuthorizationException * @throws ValidationException @@ -183,7 +181,7 @@ public function allowToStore(RestifyRequest $request) } /** - * @param RestifyRequest $request + * @param RestifyRequest $request * @throws \Illuminate\Auth\Access\AuthorizationException */ public function allowToDestroy(RestifyRequest $request) @@ -202,11 +200,149 @@ public function allowToShow($request) /** * @param $request - * @param Collection $items + * @param Collection $items * @throws \Illuminate\Auth\Access\AuthorizationException */ public function allowToViewAny($request, Collection $items) { $this->authorizeToShowAny($request); } + + /** + * Validate input array and store a new entity. + * + * @param array $payload + * @return mixed + * @throws AuthorizationException + * @throws ValidationException + */ + public static function storePlain(array $payload) + { + /** * @var RepositoryStoreRequest $request */ + $request = resolve(RepositoryStoreRequest::class); + $request->attributes->add($payload); + + $repository = resolve(static::class); + + $repository->allowToStore($request); + + return DB::transaction(function () use ($request) { + $model = self::fillWhenStore( + $request, self::newModel() + ); + + $model->save(); + + return $model; + }); + } + + /** + * Update an entity with an array of payload. + * + * @param array $payload + * @param $id + * @return mixed + * @throws AuthorizationException + * @throws UnauthorizedException + * @throws ValidationException + * @throws \Binaryk\LaravelRestify\Exceptions\Eloquent\EntityNotFoundException + */ + public static function updatePlain(array $payload, $id) + { + /** * @var RepositoryUpdateRequest $request */ + $request = resolve(RepositoryUpdateRequest::class); + $request->attributes->add($payload); + + $model = $request->findModelQuery($id, static::uriKey())->lockForUpdate()->firstOrFail(); + + /** + * @var Repository + */ + $repository = $request->newRepositoryWith($model, static::uriKey()); + + $repository->allowToUpdate($request); + + return DB::transaction(function () use ($request, $repository) { + $model = static::fillWhenUpdate($request, $repository->resource); + + $model->save(); + + return $model; + }); + } + + /** + * Returns a plain model by key + * Used as: Book::showPlain(1). + * + * @param $key + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator|\Illuminate\Database\Eloquent\Model + * @throws AuthorizationException + * @throws UnauthorizedException + * @throws \Binaryk\LaravelRestify\Exceptions\Eloquent\EntityNotFoundException + */ + public static function showPlain($key) + { + /** * @var RestifyRequest $request */ + $request = resolve(RestifyRequest::class); + + /** + * Dive into the Search service to attach relations. + */ + $repository = $request->newRepositoryWith(tap($request->findModelQuery($key, static::uriKey())->firstOrFail(), function ($query) use ($request) { + static::detailQuery($request, $query); + })); + + $repository->allowToShow($request); + + return $repository->resource; + } + + /** + * Validate deletion and delete entity. + * + * @param $key + * @return mixed + * @throws AuthorizationException + * @throws UnauthorizedException + * @throws \Binaryk\LaravelRestify\Exceptions\Eloquent\EntityNotFoundException + */ + public static function destroyPlain($key) + { + /** * @var RepositoryDestroyRequest $request */ + $request = resolve(RepositoryDestroyRequest::class); + + $repository = $request->newRepositoryWith($request->findModelQuery($key, static::uriKey())->firstOrFail(), static::uriKey()); + + $repository->allowToDestroy($request); + + return DB::transaction(function () use ($repository) { + return $repository->resource->delete(); + }); + } + + /** + * @param $model + */ + public static function stored($model) + { + // + } + + /** + * @param $model + */ + public static function updated($model) + { + // + } + + /** + * @param int $status + */ + public static function deleted($status) + { + // + } } diff --git a/src/Repositories/RepositoryFillFields.php b/src/Repositories/RepositoryFillFields.php index 26e793f9b..6873cbda2 100644 --- a/src/Repositories/RepositoryFillFields.php +++ b/src/Repositories/RepositoryFillFields.php @@ -23,11 +23,11 @@ public static function fillWhenStore(RestifyRequest $request, $model) { static::fillFields( $request, $model, - (new static($model))->collectFields($request) + static::resolveWith($model)->collectFields($request) ); static::fillExtra($request, $model, - (new static($model))->collectFields($request) + static::resolveWith($model)->collectFields($request) ); return $model; diff --git a/src/Repositories/ValidatingTrait.php b/src/Repositories/ValidatingTrait.php index 3ffecd0a3..d5ce515cc 100644 --- a/src/Repositories/ValidatingTrait.php +++ b/src/Repositories/ValidatingTrait.php @@ -29,7 +29,7 @@ abstract public static function newModel(); */ public static function validatorForStoring(RestifyRequest $request) { - $on = (new static(static::newModel())); + $on = static::resolveWith(static::newModel()); $messages = $on->collectFields($request)->flatMap(function ($k) { $messages = [];