With PHP DataServices it is just a matter of putting a little configuration php file to make your database available as a web service. I only needed few minutes to make a simple web service from my blog after figuring out my wordpress database structure, http://wpbits.wordpress.com/2007/08/08/a-look-inside-the-wordpress-database/. In this guide, I m exposing the title, date and the content of each of my blog for my service, But you can extend this more the way you prefer.

  1. Download and install WSF/PHP 1.3.2 and PHP Data Services Library. If WSF/PHP 2.0 released by the time you are reading this, (it is to be released in this week), you only need to install WSF/PHP, since the DataServices library is packed with WSF/PHP from 2.0.
  2. Drop the following file (Say WordPressService.php) in to any of your web server document directories.
    <?php
    
    // Make sure you put the DataService.php in your include path
    require_once("wso2/DataServices/DataService.php");
    
    // database configuraitons
    // you have to set your database configurations in here..
    // These entries can be copy and past from the wp-config.php in your wordpress installation
    
    $config = array(
    "db" => "mysql",
    "username"=>DB_USER,
    "password"=> DB_PASSWORD,
    "dbname"=>DB_NAME,
    "dbhost"=>DB_HOST);
    // output format, plese check the API from http://wso2.org/wiki/display/wsfphp/API+for+Data+Services+Revised
    $outputFormat = array("resultElement" => "Posts",
    "rowElement" => "post",
    "elements" => array( "title" => "post_title",
    "content" => "post_content",
    "date" => "post_date"));
    
    // sql statment to execute, note that I assume the table prefix is wp_ (so the table name is wp_posts)
    // just check $table_prefix variable in the wp-config.php of your wordpress installation
    $sql="select post_title, post_content, post_date from wp_posts where post_status='published'";
    
    // operations is consist of inputFormat (optional), outputFormat(required), sql(sql), input_mapping(optional)
    $operations = array("getPosts"=>array("outputFormat"=>$outputFormat, "sql"=>$sql));
    $my_data_service = new DataService(array("config"=>$config,"operations"=>$operations));
    
    $my_data_service->reply();
    ?>
  3. It is all. Just access the above file from a web browser, you see your service is hosted. Here is the endpoint for my service. http://ws.dimuthu.org/blog/WordpressService.php. Since I m using WSF/PHP latest svn, I m able to retrieve the wsdl automatically from http://ws.dimuthu.org/blog/WordpressService.php?wsdl. Please wait for WSF/PHP 2.0 release for ?wsdl feature.
  4. To verify whether your service deployed correctly, you may need to write a simple test client. Yea I too wrote one. Since I have the wsdl generated, I just needed to generate the code for the client from the wsdl using wsdl2php tool. There is an online version of the tool in the wsf/php demo site. Here is the generated code for my client, http://labs.wso2.org/wsf/php/wsdl2phptool.php?wsdl_url=http%3A%2F%2Fws.dimuthu.org%2Fblog%2FWordpressService.php%3Fwsdl. I added the following code to the TODO section in handling response,
        if(is_array($response->post)) {
            foreach($response->post as $post) {
    
                echo "<h2>".$post->title . " - ".$post->date."</h2>";
                echo "<p>";
                echo $post->content;
                echo "</p>";
    
                echo "<hr/>";
            }
        }

    Check my Web Service client for the above service here, http://ws.dimuthu.org/blog/WordpressClient.php.

Now I can call for my blog from any web service enabled platform.

We use the term ‘DataService’ when we expose a database as a web service. WSO2 has a separate product for the DataService space which was earlier shipped bundled with WSAS. There you write a configuration xml explaining what data to be exposed in your database and drop it in the WSAS java app server and you get the DataServices deployed without much effort. Couple of months ago we published PHP DataServices library that allow you do deploy Your DataService within a popular LAMP or WAMP stack. With the WSF/PHP 2.0.0 release, you can have the PHP DataService library bundled with the release, hopefully within next couple of weeks.

One of the main problem DataService developers face is how to design an elegant web service API that will provide the requested information from a row database straightaway. Because when we normalize a database, the very closely related data are spread in different tables just keeping the association with foreign keys. So in order to retrieve the useful information database developers have to write multiple queries to the database. Is that same with DataServices?, Do you have to write multiple web service requests to return some useful data?

No. You don’t. You have Nested Queries to save you. With nested queries you can configure your DataServices, so all the relevent information can be extracted and send as a single SOAP message. I will take a demonstration to explain this.

Say you are working for a company that manufacture or sell electrical equipments, say TVs. And your boss is asking you to prepare a whole list of customers who ordered good from them. He want order information (like order date, status of the order) plus the name of the customers in the list.

Here is the table structure that you have.
Orders

ORDERNUMBER ORDERDATE STATUS CUSTOMERID

Customers

CUSTOMERID CUSTOMERNAME

From this you have to find a list of orders with order date, status (available in Orders table) and ordered customer information available in Customers table. As you may seen you have to query both the tables to get the required data.

First you have to decide a service operation that you going to expose as a web service. Operation is uniquely identified by its name. We will give our operation the name “getOrders”. In fact in our PHP Data Service API we treat a Operation same as a query. In general operation/query has following properties associated with it. Note for the complete API please check this Oxygen Tank Page.
Properties of Data Service Operation/Query

inputFormat This is the map of input element or input parameter to the corresponding type. For our operation there is no input. So we are not setting it here.
outputFormat This is a little tricky option. You have to set several configuration under this.

resultElement The name of the wrapper response element. We will give the name “Orders” here.
rowElement The name we give to wrap the each row of the result. If we give this value something like “Order“, then the result xml will be like,

<orders>
  <order>
    order 1 details
  </order>
  <order>
    next order details
  </order>

  <order>
    etc
  </order>
</orders>
elements The name of elements that is going to be in the response XML map to the real names of columns in the actual query result.
For an example if I give this the following value.

array("Order_number" => "ORDERNUMBER",
      "Order_date" => "ORDERDATE",
      "Status" => "STATUS");

The portion of the response will be like this,

<order>
  <Order_number>
    3
  </Order_number>
  <Order_date>
    2008-06-09
  </Order_date>

  <Status>
    Shipped
  </Status>
</order>
queries Array of queries, (The correct term is nested queries). So here you can set the response to be the result of another query. In fact in order to retrieve the customer name for the corresponding order we are going to write a nested query. Just think a operation that take the customerId as a parameter and return the customer data.

getCustomer(customerId)

You have to build this operation/query from the stat as we are just building the ‘getOrders’ query

attributes, texts, elementsOrder Some other configuration options. we are not using it in here, but by the name you can have an idea what they are for.
sql The standard sql query corresponding to the operation. In our case it is

SELECT o.ORDERNUMBER,o.ORDERDATE, o.STATUS,o.CUSTOMERNUMBER FROM ds.Orders o
inputMapping Finally this describe how the names of the input elements are mapped to the names of input parameters. Since ‘getOrders’ operation doesn’t have input parameters, this is not going to be used in this particular situation.

So as I described in the queries section you have to write the whole thing for the getCustomer operation as well.

$getCustomer = array(
    "inputFormat" => array("customerNumber" => "INTEGER"),
    "outputFormat" => array(

            "resultElement" => "Customers",
            "rowElement" => "Customer",
            "elements" => array("name" => "CUSTOMERNAME")
            ),
    "sql" => "select c.CUSTOMERNAME from ds.Customers c where c.CUSTOMERNUMBER = ?",
    "inputMapping" => array("CUSTOMERNUMBER" => "customerNumber")

    );

And here is the getOrders operation/query that actually use the getCustomer as a part of it.

$getOrders = array(
    "outputFormat" => array(
        "resultElement" => "Orders",
        "rowElement" => "Order",
        "elements" =>  array(

            "Order_number" => "ORDERNUMBER",
            "Order_date" => "ORDERDATE",
            "Status" => "STATUS"),
         "queries" =>  array($getCustomer)
         ),
    "sql" => "select c.CUSTOMERNAME from ds.Customers c where c.CUSTOMERNUMBER = ?"
    );

Then we will to define the operations array which we are going feed to the DataService class. Note that now we have two operations getOrders and getCustomer. From that I used getCustomer to help the building of getOrders. And I don’t want to publish getCustomer as a separate operation. Simply the boss doesn’t ask for that operation :) . So my operation map is like this,

$operations = array("getOrders" => $getOrders);

This operation map will be given as a constructor argument of DataService class. If you are familiar with WSF/PHP WSService class, there is no much different between these two classes. You can give all the options available in the WSService class to the DataService class. Say you want to encrypt the messages, just use the “policy” and the “securityToken” options. So with DataService class you have the luxury of using WS-Security as well as WS-Reliable Messaging for free.

To finish this post I will put the complete code for the above scenario. You can check the same service option in action in the WSF/PHP Demo site (Try the demo4).

<?php
// include the DataService library.
// NOTE: this path is valid only from wsf/php 2.0.0
require_once("wso2/DataServices/DataService.php");

// set the configurations
$config = array("db" => "mysql",
   "username" => "dimuthu",
   "password" => "thiswillnotbeasecretanymore",
   "dbname" => "ds",
   "dbhost" => "localhost");

// getCustomer Query - this used by the getOrders query/operation
$getCustomer = array(
    "inputFormat" => array("customerNumber" => "INTEGER"),
    "outputFormat" => array(

            "resultElement" => "Customers",
            "rowElement" => "Customer",
            "elements" => array("name" => "CUSTOMERNAME")
            ),
    "sql" => "select c.CUSTOMERNAME from ds.Customers c where c.CUSTOMERNUMBER = ?",
    "inputMapping" => array("CUSTOMERNUMBER" => "customerNumber")

    );

// The operation/query getOrders - Read the complete blog to see what 
// each of these option stand for
$getOrders = array(
    "outputFormat" => array(
        "resultElement" => "Orders",
        "rowElement" => "Order",
        "elements" =>  array(

            "Order_number" => "ORDERNUMBER",
            "Order_date" => "ORDERDATE",
            "Status" => "STATUS"),
         "queries" =>  array($getCustomer)
         ),
    "sql" => "select c.CUSTOMERNAME from ds.Customers c where c.CUSTOMERNUMBER = ?"
    );

// operation array
$operations = array("getOrders" => $getOrders);

// create the DataService object and reply
$ds = new DataService(array("operations" => $operations,
          "config" => $config,
          "serviceName" => NextedQuerySample));

$ds->reply();

?>

© 2007 Dimuthu’s Blog | iKon Wordpress Theme by Windows Vista Administration | Powered by Wordpress