This small chapter is about using HTML select element. This element has three faces in HTML. You often encounter it as simple drop-down list and less often as select list and rarely as select list with multiple options:
Select element is used often to pick values of entity in many-to-one relation to entity of record which is being edited (e.g. person’s address, type of contact, type of relationship, …).
Here is a list of possible select
element variants:
Classic select - drop down |
|
|
---|---|---|
Select list |
|
|
Select list – multiple |
|
The concept behind this type of form input is that you select one (or more) option
s and their value
attribute is
transmitted to server’s backend named according to value of name
attribute attached to parent select
. If the
option
element lacks value
attribute, the browser simply sends its contents (visible text).
That hidden value
is very important because you can have different representation of entry for the user (e.g. textual)
and for your code (e.g. ID of some database entry).
Groups of radio
buttons and checkbox
es
work similarly, except that you have to give each of them same name
attribute and textual representation is
carried out by label
element.
See those squared brackets in third example? They tell the backend to interpret incoming data as array (you can even specify keys for that array in template’s code). Always pass values from multiple selects/checkboxes as array. You can read more about this approach in another walkthrough article.
Radio buttons are like drop-down lists, they allow only one checked value – no need for squared brackets.
Load all locations from database into select
element options and use the value attribute for id_location
.
routes.php
:
<?php
$app->get('/set-address', function(Request $request, Response $response, $args) {
try {
//retrieve all locations
$stmt = $this->db->query("SELECT id_location, city, street_name, street_number
FROM location
ORDER BY city");
$tplVars['locations'] = $stmt->fetchAll();
$this->view->render($response, 'person-address.latte', $tplVars);
} catch (PDOException $e) {
$this->logger->error($e->getMessage());
exit($e->getMessage());
}
})->setName('set-address');
person-address.latte
:
{extends layout.latte}
{block title}Set person's address{/block}
{block body}
<form method="post">
<select name="location_id">
{* use foreach to generate option list *}
{foreach $locations as $loc}
<option value="{$loc['id_location']}">
{$loc['city']} {$loc['street_name']} {$loc['street_number']}
</option>
{/foreach}
</select>
<button type="submit">Set</button>
</form>
{/block}
Now prepare your script to receive id
parameter which will represent ID of a person. Find one person in
database and print his name. Remember to pass ID of person along with new ID of his new address – you will need both
in UPDATE
query.
routes.php
:
<?php
$app->get('/set-address', function(Request $request, Response $response, $args) {
$personId = $request->getQueryParam('id');
if (empty($personId)) {
exit("Parameter 'id' is missing.");
}
try {
//retrieve the person by his id
$stmt = $this->db->prepare("SELECT * FROM person WHERE id_person = :id");
$stmt->bindValue(":id", $_GET['id']);
$stmt->execute();
$person = $stmt->fetch();
if($person) { //was person found?
$stmt = $this->db->query("SELECT id_location, city, street_name, street_number
FROM location
ORDER BY city");
$tplVars['locations'] = $stmt->fetchAll();
$tplVars['person'] = $person;
$this->view->render($response, 'person-address.latte', $tplVars);
} else {
exit('No person found');
}
} catch (PDOException $e) {
$this->logger->error($e->getMessage());
exit($e->getMessage());
}
})->setName('set-address');
person-address.latte
:
{extends layout.latte}
{block title}Set person's address{/block}
{block body}
<form method="post">
{* pass person's ID along for future UPDATE query *}
<input type="hidden" name="id" value="{$person['id_person']}"/>
{* print person name *}
<label>
Select new address for person {$person['first_name']} {$person['last_name']}:
</label>
<select name="location_id">
{* add blank option *}
<option value="">Unknown address</option>
{foreach $locations as $loc}
<option value="{$loc['id_location']}">
{$loc['city']} {$loc['street_name']} {$loc['street_number']}
</option>
{/foreach}
</select>
<button type="submit">Set</button>
</form>
{/block}
To test your page, find some person in a database and type URL like set-address?id=XXX
into browser’s address bar. Or you can prepare a link to this form from list of persons.
Almost there – preselect address of a person if he has one and tell the form where to send data:
routes.php
: (no change here):
<?php
$app->get('/set-address', function(Request $request, Response $response, $args) {
$personId = $request->getQueryParam('id');
if (empty($personId)) {
exit("Parameter 'id' is missing.");
}
try {
//retrieve the person by his id
$stmt = $this->db->prepare("SELECT * FROM person WHERE id_person = :id");
$stmt->bindValue(":id", $_GET['id']);
$stmt->execute();
$person = $stmt->fetch();
if($person) { //was person found?
$stmt = $this->db->query("SELECT id_location, city, street_name, street_number
FROM location
ORDER BY city");
$tplVars['locations'] = $stmt->fetchAll();
$tplVars['person'] = $person;
$this->view->render($response, 'person-address.latte', $tplVars);
} else {
exit('No person found');
}
} catch (PDOException $e) {
$this->logger->error($e->getMessage());
exit($e->getMessage());
}
})->setName('set-address');
person-address.latte
:
{extends layout.latte}
{block title}Set person's address{/block}
{block body}
<form method="post" action="{link store-address}">
<input type="hidden" name="id" value="{$person['id_person']}"/>
<label>
Select new address for person {$person['first_name']} {$person['last_name']}:
</label>
<select name="location_id">
<option value="">Unknown address</option>
{foreach $locations as $loc}
{* preselect person's address *}
<option value="{$loc['id_location']}"{if $person['id_location'] == $loc['id_location']} selected{/if}>
{$loc['city']} {$loc['street_name']} {$loc['street_number']}
</option>
{/foreach}
</select>
<button type="submit">Set</button>
</form>
{/block}
You can also use Latte’s n:attr macro.
<option value="..." n:attr="selected => $person['id_location'] == $loc['id_location']">
...
</option>
Now create the update script which will handle form submission – it will receive ID of person and ID of his new address (or empty string if you choose Unknown address option). Redirect back to person address selection form after update:
routes.php
:
<?php
$app->get('/set-address', function(Request $request, Response $response, $args) {
//...
})->setName('set-address');
$app->post('/set-address', function(Request $request, Response $response, $args) {
$data = $request->getParsedBody();
if (empty($data['id'])) {
exit("Parameter 'id' is missing.");
}
$data = $request->getParsedBody();
try {
$lid = !empty($data['location_id']) ? $data['location_id'] : null;
$stmt = $this->db->prepare("UPDATE person SET id_location = :lid WHERE id_person = :id");
$stmt->bindValue(":id", $data['id']);
$stmt->bindValue(":lid", $lid);
$stmt->execute();
//redirect back to address selection page
return $response->withHeader('Location', $this->router->pathFor('set-address') . '?id=' . $data['id']);
} catch (PDOException $e) {
$this->logger->error($e->getMessage());
exit($e->getMessage());
}
})->setName('store-address');
Notice that I used a different way to pass ID of person to the script in hidden form field. This means, that the
ID of a person is not visible in URL after form submit and you can find it in POST data using getParsedBody()
method. Other way is to append the ID to action
attribute of the form as in previous article about
person update.
Finally done. Take a look into your browser’s developer tools (F12) and observe what the browser sends when you select different location IDs. Also note HTTP methods and immediate redirection after update script:
Because this is quite small piece of functionality, try to incorporate this script into person update module.
Just to be complete: to group select
element option
s use optgroup
tag:
<select name="spoken_languages">
<optgroup label="Slavic">
<option value="cze">Czech</option>
<option value="svk">Slovak</option>
</optgroup>
<optgroup label="Germanic">
<option value="eng">English</option>
<option value="ger">German</option>
</optgroup>
</select>
This walkthrough chapter should help you to understand name-value concept of select
element which is not that clear
at first sight as with simple text inputs. You can try to use multiple select element and update some M:N cardinality
relationship like persons attending a meeting. This leads to passing arrays chapter.