Save multiple rows to database from dynamic forms in Laravel 5 - php

I'm using jQuery to generate two dynamic fields. Each pair of fields is displayed on the page and can have multiple instances. On submit (not ajax), each pair of fields is saved into the their own table row along with Auth::id().
There are two forms in the HTML code, both values are entered, user clicks 'Add link' then jQuery creates two hidden fields (these are the ones that get submitted) and data entered appears (appended) visually to #link-list. The original fields become empty and the process can repeat...
I'm struggling to create an array that is recognised by eloquent to save the data multiple times.
I get the error 'Undefined index: link' or whichever the second input row is in jQuery.
Blade/HTML:
{!! Form::open(['route' => ['multiple.store'], 'method' => 'post', 'role'=> 'form', 'class' => 'form']) !!}
<ul id="link-list">
<!-- append new rows -->
</ul>
<div id="newlink" class="form-inline">
<div class="form-group">
{!! Form::text('prestore', null, ['placeholder' => 'Store name', 'class' => 'form-control']) !!}
</div>
<div class="form-group">
{!! Form::text('prelink', null, ['placeholder' => 'Link / URL', 'class' => 'form-control']) !!}
</div>
<div class="form-group">
<button class="btn btn-primary submit new-row" type="button">Add store link</button>
</div>
</div>
<br/><br/>
{!! Form::submit('Submit rows', ['class' => 'btn btn-success submit']) !!}
{!! Form::close() !!}
jQuery/JavaScript
$(document).on('click', '.new-row', function() {
var store = $('#newlink input[name=prestore]').val();
var link = $('#newlink input[name=prelink]').val();
console.log(store, link);
$('<li class="not-saved">' +
''+store+'' +
'<input type="hidden" name="rows[][link]" value="' + link + '">' +
'<input type="hidden" name="rows[][store]" value="' + store + '">' +
'</li>').appendTo('#link-list').hide().fadeIn(280);
$('input[name=prestore]').val('');
$('input[name=prelink]').val('');
});
Controller:
public function store()
{
$input = Input::all();
foreach ($input['rows'] as $row) {
$items = new Multiple([
'user_id' => Auth::id(),
'store' => $row['store'],
'link' => $row['link'],
]);
$items->save();
}
}

One problem is in your JavaScript element names:
<input type="hidden" name="rows[][link]" value="' + link + '">
<input type="hidden" name="rows[][store]" value="' + store + '">
This will generate $rows like:
[
0 => ["link" => "foo"],
1 => ["store" => "bar"]
]
But your PHP code expects $rows to be like:
[
0 => [
"link" => "foo",
"store" => "bar"
],
1 => [
"link" => "foo",
"store" => "bar"
]
]
One way to generate the expected values is to specify the row keys in your elements:
<input type="hidden" name="rows[0][link]" value="' + link + '">
<input type="hidden" name="rows[0][store]" value="' + store + '">
<input type="hidden" name="rows[1][link]" value="' + link + '">
<input type="hidden" name="rows[1][store]" value="' + store + '">
Obviously this is a bit tricky given the code you've provided, so let me know if you need assistance with that.

If this helps anyone else, this is the jQuery required to work with Ben's correct answer:
var count = 0;
$(document).on('click', '.new-row', function() {
count++;
var store = $('#newlink input[name=prestore]').val();
var link = $('#newlink input[name=prelink]').val();
if ($('input[name=prestore]').val().length > 2 && $('input[name=prelink]').val().length > 2) {
$('<li class="not-saved">' +
'' + store + '' +
'<input type="hidden" name="rows[' + count + '][store]" value="' + store + '">' +
'<input type="hidden" name="rows[' + count + '][link]" value="' + link + '">' +
'</li>').appendTo('#link-list').hide().fadeIn(280);
$('input[name=prestore]').val('');
$('input[name=prelink]').val('');
} else {
console.log('At least 3 characters for each field required!');
}
});
I also added a tiny bit of validation so it wont append empty fields

I agree that this is a problem with your element names, but disagree with Ben's solution. You can keep your JavaScript as-is and handle this in PHP:
public function store()
{
$rows = Input::get('rows');
$userId = Auth::id();
for ($i = 0; $i < (count($rows) - 1); $i += 2) {
$item = new Multiple([
'user_id' => $userId,
'link' => $rows[$i]['link'],
'store' => $rows[$i + 1]['store'],
]);
$item->save();
}
}

Related

Axios post method fails with status code 500 with Laravel

as the title says, I'm having the Axios post fail when trying to pass form data to my Laravel controller when using Vue. I've seen many people with the issue, but none of the solutions have worked for me. What I've found is that the 500 status code is a bit too vague. Digging deeper into the issue I'm having, I've found that the main issue is that I have a field in my database that can't be null, here is the error I get in laravel.log
[2018-11-07 18:45:21] development.ERROR: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'string_date' cannot be null (SQL: insert into `action_logs` (`string_date`, `user_id`, `company_name`, `name`, `communication_type`, `contact`, `status`, `action_item`, `activity_key`, `assigned_to`, `updated_at`, `created_at`) values (, 1, , , , , , , 1,2, no one, 2018-11-07 18:45:21, 2018-11-07 18:45:21)) {"userId":1,"email":"frank#*******.com","exception":"[object] (Illuminate\\Database\\QueryException(code: 23000): SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'string_date' cannot be null (SQL: insert into `action_logs` (`string_date`, `user_id`, `company_name`, `name`, `communication_type`, `contact`, `status`, `action_item`, `activity_key`, `assigned_to`, `updated_at`, `created_at`) values (, 1, , , , , , , 1,2, no one, 2018-11-07 18:45:21, 2018-11-07 18:45:21))
This seems to be showing that the data from the form isn't being passed at all, or the controller can't access the data for some reason. I'm not totally sure why though, I built this following an example that worked for me before. I tried a suggestion I saw that by default Axios sends a JSON object. I tried parsing accordingly in the Laravel controller, but that did not fix the issue. I've checked to make sure the csrf token was working properly, and it is. I was able to log a message from the controller, so I know that Vue is finding the route okay. I'm, however, calling my route trying to pass in the company name, like so:
Route::post('/action-log/{url}', 'ActionLogController#store');
I have not tried removing this yet, but that's only because I can see that it was still calling the store method as expected. Here is the relevant code from my Vue component:
<template>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th><a id="sortby-date" class="action-nav" href="?sortby=date&sortdirection=desc">Date</a></th>
<th><a id="sortby-company" class="action-nav" href="?sortby=company&sortdirection=desc">Company</a></th>
<th><a id="sortby-name" class="action-nav" href="?sortby=name&sortdirection=desc">Name</a></th>
<th><a id="sortby-communication-type" class="action-nav" href="?sortby=communication-type&sortdirection=desc">Communication Type</a></th>
<th><a id="sortby-contact" class="action-nav" href="?sortby=contact&sortdirection=desc">Contact</a></th>
<th><a id="sortby-subject" class="action-nav" href="?sortby=subject&sortdirection=desc">Subject</a></th>
<th><a id="sortby-action" class="action-nav" href="?sortby=action&sortdirection=desc">Comment/Action Item</a></th>
<th>Archive</th>
<!-- check if admin?? -->
<th><a id="sortby-assigned-to" class="action-nav" href="?sortby=date&sortdirection=desc">Assigned To</a></th>
<!-- /check if admin?? -->
</tr>
</thead>
<tbody v-if="actions.length > 0">
<tr v-for="action in actions" :key="action.id">
<td>
{{ action.string_date }}
</td>
<td>
{{ action.company_name }}
</td>
<td>
{{ action.name }}
</td>
<td>
{{ action.communication_type }}
</td>
<td>
{{ action.contact }}
</td>
<td>
{{ action.status }}
</td>
<td>
{{ action.action_item }}
</td>
<td>
<input type="checkbox" :id="'archive-' + action.id" class="archive" :name="'archive-' + action.id">
</td>
<td :id="'record-' + action.id" class="assigned-to">
{{ action.assigned_to }}
</td>
</tr>
</tbody>
</table>
<!-- Button trigger modal -->
<p id="add-action" style="text-align: center;">
<button id="action-log-modal" type="button" class="btn btn-primary btn-sm" data-toggle="modal" data-target="#addAction">
Add New Action Log
</button>
</p>
<!-- Modal -->
<div class="modal fade" id="addAction" tabindex="-1" role="dialog" aria-labelledby="addActionTitle" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<form id="action-log-form" class="" name="action-log-form" role="form" method="post" :action="this.url" enctype="multipart/form-data">
<div class="modal-content">
<input type="hidden" name="_token" :value="csrf">
<div class="modal-header">
<h5 class="modal-title" id="addActionTitle">Add Action Log</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<!-- Errors -->
<div class="alert alert-danger" v-if="createAction.errors.length > 0">
<p>
<strong>Whoops!</strong> Something went wrong.
</p>
<p></p>
<ul>
<li v-for="error in createAction.errors">
{{ error }}
</li>
</ul>
</div>
<div class="form-group">
<label for="date">Date</label>
<input id="date" class="form-control form-control-sm" name="date" type="text" value="01/01/1970" placeholder="MM/DD/YYYY" required>
</div>
<div class="form-group">
<label for="company">Company</label>
<input type="text" id="company" class="form-control" name="company" :value="this.companyName" :placeholder="this.companyName" #keyup.enter="store" required readonly>
</div>
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" class="form-control" name="name" :value="this.userFullName" :placeholder="this.userFullName" #keyup.enter="store" required readonly>
</div>
<div class="form-group">
<label for="communication-type">Communication Type</label>
<select id="communication-type" name="communication_type" class="custom-select mr-sm-2" #change="store" v-model="actions.communication_type">
<option value="not-selected">Choose...</option>
<option value="phone">Phone</option>
<option value="email" selected>Email</option>
<option value="in-person">In Person</option>
<option value="fax">Fax</option>
<option value="customer-site">Customer Site</option>
</select>
</div>
<div class="form-group">
<label for="contact">Contact</label>
<input type="text" id="contact" class="form-control form-control-sm" name="contact" value="test contact" #keyup.enter="store" v-model="actions.contact" required>
</div>
<div class="form-group">
<label for="current-status">Current Status</label>
<input type="text" id="current-status" class="form-control form-control-sm" name="current_status" value="test current status" #keyup.enter="store" v-model="actions.current_status" required>
</div>
<div class="form-group">
<label for="action-item">Action</label>
<textarea id="action-item" class="form-control" name="action_item" rows="3" #keyup.enter="store" v-model="actions.action_item">test action</textarea>
</div>
<div class="form-group">
<label>Product Document</label>
<div class="custom-file">
<input type="file" id="productDocument" class="custom-file-input" name="product_document">
<label class="custom-file-label" for="productDocument">Choose file</label>
</div>
</div>
<div class="form-group">
<label>Test Data</label>
<div class="custom-file">
<input type="file" id="testData" class="custom-file-input" name="test_data">
<label class="custom-file-label" for="testData">Choose file</label>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" #click="store">Save</button>
</div>
</div>
</form>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['companyName', 'userFullName'],
data() {
return {
actions: [],
csrf: document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
createAction: {
errors: [],
date: '',
company: '',
name: '',
communication_type: '',
contact: '',
current_status: '',
action_item: ''
},
url: ''
}
},
mounted() {
// console.log('Action Log Mounted.');
this.getActions();
// updates the date when changed in add new action form
$('#date').on('changeDate', () => { this.actions.date = $('#date').val() });
},
methods: {
getActions() {
//get company name to pass to request
var currentLocation = window.location.href;
// console.log(currentLocation);
var companyName = currentLocation.split('/');
companyName = companyName[companyName.length - 1];
let url = '/action-log/' + companyName;
this.url = url;
// console.log("just stored this.url with " + this.url);
//get actions
axios.get(url)
.then(response => {
this.actions = response.data;
this.actions.company = this.companyName;
this.actions.name = this.userFullName;
})
.catch(error => {
console.log(error);
});
},
store() {
let url = window.location.href;
url = url.replace('home', 'action-logs');
console.log(this.url);
this.persistAction(
'post',
// '/' + this.url,
this.url,
this.actions,
'#addAction'
);
},
persistAction(method, uri, form, modal) {
// console.log("method = " + method);
// console.log("uri = " + uri);
// console.log("form = " + form);
// console.log("modal = " + modal);
console.log("date = " + form.date);
console.log("company = " + form.company);
console.log("name = " + form.name);
console.log("contact = " + form.contact);
console.log("current_status = " + form.current_status);
console.log("action_item = " + form.action_item);
form.errors = [];
axios[method](uri, form)
.then(response => {
this.getActions();
// console.log('seemingly successful');
form.date = '';
form.company = '';
form.name = '';
form.communicationType = 'not-selected';
form.contact = '';
form.currentStatus = '';
form.actionItem = '';
form.productDocument = '';
form.testData = '';
form.errors = [];
})
.catch(error => {
if (typeof error.response.data === 'object') {
form.errors = _.flatten(_.toArray(error.response.data));
}
else {
form.errors = ['Something went wrong. Please try again.'];
}
console.log('there was an error: ' + error);
});
}
}
}
</script>
And the code I'm trying to use from my controller:
public function store(Request $request)
{
// get company being viewed
$companyViewing = explode('/', $request->path());
$companyViewing = $companyViewing[count($companyViewing) - 1];
$data = $request->json()->all();
// Log::debug($request);
// Log::debug($data);
$action = new ActionLog();
$action->string_date = $request->date;
$action->user_id = Auth::user()->id;
$action->company_name = $request->company;
$action->name = $request->name;
$action->communication_type = $request->communication_type;
$action->contact = $request->contact;
$action->status = $request->current_status;
$action->action_item = $request->action_item;
$client_id = Company::where('name', '=', $companyViewing)->first(['id']);
$action->activity_key = '1,' . $client_id->id;
if($request->assigned_to !== null) {
$action->assigned_to = $request->assigned_to;
}
else {
$action->assigned_to = "no one";
}
$action->save();
$actions = '';
if(Auth::user()->role == 'admin') {
$actions = ActionLog::where('user_id', '=', Auth::user()->id)
->where('activity_key', '=', '1,' . $client_id->id) //return only relevant activities between AST and client, even if the client is AST!
->orderBy('created_at', 'desc')
->get();
}
else {
$actions = ActionLog::where('user_id', '=', Auth::user()->id)
->where('activity_key', '=', '1,' . $client_id->id)
->orderBy('created_at', 'desc')
->get(['id', 'string_date', 'company_name', 'name', 'communication_type', 'contact', 'status', 'action_item']);
}
return response()->json(['success' => 'Data is successfully added', 'actions' => $actions, 'role' => Auth::user()->role]);
}
EDIT: Here is the output for when I call console.log for the form data from vue.
date = 01/01/1970 app.js:47573:13
company = AST app.js:47574:13
name = Frank app.js:47575:13
contact = zxcv app.js:47576:13
current_status = test app.js:47577:13
action_item = asdf app.js:47578:13
there was an error: Error: Request failed with status code 500
Output in laravel.log when I call Log::debug($request->all()) and Log::debug($data). Both calls give this same array of information, which actually is not relevant to what I'm posting. These are the current records that already exist in the database table, which seems strange since I'm trying to get the data from the form.
[2018-11-07 19:50:14] development.DEBUG: array (
0 =>
array (
'id' => 4,
'user_id' => 1,
'company_name' => 'AST',
'name' => 'Frank',
'communication_type' => 'email',
'contact' => 'asdf',
'status' => 'fdas',
'action_item' => 'asdf',
'assigned_to' => 'no one',
'string_date' => '11/11/2018',
),
1 =>
array (
'id' => 3,
'user_id' => 1,
'company_name' => 'AST',
'name' => 'Frank',
'communication_type' => 'email',
'contact' => 'fdas',
'status' => 'fda',
'action_item' => 'fdas',
'assigned_to' => 'no one',
'string_date' => '10/24/2018',
),
2 =>
array (
'id' => 2,
'user_id' => 1,
'company_name' => 'AST',
'name' => 'Frank',
'communication_type' => 'fax',
'contact' => 'asdf',
'status' => 'asdf',
'action_item' => 'asdf',
'assigned_to' => 'rob',
'string_date' => '10/10/2018',
),
3 =>
array (
'id' => 1,
'user_id' => 1,
'company_name' => 'AST',
'name' => 'Frank',
'communication_type' => 'in-person',
'contact' => 'asdf',
'status' => 'asdf',
'action_item' => 'asdf',
'assigned_to' => 'bob',
'string_date' => '10/02/2018',
),
)
Thank you in advanced for any insight and help!
Thanks to the help of #rickdenhaan and #adam, I found that my output in the controller showed data from my database. While it seems like that there are quite a few reasons you might get a 500 response from an Axios call, my case was that I was passing the incorrect array to the call with data that didn't have anything to do with my form data and I also was binding the data in the form to the incorrect array.

Store registration is not working properly (the code goes always to the else part of the "if ($validator->passes()) {...}"

I have a form in the registration.blade.php page for a user to enter some info to do a registration in a conference.
In the form below there is a part where is used the getHtmlInput() method, that is used to show for each ticket/registration type selected by the user the custom question(s) associated with that registration type for the user to answer. And adds the "required" attribute if in the registration_type_questions pivot table the question has the "required" column as "1".
If the user is doing a registration in a conference and the user didn't select ticket/registration type(s) that have custom questions associated the registration works fine, the code enters in the "if ($validator->passes()) {...}".
Issue:
The issue is when there are custom questions associated with one or more selected ticket/registration types.
If there are custom questions that are required I want to validate that also in Laravel. So I have the code below in the storeRegistration() that shows the message "Fill all mandatory fields" if a custom question that is required was not answered.
But even if the user fill all required custom questions the code never enters in the if "if ($validator->passes()) {...}". Do you know why? Also if the question is required and the required attribute is removed from the HTML in the console and the user click in the "Store Registration" the validation errors don't appear.
The code goes always to the else part of the if " "if ($validator->passes()) {...}" and it shows with "else{dd($validator->errors());}":
MessageBag {#278 ▼
#messages: array:1 [▼
"participant_question.0" => array:1 [▼
0 => "The participant question.0 field is required."
]
]
#format: ":message"
}
Do you know where is the issue?
The storeRegistrationInfo() that stores the registration and has the validation to check if custom question(s) required were not answered:
public function storeRegistration(Request $request, $id, $slug = null, Validator $validator)
{
$user = Auth::user();
$rules = [];
$messages = [];
if (isset($request->participant_question_required)) {
dd($request->participant_question_required);
$messages = [
'participant_question.*.required' => 'Fill all mandatory fields',
];
foreach ($request->participant_question_required as $key => $value) {
$rule = 'string|max:255'; // I think string should come before max
// if this was required, ie 1, prepend "required|" to the rule
if ($value) {
$rule = 'required|' . $rule;
}
$rules["participant_question.{$key}"] = $rule;
}
}
$validator = Validator::make($request->all(), $rules, $messages);
if ($validator->passes()) {
dd('test'); // this dont appears
// create an entry in the registrations table
// create an entry in the participants table for each registered participant
// create a entry in the answer table for each answer to a custom question
if (isset($request->participant_question)) {
foreach( $request->participant_question as $key => $question ) {
$answer = Answer::create([
'question_id' => $request->participant_question_id[$key],
'participant_id' => $participants[$key]->id,
'answer' => $request->participant_question[$key],
]);
}
}
}
}
If in the database the questions tables is like:
id question conference_id
1 Phone? 1
And in the database pivot table registration_type_questions is:
id registration_type_id question_id required
1 1 1 1
The generated HTML with the getHTMLInput() based on the values of the pivot table and Questions table above is:
<form method="post" id="registration_form" action="http://proj.test/conference/1/conference-test/registration/storeRegistration">
<input type="hidden" name="_token" value="">
<p> Is not necessary additional info for the registration. Please just answer the following questions. </p>
<input type="hidden" value="" name="participant_name[]">
<input type="hidden" value="" name="participant_surname[]">
<input type="hidden" name="rtypes[]" value="1">
<div class="form-group">
<label for="participant_question">Phone?</label>
<input type="text" name="participant_question" class="form-control" required="">
<input type="hidden" name="participant_question_required[]" value="1">
<input type="hidden" value="1" name="participant_question_id[]">
</div>
<input type="submit" class="btn btn-primary" value="Store Registration">
</form>
The method getHtmlInput() that generates the html is in the Question model:
public function getHtmlInput($name = "", $options = "", $required = false, $class = "", $customtype = false)
{
$html = '';
$html .= $customtype == 'checkbox' ? "<div class='checkbox-group ".($required ? " required" : "")."'>" : '';
$html .= $customtype == 'select_menu' ? "<select name='participant_question' class='form-control' " . ($required ? " required" : "")
. ">" : '';
if (empty($options)) {
switch ($customtype) {
case "text":
$html .= "
<input type='text' name='participant_question' class='form-control'" . ($required ? " required" : "")
. ">";
break;
case "file":
$html .= "
<input type='file' name='participant_question' class='form-control'" . ($required ? " required" : "") . ">";
break;
case "long_text":
$html .= "
<textarea name='participant_question' class='form-control' rows='3'" . ($required ? " required" : "") . ">"
. $name .
"</textarea>";
break;
}
} else {
foreach ($options as $option) {
switch ($customtype) {
case "checkbox":
$html .= "
<div class='form-check'>
<input type='checkbox' name='participant_question[]' value='" . $option->value . "' class='form-check-input' >
<label class='form-check-label' for='exampleCheck1'>" . $option->value . "</label>
</div>";
break;
case "radio_btn":
$html .= "
<div class='form-check'>
<input type='radio' name='participant_question[]' value='" . $option->value . "' class='form-check-input'" . ($required ? " required" : "") . ">" .
' <label class="form-check-label" for="exampleCheck1">' . $option->value . '</label>' .
"</div>";
break;
case "select_menu":
$html .= "<option value='" . $option->value . "'>" . $option->value . "</option>";
break;
}
}
}
$html .= $customtype == 'select_menu' ? "</select>" : '';
$html .= $customtype == 'checkbox' ? "</div>" : '';
return $html;
}
Form in the view that uses the getHtmlInput() to generate the html for the custom questions:
<div class="card-body">
#include('includes.errors')
<form method="post" id="registration_form" action="{{route('conferences.storeRegistration', ['id' => $id, 'slug' => $slug])}}">
{{csrf_field()}}
#if (!is_null($allParticipants) && is_int($allParticipants))
<!-- If all_participants in conferences table is "1"
is necessary to collect the name and surname of all participants -->
#if($allParticipants == 1)
<p>Please fill the following form.</p>
#else
<!-- if is 0 is not necessary to collect the name and surname of each participant
and its used the name and surname of the auth user to do the registration-->
<!-- the registration types selected by the user can have custom questions associated if don't have no
custom question will appear, if 1 or more registration types have custom questions associated they will
appear on the form for the user to answer-->
#if(collect($selectedRtypes)->first()['questions'] == null)
<p>Is not necessary additional info for the registration. </p>
#else
<p>Is not necessary additional info for the registration. Please just answer the following
questions.</p>
#endif
#endif
<!-- for each selected ticket is necessary collect the name and surname because all_participants is "1" inside this if -->
#foreach($selectedRtypes as $k => $selectedRtype)
#foreach(range(1,$selectedRtype['quantity']) as $val)
#if($allParticipants == 1)
<h6 class="text-heading-blue mb-3 pb-2 font-weight-bold">
Participant - {{$val}} - {{$k}}</h6>
<div class="form-group font-size-sm">
<label for="name{{ $k }}_{{ $val }}"
class="text-gray">Name</label>
<input type="text" id="name{{ $k }}_{{ $val }}"
name="participant_name[]" required
class="form-control" value="">
</div>
<div class="form-group font-size-sm">
<label for="surname{{ $k }}_{{ $val }}"
class="text-gray">Surname</label>
<input type="text" id="surname{{ $k }}_{{ $val }}"
required class="form-control"
name="participant_surname[]" value="">
</div>
<!-- for each registration type if there are custom questions thet will appear so the user can answer -->
#foreach($selectedRtype['questions'] as $customQuestion)
<div class="form-group">
<label for="participant_question">{{$customQuestion->question}}</label>
<!--if the custom question is a type checkbox, radio button or select menu-->
#if($customQuestion->hasOptions() && in_array($customQuestion->type, ['checkbox', 'radio_btn', 'select_menu']))
{!! $customQuestion->getHtmlInput(
$customQuestion->name,
$customQuestion->options,
($customQuestion->pivot->required == '1'),
'form-control',
$customQuestion->type)
!!}
<!-- if the custom question is of type text, file, textarea -->
#else
{!! $customQuestion->getHtmlInput(
$customQuestion->name,
[],
($customQuestion->pivot->required == '1'),
'form-control',
$customQuestion->type)
!!}
#endif
<input type="hidden"
name="participant_question_required[]"
value="{{ $customQuestion->pivot->required }}">
<input type="hidden"
value="{{ $customQuestion->id }}"
name="participant_question_id[]"/>
</div>
#endforeach
#else
<input type="hidden" value=""
name="participant_name[]"/>
<input type="hidden" value=""
name="participant_surname[]"/>
#endif
<input type="hidden" name="rtypes[]"
value="{{ $selectedRtype['id'] }}"/>
#endforeach
<!-- is not necessary collect info of each participant and its used the name and surname of the auth user
to do the registration -->
#if ($allParticipants == 0)
<!-- if the selected registration types have custom questions associated they will appear in the form
so the user can answer -->
#foreach($selectedRtype['questions'] as $customQuestion)
<div class="form-group">
<label for="participant_question">{{$customQuestion->question}}</label>
<!-- if the custom question is of type checkbox, radio button or select menu -->
#if($customQuestion->hasOptions() && in_array($customQuestion->type, ['checkbox', 'radio_btn', 'select_menu']))
{!! $customQuestion->getHtmlInput(
$customQuestion->name,
$customQuestion->options,
($customQuestion->pivot->required == '1'),
'form-control',
$customQuestion->type)
!!}
<!-- if the checkbox is of type text, textarea, file-->
#else
{!! $customQuestion->getHtmlInput(
$customQuestion->name,
[],
($customQuestion->pivot->required == '1'),
'form-control',
$customQuestion->type)
!!}
#endif
<input type="hidden"
name="participant_question_required[]"
value="{{ $customQuestion->pivot->required }}">
<input type="hidden"
value="{{ $customQuestion->id }}"
name="participant_question_id[]"/>
</div>
#endforeach
#endif
#endforeach
#endif
<input type="submit" class="btn btn-primary" value="Store Registration"/>
</form>
</div>
$rules before "$validator = Validator::make($request->all(), $rules);" shows:
array:1 [▼
"participant_question.0" => "required|string|max:255"
]
request->all() before "$validator = Validator::make($request->all(), $rules); "shows:
array:7 [▼
"_token" => ""
"participant_name" => array:1 [▼
0 => null
]
"participant_surname" => array:1 [▼
0 => null
]
"rtypes" => array:1 [▼
0 => "1"
]
"participant_question" => "j"
"participant_question_required" => array:1 [▼
0 => "1"
]
"participant_question_id" => array:1 [▼
0 => "1"
]
According to the documentation:
If you do not want to use the validate method on the request, you may create a validator instance manually using the Validator facade. The make method on the facade generates a new validator instance:
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
if ($validator->fails()) {
return redirect('post/create')
->withErrors($validator)
->withInput();
}
// Store the blog post...
}
They don't seem to be passing the Validator as a parameter as you are, rather they are statically calling the function without any reference to the request or any other parameters.
Secondly, consider using the nullable validation flag which allows you to define optional fields.
We have a pass method there :
$rules = ['name' => 'unique'];
$input = ['name' => null];
Validator::make($input, $rules)->passes(); // true
So define your rules and number of inputs and give it a go.
You can use dd(Validator::make($input, $rules)->passes();) it will give you true or false, False for not failed validation and true for pass validation.
Further use it like so :
if(Validator::make($input, $rules)->passes(); // true){
//On pass validation, This section will execute
}

get checked option value in laravel

I have input group include 2 options checkbox and input field.
I want to get value of input field only if checkbox is checked
otherwise ignore that input field.
Codes
#foreach($list as $columns)
<div class="input-group">
<span class="input-group-addon">
<input type="checkbox" checked="checked">
</span>
<input type="text" name="{{$columns}}" value="{{$columns}}" class="form-control">
</div><!-- /input-group -->
#endforeach
currently I get all my inputs in my controller regardless they are checked or not:
$column = $request->except('_token');
How do I pass this to my controller function?
UPDATE
as requested : my code $list dd is:
array:27 [▼
0 => "id"
1 => "title"
2 => "slug"
3 => "imageOne"
4 => "imageTwo"
5 => "short_description"
6 => "description"
7 => "price"
8 => "meta_description"
9 => "meta_tags"
10 => "arrivalDays"
11 => "height"
12 => "weight"
13 => "lenght"
14 => "width"
15 => "sku"
16 => "stock"
17 => "label"
18 => "label_from"
19 => "label_to"
20 => "label_color"
21 => "status_id"
22 => "brand_id"
23 => "category_id"
24 => "subcategory_id"
25 => "created_at"
26 => "updated_at"
]
UPDATE 2
To be clear how it works exactly i included screenshot
SEE IT
1) change the name of checkbox to cb[] and input to input[]
#php
$no = 0;
#endphp
#foreach($list as $columns)
#php
$no+=1;
#endphp
<div class="input-group">
<span class="input-group-addon">
<input type="checkbox" name="cb[{{$no}}]" checked="checked">
</span>
<input type="text" name="input[{{$no}}]" value="{{$columns}}" class="form-control">
</div><!-- /input-group -->
#endforeach
2)filter your $request and include just the checked checkbox
$input = $request->except('_token');
foreach ($input['cb'] as $key => $value) {
if ($value== 'on') {
$getRealInput[$key] = $input['input'][$key];
}
}
return $getRealInput;
Try This:
1) first of all mention checkbox name like this :
#foreach($list as $columns)
<div class="input-group">
<span class="input-group-addon">
<input name="checkbox" type="checkbox" checked="checked">
</span>
<input type="text" name="{{$columns}}" value="{{$columns}}" class="form-control">
</div><!-- /input-group -->
#endforeach
2) In Controller try this:
$inputs = asset($request->checkbox) && $request->checkbox !='' ? $request->except('_token') : []; //if you want to remove input fields use unset inputs instead of []
dd($inputs)
You could name the checkbox as an array
#foreach($list as $columns)
<div class="input-group">
<span class="input-group-addon">
<input type="checkbox" name="cb[]" value="{{ $columns }}">
</span>
<input type="text" name="{{$columns}}" value="{{$columns}}" class="form-control">
</div><!-- /input-group -->
#endforeach
and in your controller you could get the checked items as
foreach ($request->cb as $checked) {
$request->{$checked} // The textbox corresponding to the selected checkbox
}
You should try this:
Updated Answer:
#foreach($list as $columns)
<div class="input-group">
<span class="input-group-addon">
<input type="checkbox" name="columns[]" value="{{ $columns }}">
</span>
<input type="text" name="{{$columns}}" value="{{$columns}}" class="form-control">
</div><!-- /input-group -->
#endforeach
In controller :
$columns = $request->columns;
print_r($columns);
First of all, identify each checkboxes with unique names(use columns[]).
Here I guess you don't expect manual entry(modification) as you populate the value of $columns in your text input field. If so, yoou could use a label instead:
#foreach($list as $key => $columns)
<div class="input-group">
<span class="input-group-addon">
<input name="columns[]" type="checkbox" value={{$key}} checked="checked">
</span>
<label class="form-control">{{$columns}}</label>
</div>
#endforeach
UPDATE:
VIEW
#foreach($list as $key => $columns)
<span class="input-group-addon">
<input name="col_keys[]" type="checkbox" value={{$key}} checked="checked">
</span>
<input type="text" name="col_texts[]" value="{{$columns}}" class="form-control">
#endforeach
CONTROLLER
Now $request->col_keys will have an array of keys that are valid for $request->col_texts. So do a sorting:
$valid_keys = $request->col_keys;
$all_texts = $reqest->col_texts;
foreach($all_texts as $key => $item) {
if (($key = array_search($key, $valid_keys)) === false) {
unset($all_texts($key));
}
}
SOLVED
First of all I have to thank everyone who tried to help me solve this issue specially Wahyu Fadzar. Here is how I solved my issue:
I changed my blade code to:
#php $no = 0; #endphp //Wahyu Fadzar idea
#foreach($list as $columns)
#php $no+=1; #endphp //Wahyu Fadzar idea
<div class="input-group">
<span class="input-group-addon">
<input type="checkbox" name="cb[{{$no}}]" checked="checked"> //Wahyu Fadzar idea
</span>
<input type="text" name="customname[{{$no}}]" value="{{$columns}}" //Wahyu Fadzar ideaclass="form-control">
<input type="hidden" name="defaultname[{{$no}}]" value="{{$columns}}"> // Added
</div><!-- /input-group -->
#endforeach
Controller
public function export(Request $request) {
// defaultname that i added in my blade used here (explaination below)
$input = $request->except('_token');
foreach ($input['cb'] as $key => $value) {
if ($value== 'on') {
$getRealInput[$key] = $input['defaultname'][$key];
}
}
$products = Product::select($getRealInput)->get(); //getting table selected columns
Excel::create('products', function($excel) use($products, $request) {
$excel->sheet('sheet 1', function($sheet) use($products, $request){
//Wahyu Fadzar code used here to get selected columns data
$input = $request->except('_token');
foreach ($input['cb'] as $key => $value) {
if ($value== 'on') {
$getCustomInput[$key] = $input['customname'][$key];
}
}
$sheet->fromArray($products, null, 'A1', false, false);
$sheet->row(1, $getCustomInput);
});
})->export('csv');
return redirect()->back();
}
Explanations
I had issue before when I used Wahyu code it would remove my inputs from CSV file as i unchecked them, but their data still exist in my file
so I added
< input type = " hidden " name = " defaultname[{{$no}}] " value = " {{$columns}} " >
and by this i will get my default column names even if i edit it for export. otherwise it would return error, eg. I changed my title column to name it says there is no column name! so by this my title column will stay title for select query.
I used it in:
$products = Product::select($getRealInput)->get();
I used Wahyu code in my export part in order to get custom input names for my CSV file and not the default ones.
< input type = " text " name = " customname[{{$no}}] " value = "
{{$columns}} " class = " form-control " >
goes to:
if ($value== 'on') {
$getCustomInput[$key] = $input['customname'][$key];
}
inside my export query.
Hope it help others as well.
THANK YOU ALL.

Array of values from the same radio button group

I'm making a website in Laravel where user needs to enter his pseudonym, and on the settings page he can add multiple pseudonyms with jQuery. When he clicks the plus button, a text field, radio button and minus fields are added.
Radio buttons are there to mark one of those nicknames as default.
Image: screenshot of how the inputs look like
How do I pass an array of values out of those radio buttons? ie, if I selected 3rd out of 4 pseudonyms to be default:
{'0' => null, '1' => null, '2' => '1', '3'=> null}
So I can merge this array into pseudonyms array to be like:
{'0' => {'0' => 'John Doe', '1' => null},
'1' => {'0' => 'John Smith', '1' => null},
'2' => {'0' => 'John Wayne', '1' => '1'},
'3' => {'0' => 'John X', '1' => null}}
Finally, I would save it like this, or some other method that would also check if already existent and then update, but that's not important right now:
foreach($pseudonyms as $pseudonym)
$user->pseudonyms()->create(['name' => $pseudonym[0], 'status' => $pseudonym[1]]);
I'm probably just inexperienced and don't know some html/php basics that will let me do that, but what can I do :)
Anyway, here's the Blade part where inputs are:
<div class="input_fields_wrap">
<div class="row">
<div class="col-md-8">
{!! Form::text('pseudonyms[]', null,['class' => 'form-control']) !!}
</div>
<div class="col-md-2">
{!! Form::radio('default[]', 1, null,['class' => 'form-control']) !!}
</div>
<div class="col-md-2">
{!! Form::button('<i class="fa fa-plus" aria-hidden="true"></i>',['class'=>'btn btn-default add_field']) !!}
</div>
</div>
</div>
And here is the part of jQuery that appends the additional fields in .input_fields_wrap
$(document).ready(function() {
var max_fields = 5; //maximum input boxes allowed
var wrapper = $(".input_fields_wrap .row"); //Fields wrapper
var add_button = $(".add_field"); //Add button ID
var added_fields = 1; //initial text box count
$(add_button).click(function(e){ //on add input button click
e.preventDefault();
if(added_fields < max_fields){ //max input box allowed
$(wrapper).append('<div class="added"><div class="col-md-8 class1"><input type="text" name="pseudonyms[]" class="form-control"/></div>' +
'<div class="col-md-2 class2" ><input type="radio" name="default[]" value="1" class="form-control"/></div>' +
'<div class="col-md-2 class3" ><input type="button" value="-" class="remove_field btn btn-default add_field fa fa-minus"></div></div>'); //add input box
added_fields++; //text box increment
}
});
$(wrapper).on("click",".remove_field", function(e){ //user click on remove text
e.preventDefault();
$(this).closest('.added').remove();
added_fields--;
})
});
Thank you for your comments!
Of course, solution was working on something else, coming back and having an answer after 2 minutes thinking about it. Created new array with a key from the radio button's value, so I could merge it with the other array with
foreach($request->pseudonyms as $key => $value)
{
if(array_key_exists($key, $default))
{
$pseudonyms[$key] = array($value, $default[$key]);
} else {
$pseudonyms[$key] = array($value, null);
}
}

Laravel edit form can't update child/parent row

Im new to laravel and i want to make an update function but it doesn t work. I get my update form correctly but when i submit it i get an error that says it can t updata a parent/child row
This is my function
public function update($verhaal, $wereld)
{
$wereld = Werelden::where('wereld_id', $wereld)->update([
'wereld_id' => $wereld,
'naam' => 'naam',
'beschrijving' => 'beschrijving',
'verhaal_id' => 'verhaal_id'
]);
return redirect('/');
}
This are my routes
Route::get('verhalen/{verhaal}/bekijken/{wereld}/edit', 'WereldController#BewerkWereld');
Route::put('verhalen/{verhaal}/bekijken/{wereld}/edit', 'WereldController#update');
And as last i have my html form
<div class="panel-body">
#include('common.errors')
<form action="{{ url('/verhalen/'. $wereld->verhaal_id .'/bekijken/'. $wereld->wereld_id .'/edit')}} " method="POST">
{{ csrf_field() }}
{{ method_field('PUT') }}
<input type="text" class="form-control verhaal-toevoegen" value="{{ $wereld->naam }}" name="naam">
<textarea class="form-control" id="verhaal-text" name="verhaal">{{ $wereld->beschrijving}}</textarea>
<button type="submit" class="btn btn-primary">Verhaal bijwerken</button>
</form>
</div>
I hope someone can help me
public function update($verhaal, $wereld)
{
$wereld = Werelden::where('wereld_id', $wereld)->update([
'wereld_id' => $wereld,
'naam' => 'naam',
'beschrijving' => 'beschrijving',
'verhaal_id' => $verhaal_id // "verhaal_id" It is not an id
]);
return redirect('/');
}
uses columns necesaries only
$wereld = Werelden::where('wereld_id', $wereld)->update([
'wereld_id' => $wereld,
'naam' => 'naam',
'beschrijving' => 'beschrijving',
]);

Categories