Lumen - validation rules in model? - laravel

I am creating a REST API and would like to attach some validation rules to the CREATE and UPDATE endpoints. The validation rules for these two endpoints would be exactly the same, so I would like to be able to specify them in one place only. Coming from a Laravel background I normally create Form Requests, or put the validation rules in the model within a rules() function.
However I think Lumen does validation a bit differently and their documentation suggests to put the validation logic in the router: https://lumen.laravel.com/docs/master/validation
However personally I don't think that is the best place for it and would prefer to put in the model instead. I tried using the rules() function within the model but that doesn't seem to do anything.
My create and update methods look like this:
public function create(Request $request)
{
$product = Product::create($request->all());
return response()->json($product, 201);
}
public function update($id, Request $request)
{
$product = Product::findOrFail($id);
$product->update($request->all());
return response()->json($product, 200);
}
Is it possible for me to put my validation rules within my Product model and have them run automatically?
Here is how I have attempted to do it in my model:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'price', 'description',
];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = [];
/**
* Set model validation rules.
*
* #return array
*/
public function validate()
{
return [
'name' => 'required',
'price' => 'required',
'description' => 'required',
];
}
}

As you mentioned and in accordance with the Lumen validation documentation (as of 5.8):
Form requests are not supported by Lumen. If you would like to use form requests, you should use the full Laravel framework.
Lumen doesn't have anything similar to the automatic validation provided by Form Requests in Laravel, therefore you will need to perform validation manually.
Product
class Product extends Model
{
/**
* Get model validation rules.
*
* #return array
*/
public static function getValidationRules()
{
return [
'name' => 'required',
'price' => 'required',
'description' => 'required',
];
}
}
The above defines your Product model validation rules as a static methods, the rules should be the same for all your Product objects. You may want to consider scenarios where a field should be unique in your database, providing an argument to the method would be an option.
ProductController
class ProductController extends Controller
{
public function create(Request $request)
{
// Perform your validation
$validatedData = $request->validate(Product::getValidationRules());
// The Product is valid
$product = Product::create($request->all());
return response()->json($product, 201);
}
public function update($id, Request $request)
{
$product = Product::findOrFail($id);
// Perform your validation
$validatedData = $request->validate(Product::getValidationRules());
// The Product is valid
$product->update($request->all());
return response()->json($product, 200);
}
}
Where your validation fails with the above, Lumen will automatically redirect the user back to the previous location and errors flashed to the session (as with Laravel). Obviously you can alter this workflow if you desire.

The same validate method should be available at the controller level as well. So if you want to reuse the rules you could do something like this:
private $product_rules = [
'name' => 'required',
];
public function create(Request $request)
{
$this->validate($request, $this->product_rules);
$product = Product::create($request->all());
return response()->json($product, 201);
}
public function update($id, Request $request)
{
$product = Product::findOrFail($id);
$this->validate($request, $this->product_rules);
$product->update($request->all());
return response()->json($product, 200);
}

Related

How to use the ignore rule in Form Request Validation

this is PostsRequest.php in http/request:
<?php
namespace App\Http\Requests;
use App\Post;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class PostsRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'title' => ['required','max:255', Rule::unique('posts')->ignore($this->id)],
'slug' => ['required', Rule::unique('posts')->ignore($this->id),],
'content' => 'required',
'type' => 'required|in:blog,download,page',
'status' => 'required',
];
}
}
and this is edit() method in PostController.php
public function update(PostsRequest $request, $id)
{
$validated = $request->validated();
$validated['user_id'] = auth()->user()->id;
$post = Post::find($id)->fill($validated);
$post->save();
return redirect()->action('PostController#index');
}
Problem: show error in update page that this value is already exists.
who to resolve problem unique fields in edit form?
Problem Solved
change:
Rule::unique('posts')->ignore($this->route('id'))
with:
Rule::unique('posts')->ignore($this->route('post'))
If you're wanting to resolve the $id from the route then you can use the route() method in your request class e.g.
Rule::unique('posts')->ignore($this->route('id'))

How to add errors after FormRequests validation?

How to add errors after FormRequests validation?
password_repository->update() will return an error if the current passwords entered do not match.
password_repository->update() calls an external API.
I want to add an error in the controller depending on the return value of the repository.
In PasswordRequest, validation after calling the external API cannot be described, so I am in trouble.
For this reason I want to add an error in the controller after doing password_repository->update().
PasswordController.php
public function completeEdit(PasswordRequest $request)
{
$input = $request->only(['password', 'new_password']);
$data = $this->password_repository->update($input);
//I want to add an error at this point!!!
return view('pages.password.edit.complete');
}
}
PasswordRequest.php
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class PasswordRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'password' => 'required',
'new_password' => 'required|confirmed',
'new_password_confirmation' => 'required',
];
}
}
Redirect with errors could help you.
return redirect()->back()->withErrors([
'Password not correct',
]);
Or return to a specific route.
return redirect()->route('password.create')->withErrors([
'Password not correct',
]);

Laravel 5.7: Custom attributes for form validation not working

I'd like to validate a form using both custom messages and attributes. Instead of name: The name may not be greater than 20 characters. the user should see Name: Please use fewer characters., for example.
I'm using AJAX and both keys and values of the response.data.errors object that Laravel returns. Im using Laravel 5.7.
This is a simplified version of the validator function in my RegisterController.
protected function validator(array $data)
{
// Nice attribute names
$attributes = [
'name' => 'Name',
// ...
];
// Custom messages
$messages = [
'max' => 'Please use fewer characters.'
// ...
];
// Rules
$rules = [
'name'=> 'required|max:20',
// ...
];
// Working for messages, but not for attribute names
$validator = Validator::make($data, $rules, $messages, $attributes);
// Also not working
// $validator->setAttributeNames($attributes);
return $validator;
}
When there's a validation error, the user get's a message like name: Please use fewer characters.. That means the message from my custom array is displayed, but the default attribute name is used. What's wrong here?
Attributes do not replace key names, they are used to change the appearance of a key within a message - i.e The Name field is required - to achieve what you're trying to do in your question you'll need to create a new data array.
protected function validator(array $data)
{
$data = [
'Name' => $data['name'] ?? '',
// ...
];
// ...
Validator::make($data, $rules, $messages);
}
use Laravel Form Request, scroll down to Customizing The Error Messages section. Check out the below sample code.
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class UserRegistrationForm extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'name' => 'required|max:20',
];
}
/**
* Get the error messages for the defined validation rules.
*
* #return array
*/
public function messages()
{
return [
'name.max' => 'Please use less characters'
];
}
}
In controller
public function register(UserRegistrationForm $request)
{
// do saving here
}
That is coming from validation.php located in resources/Lang/xx/
EDIT :
You'll have to use
$messages = [
'name.max' => 'Your sentence here',
];

The effects of adding a Service Layer to a Laravel application

I have been developing with Zend Framework for a number of years and am now learning Laravel.
In my previous applications I usually have a Service Layer that is called by controllers. The Service Layer sits across the top of a Mapper and a Domain Model and is responsible for some application logic, raising events, some input filtering, etc.
Is there any reason why I should not implement a Service Layer in Laravel? In the examples that I have seen so far, controllers work directly with domain objects (or more accurately, active records).
If my Laravel controllers called my Service Layer, would I lose any of the advantages of Laravel? (As far as I can see I can still use Route/Model binding).
As a secondary question - what would be the best way to implement my Service Layer? As a collection of Service Providers, perhaps?
I also switched to Laravel coming from Zend and missed my Services. To sooth myself I have implemented a Service namespace which sits in namespace App\Services. In there I do all my Model or data handeling I don't want to see in my controller etc.
An example of my controller layout:
<?php
namespace App\Http\Controllers;
use App\Services\Contact as ContactService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Lang;
class IndexController extends Controller
{
/**
* Create a new controller instance.
*
* #param Request $request
* #return void
*/
public function __construct(Request $request)
{
$this->_request = $request;
}
/**
* Standard contact page
*
* #return contact page
*/
public function contact(ContactService $contactService)
{
$errors = null;
$success = false;
if ($this->_request->isMethod('post')) {
$validator = $contactService->validator($this->_request->all());
if ($validator->fails()) {
$errors = $validator->errors();
} else {
$contactService->create($validator->getData());
$success = true;
}
}
return view('pages/contact', ['errors' => $errors, 'success' => $success]);
}
}
The services return validators, handle cruds, basically do everything that I don't want to see in my Controller just like I had it in my Zend projects.
Example of Service:
<?php
namespace App\Services;
use Validator;
use Mail;
use App\Models\Contact as ContactModel;
class Contact
{
/**
* Get a validator for a contact.
*
* #param array $data
* #return \Illuminate\Contracts\Validation\Validator
*/
public function validator(array $data)
{
return Validator::make($data, [
'email' => 'required|email|max:255',
'phone' => 'max:255',
'firstName' => 'required|max:255',
'lastName' => 'required|max:255',
'message' => 'required'
]);
}
/**
* Create a new contact instance after a valid form.
*
* #param array $data
* #return ContactModel
*/
public function create(array $data)
{
//Handle or map any data differently if needed, just for illustration
$data = [
'email' => $data['email'],
'firstName' => $data['firstName'],
'lastName' => $data['lastName'],
'language' => $data['language'],
'phone' => $data['phone'],
'message' => $data['message']
];
// Send an email
Mail::send('emails.contact', ['data' => $data], function ($m) use ($data) {
$m->from(config('mail.from.address'), config('mail.from.name'));
$m->to(env('MAIL_TO', 'hello#world.com'), env('MAIL_TO'))->subject('Contact form entry from: ' . $data['firstName']);
});
return ContactModel::create($data);
}
}
See this post: “Design Pattern : Service Layer with Laravel 5” #Francoiss https://m.dotdev.co/design-pattern-service-layer-with-laravel-5-740ff0a7b65f.. in addition to the above answer you can also encapsulate the validation code inside a separate validator class

Validation fileds from ajax request [Laravel 5]

Here is my validation request :rules
<?php
namespace App\Http\Requests;
use App\Http\Requests\Request;
use Illuminate\Support\Facades\Auth;
class UpdateCommentRequest extends Request {
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize() {
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules() {
$user = Auth::user()->id;
return [
'comment' => 'required|between:15,600',
'projectID' => "required|exists:project_group,project_id,user_id,$user|numeric",
'order' => "required|numeric",
'level' => "required|numeric"
];
}
}
And in my model I have like this:
public function apiUpdateComment(UpdateCommentRequest $request){
$comment = Comment::find(Input::get("order"));
$comment->text = Input::get('comment');
if($comment->save()){
return 'success';
}
}
This fileds I need to validate agins rules array:
array(
'comment' => Input::get('comment'),
'projectID' => Input::get('projectID'),
'order' => Input::get("order"),
'level' => Input::get("level"),
);
I need to check if all rules are ok and then update comment... Anyone can help?
public function apiUpdateComment(UpdateCommentRequest $request){
$comment = Comment::find($request->get("order"));
$comment->text = $request->get('comment');
if($comment->save()){
return 'success';
}
}
The logic behind the code:
A post request is send the the server and the route file sends it the the apiUpdateComment with all variables inside the $request. But before the code of the function is executed the validator checks the rules in your UpdateCommentRequest. If the test fails it will return errors. If it pass a comment with the id will be updated.

Resources