Detail View / Detail Lists
So far, we've implemented the interface for creating and modifying record in
a single table. You can easily add new fields to the landlords table such as
address, date of birth and so on. This doesn't change the structure of the
application and therefore doesn't add to programming complexity.
Next we'll add a "Phone Numbers" section in the landlord view, so that a list
of phone numbers can be maintained.
The first step is to design the database table. Since a landlord can have zero,
one or many associated phone numbers, we'll be using a one-to-many
relationship. This is accomplished by having the primary key of the main
table as the foreign key in the sub table.
For example, the primary key of the landlords table is llid. We add the llid
field to the landlordphonenumbers field:
lpid primary key, auto increment, unsigned integer
llid unsigned integer, indexed
phone varchar(255)
Note the table name follows the convension of main record's name plus sub
record's name in plural form.
Now populate this table with some test data, so that at least three numbers are
associated with a landlord that's already in the database.
Add a Phone Numbers section in the landlord view:
//showlandlord.php
function showlandlord($llid=null){
// ...
?>
<div class="sectionheader">Phone Numbers</div>
<table>
<?
$query="select * from landlordphonenumbers
where llid=$llid order by lpid";
$rs=sql_query($query,$db);
while ($myrow=sql_fetch_array($rs)){
$phone=$myrow['phone'];
?>
<tr><td><?echo $phone;?></td>
<td><a>[x]</a></td>
</tr>
<?
} // while
?>
<tr><td>
<input id="landlordphone_new_<?echo $llid;?>">
</td><td>
<button onclick="addlandlordphone(<?echo $llid;?>);">
Add Phone
</button>
</td></tr>
</table>
<?
// ...
}
Reload the page and you should see the 3 phone numbers that are added to
the database are displayed in the landlord view. This should provide you a
basis to visualize the rest of the functionality.
What do you think should happen when the user enters a number in the input
field and click on the "Add Phone" button? Well, a new row of the added
number should show up in addition to the existing 3 numbers. And when the
[x] button is clicked on, the phone number should be removed from the list.
Now without writing any code, think about how the above two interactions,
namely insertion and deletion, could be implemented.
The button should invoke a JavaScript function that gathers the new number.
The phone number is then handled by a server-side function that adds the
number to the database. Upon completion, the new list of numbers should be
displayed in the landlord view.
Read the last sentence again. Also read
showlandlord.inc.php again.
You must realize that the current implementation of
showlandlord has two
issues. First of all, there is nowhere to inject the server response. Second, the
phone number insertion function has no display function to piggy back to.
The first issue can be resolved by adding a container:
<div class="sectionheader">Phone Numbers</div>
<div id="landlordphonenumbers_<?echo $llid;?>">
...
</div>
Take a look at what we did in
updatelandlord server handler. We called an
independent
showlandlord function. Similar we could factor out the code for
displaying phone numbers and store it in a separate file.
Create a file
listlandlordphonenumbers.inc.php in the
icl folder.
<?php
function listlandlordphonenumbers($llid){
global $db;
$query="select * from landlordphonenumbers
where llid=$llid order by lpid";
$rs=sql_query($query,$db);
while ($myrow=sql_fetch_array($rs)){
$phone=$myrow['phone'];
?>
<tr><td><?echo $phone;?></td>
<td><a>[x]</a></td>
</tr>
<?
} // while
?>
<tr><td>
<input id="landlordphone_new_<?echo $llid;?>">
</td><td>
<button onclick="addlandlordphone(<?echo $llid;?>);">
Add Phone
</button>
</td></tr>
</table>
<?
}
The list container still stays in showlandlord.inc.php:
<?php
include 'icl/listlandlordphonenumbers.inc.php';
function showlandlord($llid=null){
// ...
?>
<div class="sectionheader">Phone Numbers</div>
<div id="landlordphonenumbers_<?echo $llid;?>">
<? listlandlordphonenumbers($llid); ?>
</div>
<?
// ...
}
The steps to follow are straightforward. We add a client-side handler in
landlords.js:
addlandlordphone=function(llid){
var phone=gid('landlordphone_new_'+llid).value;
phone=encodeHTML(phone);
ajxpgn('landlordphonenumbers_'+llid,
document.codepage.appsettings+
'?cmd=addlandlordphone&llid='+llid+
'&phone='+phone);
}
In myservices.php, add a clause to the switch:
case 'addlandlordphone':
include 'icl/addlandlordphone.inc.php';
addlandlordphone();
break;
Implement the insertion function in
addlandlordphone.inc.php:
<?php
include 'icl/listlandlordphonenumbers.inc.php';
function addlandlordphone(){
$llid=GETVAL('llid');
$phone=GETVAR('phone');
global $db;
$query="insert into landlordphonenumbers (llid,phone)
values($llid, '$phone')";
sql_query($query,$db);
listlandlordphonenumbers($llid);
}
At this point you should see a pattern. The content is first displayed in a tab,
or any kind of DIV container. The content contains components such as input
fields that collect data from the user. A trigger such as a button or link
invokes a JavaScript function that assembles the user input and sends it to a
server-side switch. The switch clause further includes and calls the serverside handler. If the handler modifies the data, it also includes the displaying
function and piggyback the updated result that's injected back to the original
container. This flow is the bread and butter of building web applications; it is
used extensively in Gyroscope.
Let's review this process again by adding deletion of phone numbers from
the list.
First, we add the "trigger" for deleting a record.
In
listlandlordphonenumbers, add an event to the [x] button:
while ($myrow=sql_fetch_array($rs)){
$lpid=$myrow['lpid'];
$phone=$myrow['phone'];
?>
<tr><td><?echo $phone;?></td><td>
<a onclick="dellandlordphone(<?echo $lpid;?>,<?echo $llid;?>);">
[x]
</a>
<?
</td></tr>
}//while
Note that we're passing both IDs to the JavaScript handler. The first ID, lpid,
uniquely identifies a landlord-phone pair in the database. It is all we need to
delete a record. The landlord ID is needed both for calling the display
function as well as targeting the container.
In
landlords.js, we add the deletion handler:
dellandlordphone=function(lpid,llid){
if (!confirm('Are you sure?')) return;
ajxpgn('landlordphonenumbers_'+llid, document.appsettings.codepage+
'?cmd=dellandlordphone&lpid='+lpid+'&llid='+llid);
}
Add a clause to the switch in
myservices.php:
case 'dellandlordphone':
include 'icl/dellandlordphone.inc.php';
dellandlordphone();
break;
Create the server-side handler
dellandlordphone.inc.php:
<?php
include 'icl/listlandlordphonenumbers.inc.php';
function dellandlordphone(){
$lpid=GETVAL('lpid');
$llid=GETVAL('llid');
global $db;
$query="delete from landlordphonenumbers where
lpid=$lpid and llid=$llid";
sql_query($query,$db);
listlandlordphonenumbers($llid);
}
Again, lpid is enough to uniquely locate and delete the record. The reason
llid is added to the where-clause is because it could catch logic programming
errors. It's a good sanity check to have.