How to produce repetitive HTML form fields? - php

I am using PHP to create a web application where user has to submit a form. Currently I am using simple html to render out the form just like given below.
<?php ?>
<form class='form-horizontal' method='post'>
<div class='form-group mail-right-element'>
<label class='col-sm-12 control-label mail-form-single-line-label' for='name 1'>Label 1</label>"
<div class='col-sm-12'>
<input name='name1' class='form-control validate' maxlength='100' placeholder='placeholder' value='value1'/>");
</div>
</div>
..
..
..// Some 20+ similar form fields
..
<div class='form-group mail-right-element'>
<label class='col-sm-12 control-label mail-form-single-line-label' for='name 20'>Label 20</label>"
<div class='col-sm-12'>
<input name='name20' class='form-control validate' maxlength='100' placeholder='placeholder' value='value20'/>");
</div>
</div>
</form>
This looks really messy and hardly maintainable. So I went the php way as given below.
<?php
function GetFormRowHtml($label, $name, $maxlength, $placeholder, $value)
{
return "<div class='form-group mail-right-element'>"
. "<label class='col-sm-12 control-label mail-form-single-line-label' for='$name'>$label</label>"
. "<div class='col-sm-12'>"
. "<input name='$name' class='form-control validate' maxlength='$maxlength' placeholder='$placeholder' value='$value'/>"
. "</div></div>";
}
?>
<form class='form-horizontal' method='post'>
<?php
echo GetFormRowHtml('Label1', 'Name1', 'abc', 'abc');
echo GetFormRowHtml('Label2', 'Name2', 'abc', 'abc');
echo GetFormRowHtml('Label3', 'Name3', 'abc', 'abc');
?>
</form>
Is there any better approach to do this? I've read that having html in php is a really bad design. So should I go with the html way or continue using this approach? What are the disadvantages of using above method?

You could have the input as a separate PHP file that you'd use for including:
input-template.php
<div class='form-group mail-right-element'>
<label class='col-sm-12 control-label mail-form-single-line-label' for='<?= $name ?>'><?= $label ?></label>"
<div class='col-sm-12'>
<input id='<?= $name ?>' name='<?= $name ?>' class='form-control validate' maxlength='100' placeholder='<?= $placeholder ?>' value='<?= $value ?>'/>");
</div>
</div>
And in your HTML:
<form class='form-horizontal' method='post'>
<?php
$values = [
[ "Label 1", "Name 1", "abc" , "abc" ],
[ "Label 2", "Name 2", "abc" , "abc" ],
[ "Label 3", "Name 3", "abc" , "abc" ],
[ "Label 4", "Name 4", "abc" , "abc" ]
];
foreach ($values as $array) {
list($label,$name,$placeholder,$value) = $array;
include "input-template.php"
}
?>
</form>
The reason I personally Opt for this style is that IDEs generally can analyze the HTML in the template and help with formatting and detecting of errors while string HTML would usually not be treated that way.

The best thing to do when looking at a common problem like this is to look at the some of the PHP frameworks and see how they handle it.
Looking at Laravel or Yii they abstract any inputs or labels into functions which generate the markup. So you're on the right track. The main issue with your code above is that you are combining layout with the label / field markup. Inherently this isn't' a massive issue but doesn't give your code much flexibility.
Markup should be moved into its own partial file so it's easy to maintain, form php functions should be more explicit generally.

This is my approach ...
function GetFormRowHtml($label, $name, $maxlength, $placeholder, $value)
{
/**
* #var array
* Grouping all searches and replaces in one array
*/
$sar = [
'{label}' => $label,
'{name}' => $name,
'{maxlength}' => $maxlength,
'{placeholder}' => $placeholder,
'{value}' => $value
];
/**
* #var $elementTemplate
* Template for a row
*/
$elementTemplate = "
<div class='form-group mail-right-element'>
<label class='col-sm-12 control-label mail-form-single-line-label' for='{name}'>{label}</label>
<div class='col-sm-12'>
<input name='{name}' class='form-control validate' maxlength='{maxlength}' placeholder='{placeholder}' value='{value}'/>
</div>
</div>
";
return str_replace(array_keys($sar), array_values($sar), $elementTemplate);
}
?>
<form class='form-horizontal' method='post'>
<?php
echo GetFormRowHtml('Label1', 'Name1', 'abc', 'abc');
echo GetFormRowHtml('Label2', 'Name2', 'abc', 'abc');
echo GetFormRowHtml('Label3', 'Name3', 'abc', 'abc');
?>
</form>
The approach from apokryfos is another good solution.

Related

Laravel 5.7 validate fields with asterisk, required_if

I have a Vue form that let users add work experience for there profile.
Users can add extra experience by clicking on a button. Clicking on that will add an new item with new input fields. I can't add the whole script because it's quit big. But here is an example to give you an idea:
<div class="item">
<div class="row">
<div class="form-group col-md-6">
<label class="form-label">Title</label>
<input type="text" name="experiences[0][title]" class="form-control">
</div>
<div class="form-group col-md-6">
<label class="form-label">Institution</label>
<input type="text" name="experiences[0][institution]" class="form-control">
</div>
</div>
<div class="row">
<div class="col-md-12">
<textarea name="experiences[0][comments]" class="form-control"></textarea>
</div>
</div>
</div>
<div class="item">
<div class="row">
<div class="form-group col-md-6">
<label class="form-label">Title </label>
<input type="text" name="experiences[1][title]" class="form-control">
</div>
<div class="form-group col-md-6">
<label class="form-label">institution </label>
<input type="text" name="experiences[1][institution]" class="form-control">
</div>
</div>
<div class="row">
<div class="col-md-12">
<textarea name="experiences[1][comments]" class="form-control"></textarea>
</div>
</div>
</div>
After each element there is a button to add a new row. This works fine but I have some validation issues.
I only want to validate the fields if one of the fields has a value. For example:
If experiences[0][institution] has a value, experiences[0][title] and experiences[0][comments] are required.
This has to work in every order. If title has a value, the other fields are required.
I can't really find out a way how to validate this. This is my validation rule:
$this->validate(request(), [
'experiences.*.title' => 'required_if:experiences.*.institution,null',
'experiences.*.institution' => 'required_if:experiences.*.title,null',
]);
Problem here is that it simply doesn't validate. I can't figure out how to make a rule that says, if field X has a value, Y and Z are required.
Hope anyone here can help me finding a solution! :)
Like Azeame said, make a custom validation rule and check if all values are filled or all are empty. Something in de lines of:
public function passes($attribute, $value)
{
$required = ['title','institution','comments'];
$experience = collect($value)->reject(function ($item, $key) {
return empty($item);
});
if (count($experience) == 0) {
return true;
}
foreach ($required as $field) {
if ( !$experience->has($field)) {
return false;
}
}
return true;
}
Maybe there is a beter way, but this should work.
required_if doesn't work with null as it will treat it as a string ("null"), it will work with boolean values though.
Instead you can use the required_without rule:
$this->validate(request(), [
"experiences.*.title" => "required_without:experiences.*.institution",
"experiences.*.institution" => "required_without:experiences.*.title",
]);
Example
$experiences = [
[
"title" => "My title",
"institution" => "",
"comments" => "<p>My first description</p>", //passes
],
[
"title" => "",
"institution" => "My title",
"comments" => "<p>My second description</p>", //passes
],
[
"title" => "My title",
"institution" => "My title",
"comments" => "<p>My third description</p>", //passes
],
[
"title" => "",
"institution" => null,
"comments" => "<p>My forth description</p>", //fails
],
];
$rules = [
"experiences.*.title" => "required_without:experiences.*.institution",
"experiences.*.institution" => "required_without:experiences.*.title",
];
$validator = Validator::make(compact('experiences'), $rules);
dd($validator->fails());
Write a custom rule with php artisan make:rule and in the passes() function write a check to ensure that all of the array keys are present and also that at least 2 of the array values are not null. I'm thinking something like this:
function passes($attribute, $value){
if(array_keys($value) !== ['title','institution','comments']){
return false;
}
if(empty($value['title']) && empty($value['institution'])){
return false;
}
return true;
}
and in your $this->validate pass the rule as ['experiences.*' =>['array', new CustomRule()] instead of required_if...
I haven't checked this so feel free to edit if it's broken.

Codeigniter not updating my data

So I'm new to codeigniter and developing a viewing catalogue website. And so far my update function isn't working and I've been at this for 3 hours
so here's my view:
<?php echo validation_errors(); ?>
<?php echo form_open('catalogPages/updateEvent'); ?>
<input type="hidden" name="id" value="<?php echo $events['event_id']; ?>">
<div class="form-group">
<label>Title</label>
<input type="text" class="form-control" name="title" placeholder="Add
Title" value= "<?php echo $events['event_name']; ?>">
</div>
<div class="form-group">
<label>Body</label>
<textarea id="editor1" class="form-control" name="body" placeholder="Add
Body" value = "<?php echo $events['event_desc']; ?>"></textarea>
</div>
<button type="submit" class="btn btn-default">Submit</button>
My Model:
public function editEvent()
{
$data = array(
'event_name' => $this->input->post('event_name'),
'event_desc' => $this->input->post('event_desc'),
'event_id' => $this->input->post('event_id')
);
$this->db->where('event_id', $this->input->post('event_id'));
return $this->db->update('events', $data);
}
Controller:
public function editEvent($id = NULL)
{
$this->load->model('event_model');
$data['events'] = $this->event_model->get_event($id);
$this->load->view('adminEventUpdate', $data);
}
public function updateEvent()
{
$this->load->model('event_model');
//$id = $this->input->posts('event_id');
$this->event_model->editEvent();
}
It works but so far when I try to update the data doesn't seem to be updated but rather doesn't change at all.
When this Html form is submitted, the $_POST array has the keys:
'id', 'title', and 'body'.
There are no inputs named 'event_id', 'event_name', 'event_desc'.
The Html input properties 'name' need to change.

Advice for voting form with php and bitrix

Currently working with Bitrix CMS and need to make vote for users from specific group. With knowing id of group i made a form with bunch of radio buttons inside. When someone vote, result shoud be writen in file (or database) and page with "Thank you for voting!" should be displayed.
My question is what is the best way to do it?
I tried a few different ways:
1) this tutorial, not working
2) two another web tutorials, can link if you ask me
3) Voting form from bitrix, but for that module of the system should be change, and that not advised.
Below is page with vote itself, but "poll.php" makes me confused because of not knowing right way to do it. So good advice would be really appreciated.
p.s. you can ignore 'Bitrix' part, most important is php
/*get users from group in array*/
<?$arUsers = CGroup::GetGroupUser(20);
$iCountUsers = 0;
$arUsersbyGroupID = array();
foreach($arUsers as $arUser) {
$arUserbyID = CUser::GetByID($arUser);
$rsUserbyID = $arUserbyID -> Fetch();
$arUsersbyGroupID[] = $rsUserbyID; }
$iCountUsers++;?>
<div class="wrapper">
<div id="poll-container">
<form class="grid" action="poll.php" method="post" accept-charset="utf-8">
/*for each create element with radio button*/
<?$iVoteCount = 0;
$allopt = array();
foreach($arUsersbyGroupID as $Vote) {
$photo = $Vote["PERSONAL_PHOTO"];
if (!empty($photo)) {
$name = "{$Vote["NAME"]} {$Vote["LAST_NAME"]}";?>
<div class="cell">
<div class="cell_img"><?echo CFile::ShowImage($photo)?></div>
<div class="cell_caption">
<input type="radio" value="<?=$iVoteCount?>" name="vote" onclick="getVote(this.value)" />
<label for='opt<?=$iVoteCount?>'><?echo $name?></label>
</div>
</div>
$iVoteCount++;
}
} ?>
<div id="sub"><input type="submit" value="Vote" /></div>
</form>
</div>
</div>
You shouldn't write your fully custom solution for creating polls. In Bitrix Framework we have a module called - vote
With supply of this module you can create standard polls or if they are not fit to your tasks - you can customize them, but you should not be writing all these code from scratch.
For basic polls configuration you can read official online courses (in Russian) - http://dev.1c-bitrix.ru/learning/course/index.php?COURSE_ID=41&LESSON_ID=2859
Use bitrix api to transfer your array data using $_POST or $_REQUEST global varibles binding them to your custom form name[] . Just add bitrix api code on after submit event. Voting results may be pasted into the custom lead fields that should be previously created in BX manually. Check out this example. It was fully workable and tested.
<?php
$queryUrl ='';
$_SERVER['REMOTE_ADDR'] = $ip;
$queryData = http_build_query($basebitrix24 = array(
'fields' => array(
"TITLE" =>'one',
"NAME" => 'two',
"LAST_NAME" => $leadData['question_one'],
"STATUS_ID" => "NEW",
"OPENED" => "Y",
"UF_CRM_1491235024" => $leadData['question_second'],
"UF_CRM_1491235124" => $leadDataQ1[$answer],
"UF_CRM_1491235376" => $leadData['question_third'],
"ASSIGNED_BY_ID" => 16,
'SOURCE_ID' => $i,
"PHONE" => array(array("VALUE" => $_REQUEST['phone'], "VALUE_TYPE" => "WORK" )),
"EMAIL" => array(array("VALUE" => $_REQUEST['email'], "VALUE_TYPE" => "WORK" )),
),
'params' => array("REGISTER_SONET_EVENT" => "Y")
));
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_SSL_VERIFYPEER => 0,
CURLOPT_POST => 1,
CURLOPT_HEADER => 0,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_URL => $queryUrl,
CURLOPT_POSTFIELDS => $queryData,
));
$result = curl_exec($curl);
curl_close($curl);
$result = json_decode($result, 1);
?>
The requests are not about voting but there is no very much trouble to use same method for any custom form too. Form code in example purposes.
if (isset($_POST['s_submit'])) {
#$email = strtolower(trim($_POST['email']));
#$name = trim($_POST['name']);
if (isset($_POST) && count($_POST) != 0) {
if (#isset($email) && #$email != '') {
$line = check_record_exist($removelist, $email, $form_type);
if ($line) {
$line--;
delete_line_from_file($removelist, $line);
}
if (!check_record_exist($addlist, $email, $form_type)) {
add_record($addlist, $email, $name);
$sm_res = 1;
} else {
$sm_res = 3;
}
} else
echo '<font color="#FF0000">Please enter e-mail address</font>';
}
}
if (isset($sm_res)) {
switch ($sm_res) {
case '1': echo '<b>You have successfully subscribed</b>';
break;
case '2': echo '<b>You have successfully unsubscribed</b>';
break;
case '3': echo '<b>You are already subscribed</b>';
break;
case '4': echo '<b>You are already unsubscribed</b>';
break;
}
unset($sm_res);
} else {
<form name="contactform" id="sky-form" method="post" action="">
<fieldset>
<div class="row">
<div class="sky-form epochta">
<section class="col col-6" >
<label class="input"><i class="icon-append icon-envelope-alt"></i> <h3>EMAIL </h3><input type="email" name="email" value="<?$_REQUEST['email']?>"> </label>
</section>
<?php if ($form_type != 1) { ?>
<section class="col col-6">
<label class="input"> <i class="icon-append icon-user"></i><h3>NAME<h3> <input type="text" name="name" size="15" value="<?php echo #trim(strip_tags($_REQUEST['name'])); ?>">
</label>
</section>
<?php } ?>
<section class="col col-12" >
<input type="hidden" name="saved" value="yes">
<input type="submit" class="subbutton" name="submit" value="">
<td align="center" colspan="2"><font face="Verdana" size="-2">ePochta Subscription Manager</font>
</section>
</div>
</div>
</fieldset>
</form>
?>

Yii 2 - radioList Template

I want to add template to radioList in yii2, which I tried, but I am unable to get the proper o/p.
The HTML is
<div class="input-wrap">
<label class="gender-head">Gender</label>
<label class="signup-radio">
<input type="radio" name="signup-gender" id="signupMale" checked tabindex="3" />
<i></i>
<span>Male</span>
</label>
<label class="signup-radio">
<input type="radio" name="signup-gender" id="signupFemale" tabindex="3" />
<i></i>
<span>Female</span>
</label>
</div>
The o/p should look like this
The Yii2 code which I tried is...
<div class="input-wrap">
<div class="clearfix">
<?= $form->field($model, 'gender', ['radioTemplate' => '<label class="gender-head">{label}</label><label class="signup-radio">{input}</label>'])->inline()->radioList([1 => 'Male', 0 => 'Female'], ['separator' => '', 'tabindex' => 3]); ?>
</div>
<div class="help-block"></div>
</div>
I have searched a lot on the template but did not get any proper response.
I finally got the way through which we can modify the input tag generation logic in Yii2
To get the above result of the radio buttons, I have developed the following code
<div class="input-wrap">
<div class="clearfix" id="UserLogin-gender">
<label class="radio-head">Gender</label>
<?=
$form->field($model, 'gender')
->radioList(
[1 => 'Male', 0 => 'Female'],
[
'item' => function($index, $label, $name, $checked, $value) {
$return = '<label class="modal-radio">';
$return .= '<input type="radio" name="' . $name . '" value="' . $value . '" tabindex="3">';
$return .= '<i></i>';
$return .= '<span>' . ucwords($label) . '</span>';
$return .= '</label>';
return $return;
}
]
)
->label(false);
?>
</div>
<div class="help-block"></div>
</div>
The "item" option in the radioList is a callback function to the input generation logic written in Yii2. We can easily modify the layout of each element generated using this callback function and it's parameters.
maybe i'am too late but you can try this
<?= $form->field($model, 'abc')->inline()->radioList(['example1' => 'example1', 'example2' => 'example2'])->label(false) ?>
Source
$form->field($model, 'gender')
->radioList(array(1 => 'Male', 0 =>'Female'), array('class' => 'i-checks'));

Zend Framework 2 - Form Element Decorators

I want to force the Zend form into Twitter Bootstrap style. I currently iterate through the form fields and write the form info into my bootstrap div construction.
I saw in Zend Framework 1(!) that there is a way to do this within a decorator. But for some reason the doc for version 2 doesn't cover this point...
I'd like to do something like this:
protected $_format = '<label for="%s">%s</label>'
. '<input id="%s" name="%s" type="text" value="%s"/>';
public function render($content)
{
$element = $this->getElement();
$name = htmlentities($element->getFullyQualifiedName());
$label = htmlentities($element->getLabel());
$id = htmlentities($element->getId());
$value = htmlentities($element->getValue());
$markup = sprintf($this->_format, $name, $label, $id, $name, $value);
return $markup;
}
Any ideas?
I'm using partials now. I'm iterating over the attributes, build a few exceptions for eg CSRF and Submit... This works pretty smooth:
View
echo $this->partial('partial/form-partial', array(
'form' => $this->form,
'url' => $this->url('whatever', array('action' => 'add')))); ?>
Partial
<?php
$form = $this->form;
$form->setAttribute ( 'action', $this->url () );
$form->prepare ();
echo $this->form ()->openTag ( $form );
foreach ( $form as $element ) :
?>
<div
class="control-group <?php if($this->formElementErrors($element)) echo "error" ?>">
<label class="control-label"><?php echo $element->getLabel() ?></label>
<div class="controls">
<?php echo $this->formElement ( $element );
if ($this->formElementErrors ( $element ))
?>
<span class="help-inline"><?php echo $this->formElementErrors($element) ?></span>
</div>
</div>
<?php
endforeach;
echo $this->form ()->closeTag ( $form );
?>
The exceptions are left out for clearity's sake...
I did it the way #Rufinus mentioned. See this Tutorial on how to create View Helpers in ZF2 http://blog.evan.pro/creating-a-simple-view-helper-in-zend-framework-2
In my case I simply wanted to wrap form elements with list items so I extended the original ZF2 View Helpers and let them do the rendering of the elements. I Just wrapped what they return:
View Helper FormCollection.php
<?php
// ./src/Application/View/Helper/FormCollection.php
namespace Application\View\Helper;
use Zend\Form\ElementInterface;
use Zend\Form\View\Helper\FormCollection as BaseFormCollection;
class FormCollection extends BaseFormCollection {
public function render(ElementInterface $element) {
return '<ul>'.parent::render($element).'</ul>';
}
}
View Helper FormElement.php
<?php
// ./src/Application/View/Helper/FormElement.php
namespace Application\View\Helper;
use Zend\Form\ElementInterface;
use Zend\Form\View\Helper\FormElement as BaseFormElement;
class FormElement extends BaseFormElement {
public function render(ElementInterface $element) {
if ($element->getOption('required')) {
$req = 'required';
}
$type = $element->getAttribute('type');
$name = $element->getAttribute('name');
return sprintf('<li class="%s %s %s">%s</li>', $name, $req, $type, parent::render($element));
}
}
while my view looks like this and didn't need to be modified for the changes to take effect.
<?php
$form = $this->form;
$form->prepare();
echo $this->form()->openTag($form);
echo $this->formCollection($form);
echo $this->form()->closeTag($form);
worked like a charm.
I tried Ron's Partial method, the result would like this which not Bootstrap 3 intended.
<form id="tea" name="tea" method="POST" action="/tea/add">
...
<div class="form-group">
<label class="control-label">Brand</label>
<div class="form-control">
<input type="text" value="" name="brand">
</div>
...
We know, in order to use bootstrap 3 predefined Form style, we need to define style to input element: form-control, rather than its wrapping element.
My Partial way is as following.
echo $this->form()->openTag($form);
foreach ($form as $element) :?>
<div class="form-group">
<?php
if ($element->getOption('required')) { $req = 'required'; }
$type = $element->getAttribute('type');
$name = $element->getAttribute('name');
$label = $element->getLabel();
?>
<?php if ($name == 'id') { ?>
<div class="hidden"><?php echo $this->formElement($element); ?></div>
<?php } else if ($name == 'submit') { ?>
<input class='btn' name='submit' type='submit' value='Add'>
<?php } else if ($label != '') { ?>
<label class="control-label"><?php echo $label ?></label>
<input class='form-control' name='<?php echo $name ?>' type='<?php echo $type ?>'>
<?php } ?>
</div>
<?php
endforeach;
echo $this->form()->closeTag();
Well, we could get the result.
<form id="tea" name="tea" method="POST" action="/tea/add">
...
<div class="form-group">
<label class="control-label">Brand</label>
<input class="form-control" type="text" name="brand">
</div>
...
How to attach custom styles into zf2 forms has mentioned : to add class attribute to the Form element.
class TeaForm extends Form
{
public function __construct($name = null)
{
// we want to ignore the name passed
parent::__construct('tea');
$this->add(array(
'name' => 'id',
'type' => 'Hidden',
));
$this->add(array(
'name' => 'brand',
'type' => 'Text',
'options' => array(
'label' => 'Brand',
),
/** **define class attribute** **/
'attributes' => array(
'class' => 'form-control',
),
));
....
It looks quite simple, but, the problem is the input element would be wrapped into the label element, which still not what Bootstrap 3 intended.
<form id="tea" role="form" name="tea" method="POST" action="/tea/add">
<input type="hidden" value="" name="id">
<label>
<span>Name</span>
<input class="form-control" type="text" value="" name="name">
</label>
...
In my opinion, the Partial method is still one flexible and light choice. Tea Box is one ZF2 practice, you could find all above mentioned code and description from Gibhub
This will make the code easier.
http://php.net/manual/en/function.echo.php
<?php
$form->prepare();
echo
$this->form()->openTag($form),
$this->formCollection($form),
$this->form()->closeTag($form);

Categories