diff --git a/src/EchoIt/JsonApi/Exception/Validation.php b/src/EchoIt/JsonApi/Exception/Validation.php new file mode 100644 index 0000000..78732d5 --- /dev/null +++ b/src/EchoIt/JsonApi/Exception/Validation.php @@ -0,0 +1,42 @@ + + */ +class Validation extends Exception +{ + protected $httpStatusCode; + protected $validationMessages; + + /** + * Constructor. + * + * @param string $message The Exception message to throw + * @param int $code The Exception code + * @param int $httpStatusCode HTTP status code which can be used for broken request + * @param Illuminate\Support\MessageBag $messages Validation errors + */ + public function __construct($message = '', $code = 0, $httpStatusCode = 500, ValidationMessages $messages = NULL) + { + parent::__construct($message, $code); + + $this->httpStatusCode = $httpStatusCode; + $this->validationMessages = $messages; + } + + /** + * This method returns a HTTP response representation of the Exception + * + * @return JsonApi\MultiErrorResponse + */ + public function response() + { + return new MultiErrorResponse($this->httpStatusCode, $this->code, $this->message, $this->validationMessages); + } +} diff --git a/src/EchoIt/JsonApi/Handler.php b/src/EchoIt/JsonApi/Handler.php index 7243ac6..d42d952 100644 --- a/src/EchoIt/JsonApi/Handler.php +++ b/src/EchoIt/JsonApi/Handler.php @@ -526,6 +526,33 @@ protected function handleGetDefault(Request $request, $model) return $results; } + /** + * Validates passed data against a model + * Validation performed safely and only if model provides rules + * + * @param EchoIt\JsonApi\Model $model model to validate against + * @param Array $values passed array of values + * + * @throws Exception\Validation Exception thrown when validation fails + * + * @return Bool true if validation successful + */ + protected function validateModelData(Model $model, Array $values) + { + $validationResponse = $model->validateArray($values); + + if ($validationResponse === true) { + return true; + } + + throw new Exception\Validation( + 'Bad Request', + static::ERROR_SCOPE | static::ERROR_HTTP_METHOD_NOT_ALLOWED, + BaseResponse::HTTP_BAD_REQUEST, + $validationResponse + ); + } + /** * Default handling of POST request. * Must be called explicitly in handlePost function. @@ -537,6 +564,8 @@ protected function handleGetDefault(Request $request, $model) public function handlePostDefault(Request $request, $model) { $values = $this->parseRequestContent($request->content, $model->getResourceType()); + $this->validateModelData($model, $values); + $model->fill($values); if (!$model->save()) { diff --git a/src/EchoIt/JsonApi/Model.php b/src/EchoIt/JsonApi/Model.php index 8a0d323..05fc123 100644 --- a/src/EchoIt/JsonApi/Model.php +++ b/src/EchoIt/JsonApi/Model.php @@ -1,5 +1,6 @@ resourceType ?: $this->getTable()); } + /** + * Validate passed values + * + * @param Array $values user passed values (request data) + * + * @return bool|Illuminate\Support\MessageBag True on pass, MessageBag of errors on fail + */ + public function validateArray(Array $values) + { + if (count($this->getValidationRules())) { + $validator = Validator::make($values, $this->getValidationRules()); + + if ($validator->fails()) { + return $validator->errors(); + } + } + + return True; + } + + /** + * Return model validation rules + * Models should overload this to provide their validation rules + * + * @return Array validation rules + */ + public function getValidationRules() + { + return []; + } + /** * Convert the model instance to an array. This method overrides that of * Eloquent to prevent relations to be serialize into output array. diff --git a/src/EchoIt/JsonApi/MultiErrorResponse.php b/src/EchoIt/JsonApi/MultiErrorResponse.php new file mode 100644 index 0000000..54bbb42 --- /dev/null +++ b/src/EchoIt/JsonApi/MultiErrorResponse.php @@ -0,0 +1,39 @@ + + */ +class MultiErrorResponse extends JsonResponse +{ + /** + * Constructor. + * + * @param int $httpStatusCode HTTP status code + * @param mixed $errorCode Internal error code + * @param string $errorTitle Error description + * @param Illuminate\Support\MessageBag $errors Validation errors + */ + public function __construct($httpStatusCode, $errorCode, $errorTitle, ValidationMessages $errors = NULL) + { + $data = [ 'errors' => [] ]; + + if ($errors) { + foreach ($errors->keys() as $field) { + + foreach ($errors->get($field) as $message) { + + $data['errors'][] = [ 'status' => $httpStatusCode, 'code' => $errorCode, 'title' => 'Validation Fail', 'detail' => $message, 'meta' => ['field' => $field] ]; + + } + + } + } + + parent::__construct($data, $httpStatusCode); + } +}