How to get element by parent classname in PHP - php

HTML markup:
<form>
<div class="required">
<input id="name" name="name">
</div>
<div class="required">
<input id="email" name="email">
</div>
<div class="non-required">
<input id="other" name="other">
</div>
...
alot of input here
...
</form>
PHP:
<?php
extract($_POST, EXTR_PREFIX_ALL, 'input');
if (empty($input_name) || empty($input_email) || empty($input_other) || ... alot of input here...) { // i want only the input that has `required` class in this line
// main function here
}
?>
I can manually edit it but how can auto select input that has required class for the PHP main function?
Thanks.

You can't access the parent's class name. This information is not transmitted when the user submits the form.
The only information available in $_POST are the name and value of the input element. You could define the names of your input elements to represent required / non required like that:
<form>
<div class="required">
<input id="name" name="required[name]">
</div>
<div class="required">
<input id="email" name="required[email]">
</div>
<div class="optional">
<input id="another" name="optional[another]">
</div>
<div class="required">
<input id="other" name="required[other]">
</div>
</form>
Using this schema you will have two sub arrays in $_POST, named required and optional:
Array //$_POST
(
[required] => Array
(
[name] => value,
[email] => value,
[name] => value
),
[optional] => Array
(
[another] => value
)
)
Warning
If you're using this solution please make sure you're validating the input correctly. You are going to trust the user agent to provide correct information about the fields. Look at Trincot's answer for a purely server side solution.

Since you produce the HTML yourself, you actually know which input elements have the class "required". So I would propose you first create an array with the required fields, and generate the HTML from that with dynamically class values.
Then later you can use the same array to check for emptiness:
HTML generation:
<?php
// define the list of required inputs:
$required = array("name", "email");
// define a function that returns "required" or "non-required"
// based on the above array.
function req($name) {
global $required;
return in_array($required, $name) ? 'required' : 'non-required';
}
// Now generate the classes dynamically, based on the above:
?>
<form>
<div class="<?=req('name')?>">
<input id="name" name="name">
</div>
<div class="<?=req('email')?>">
<input id="email" name="email">
</div>
<div class="<?=req('other')?>">
<input id="other" name="other">
</div>
...
alot of input here
...
</form>
Then in the processing of the input, use the above function again:
<?php
// This extract is not needed for the next loop, but you might need it still:
extract($_POST, EXTR_PREFIX_ALL, 'input');
// go through all inputs that are required and test for empty
// until you find one, and produce the appropriate response
foreach($required as $name) {
if (empty($_POST[$name])) {
// main (error?) function here
break; // no need to continue the loop as we already found an empty one
}
}
?>

Related

naming a dynamic number of form inputs incrementally

I generate a dynamic quantity of form inputs, based on a user-submitted number ($transponum).
I use str_repeat to build a string containing the desired number of inputs.
But I'm having trouble naming the inputs. I need:
<input type="text" name="transpo(a number that increases every repeat)" />
How can I achieve that with PHP?
Here is my code:
<form id="form" name="form" method="post" action="step8.php" role="form">
<div class="form-group"><?php
$a = 1;
$str.= "<label class=\"control-label\" for=\"exampleInputEmail1\">Name</label>";
$str.= "<input style=\"width: 60%;\" type=\"text\" class=\"form-control\" id=\"transponum\" name=\"transponame".$a."\" placeholder=\"Name\">";
$str.= "<label class=\"control-label\" for=\"exampleInputEmail1\">ID</label>";
$str.= "<input style=\"width: 60%;\" type=\"text\" class=\"form-control\" id=\"transponum\" name=\"transpoid".$a."\" placeholder=\"Name\"><hr>";
$str.= $a = $a+1;
echo str_repeat($str, $transponum);
?></div>
<button type="submit" class="btn-success btn" name="submit">Next</button>
</form>
I have several suggestions, detailed below:
Use a loop
Use a for loop rather than str_repeat.
Use the loop to increment your $a variable from 1 to $transponum.
for ($a=1;$a<=$transponum;$a++) { ... }
Post as arrays
Name your inputs so that they post as arrays. That way, you don't need to dynamically build the input names.
<input name="names[]" />
<input name="ids[]" />
Notice the square brackets after the variable name, that's what makes it an array. You can group the elements into different arrays by assigning the same name to different elements.
-- FAQ # php.net
Then, you'll end up with a $_POST array like this:
Array
(
[names] => Array
(
[0] => 'name 1',
[1] => 'name 2',
[2] => 'name 3',
...
),
[ids] => Array
(
[0] => 'id 1',
[1] => 'id 2',
[2] => 'id 3',
...
)
)
Remove duplicated IDs
Element IDs must be unique. Associate labels with inputs implicitly by placing the <input> elements inside their respective <label> elements. Then you can remove the duplicated IDs.
To associate a label with another control implicitly, the control element must be within the contents of the LABEL element. -- forms # w3.org
<label>Name: <input /></label>
Also see this simple label example at developer.mozilla.org, for reference.
Complete Example
Here's an example that includes all of my suggestions:
<form id="form" name="form" method="post" action="step8.php" role="form">
<div class="form-group"> <?php
for ($a=1;$a<=$transponum;$a++) {
?><label>
<span>Name</span>
<input type="text" name="transponame[<?=$a?>]" placeholder="Name">
</label>
<label>
<span>ID</span>
<input type="text" name="transpoid[<?=$a?>]" placeholder="Name">
</label>
<hr><?php
}
?></div>
<button type="submit" class="btn-success btn" name="submit">Next</button>
</form>
Edit:
Note that specifying the $a value in input names is probably not necessary. In your context, empty brackets will work just as well:
<input type="text" name="transponame[]" placeholder="Name">
Specifying array keys is optional in HTML. If you do not specify the keys, the array gets filled in the order the elements appear in the form. -- FAQ # php.net
To add to showdev's answer.
You cannot use str_repeat to achieve what you have in mind. str_repeat literally repeats a string. See the documentation page.
In your case, you have to use either a loop or create array type input names.
Make sure you don't use duplicate Ids
<form id="form" name="form" method="post" action="step8.php" role="form">
<div class="form-group">
<?php for($i=0;$i<transponum;$i++){ ?>
<label class="control-label" for="exampleInputEmail1">Name</label>";
<input style="width: 60%;" type="text" class="form-control" id="transponum" name="transponame<?php echo $i;?>" placeholder="Name">
<label class="control-label" for="exampleInputEmail1">ID</label>
<input style="width: 60%;" type="text" class="form-control" id="transponumLabel" name="transpoid<?php echo $i;?>" placeholder="Name"><hr>
<?php } ?>
</div>
<button type="submit" class="btn-success btn" name="submit">Next</button>
</form>

Map dynamic array of checkboxes without index

This question builds further on the question asked here: How to map dynamic array of input fields .
I have a dynamic set of rows with each it's own input fields. These rows can be dynamically added to the DOM, so I have to use input arrays without an index ( eg fieldname[] instead of fieldname[1] etc).
The problem occurs when I use checkboxes in these rows. Since checkboxes are not submitted when they are not checked, I see no way of knowing which submitted checkbox belongs to which row values.
Example of my form:
<form>
<div class="row">
<input type="text" name="product[]">
<input type="text" name="qty[]">
<input type="checkbox" name="projectline[]">
</div>
<div class="row">
<input type="text" name="product[]">
<input type="text" name="qty[]">
<input type="checkbox" name="projectline[]">
</div>
<div class="row">
<input type="text" name="product[]">
<input type="text" name="qty[]">
<input type="checkbox" name="projectline[]">
</div>
</form>
I found an answer to a similar problem here: php array of checkboxes , but the answer obviously only applies to arrays with an index.
What is the best approach here?
EDIT :
I also check the form for errors server-side and redirect it back if it is faulty, So I need to be able to 'reconstruct' the form based on the submitted values.
One trick I've seen used for this is to put a hidden field before each checkbox that submits the same field with a value of 0. That way, if you check the checkbox it will overwrite the 0 value with the checkbox value, but if you don't, you'll get a 0 for unchecked instead of nothing in your data.
The answer from the comments of keeping a running total of indexes could work, too, but is a bit more complicated depending on how and when the DOM can be modified.
I ended up assigning an index number to each of the rows, generating a new random id each time a row is added. I used jQuery for the clone functions and event binding.
Below is my complete solution.
This is my original form:
<form>
<div class="row">
<input type="text" name="product[0]">
<input type="text" name="qty[0]">
<input type="checkbox" name="projectline[0]">
</div>
</form>
I have a template row that I use to make clones of:
<div id="templaterow">
<input type="text" name="product[%%index%%]">
<input type="text" name="qty[%%index%%]">
<input type="checkbox" name="projectline[%%index%%]">
</div>
A button to clone the row:
<button id="addrow" value="add new row"/>
And a function bound to the button:
$('#addrow').on('click',function()
{
//template row is cloned and given the right attributes:
var clone = $('#templaterow').clone(true, true);
$('.row').last().after(clone);
clone.addClass('row').removeAttr('id');
// the %%index%% placeholder is replaced by a random index number between 100 and 9999999
clone.html(function (index, html) {
var rndIndex = Math.floor((Math.random() * 9999999) + 100);
return html.replace(new RegExp('%%index%%','g'),rndIndex);
});
});

Hidden element of a form array is not getting set in PHP

So I've been tasked with crawling through a smallish PHP application to fix bugs and improve things where I can. One thing I noticed was that updates were not updates, they were deletes+inserts, so I'm doing what I can to remedy that.
To that end, I've added a hidden element to each row of a large table of items which will contain the id of said item so that I know what to update. Problem is that it isn't getting set in the items array in $_POST, and I haven't any clue why.
As an example, here's a row in HTML:
<div class="row">
<div class="c item_index"><sub>1</sub></div>
<input type="hidden" name="ordered_items[1][id]" id="item_id1" value="9" disabled="">
<div class="c qty">
<input type="text" name="ordered_items[1][quantity]" id="quantity1" value="12">
</div>
<div class="c vendor_num">
<input type="text" name="ordered_items[1][vendor_number]" id="vendor_num1" value="">
</div>
<div class="c item_desc">
<input type="text" name="ordered_items[1][description]" id="desc1" value="12">
</div>
<div class="c cost_per">
<input type="text" name="ordered_items[1][cost_per]" id="cost1" value="12.00">
</div>
<div class="c total">
<input type="text" name="ordered_items[1][total]" class="total" placeholder="0.00"
id="total1" value="144.00" readonly="">
</div>
</div>
Here's it's entry in $_POST:
[ordered_items] => Array
(
[1] => Array
(
[quantity] => 12
[vendor_number] =>
[description] => 12
[cost_per] => 12.00
[total] => 144.00
)
)
I appreciate any and all suggestions!
Remove the disabled="" attribute. Disabled inputs are not submitted when posting.
From the HTML specification
A number of attributes are boolean attributes. The presence of a boolean attribute on an element represents the true value, and the absence of the attribute represents the false value.
If the attribute is present, its value must either be the empty string or a value that is an ASCII case-insensitive match for the attribute's canonical name, with no leading or trailing whitespace.
Note: The values "true" and "false" are not allowed on boolean attributes. To represent a false value, the attribute has to be omitted altogether.
Try removing the disabled="" attribute in the hidden field.

ZF2 how to wrap content in form fieldset?

I have form with fieldsets:
$formConfig = array(
'fieldsets' => array(
...
);
);
$factory = new Zend\Form\Factory();
$form = $factory->createForm($formConfig);
echo $this->form($form);
It renders something like this:
<form>
<fieldset>
<legend>Fieldset label</legend>
<label><span>Elem 1</span><input type="text" name="f1[el1]" /></label>
<label><span>Elem 2</span><input type="text" name="f1[el2]" /></label>
<label><span>Elem 3</span><input type="text" name="f1[el3]" /></label>
</fielset>
</form>
The problem is that I need to wrap content after legend:
<form>
<fieldset>
<legend>Fieldset label</legend>
<div class="wrapper">
<label><span>Elem 1</span><input type="text" name="f1[el1]" /></label>
<label><span>Elem 2</span><input type="text" name="f1[el2]" /></label>
<label><span>Elem 3</span><input type="text" name="f1[el3]" /></label>
<div>
</fielset>
</form>
How can I do that?
Once more you need to understand that a Zend\Form\Fieldset does not equal a HTML <fieldset>! A Zend\Form\Fieldset merely is a collection of Zend\Form\Element that usually represent one entity and you could provide several entities with data from one Form.
Now when it comes to rendering the form, the first thing you should learn about are the several Zend\Form\View\Helper-Classes. You are using the form() view-helper, which automatically translates all Zend\Form\Element using formRow() and all Zend\Form\Fieldset using formCollection(). But you don't want to do that!
When wanting your preferred output, you will be needed to render the form yourself. Something like this could be your view-template:
<?=$this->form()->openTag($form);?>
<fieldset>
<div class="wrapper">
<?=$this->formRow($form->get('f1')->get('el1'));?>
<?=$this->formRow($form->get('f1')->get('el2'));?>
<?=$this->formRow($form->get('f1')->get('el3'));?>
</div>
</fieldset>
<?=$this->form()->closeTag();?>
Now, this already has a little comfort within it, as you'd be using formRow(). You could also split up each form-row and go the very detailled way like:
<label>
<span><?=$this->formLabel($form->get('f1')->get('el1'));?></span>
<?=$this->formInput($form->get('f1')->get('el1'));=>
<?=$this->formElementErrors($form->get('f1')->get('el1'));?>
</label>
Even there, formInput() still is a magic that derives into things like formText(), formSelect(), formTextarea(), etc.., etc...

cloning form elements and the return a template file

I am building a website, where on the click of a link you can clone the form elements, what I am wanting to know is that, when I send the $_POST to my controller and check that the information submitted is correct, how do I then return a template that has enough elements so that errors can be rectified, so example my original form looks like this,
<fieldset class="entry">
<label for="email_address">Email Address</label>
<input type="text" name="email_address[]" value="" class="text small"/>
<label for="firstname">Firstname</label>
<input type="text" name="firstname[]" value="" class="text small"/>
<label for="surname">Surname</label> <input type="text" name="surname[]" value="" class="text small"/>
</fieldset>
how can I return the correct amound of fieldsets based on the $_POST?
To answer this:
How can I return the correct amount of fieldsets based on the $_POST?
If each fieldset only has one instance of each bracketed[] field name, you can just count how many were submitted (of any of the fields).
$number_of_fieldsets = count((array) $this->input->post('email_address'));
I've used $this->input->post() (since you're using CI) in case the value is not set (it will return false), you may use some isset() logic instead if you wish.
I've cast to array here in case the return value of $this->input->post('email_address') is false (count will return 0) or for some reason, a string (count will return 1). This is just a mild attempt at being defensive, you will probably want to handle unexpected results with your own methods.
Once again, it doesn't matter which field you choose to count.

Categories