Laravel unique validation if ID isn't in request - laravel

I want to do some validation for a field. Right now works for unique values, the problem is that on Update I get the same error. So I want to filter the request, if that post request contain ID field then this field shouldn't be unique.
public function rules()
{
return [
'customer_id' => 'required|unique:customers',
];
}

You can use Rule class' unique method for the update method
public function rules()
{
return [
'customer_id' => [
'required',
Rule::unique('customers')->ignore($customer->customer_id),
];
}
Laravel docs: https://laravel.com/docs/8.x/validation#rule-unique
For common rules() function it can be done as
use Illuminate\Validation\Rule;
class CustomerController extends Controller
{
protected function rules($customer)
{
return [
'customer_id' => [
'required',
Rule::unique('customers')->ignore($customer->exists ? $customer->customer_id : null),
];
}
public function store(Request $request)
{
$customer = new Customer;
$request->validate($this->rules($customer));
}
public function update(Request $request, Customer $customer)
{
$request->validate($this->rules($customer);
}
}
In my case I have a single method for store/update and I check If I have an ID or not. Also I added $customer = request()->all(); and ignore($customer['ID'] , that is for my specific case.
Laravel Docs warns against passing user controller request input to the ignore method
For your specific case you can do
$customer = !empty($request->input('ID') ? Customer::findOrFail($request->input('ID')) : new Customer;
//Then pass the customer to the rules()
$validated = $request->validate($this->rules($customer));

Related

Can't pass Request from controller to another controller's view

I'm trying to pass a Request from a controller, but for reasons I don't understand the data simply isn't being passed through to the view and I get Undefined variable: request. I have confirmed that right up until the redirect to the action the request is populated with all the additional variables, so the issue must be after that.
ManufacturerController
public function decode(Manufacturer $manufacturer, Request $request) {
$validated = $request->validate([
"id" => ["required","min:5","max:30", "alpha_num"],
"email" => ["email","required","max:255"]
]);
$request->merge([
"manufacturer" => $manufacturer
]);
// Pass the Request to the Manufacturer model and return a modified version of it
$request = $manufacturer->oneplus($request);
return redirect()->action([TransactionController::class, "index"])->with($request);
}
abort(404);
}
Manufacturer model:
public function oneplus($request) {
$id = $request->id;
/* BUSINESS LOGIC THAT GENERATES $new FROM $id... */
$request->merge([
'new' => $new
]);
return $request;
}
Route in web.php
Route::get('/payment', [TransactionController::class, "index"]);
TransactionController:
public function index()
{
return view('payment');
}
payment.blade.php
{{ dd($request->new) }}
The problem when using redirects is that the redirect will cause a brand new request to happen. When using redirect()->with('variable', 'value') you need to then access that variable using:
session('variable')`
the reason being that the variable is "flashed" to the next request via the session (in practice it's not sent to the next request, it's just available for the next request through the session and then disappears).
While this may be an easy solution to your problem a better solution is to not use a redirect if possible. Here's a simplification of an alternative:
ManufacturerController:
public function decode(Manufacturer $manufacturer, Request $request) {
$validated = $request->validate([
"id" => ["required","min:5","max:30", "alpha_num"],
"email" => ["email","required","max:255"]
]);
$request->merge([
"manufacturer" => $manufacturer
]);
// Pass the Request to the Manufacturer model and return a modified version of it
$request = $manufacturer->oneplus($request);
$transactionController = app()->make(TransactionController::class);
return $transactionController->index($request);
}
TransactionController:
public function index(Request $request)
{
return view('payment')->with("request", $request);
}
This will call the other controller method within the same request.
You need to make few changes in TransactionController and ManufacturerController to make it work
TransactionController:
public function index(Request $request)
{
return view('payment', [
'request' => $request->session()->get('request')
]);
}
ManufacturerController:
public function decode(Manufacturer $manufacturer, Request $request) {
$validated = $request->validate([
"id" => ["required","min:5","max:30", "alpha_num"],
"email" => ["email","required","max:255"]
]);
$request->merge([
"manufacturer" => $manufacturer
]);
// Pass the Request to the Manufacturer model and return a modified version of it
$request = $manufacturer->oneplus($request);
return redirect()->action([TransactionController::class, "index"])->with('request', $request->all());
}
abort(404);
}
You can pass like this
ManufacturerController :
return redirect()->action(
[TransactionController::class, "index"],
['data' => $request]
);
Route in web.php
// ? = Optional
Route::get('/payment/{data?}', [TransactionController::class, "index"]);
TransactionController:
public function index($data)
{
return view('payment');
}

Validate specific rule - Laravel

I am using latest Laravel version.
I have requests/StoreUser.php:
public function rules() {
return [
'name' => 'required||max:255|min:2',
'email' => 'required|unique:users|email',
'password' => 'required|max:255|min:6|confirmed'
];
}
for creating a user.
Now I need and to update the user, but how can I execute only specific rules ?
For the example, if name is not provided, but only the email, how can I run the validation only for the email ?
This is easier than you thought. Make rules depend on the HTTP method. Here is my working example.
public function rules() {
// rules for updating record
if ($this->method() == 'PATCH') {
return [
'name' => 'nullable||max:255|min:2', // either nullable or remove this line is ok
'email' => 'required|unique:users|email',
];
} else {
// rules for creating record
return [
'name' => 'required||max:255|min:2',
'email' => 'required|unique:users|email',
'password' => 'required|max:255|min:6|confirmed'
];
}
}
You can separate your StoreUser request to CreateUserRequest and UpdateUserRequest to apply different validation rules. I think, this separation makes your code more readable and understandable.
Any HttpValidation request in laravel extends FormRequest that extends Request so you always have the ability to check request, auth, session, etc ...
So you can inside rules function check request type
class AnyRequest extends FormRequest
{
public function rules()
{
if ($this->method() == 'PUT'){
return [
]
}
if ($this->method() == 'PATH') {
return [
]
}
}
}
If things get complicated you can create a dedicated new HttpValidation request PostRequest PatchRequest
class UserController extends Controller
{
public function create(CreateRequest $request)
{
}
public function update(UpdateRequest $request)
{
}
}
See also the Laravel docs:
https://laravel.com/docs/5.8/validation
https://laravel.com/api/5.8/Illuminate/Foundation/Http/FormRequest.html

Laravel | Validate generated value

I have an endpoint for data create.
The request is "name". I need to generate "slug" and validate that slug is unique.
So, let's say
book_genres table.
id | name | slug
Request is ["name" => "My first genre"].
I have a custom request with a rule:
"name" => "string|unique:book_genres,name".
I need the same check for the slug.
$slug = str_slug($name);
How can I add this validation to my custom request?
Custom request class:
class BookGenreCreate extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
"name" => 'required|string|unique:book_genres,name',
];
}
}
So basically what you want to do is try to manipulate the request data before validation occurs. You can do this in your FormRequest class by overriding one of the methods that is called before validation occurs. I've found that this works best by overriding getValidatorInstance. You can then grab the existing data, add your slug to it and then replace the data within the request, all before validation occurs:
protected function getValidatorInstance()
{
$data = $this->all();
$data['slug'] = str_slug($data['name']);
$this->getInputSource()->replace($data);
return parent::getValidatorInstance();
}
You can also add the rules for your slug to your rules method as well:
public function rules()
{
return [
"name" => 'required|string|unique:book_genres,name',
"slug" => 'required|string|unique:book_genres,slug',
];
}
So your class will look something like this:
class BookGenreCreate extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'name' => 'required|string|unique:book_genres,name',
'slug' => 'required|string|unique:book_genres,slug',
];
}
protected function getValidatorInstance()
{
$data = $this->all();
$data['slug'] = str_slug($data['name']);
$this->getInputSource()->replace($data);
return parent::getValidatorInstance();
}
}
Now when the request comes through to your controller, it will have been validated and you can access the slug from the request object:
class YourController extends Controller
{
public function store(BookGenreCreate $request)
{
$slug = $request->input('slug');
// ...
}
}
You can add the 'slug' to the request, then use validations as usual.
rules() {
// set new property 'slug' to the request object.
$this->request->set('slug', str_slug($request->name));
// rules
return [
'name' => 'string|unique:book_genres,name',
'slug' => 'string|unique:book_genres,slug'
]
}

change value of $request before validation in laravel 5.5

I have a form in route('users.create').
I send form data to this function in its contoller:
public function store(UserRequest $request)
{
return redirect(route('users.create'));
}
for validation I create a class in
App\Http\Requests\Panel\Users\UserRequest;
class UserRequest extends FormRequest
{
public function rules()
{
if($this->method() == 'POST') {
return [
'first_name' => 'required|max:250',
It works.
But How can I change first_name value before validation (and before save in DB)?
(Also with failed validation, I want to see new data in old('first_name')
Update
I try this:
public function rules()
{
$input = $this->all();
$input['first_name'] = 'Mr '.$request->first_name;
$this->replace($input);
if($this->method() == 'POST') {
It works before if($this->method() == 'POST') { But It has not effect for validation or for old() function
Override the prepareForValidation() method of the FormRequest.
So in App\Http\Requests\Panel\Users\UserRequest:
protected function prepareForValidation()
{
if ($this->has('first_name'))
$this->merge(['first_name'=>'Mr '.$this->first_name]);
}
Why not doing the validation in the controller? Than you can change things before you validate it and doing your db stuff afterward.
public function store(Request $request)
{
$request->first_name = 'Mr '.$request->first_name;
Validator::make($request->all(), [
'first_name' => 'required|max:250',
])->validate();
// ToDo save to DB
return redirect(route('users.create'));
}
See also https://laravel.com/docs/5.5/validation
Simply use
$request->merge(['New Key' => 'New Value']);
In your case it can be as follows for saving
$this->merge(['first_name'=>'Mr '.$this->first_name]);

laravel 5 form request validation issue

in my laravel 5.1 app, I've got a Book model with a required "Title" field and several others non-required fields. To validate Book create/update, I use form request validation like this:
class StoreBookRequest extends Request
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'title' => 'required',
'year' => 'numeric',
'pages' => 'numeric',
];
}
}
I then type-hint the request on the controller action and everytning works fine. Now I need to create a new controller action that updates only one of the non-required fields. To do so, I created another request like this:
class StoreReviewRequest extends Request
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'vote' => 'numeric',
];
}
}
and I type-hint the request in the controller action:
public function updateReview(StoreReviewRequest $request, Book $book)
{
$input = array_except(Input::all(), '_method');
$book->update($input);
Session::flash('message', 'Review updated');
return redirect('/book');
}
The problem is that when I use the new controller action, the update form does not pass validation, but complains about missing "Title" field, even tough I'm not decalring that field as required in my StoreReviewRequest class. What am I doing wrong? Thanks!
As #Needpoule suggested, I was using the wrong action in my form.

Resources