内容


使用 XForms 创建动态 Web 搜索

探索 XForms 的灵活性

Comments

简介

本文演示了如何创建一个足够灵活的接口,使用这个接口能够查看若干个基于 XML 的 API 产生的结果。方法是实现一个搜索引擎客户机供用户在其中选择搜索数据的引擎并自动接收合适的条目字段和数据。此外,表单加载了“后台中”的数据,也就是说,在页面上显示结果不需要完全重新加载。

本例中使用的 API 包括 Yahoo! 和 Teoma 搜索引擎,但文章传达的思想适用于任何能返回 XML 的 API。这两个搜索引擎都有一个基于 REST 的接口,也就是说提供给它们一个 URL 和所有参数,它们将返回带有结果的 XML。

XForms 实现对内容的动态更改无需通过将 XForms 可视化表示(visual presentation)链接到 XML 数据结构来重新提交来自服务器的页面。当 XML 数据结构发生变化时,可视化表示将被重新计算并作出相应变化而无需重新加载页面。更改隐藏在表示下面的 XML 数据可以通过很多种方法实现。比如说,本例中的表单包含了一个下拉菜单,它列出了可用的搜索引擎。用户只能填写与活动引擎关联的字段。基本引擎表单如图 1 所示。

图 1. 尚未进行任何搜索的搜索表单
尚未进行任何搜索的搜索表单
尚未进行任何搜索的搜索表单

先决条件

在开始创建表单之前,需要准备好如下软件:

  • 能显示 XForms 的浏览器,例如带有 XForms 扩展的 Firefox(参阅 参考资料 中的下载链接)。
  • 支持 PHP 的 Web 服务器。特定于本例,PHP 必须带有 cURL 扩展才能访问搜索引擎(参阅 参考资料 中的下载链接)。
  • 一个 Yahoo 搜索的应用程序 ID。Teoma 不需要 ID。可从 Yahoo Web 站点获得此 ID(参阅 参考资料)。

创建 XForms 数据结构

为支持本例的搜索引擎,表单使用了三个实例来实现数据结构。每个实例都有惟一的名称,以便稍后检索。其中两个元素是空容器,用作搜索引擎返回的 XML 的加载点。另一个实例包含了搜索的输入数据:Yahoo 搜索的应用程序 ID、查询字符串(定义为 q)、两个 Yahoo 特有的字段(siteresults)。这些数据位于 box 实例中,如清单 1 所示。

清单 1. XML 数据结构,第 1 部分
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html
   xmlns="http://www.w3.org/1999/xhtml"
   xmlns:y="urn:yahoo:srch"
   xmlns:xforms="http://www.w3.org/2002/xforms" 
   xmlns:ev="http://www.w3.org/2001/xml-events"
>

  <head>
    <title>Using XForms To Build A Dynamic Search Engine</title>
    <xforms:model id="model_dynsrch">

      <xforms:instance id="box">
        <search xmlns="">
          <engine>Yahoo</engine>
          <appid>your_yahoo_app_id</appid>
          <q>horse</q>
          <site></site>
          <results></results>
          <submitTeoma/>
          <submitYahoo/>
        </search>
      </xforms:instance>

      <xforms:bind nodeset="instance('box')/site" 
               relevant="instance('box')/engine = 'Yahoo'" />
      <xforms:bind nodeset="instance('box')/results" 
               relevant="instance('box')/engine = 'Yahoo'" />
      <xforms:bind nodeset="instance('box')/submitYahoo" 
               relevant="instance('box')/engine = 'Yahoo'" />
      <xforms:bind nodeset="instance('box')/submitTeoma" 
               relevant="instance('box')/engine = 'Teoma'" />
      
      <xforms:instance id="yahoo">
        <ResultSet xmlns="urn:yahoo:srch"/>
      </xforms:instance>

      <xforms:instance id="teoma">
        <SEARCHRESULTS xmlns=""/>
      </xforms:instance>

很显然,这个 XHTML 的独到之处应该就是在 HTML 标记中使用了 xmlns:y="urn:yahoo:srch"。这与 Yahoo 搜索的数据实例有关。Yahoo 使用 xmlns="urn:yahoo:srch" 返回搜索数据。因为 Yahoo 只识别这个特定的名称空间,所以我们在引用其数据时也必须使用该名称空间;否则,将显示为“不可见(invisible)”。后面我们将看到所有引用 Yahoo 搜索结果的 XForms 代码都使用了在头部指定的 y:。(记住,前缀是什么无关紧要;真正起作用的是名称空间。)此处的实例定义中,为搜索结果的 XML 数据节点了调用了名称空间。

box 实例同时包含了基本信息(2)和站点特定的信息(appidsiteresults)。我们将根据引擎元素的值控制站点特定信息的外观。比方说,绑定元素指定了只有当引擎为“Yahoo”时,siteresults 元素才会“关联(relevant)”在一起。稍后,当我们创建真正的表单时,会发现尽管这些项都在页面上,但是只有关联项才会在页面上显示出来。使用 submitYahoosubmitTeoma 元素可以类似地控制 Submit 按钮的可见性。

还有一个相当细微的地方需要注意,稍不注意便有可能影响到其后的设计。如 清单 1所示,三个实例的名称分别为 boxyahooteoma。请记住,这些名称都是特定于这些实例数据结构的。并不能把它们当做标识符随意使用。在本例后面的 switch/case/toggle 机制中重用 teomayahoo 这两个 ID 会是个不小的诱惑,但是由于它们的名称都引用了实例数据所以并不能正常运行。

submission 的定义

清单 2 显示了 XForm 其余的数据结构。剩下的两项为 submission 元素。本例使用了两个 submission 元素,分别对应于不同的搜索。对于这些 submission 元素,有三点需要注意。这三点各自发挥自己的作用,确保“后台搜索”顺利进行而不用重新加载页面。每个 submission 元素都指定了一个 replace 和 instance 关键字。请注意:每个 submission 元素都是使用 清单 1 中选定的名称来引用实例的。这个机制指定:从该 submission 元素返回的数据将替代指定的实例节点中的 XML 数据。这样便可以清除搜索或者用新搜索结果替代原搜索。请注意,两个 submission 元素都使用了 ref="instance('box')"。这将指示 submission 动作使用 box 实例的 XML 作为输入,并且由于此引擎的实质在于使用户能搜索不同的引擎而无需分别键入相同的搜索字符串,因此两个 submission 元素都引用了相同的输入数据。Yahoo 特定信息同样也传递给了 Teoma 搜索引擎,但是会被 Teoma PHP 代理忽略。

清单 2. XML 数据结构,第 2 部分
      <xforms:submission id="submit_teoma" separator="&"

                         action="teomaproxy.php" method="get"
                         ref="instance('box')"
                         replace="instance" instance="teoma"/>
      
      <xforms:submission id="submit_yahoo" separator="&"
                         action="yahooproxy.php" method="get"
                         ref="instance('box')"
                         replace="instance" instance="yahoo"/>
    </xforms:model>
  </head>

两个 submission 定义中都使用了 PHP 脚本:<engine>proxy.php。这两个脚本在本质上是相同的,只是基本 URL 和包含的数据有所不同。这些 PHP 脚本使用 cURL 扩展名分别在两个引擎中检索 XML 数据,并将数据返回给 XForms。这样,数据处理实际上就是在 XForms 实现中进行的。清单 3 显示了 Yahoo PHP 脚本。前面已经说过,Teoma 脚本与它几乎是相同的。

清单 3. Yahoo 代理脚本
<?php

$appid = urlencode($_GET['appid']);
if($_GET['site'] != '')
    $q = urlencode($_GET['q']." site:".$_GET['site']);
else
    $q = urlencode($_GET['q']);
$results = urlencode($_GET['results']);

$url = 
"http://api.search.yahoo.com/WebSearchService/V1/webSearch?appid=$appid&query=$q&results=
$results";

$curl = curl_init();

curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADER, false);

$str = curl_exec($curl);
curl_close($curl);

echo $str;

?>

由于使用 GET 作为 submission 的方法,PHP 脚本就像从传统 Web 表单中那样接收此信息。一旦输入了用于搜索的适当 URL,cURL 将提交该信息并返回结果。

同样也可以使用 POST 方法,但必须确保正确地处理 URL 编码数据。当提交的对象是搜索引擎 API 时,也可以直接向表单提交 XML。例如,当 MSN Live Search API 处理 SOAP 事务时。清单 4 显示了如何处理用 POST 提交的信息。

清单 4. 使用 POST 的 Yahoo 代理 PHP 脚本
<?php

$xml = $HTTP_RAW_POST_DATA;
$doc = new DomDocument('1.0');
$doc->loadXML($xml);
$appid = $doc->getElementsByTagName("appid")->item(0)->nodeValue;
$query = $doc->getElementsByTagName("q")->item(0)->nodeValue;
...

接下来我们回到表单本身

XForms 表示

XForms 表示包括两个基本的部分:输入部分和输出部分。这两个分开的部分相互之间进行交互,为完全不同的底层数据结构提供了一个无缝的接口。这些底层数据结构由各自的搜索引擎定义,并不受 XForms 创建者控制。清单 5 显示输入部分中的第一部分。

XForms 输入表示

在 XForms 表示的输入部分中,需要注意用于把搜索文本和 box 实例中的 q 节点链接起来的方法和 submit 定义中 toggle 元素。这些 toggle 代码行用于控制输出部分中两种情况之间的转换。前面我们提到过应注意定义的名称,这里显得格外重要。本例中,Teoma 搜索的情形被命名为 teo 而不是 teoma,这样便不会与实例名相冲突。Yahoo 提交也使用了类似的命名方法。

清单 5. XForms 表示代码,第 1 部分
  <body>
    <p>
      <xforms:select1 ref="instance('box')/engine">
         <xforms:label>Choose a search engine:  </xforms:label>
         <xforms:item>
            
<xforms:label>Yahoo</xforms:label><xforms:value>Yahoo</xforms:value>
         </xforms:item>
         <xforms:item>
            
<xforms:label>Teoma</xforms:label><xforms:value>Teoma</xforms:value>
         </xforms:item>
      </xforms:select1>

   </p>
    <p>
      <xforms:input ref="instance('box')//q" >
        <xforms:label>Search Text: </xforms:label>
      </xforms:input>

      <xforms:input ref="instance('box')/site" >
        <xforms:label>Site: </xforms:label>
      </xforms:input>

      <xforms:select1 ref="instance('box')/results" >
        <xforms:label>Results displayed: </xforms:label>
        <xforms:item>
            <xforms:label>Ten</xforms:label>
            <xforms:value>10</xforms:value>
        </xforms:item>
        <xforms:item>
            <xforms:label>Twenty</xforms:label>
            <xforms:value>20</xforms:value>
        </xforms:item>
        <xforms:item>
            <xforms:label>Thirty</xforms:label>
            <xforms:value>30</xforms:value>
        </xforms:item>
      </xforms:select1>

    </p>
    <p>
      <xforms:submit submission="submit_teoma" ref="instance('box')/submitTeoma">
        <xforms:label>Search using Teoma</xforms:label>
        <xforms:toggle ev:event="DOMActivate" case="teo"/>
      </xforms:submit>

      <xforms:submit submission="submit_yahoo" ref="instance('box')/submitYahoo">
        <xforms:label>Search using Yahoo!</xforms:label>
        <xforms:toggle ev:event="DOMActivate" case="yah"/>
      </xforms:submit>
    </p>

请注意,表单列出了所有的信息,包括站点特定的字段。还要注意页面同时列出了两个按钮。然而,数据的真实外观是由顶部的选择列表控制的 engine 字段值决定的。当值为 Yahoo 时,显示的是所有的 Yahoo 信息,如 图 1 所示。当值为 Teoma 时,显示的是所有的 Teoma 信息,如图 2 所示。

图 2. 使用 Teoma 搜索
使用 Teoma 搜索
使用 Teoma 搜索

XForms 输出表示

XForms 的几个很有用的特性,例如“repeat”和“switch/case”特性,使两种格式不同的搜索结果无缝统一地表示了出来。清单 6 显示了 XForms 表示的输出部分。

清单 6. XForms 表示代码,第 2 部分
      <xforms:label><h3>Search Results:</h3></xforms:label>
    <xforms:switch>
      <xforms:case id="teo" selected="true">
        <xforms:repeat nodeset="instance('teoma')//RESULT">
          <p>
            <xforms:label><b>Title:</b> <xforms:output 
ref="TITLE"/></xforms:label><br/>
            <xforms:label><b>URL:</b> <xforms:output 
ref="URL"/></xforms:label><br/>
            <xforms:label><b>Description:</b> <xforms:output 
ref="ABSTRACT"/></xforms:label>
          </p>
        </xforms:repeat>
      </xforms:case>

      <xforms:case id="yah">
        <xforms:repeat nodeset="instance('yahoo')//y:Result">
          <p>
            <xforms:label><b>Title:</b> <xforms:output 
ref="y:Title"/></xforms:label><br/>
            <xforms:label><b>URL:</b> <xforms:output 
ref="y:Url"/></xforms:label><br/>
            <xforms:label><b>Description:</b> <xforms:output 
ref="y:Summary"/></xforms:label>
          </p>
        </xforms:repeat>
      </xforms:case>
    </xforms:switch>
    
  </body>
</html

首先应注意 switch 语句,它与附带的 case 语句结合使用。前面的部分讨论了两个 submission 元素并重点研究了每个 submit 定义中的两条语句,该语句引用了与 case 语句引用的相同的标志,即 teoyah。toggle 动作使在任意给定时间都有其中的一个 case 有效。作用是只显示附带的 XForms 定义中的一种。

case 语句中,Yahoo 与 Teoma 输出显示的基本格式非常相似。搜索引擎运行时,将生成很多 results 节点。对于大部分搜索引擎 API 而言,返回的搜索项数目是可配置的。因此,只要对此接口进行改进使其支持该特性,便可以改变 results 节点的数目。XForms 对从 XML 数据结构中找到的所有节点都将运行 "repeat"。这样便创建了一列结果,而无需 XForms 创建者预先知道它的数目,也无需使用 JavaScript 修改页面源数据结构来为每个节点提供显示元素。

图 3 显示了使用 Teoma 产生的搜索结果。Teoma 的情况比较简单一点。Web 站点记录了 Teoma 返回的 XML 结构(参阅 参考资料 中的链接)。因为 Teoma 并没有为数据指定名称空间,所以只能使用 XML 中给定的名称来引用节点。

图 3. Teoma 搜索结果
Teoma 搜索结果
Teoma 搜索结果

图 4 显示了使用 Yahoo 得到了搜索结果。考虑到 Yahoo 能够将结果限定在一个站点之内并且能决定显示多少结果,Yahoo 的 XML 字段名称与 Teoma 有所不同。不过它们之间还有另一个重要的区别:Yahoo 在返回的 XML 中指定了名称空间。这需要 XForms 引用使用该名称空间。XHTML 在其头部把名称空间定义为 y清单 4 用粗体显示了所有 XForms ref 说明符上的这些 y: 说明符。

图 4. Yahoo 搜索结果
Yahoo 搜索结果
Yahoo 搜索结果

结束语

XForms 在显示方式这方面具有极大的灵活性,能动态地适应不同的情况,以提供无缝的用户体验。本文使用其中的一些功能创建了一个搜索客户机,它能根据用户选择的搜索引擎显示不同的界面。可以在自己的基于 XForms 的应用程序中使用这些思想,创建能适应用户选择和可用数据的接口。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML, Web development
ArticleID=228410
ArticleTitle=使用 XForms 创建动态 Web 搜索
publish-date=06042007