Laravel Validating Arrays Select fields - laravel

Laravel easily validates array based form input fields
<input name='input_name[0][0]">
<input name='input_name[0][1]">
with
'input_name.* = 'required',
https://laravel.com/docs/5.5/validation#validating-arrays
But how can I validate array based select fields?
I have a form where customer info is added, user has to choose the customer's gender and it's possible to add infinite number of customer.
So i have a select for 1 customer:
<select name="gender[0]">
<option selected="selected" disabled="disabled" hidden="hidden" value="">Gender</option>
<option value="Male">Male</option>
<option value="Female">Female</option></select>
and then
<select name="gender[1]">...
<select name="gender[N]">
When I set the rule as:
'gender.*' => 'required'
It doesn't recognize an 'unchoosen' select-box as an error....
But if I update validation rules to:
'gender[0]'=>'required'
'gender[1]'=>'required'
'gender[N]'=>'required'
It works absolutely fine... (by "it works", I mean that it returns a mistake "Gender field is required").
So, apparently Laravel has some problems with array based select names.
Appreciate any help!

public function rules() {
$rules = [];
$gender = $this->input('gender');
foreach ($gender as $index => $item) {
$rules["gender.{$index}"] = 'required';
}
return $rules;
}

I've decided o answer the question myself.
1) First solution is to make the first "placeholder" option not disabled (as was in my case, see above - it's because I use LaravelCollective and they have it by default):
<select name="gender[0]">
<option selected="selected" hidden="hidden" value="">Gender</option>
<option value="Male">Male</option>
<option value="Female">Female</option></select>
When you remove 'disabled' from option-1 of your select then it sends ' ' when posting (instead of sending nothing with 'disabled'). So it sends
gender[0] = '';
gender[1] = '';
etc...
Actually, if you have a lot of gender[N] (or maybe other array based selects) I think it's the neatest solution.
2) Second solution is provided below by omadonex:
public function rules() {
$rules = [];
$gender = $this->input('gender');
foreach ($gender as $index => $item) {
$rules["gender.{$index}"] = 'required';
}
return $rules;
}
in this case you'll have a separate rule for every array-based select and it will work (see why in the end of my topicstarter-post). I prefer this solution less than 1st one because you'll have a long list of "gender"-rules if you have a lot of ...
3) Also I've undertood why 'gender[0]'=>'required'
works
and 'gender.*' => 'required'
does not
for array based selects like <select name=gender[0]>
It's kind of obvious if you think about it: when POSTING select tag with first option (a placeholder) being disabled, as in my example above:
<option selected="selected" disabled="disabled" hidden="hidden" value="">Gender</option>
the $POST sends nothing....
So if Laravel's validation rule is 'gender[0]'=>'required' Laravel "thinks": "OKAY, I've received no "gender[0]", but I know what is required exactly ("gender[0]", of course) . I have to send a mistake because there is no "gender[0]".
But if rule is 'gender.*' => 'required' and Laravel get's no input of "gender" kind, then it also doesn't know what EXACTLY is required ('gender.*' may mean gender[0]... gender [12345] ... gender[anything]). Laravel can't send a mistake, because infinite number of gender[...] is missing, so he simply omits it....
PS. If you work with LaravelCollective forms of newer versions, they create placeholder "disabled" by default. Here is a macro to avoid it.
{!!
Form::macro('selectNonDisabled', function($value, $placeholder, $array, $disabled=null, $class=null) {
$select = "<select class='form-control $class' $disabled name='$value'>";
$select .= "<option selected='selected' hidden='hidden' value=''>$placeholder</option>";
foreach ($array as $key => $value) {
$select .= "<option value='$key'>$value</option>";
}
$select .= "</select>";
return $select;
});
!!}

Related

Laravel Livewire Model - public variable state

I am using Livewire to create a form which databinds to the model like this, notice <select/> has multiple attribute:
#php
$items = ['bag','hat','mug','stickers'];
#endphp
<select wire:model="extra" multiple >
<option disabled value="select" >Select</option>
#foreach ($items as $item)
<option value="{{$item}}" >{{$item}}</option>
#endforeach
</select>
and the model class has a var on top:
public $extra = [''];
I would like to select multiple <option/> with just a click, currently you have to use the keyboard [command] + click.
I am trying to add logic in the model class but state of public $extra = ['']; is an issue.
example:
<option value="{{$item}}" wire:click="buildArr('{{$item}}')">{{$item}}</option>
then from model, $this->extra[] does not build on the array but rather refreshes and returns the last <option/> clicked:
public function buildArr($item){
$this->extra[]= $item;
}
How can I allow 1 click to build on this array? Do I need AlpineJS?
the answer to my problem was here https://laravel-livewire.com/docs/2.x/alpine-js#interacting-with-livewire-from-alpine
using: $wire.myMethod() from blade.

Laravel : how to get $payment_gateway value

I'm trying to create a feature where, when i create a new booking i can choose the payment method like via xendit or transfer. But when i tried to submit the output of the payment method is still offline payment because of this code {{$row->gatewayObj ? $row->gatewayObj->getDisplayName() : ''}} , and not xendit. How do i fix this??
The Controller :
public function create(Request $request){
// $this->checkPermission('news_create');
$allServices = get_bookable_services();
$whatsAppBookableServices = ["art", "food", "gear", "car", "hotel"];
$payment_gateway = ["xendit", "offline payment"];//tambahan Nicho
$row = new BookingOffline();
$row->fill([
'status' => 'publish',
]);
$data = [
// 'categories' => NewsCategory::get()->toTree(),
'row' => $row,
'breadcrumbs' => [
[
'name' => __('Report'),
'url' => 'admin/module/report/booking'
],
[
'name' => __('Add Booking By WA'),
'class' => 'active'
],
],
'bookableServices' => array_keys($allServices),
'whatsAppBookableServices' => $whatsAppBookableServices,
'payment_gateway' => $payment_gateway,//tambahan Nicho
];
return view('Report::admin.booking.create', $data);
}
The Blade file :
<td>
{{$row->gatewayObj ? $row->gatewayObj->getDisplayName() : ''}}
</td>
The gatewayObj :
function get_payment_gateway_obj($payment_gateway)
{
$gateways = get_payment_gateways();
if (empty($gateways[$payment_gateway]) or !class_exists($gateways[$payment_gateway])) {
return false;
}
$gatewayObj = new $gateways[$payment_gateway]($payment_gateway);
return $gatewayObj;
}
There are still missing pieces to the puzzle, so I cannot provide you with a code snippet to implement.
However, I think you should be able to diagnose it this way:
Check your controller.
Do a die-dump of the $data just above the line containing return view.... Like so: dd($data['payment_gateway'])
Then refresh the page in your browser and see if the $data object is exactly how you want it. The value should be ["xendit", "offline payment"].
Check your form
I suppose you have a form element like a <select></select>, which is iterating over the values of the $data['payment_gateway'] array. If you do not have this, how are your users choosing between the payment options?
Next, make sure that each iteration of payment gateway is being submitted properly. YOu did not include the snippet that handles form submission, but if you're using a <select> element, the options each need to be submitted with a value.
If we hardcode the select, you will have something like this:
<select name="payment_gateway">
<option value="xendit">Xendit</option>
<option value="offline">Offline Payment</option>
</select>
So when the server receives this form information, it knows the exact value of payment gateway to use. Dynamically, it could look like this:
<select name="payment_gateway">
#foreach($data['payment_gateways'] as $gateway)
<option value="{{ $gateway }}">{{ $gateway }}</option>
#endforeach
</select>
Intercept the request and check that your payment_gateway is being submitted properly.
Go to the controller method that handles your form, and do something like dd($request->all())
Then inspect the value of payment_gateway.

How to create filter with easy way in laravel 8 using controller and checkbox

I am creating filter for the products, I tried using jquery for this before but I am not able to fix the pagination in laravel. So, I am trying this to fix the pagination as well as reduce the database query, bcoz in jquery everytime I click on any category or sub category, then it hits the database so the number of execution was more, So I replace the jquery and trying to use the button to do the same. But stuck with the array.
Can anyone help me with this?
$category = ["p1","p2","p3"];
$sub_category = ["c1","c2","c3","c4"];
$data = [];
$count = 0;
foreach ($cate as $category){
$data2 =[];
$count1 = 0;
foreach ($sub_cate as $sub){
$data2[] = [
'sub_category_id' => $category.$count1++,
'sub_category' => $sub,
'url' => $category.'|'.$sub
];
}
$data[] = [
'category_id' => 'category_'.$count++,
'category' => $category,
'sub_category' => $data2
];
}
In blade file :
#foreach($filters as $filter)
<div class="custom-control custom-checkbox mb-2">
<input type="checkbox" class="custom-control-input category_filter1" id={{$filter["category_id"]}} name="category_filter[]" value="{{$filter["category"]}}">
<label class="custom-control-label w-100" for="{{$filter["category_id"]}}">{{$filter["category"]}}</label>
#foreach($filter["sub_category"] as $sub)
<div class="custom-control custom-checkbox mb-2">
<input type="checkbox" class="custom-control-input category_filter1" id={{$sub["sub_category_id"]}} name="sub_category_filter[]" value="{{$sub["url"]}}">
<label class="custom-control-label w-100" for="{{$sub["sub_category_id"]}}">{{$sub["sub_category"]}}</label>
</div>
#endforeach
</div>
#endforeach
After using this code I am getting this type of data :
{"category_filter":["p1","p2"],"sub_category_filter":["p1|c1","p1|c2","p1|c3","p1|c1"]}
So basically the array structure is like this :
For now everything looks nice but, When I try to combine the array into once then it look some time to sort and rearrange itself, So it there any easy way to this, Each time I change the filters it will take time and then it fetch result from the database. For some reason I thought that URL which is custom made will help me but it only taking us to the wrong direction.
apply condition in conrtoller like this
if($request->input('category_filter') == "p1"){
if($request->input('sub_category_filter') == "p1|c1"){
... do your code as per your need
}
}
I share Sample Example:
assume I want to filter data like All, approved, pending, decline... then I write script in controller like this
if($request->input('filtertype')){
$filtertype = $request->input('filtertype');
if($filtertype == "All"){
$deals = Deal::orderBy('updated_at', 'desc')->get();
}else{
$deals = Deal::where('deal_isApproved', '=', $filtertype)
->orderBy('updated_at', 'desc')->get();
}
}
return view('admin.deals.index',compact('deals'));

How to add validation for checkbox in laravel 5?

I have an array of checkboxes including a hidden checkbox so i can send a value 0 if a checkbox is not checked.
#foreach($records as $record)
<tr>
<td>{{ $record->name }}</td>
<td>{{ $record->id }}</td>
<td>
<input type="hidden" name="record_checkbox[{{ $loop->index }}]" value="0" />
<input type="checkbox" class="form-control name="record_checkbox[{{ $loop->index }}]" value="1" />
</td>
</tr>
#endforeach
I want to validate that only 0 or 1 is sent when a user submits the form.
In my controller I have tried using this:
$this->validate($request, [
'record_checkbox[]' => 'integer|boolean|min:0|max:1',
]);
But when I use browser developer tools and manually update the value field of checkbox to 100, it still accepts it and stores it in database.
So how can I only allow 0 or 1 by proper validation?
Remove brackets if you want to validate all array inputs:
$this->validate($request, [
'record_checkbox' => 'in:0,1',
]);
Edit
Create custom rule: inside app/Providers/AppServiceProvider.php under boot method add the following:
use Illuminate\Support\Facades\Validator;
...
public function boot() {
Validator::extend('validate_checkboxes', function ($attribute, $values, $parameters, $validator) {
foreach( $values as $value ) {
if (!in_array($value, [0, 1])) {
return false;
}
return true;
}
});
}
And then in your controller:
$this->validate($request, [
'record_checkbox' => 'validate_checkboxes',
]);
I've been trying to find a good answer to this for about a month and I think I finally got a good solution, so I'll share it. I have something similar, where I have like 3 or 4 checkbox fields that determine tiny int switches in my database. The way I did this is to add this line in app/Providers/AppServiceProvider.php:
Validator::extendImplicit('checkbox', function($attribute, $value, $parameters, $validator)
{
$data = $validator->getData();
$data[$attribute] = ($value == "1" || strtolower($value) == "true" || strtolower($value) == "on")? "1": "0";
$validator->setData($data);
return true;
});
Explanation:
Validator::extendImplicit
This makes sure that, regardless of whether the attribute is in the request object or not (and if it was unchecked it will not be) it will still run this rule anyways.
$data = $validator->getData();
$data[$attribute] = ($value == "1" || strtolower($value) == "true" || strtolower($value) == "on")? "1": "0";
$validator->setData($data);
Once we enter this function we will get the data array and add the entry for the checkbox as either a 0 or a 1 (note I'm not changing the request object, although you could do that too with something like request->merge() or request()->add()), it then sets the new data array. So in your controller you would simply:
$data = $request->validate(['record_checkbox' => 'checkbox']);
And your data array will contain the field 'record_checkbox' set to either 0 or 1. You can now use this array to fill() your model.
Hope this helps!
Let try:
use Illuminate\Validation\Rule;
$request->validate([
'record_checkbox[]' => [
'required|in_array:[0, 1]'
]
]);

Populate form without model in laravel

My application is made in laravel for a competition admin.
I have 'create' and 'edit' forms on Teams and Players. One team has multiple players.
I would like to link from the Team page to the 'create player' page. The Create Player page does not use a model (doesn't bind). How can I still prefill the select box with the team from the team page? Can I bind without saving a record in the database?
What should my routes be like?
Pass the team ID in the URL?
/players/create?team={teamId}
PlayersController#create method:
$teams = Team::all();
return view('players.create', compact('teams'));
players.create view:
<select name="team">
#foreach ($teams as $team) {
<option value="{{ $team->id }}"{{ $request->has('team') && $request->query('team') === $team->id ? ' selected' : '' }}>{{ $team->name }}</option>
#endforeach
</select>
You could make a route for example
// teamId is optional
Route::get('player/create/{teamId?}', ['as' => 'player_create', function ($teamId = null) {
// You can of course better do this logic in a controller!
// just an example :)
// check if $teamId is null here for example
// Or whatever logic you want to grab a team by
$team = Team::find($teamId);
$teams = Team::all();
// Again.. or whatever way you want to pass your data!
return view('player.create', ['teamName' => $team->name, 'teams' => $teams, 'whatever' => 'elseyouneed']);
}]);
And in the form of your view:
{!! Form::select('team', $teams, $teamName) !!}
Edit
since html isn't part of the core anymore, you can't use that out of the box so I suppose Chris' approach is better. You could however install a package for it.

Resources