Tải bản đầy đủ - 0 (trang)
Chapter 17. Incorporating Query Results into Web Pages

Chapter 17. Incorporating Query Results into Web Pages

Tải bản đầy đủ - 0trang

17.1 Introduction

When you store information in your database, you can easily retrieve it for use on the Web in

a variety of ways. Query results can be displayed as unstructured paragraphs or as structured

elements such as lists or tables; you can display static text or create hyperlinks. Query

metadata can be useful when formatting query results, too, such as when generating an HTML

table that displays a result set and uses its metadata to get the column headings for the table.

These tasks combine query processing with web scripting, and are primarily a matter of

properly encoding any special characters in the results (like



& or <) and adding the



appropriate HTML tags for the types of elements you want to produce.

This chapter shows how to generate several types of web output from query results. It also

covers techniques for inserting binary data into your database and for retrieving and

transferring that kind of information to clients. (It's easiest and most common to work with

text for creating web pages from database content, but you can also use MySQL to help

service requests for binary data such as images, sounds, or PDF files.)

The recipes here build on the techniques shown in Chapter 16 for generating web pages from

scripts and for encoding output for display. See that chapter if you need some background in

these topics.

The scripts from which the examples in this chapter are drawn can be found in the



recipes distribution under the directories named for the servers used to run them. For

Perl, PHP, and Python examples, look under the apache directory. For Java (JSP) examples,

look under tomcat; you should already have installed these in the process of setting up the



mcb application context (Recipe 16.4). The exception to this is that some of the utility

routines used here are found in library files in the lib directory.

Note that not all languages are represented in every section. If a particular section seems to

be "missing" an example for the language you're interested in, check the



recipes



distribution; it may contain the implementation you want, even if it's not shown here.



17.2 Displaying Query Results as Paragraph Text

17.2.1 Problem

You want to display a query result as free text.



17.2.2 Solution

Display it with no surrounding HTML structure other than paragraph tags.



17.2.3 Discussion



Paragraphs are useful for displaying free text with no particular structure. In this case all you

need to do is retrieve the text to be displayed, encode it to convert special characters to the

corresponding HTML entities, and wrap each paragraph within



and

tags. The



following examples show how to produce a paragraph for a status display that includes the

current date and time, the server version, the client username, and the current database

name (if any). These values are available from the following query:



mysql> SELECT NOW( ), VERSION( ), USER( ), DATABASE( );

+---------------------+-----------------+----------------+------------+

| NOW( )

| VERSION( )

| USER( )

| DATABASE( ) |

+---------------------+-----------------+----------------+------------+

| 2002-05-18 11:33:12 | 4.0.2-alpha-log | paul@localhost | cookbook

|

+---------------------+-----------------+----------------+------------+

In Perl, the CGI.pm module provides a



p( ) function that adds paragraph tags around the



string you pass to it.



p( ) does not HTML-encode its argument, so you should take care of

that by calling escapeHTML( ):

($now, $version, $user, $db) =

$dbh->selectrow_array ("SELECT NOW( ), VERSION( ), USER( ), DATABASE(

)");

$db = "NONE" unless defined ($db);

$para = <
Local time on the MySQL server is $now.

The server version is $version.

The current user is $user.

The current database is $db.

EOF

print p (escapeHTML ($para));

In PHP, you can print the



and

tags around the encoded paragraph text:



$query = "SELECT NOW( ), VERSION( ), USER( ), DATABASE( )";

$result_id = mysql_query ($query, $conn_id);

if ($result_id)

{

list ($now, $version, $user, $db) = mysql_fetch_row ($result_id);

mysql_free_result ($result_id);

if (!isset ($db))

$db = "NONE";

$para = "Local time on the MySQL server is $now."

. " The server version is $version."

. " The current user is $user."

. " The current database is $db.";

print ("

" . htmlspecialchars ($para) . "

\n");

}

Or, after fetching the query result, you can print the paragraph by beginning in HTML mode

and switching between modes:







Local time on the MySQL server is

.

The server version is

.

The current user is

.

The current database is

.



To display a paragraph in Python, do something like this::



cursor = conn.cursor ( )

cursor.execute ("SELECT NOW( ), VERSION( ), USER( ), DATABASE( )")

row = cursor.fetchone ( )

if row is not None:

if row[3] is None: # check database name

row[3] = "NONE"

para = ("Local time on the MySQL server is %s." +

" The server version is %s." +

" The current user is %s." +

" The current database is %s.") % (row)

print "

" + cgi.escape (para, 1) + "

"

cursor.close ( )

In JSP, the display might be produced as follows:





SELECT NOW( ), VERSION( ), USER( ), DATABASE( )















Local time on the server is .

The server version is .

The current user is .

The current database is .



The JSP script uses



rowsByIndex so that the result set row's columns can be accessed



by numeric index.



17.3 Displaying Query Results as Lists

17.3.1 Problem

A query result contains a set of items that should be displayed as a structured list.



17.3.2 Solution



Write the list items within the proper HTML tags for the desired type of list.



17.3.3 Discussion

More structured than paragraphs and less structured than tables, lists provide a useful way to

display a set of individual items. HTML provides several styles of lists, such as ordered lists,

unordered lists, and definition lists. You may also wish to nest lists, which requires list-withinlist formatting.

Lists generally consist of an opening and closing tag that surround a set of items, each of

which is delimited by its own tags. List items correspond naturally to rows returned from a

query, so generating an HTML list structure from within a program is a matter of encoding

your query result, surrounding each row with the proper item tags, and adding the opening

and closing list tags. Two approaches to list generation are common. If you want to print the

tags as you process the result set, do this:

1. Print the list opening tag.

2. Fetch and print each result set row as a list item, including the item tags.

3. Print the list closing tag.

Alternatively, you can process the list in memory:

1. Store the list items in an array.

2. Pass the array to a list generation function that adds the appropriate tags, then print

the result.

The examples that follow demonstrate both approaches.



17.3.4 Ordered Lists

An ordered list consists of items that have a particular sequence. Browsers typically display

ordered lists as a set of numbered items:



1. First item

2. Second item

3. Third item

You need not specify the item numbers, because the browser will add them automatically. The

HTML for an ordered list begins and ends with



    and
opening and closing tags,

and contains items surrounded by
  • and
  • tags:



    1. First item


    2. Second item


    3. Third item






    Suppose you have an



    ingredient table that contains numbered ingredients for a cooking



    recipe:



    +----+---------------------------------+

    | id | item

    |

    +----+---------------------------------+

    | 1 | 3 cups flour

    |

    | 2 | 1/2 cup raw ("unrefined") sugar |

    | 3 | 3 eggs

    |

    | 4 | pinch (< 1/16 teaspoon) salt

    |

    +----+---------------------------------+

    The table contains an



    id column, but you need only fetch the text values in the proper order



    to display them as an ordered list, because a browser will add item numbers itself. The items

    contain the special characters



    " and <, so you should HTML-encode them before adding the



    tags that convert the items to an HTML list. The result will look like this:





    1. 3 cups flour


    2. 1/2 cup raw ("unrefined") sugar


    3. 3 eggs


    4. pinch (< 1/16 teaspoon) salt




    One way to create such list from a script is by printing the HTML as you fetch the rows of the

    result set. Here's how you might do so in a JSP page using the JSTL tags:





    SELECT item FROM ingredient ORDER BY id













    In PHP, the same operation can be performed like this:



    $query = "SELECT item FROM ingredient ORDER BY id";

    $result_id = mysql_query ($query, $conn_id);

    if (!$result_id)

    die (htmlspecialchars (mysql_error ($conn_id)));

    print ("
      \n");

      while (list ($item) = mysql_fetch_row ($result_id))

      print ("
    1. " . htmlspecialchars ($item) . "
    2. \n");

      mysql_free_result ($result_id);

      print ("
    \n");

    It's not necessary to add the newlines after the closing tags as this example does; web

    browsers don't care if they're present or not. I like to add them because the HTML produced

    by a script is easier to examine directly if it's not all on a single line, which simplifies

    debugging.



    The preceding examples use an approach to HTML generation that interleaves record fetching

    and output generation. It's also possible to separate or decouple the two operations: retrieve

    the data first, then write the output. Queries tend to vary from list to list, but generating the

    list itself often is fairly stereotypical. If you put the list-generation code into a utility function,

    you can reuse it for different queries. The two issues the function must handle are HTMLencoding of the items (if they aren't already encoded) and adding the proper HTML tags. For

    example, in PHP, a function



    make_ordered_list( ) can be written as follows. It



    takes the list items as an array argument and returns the list as a string:



    function make_ordered_list ($items, $encode = TRUE)

    {

    if (!is_array ($items))

    return ("make_ordered_list: items argument must be an array");

    $str = "
      \n";

      reset ($items);

      while (list ($k, $v) = each ($items))

      {

      if ($encode)

      $v = htmlspecialchars ($v);

      $str .= "
    1. $v
    2. \n";

      }

      $str .= "
    \n";

    return ($str);

    }

    After writing the utility function, you can fetch the data and print HTML like so:



    # fetch items for list

    $query = "SELECT item FROM ingredient ORDER BY id";

    $result_id = mysql_query ($query, $conn_id);

    if (!$result_id)

    die (htmlspecialchars (mysql_error ($conn_id)));

    $items = array ( );

    while (list ($item) = mysql_fetch_row ($result_id))

    $items[ ] = $item;

    mysql_free_result ($result_id);

    # generate HTML list

    print (make_ordered_list ($items));

    In Python, the utility function can be defined like this:



    def make_ordered_list (items, encode = 1):

    if type (items) not in (types.ListType, types.TupleType):

    return ("make_ordered_list: items argument must be a list")

    list = "
      \n"

      for item in items:

      if item is None:

      # handle possibility of NULL item

      item = ""

      # make sure item is a string, then encode if necessary

      if type (item) is not types.StringType:

      item = `item`

      if encode:

      item = cgi.escape (item, 1)

      list = list + "
    1. " + item + "
    2. \n"



      list = list + "
    \n"

    return list

    And used as follows:



    # fetch items for list

    query = "SELECT item FROM ingredient ORDER BY id"

    cursor = conn.cursor ( )

    cursor.execute (query)

    items = [ ]

    for (item,) in cursor.fetchall ( ):

    items.append (item)

    cursor.close ( )

    # generate HTML list

    print make_ordered_list (items)

    The PHP and Python versions of



    make_ordered_list( ) check their first argument



    to make sure it's an array. If it's not, they return an error string indicating the problem.

    Returning a descriptive string makes problems immediately obvious in the web page when you

    look at the output produced by the function. You could return some other kind of error

    indicator if you like, or perhaps raise an exception or terminate the script.

    The second argument to



    make_ordered_list( ) indicates whether it should



    perform HTML-encoding of the list items. The easiest thing is to let the function handle this for

    you (which is why the default is true). However, if you're creating a list from items that

    themselves include HTML tags, you wouldn't want the function to encode the special

    characters in those tags. For example, if you're creating a list of hyperlinks, each list item will

    contain



    tags. To prevent these from being converted to <a>, pass

    make_ordered_list( ) a second argument of FALSE (for PHP) or 0 (for Python).

    If your API provides functions to generate HTML structures, you need not write them yourself,

    of course. That's the case for Perl CGI.pm module: generate each item by invoking its



    li(



    ) function to add the opening and closing item tags, save up the items in an array, and pass

    the array to ol( ) to add the opening and closing list tags:

    my $query = "SELECT item FROM ingredient ORDER BY id";

    my $sth = $dbh->prepare ($query);

    $sth->execute ( );

    my @items = ( );

    while (my $ref = $sth->fetchrow_arrayref ( ))

    {

    # handle possibility of NULL (undef) item

    my $item = (defined ($ref->[0]) ? escapeHTML ($ref->[0]) : "");

    push (@items, li ($item));

    }

    print ol (@items);



    The reason for converting



    undef (NULL) values to the empty string is to avoid having Perl

    generate uninitialized-value warnings when run with the -w option. (The ingredient

    table doesn't actually contain any NULL values, but the technique is useful for dealing with

    tables that might.)

    The previous example intertwines record fetching and HTML generation. To use a more

    decoupled approach that separates fetching the items from printing the HTML, first retrieve

    the items into an array. Then pass the array by reference to



    li( ) and the result to ol(



    ):

    # fetch items for list

    my $query = "SELECT item FROM ingredient ORDER BY id";

    my $item_ref = $dbh->selectcol_arrayref ($query);

    # generate HTML list, handling possibility of NULL (undef) items

    $item_ref = [ map { defined ($_) ? escapeHTML ($_) : "" } @{$item_ref} ];

    print ol (li ($item_ref));

    Note two things about the









    li( ) function:



    It doesn't perform any HTML-encoding; you must do that yourself.

    It can handle a single value or an array of values. However, if you pass an array, you

    should pass it by reference. When you do that,



    li( ) adds
  • and




  • tags to each array element, then concatenates them and returns the resulting string.

    If you pass the array itself rather than a reference,



    li( ) concatenates the items



    first, then adds a single set of tags around the result, which is usually not what you

    want. This behavior is shared by several other CGI.pm functions that can operate on

    single or multiple values. For example, the table data

    set of



    td( ) function adds a single



    and tags if you pass it a scalar or list. If you pass a list



    reference, it add the tags to each item in the list.



    Should You Intertwine or Decouple Record

    Fetchingand HTML Generation?

    If you want to write a script in a hurry, you can probably get it running most quickly

    by writing code that prints HTML from query rows as you fetch them. There are,

    however, certain advantages to separating data retrieval from output production.

    The most obvious ones are that by using a utility function to generate the HTML, you

    have to write the function only once, and you can share it among scripts. (And if

    your API provides the function, you don't have to write it even once; so much the

    better.) But there are other benefits as well:







    Functions that generate HTML structures can be used with data obtained

    from other sources, not just from a database.







    The decoupled approach takes advantage of the fact that you need not

    generate output directly. You can construct a page element in memory, then

    print it when you're ready. This is particularly useful for building pages that

    consist of several components, because it gives you more latitude to create

    the components in the order that's most convenient.







    Decoupling record fetching and output generation gives you more flexibility

    in the types of output you produce. If you decide to generate an unordered

    list rather than an ordered list, just call a different output function; the data

    collection phase need not change. This is true even if you decide to use a

    different output format (XML or WML rather than HTML, for example). In this

    case, you still need only a different output function; data collection remains

    unchanged.







    By prefetching the list items, you can make adaptive decisions about what

    type of list to create. Although we are not yet to the point of discussing web

    forms, they make heavy use of their own kinds of lists. In that context,

    having items in hand before generating an HTML structure from them can be

    useful if you want to choose the list type based on the size of the list. For

    example, you can display a set of radio buttons if the number of items is

    small, or a pop-up menu or scrolling list if the number is large.



    17.3.5 Unordered Lists

    An unordered list is like an ordered list except that browsers display all the items with the

    same marker character, such as a bullet:



    · First item

    · Second item

    · Third item

    "Unordered" refers to the fact that the marker character provides no sequence information.

    You can of course display the items in any order you choose. The HTML for an unordered list is



    the same as for an ordered list except that the opening and closing tags are



      and



    rather than
      and
    :



    • First item


    • Second item


    • Third item




    For APIs where you print the tags directly, use the same procedure as for ordered lists, but

    print



      and
    instead of
      and
    . Here is an example in JSP:





    SELECT item FROM ingredient ORDER BY id













    In Perl, create an unordered list by using the CGI.pm



    ul( ) function rather than ol( ):



    # fetch items for list

    my $query = "SELECT item FROM ingredient ORDER BY id";

    my $item_ref = $dbh->selectcol_arrayref ($query);

    # generate HTML list, handling possibility of NULL (undef) items

    $item_ref = [ map { defined ($_) ? escapeHTML ($_) : "" } @{$item_ref} ];

    print ul (li ($item_ref));

    If you're writing your own utility function for unordered lists, it's easily derived from a function

    that generates ordered lists. For example, it's simple to adapt the PHP and Python versions of



    make_ordered_list( ) to create make_unordered_list( ) functions,

    because they differ only in the opening and closing list tags used.



    17.3.6 Definition Lists

    A definition list consists of two-part items, each including a term and a definition. "Term" and

    "definition" have loose meanings, because you can display any kind of information you want.

    For example, the following



    doremi table associates the name of each note in a musical



    scale with a mnemonic phrase for remembering it, but the mnemonics aren't exactly what

    you'd call definitions:



    +----+------+----------------------------+

    | id | note | mnemonic

    |

    +----+------+----------------------------+

    | 1 | do

    | A deer, a female deer

    |

    | 2 | re

    | A drop of golden sun

    |



    | 3 | mi

    | A name I call myself

    |

    | 4 | fa

    | A long, long way to run

    |

    | 5 | so

    | A needle pulling thread

    |

    | 6 | la

    | A note to follow so

    |

    | 7 | ti

    | I drink with jam and bread |

    +----+------+----------------------------+

    Nevertheless, the



    note and mnemonic columns can be displayed as a definition list:



    do

    A deer, a female deer

    re

    A drop of golden sun

    mi

    A name I call myself

    fa

    A long, long way to run

    so

    A needle pulling thread

    la

    A note to follow so

    ti

    I drink with jam and bread

    The HTML for a definition list begins and ends with



    and
    tags. Each item has a

    term delimited by
    and
    , and a definition delimited by
    and
    :



    do


    re


    mi


    fa


    so


    la


    ti






    A

    A

    A

    A

    A

    A

    I



    deer, a female deer


    drop of golden sun

    name I call myself

    long, long way to run

    needle pulling thread

    note to follow so

    drink with jam and bread



    In a JSP page, you can generate the definition list like this:





    SELECT note, mnemonic FROM doremi ORDER BY note















    In PHP, create the list like this:



    $query = "SELECT item FROM ingredient ORDER BY id";

    $result_id = mysql_query ($query, $conn_id);

    if (!$result_id)



    Tài liệu bạn tìm kiếm đã sẵn sàng tải về

    Chapter 17. Incorporating Query Results into Web Pages

    Tải bản đầy đủ ngay(0 tr)

    ×