I'm using (and loving) the Laravel 5 form request validation so far. But now I'm stuck trying to validate related models.
Say I have a Car model which has many Wheel models and I'd like to validate a new car. In the CarController#store method I'm using the following to validate the car, but this does of course not validate the wheels.
public function store(StoreCarRequest $request)
{
// Create the car
}
What needs to be done to validate the Wheels of a Car? Can this be done with form request validation?
Edit
I'm getting closer. The remaining issue is that I need to iterate through the wheels which are stored in an array (because the HTML input name is e.g. wheels[0][color]), but the validator is searching for a color field, not wheels[0][color].
public function store(StoreCarRequest $r)
{
// Validate the wheels
foreach ($r->input('wheels') as $wheel)
{
// TODO: use $wheel somewhere?
$snr = new StoreWheelRequest;
$this->validate($r, $snr->rules()); // Validation fails: 'color' field is required
}
// Validation passed, create the car (and the wheels)
}
StoreCarRequest.php
public function rules()
{
return [
'engine' => 'required',
'seats' => 'required',
];
}
StoreWheelRequest.php
public function rules()
{
return [
'color' => 'required',
];
}
The HTML form
<form method="POST" action="...">
<input type="text" name="engine">
<input type="text" name="seats">
<input type="text" name="wheels[0][color]">
<input type="text" name="wheels[1][color]">
<input type="text" name="wheels[2][color]">
<input type="text" name="wheels[3][color]">
<input type="submit" value="Create">
</form>
After some more Googling, I stumbled upon the following article: http://ericlbarnes.com/laravel-array-validation/
It boils down to adding rules dynamically inside the rules() method by iterating over the input array.
You can use dot notation to validate nested arrays :
public function rules()
{
return [
'engine' => 'required',
'seats' => 'required',
'wheel.color' => 'required',
];
}
Related
I have a model with some typical columns and one json column. The Json is casted as array:
Model:
protected $casts = [
'client' => 'array'
];
In my Livewire component, I created the following validation rule
Livewire component:
protected $rules = [
'editing.name' => 'required',
...
'editing.client' => 'present|array',
'editing.client.*.tenant' => 'required',
];
I call the 'editModal' method where I type-hint the model and set a public property with it's attributes. Already filtered to the specific item.
Livewire component:
public function editModal(TokenCacheProvider $provider)
{
$this->editing = $provider;
$this->dispatchBrowserEvent('open-modal', ['modal' => 'edit']);
}
My blade is a simple boiler blade component:
Blade:
<div>
<x-input.group inline borderless for="name" label="Name" :error="$errors->first('editing.name')"/>
<x-input.text name="name" class="w-full" wire:model="editing.name" />
</div>
<div>
<x-input.group inline borderless for="name" label="Tenant" :error="$errors->first('editing.client.tenant')"/>
<x-input.text name="tenant" class="w-full" wire:model="editing.client.tenant" />
</div>
Once I load the page I get the following type exception
foreach() argument must be of type array|object, string given
This is because the client attribute is still a string as in the database. It should be an array as I casted it:
So, I don't understand why the client attribute is still a string and not an array as casted.
Thank you
Well it's more a work-around than a solution but Daantje found an Livewire issue on Github which might explain this behavior.
I've changed the architecture from one to two public properties. One for the actual model and a second for the json column.
Livewire component (truncated)
public MyModel $editing; // the model
public array $client; // for the json attribute
protected $rules = [
'editing.name' => 'required',
...
'client.foo' => 'required',
'client.bar' => 'required',
'client.baz' => 'required',
...
];
public function editModal(MyModel $model)
{
$this->editing = $model;
$this->client = json_decode($model->client,true);
$this->dispatchBrowserEvent('open-modal', ['modal' => 'edit']);
}
public function save()
{
$this->validate();
$this->editing->client = json_encode($this->client);
$this->editing->save();
$this->dispatchBrowserEvent('close-modal', ['modal' => 'edit']);
$this->event('Saved', 'success');
}
Two blade input field examples:
<!-- ORM field(s) -->
<div>
<x-input.group inline borderless for="name" label="Name" :error="$errors->first('editing.name')"/>
<x-input.text name="name" wire:model="editing.name" />
</div>
<!-- Json field(s) -->
<div>
<x-input.group inline borderless for="foo" label="Foo" :error="$errors->first('client.foo')"/>
<x-input.text name="foo" wire:model="client.foo" />
</div>
Well, this works but as mentioned it's more a workaround
In my controller, I validate the input fields and if any validation fails I want to return back to the form and refill the previous values that the user was inputing (if there is any other easier way instead this please tell me). But here is what I'm doing:
Controller.php:
public function store(Request $request, AnexoController $anexoController)
{
$validator = Validator::make(
$request->all(),
$rules,
$messages
);
if($validator->fails())
{
return Redirect::to($request->headers->get('referer'))
->withErrors($validator)
->withReq($request->all());
}
// continues the function...
}
At the view.blade.php:
<input type="text"
maxlength="256"
id="text-input"
name="id"
placeholder="Código"
class="form-control"
#if(isset($req)) value="{{$req->id}}"
#else value="$REQ VARIABLE NOT SET"
#endif
required>
And when I test it, the field id value is set to "$REQ VARIABLE NOT SET" but $errors from $validator variable is set. So, why isn't the variable $req being set in this context? Thanks in advance.
Use this
$request->validate([
//rules
]);
It will automatically return to form page with all the errors and inputs,
In your form you'll neet do add a helper in value of inputs for old inputs
<input type="text" value={{ old('email')}} name="email">
see https://laravel.com/docs/5.8/validation & https://laravel.com/docs/5.8/helpers
I'm using Laravel 5.6 to develop a website.
Currently, I want to write a test codes for the website. I'm also new to building a website in general and this is learning curve for me to learn what I'm doing wrong.
I created a Profile based on a User model and the Profile should only be editable by the authenticated User only.
The form is actually working without errors on the browser side but once i run phpunit, it will fail.
Test Script:
/** #test */
public function an_authenticated_user_can_view_the_profile_page()
{
// Generate fake instance of authenticated user
$this->be($user = factory('App\User')->create());
// Will get the URL
$response = $this->get('/profile/'.$user->name);
// Check whether the string exists
$response->assertSee('Personal details for '.$user->name);
}
Controller:
class ProfileController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function show(User $user)
{
return view('user.profiles.show', compact('user'));
}
public function update(Request $request)
{
$this->validate(request(), [
'company' => 'required',
'street' => 'required',
'city' => 'required',
'zip_code' => 'required',
'state' => 'required',
'country' => 'required',
'phone' => 'required',
]);
$profile = \Auth::user()->profile;
$profile->update($request->all());
return back()->with('success', 'Profile updated!');
}
}
View:
<div class="heading">
<h3 class="text-uppercase">Personal details for {{ $user->name }}</h3>
</div>
<form method="POST" action="/profile">
{{method_field('PATCH')}}
{{csrf_field()}}
<input type="hidden" value="{{ $user->profile->id }}" name="id">
<div class="col-md-6">
<div class="form-group">
<label for="company">Company</label>
<input id="company" type="text" class="form-control" name="company" value="{{ $user->profile->company }}" required>
</div>
</div>
</form>
Image of the commented out Form test:
Commented Form
Image of the not commented Form test:
Not commented Form
I am rather confused why my test is failing once I insert the form with a value tag. If i commented out the form or just remove the value tag, the test will pass.
Been searching for the few days and still can't find the right answer to this. Am i using the right Assertion? What am I missing here? Any inputs will help me to further understand this. Thanks!
I found the answer. It was actually the factory that I've created.
In the User model, every registration leads to creating an empty Profile.
This is the new way of how I write the test script:
/** #test */
public function an_authenticated_user_can_view_the_profile_page()
{
//Generate a fake profile
$profile = factory('App\Profile')->create();
// Assign it to the user
$user = $profile->user;
// Authenticate the user
$this->be($user);
// Will get the URL
$response = $this->get('/profile/'.$user->name);
// Check whether the string exists
$response->assertSee('Personal details for '.$user['name']);
}
I have a project that consists of multilingual functionality. So I'm creating a form that consists of multiple languages to be inserted if User wants.
But validation should be like if any of the fields having the same name consist value then leave rest one like as
Form
<form>
<input type="text" name="name[EN]" />
<input type="text" name="name[AR]" />
<input type="text" name="name[FR]" />
<input type="submit" value="submit" />
</form>
Now I want to validate that if any one of these fields have value then submit form else throw a validation
So I've tried Laravels required_if, but it didn't work.
public function rules()
{
return [
'name.*' => 'required_if:name,1',
];
}
So how to make such validation in laravel
I've searched but could not find a built in way of doing this. You can create your own rule using
php artisan make:rule NonEmptyFiltered
and then define the rule as:
class NotEmptyFiltered implements Rule {
public function passes($attribute, $value) {
return !empty(array_filter($value));
}
public function message() {
return ':attribute must have at least one non-null element';
}
}
You can then do:
public function rules()
{
return [
'name' => [ new NonEmptyFiltered() ]
];
}
I believe this should do the trick:
$rules = [
'name.EN' => 'required_without_all:name.AR,name.FR',
'name.AR' => 'required_without_all:name.EN,name.FR',
'name.FR' => 'required_without_all:name.EN,name.AR',
];
The rule makes the field to be required if the other fields are empty https://laravel.com/docs/5.6/validation#rule-required-without-all.
I am learning/using Laravel5 and using the Request generator tool to make a custom request validation handler;
php artisan make:request <nameOfRequestFile>
And I find that my validation always passes, but I don't know why.
On my view I do a vardump of the errors;
{{ var_dump($errors) }}
And when I submit an empty form it always inserts a new record, when it should fail.
The var dump reports;
object(Illuminate\Support\ViewErrorBag)#135 (1) { ["bags":protected]=> array(0) { } }
Which seems to suggest the error bag is always empty.
My request file is pretty simple
namespace App\Http\Requests;
use App\Http\Requests\Request;
class PublishDriverRequest extends Request
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'name.required|min:3' => 'Please provide a name.',
'companyName.required|min:3' => 'Please provide a company name.'
];
}
}
I checked my input form and the names are the same;
<input type="text" name="name" id="name" class="form-control">
<input type="text" name="companyName" id="companyName" class="form-control">
And I checked my database, the names are the same too.
My controller when storing the data is equally straight forward;
public function store(PublishDriverRequest $request) {
Driver::create($request->all());
return redirect('/');
}
So I'm not sure why the validation always passes; if I submit an empty form it should fail as the rules indicate minimum length and required.
What am I doing wrong?
Change you validation rules to:
public function rules()
{
return [
'name' => 'required|min:3',
'companyName' => 'required|min:3',
];
}
To show custom validation error:
public function messages()
{
return [
'name.required' => 'Please provide a name.',
'companyName.required' => 'Please provide a company name.',
];
}
Note: Use message() method before rules()