Laravel | Validate generated value - laravel

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'
]
}

Related

Laravel unique validation if ID isn't in request

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));

Laravel default values for fields in FormRequest

Can I set a default value to a not-existing field in a FormRequest in Laravel?
For example, if a field called "timezone" does not exist in the incoming request, it get set to "America/Toronto".
Well I wrote a trait for this, which checks a function called 'defaults' exist in the form request it will replace the default values
trait RequestDefaultValuesTrait {
protected function prepareForValidation(){
// add default values
if( method_exists( $this, 'defaults' ) ) {
foreach ($this->defaults() as $key => $defaultValue) {
if (!$this->has($key)) $this->merge([$key => $defaultValue]);
}
}
}
}
the thing that you need to do is adding this trait to FormRequest class and then add a function like this:
protected function defaults()
{
return [
'country' => 'US',
'language' => 'en',
'timezone' => 'America/Toronto',
];
}
Being honest I don't link this method, but It works.
Try this
if(!$request->has('timezone') {
$request->merge(['timezone' =>'America/Toronto']);
}
I'm not so sure if you need to do it in this way, but if you want to:
class CreateUpdateDataFormRequest extends Request
{
public function authorize()
{
return true;
}
public function rules()
{
return [];
}
protected function getValidatorInstance()
{
$data = $this->all();
if(!isset($data['timezone'])) {
$data['timezone'] = 'America/Toronto';
$this->getInputSource()->replace($data);
}
// modify data before sending to validator
return parent::getValidatorInstance();
}
in request class add
public function prepareForValidation()
{
$this->mergeIfMissing([
'timezone' => 'America/Toronto'
]);
}

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

How to add own data to POST in YII2?

i've question about adding some data outside form and send it with form data. Look! I have 3 fields ActiveForm:
name (text)
email (email)
course (hidden)
Ok, but i need to add one more named "status". I do not want to add hidden fields, just want to add inside controller or model.
How?
Controller:
public function actionFree()
{
$model = new SubscribeForm();
$this->view->title = "ШКОЛА ПИСАТЕЛЬСКОГО МАСТЕРСТВА: Новичок курс";
if ($post = $model->load(Yii::$app->request->post())) {
if ($model->save()) {
Yii::$app->session->setFlash('success', 'Данные приняты');
return $this->refresh();
}
else {
Yii::$app->session->setFlash('error', 'Ошибка');
}
}
else {
// страница отображается первый раз
return $this->render('free-course', ['model' => $model, 'course_id' => 1]);
}
}
Model:
class SubscribeForm extends ActiveRecord
{
public $fio;
public $email;
public $course;
public $status;
public static function tableName()
{
return 'users';
}
public function rules()
{
return [
// username and password are both required
[['fio', 'email'], 'required'],
[['email'], 'unique'],
['email', 'email'],
['email', 'safe']
];
}
}
You could just set the value in your controller, like this:
public function actionFree()
{
$model = new SubscribeForm();
$model->status = 'your-status-value';
// ... the rest of your code
Or you could add a default value in your model. This way you can still overrule the value from the controller or a form field, but will get this value when nothing else is supplied.
public function rules()
{
return [
['status', 'default', 'value' => 'your-default-status-value'],
// .. other rules
];
}

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