API for DataService Revised
This is how a DataService in PHP script would look like,
<?php
$config
$query+
$operation+
[DataService construction and reply code]
?>
Defining Config
Since the PHP/DataServices was implemented op top of PDO (http://php.net/pdo) which abstracts several database engines like mysql, postgresql and etc., we have additional option to selected the database engine from the configuration. So the new configuration for the data services accepts the following array.
$config = array(
"db" => "DB_ENGINE_NAME",
"dbname" => "DB_NAME",
"dbhost" => "DB_HOST",
"username" => "DB_USERNAME",
"password" => "DB_PASSWORD"
"dsn" => "DATA_SOURCE_NAME");
"dsn" (optinal): you can give the 'Data Source Name' directly which may include 'db' 'dbname' and 'dbhost' feild. Please check the PDO driver correspond to your database, http://www.php.net/manual/en/pdo.drivers.php
"db" (required if 'dsn' not provided) : select the database engine, you should have installed PDO drivers for that http://www.php.net/manual/en/pdo.drivers.php
"dbname" (required if 'dsn' not provided): name of the database
"dbhost" (required if 'dsn' not provided): database host
"username" (optional) : username for database access
"password" (optional): password for database access if it is exist
Defining Queries
$query = array(
"inputFormat" => $input_format,
"outputFormat" => $output_format,
"sql" => $sql,
"inputMapping" => $Input_mapping);
"inputFormat" (optional): defines the format of the input, i.e. a map of input param name and their types.
e.g: array("input_param1" => "STRING", "input_param2" => "INT");
"outputFormat" (required): defines the output format of the query
it should have the following format.
array("resultElement" => RESPONSE_PAYLOAD_ROOT_ELEMENT_NAME,
"rowElement" => ELEMENT_NAME_FOR_EACH_ROW_ENTRY,
"useNil" => WHEHTER_NIL_OR_NOT,
"defaultNamespace" => NAMESPACE,
"columnDefault" => COLUMN_DEFAULT,
"elements" => ELEMENT_ARRAY,
"attributes" => ATTRIBUTE_ARRAY,
"texts" => TEXT_ARRAY,
"headers" => HEADER_ARRAY,
"queries" => QUERY_ARRAY,
"elementsOrder" => ELEMENTS_ORDER);
The above options are described in following part,
"resultElement" (required): Name of the payload root
"rowElement" (required): Name of the element for each row entry
"useNil": Whether the elements are nillable
"defaultNamespace" : namespace for the elements/attributes
"columnDefault": if the column is not described for any attribute or element specify what it should be by default, possible values are "element", "attribute", "text", default to "element"
"elements": array of elements
"attributes": array of attributes
"texts": array of test
The elements, attributes and texts should be in the following format,
array("element/attribute/text name" => JUST_COLUMN_NAME_OR_ARRAY);
if the JUST_COLUMN_NAME_OR_ARRAY is a string it is just the column name which is the more frequent scenario, if it is an array it should be in the following format,
array("column" => COLUMN_NAME,
"param" => PARAM_NAME,
"value" => PARAM_VALUE_CONST,
"xsdType" => XSD_TYPE);
"column": The column name of the output entry
"param": if any input param should be copied here, give the name of the param,
"value": an hard coded value, NOTE that you have to set either "param" option or "column" option or value and one would be considered if any two are given.
"xsdType": if the value should be mapped to any xsd type, provide it here, (use for WSDL generation)
"queries": array of queries which would embed in to the final payload. This is how we are going to support nested queries.
"elementsOrder": provide the element order information, i.e. it will be an array of element names, in the order it should appear, for text node we can give identifier on "texts" option as a key an provide that in the "elementsOrder" to set the order.
"sql" (required): SQL query to execute.
"inputMapping" (optional) : it is a map of parameter in current context to the input parameters in the query. If any case this is missing or some input parameter is missing in that array, it is assumed that the both values are same.
NOTE: In java this mapping is done in the call-query element, but in php we are merging the call-query and query in order to simplify the array based API.
Here are some examples of use of this API.
Defining Operations
The above defined query itself can be treated as a operation, since it is a mix of a query and the input mapping. so the operation map is simply a map of operation name to corresponding query.
$operations = array("operation_name" => $query, ..);
DataService construction and reply code
After you filled the above mentioned config, quires and operations ready to create DataService object and call its reply method.
$ds = new DataService("config" =. $config, "operations" => $operations);
$ds->reply();
Samples
Scenario1: No Nested Queries
A simple Service which doesn't have nested quires would be like this,
$input_format = array("lastName" => "STRING", "firstName" => "STRING");
$output_format = array(
"resultElement"=>"customer-addresses",
"rowElement"=>"customer-address",
"elements"=>array(
"customernumber"=>"CUSTOMERNUMBER",
"contactlastname"=>"CONTACTLASTNAME",
"addressline1"=>"ADDRESSLINE1",
"city"=>"CITY",
"state"=>"STATE",
"postalcode"=>POSTALCODE",
"country"=>"COUNTRY"),
);
$input_mapping = array("lastName" => "lastName", "firstName" => "firstName");
$sql = "select CUSTOMERNUMBER, CONTACTLASTNAME, CONTACTFIRSTNAME, ADDRESSLINE1, ADDRESSLINE2, CITY, STATE, POSTALCODE, COUNTRY from Customers where CONTACTLASTNAME = ? and CONTACTFIRSTNAME = ?";
$operations = array("customerAddress" =>
array("inputFormat" => $input_format,
"outputFormat" => $output_format,
"sql" => $sql,
"inputMapping" => $input_mappping));
Note: in here "inpuMapping" is optional, if it is not given the mapping is assumed one to one.
The above service in JAVA would be defined in the following XML,
<operation name="customerAddress">
<call-query href="customerAddressSQL" >
<with-param name="lastName" query-param="lastName" />
<with-param name="firstName" query-param="firstName" />
</call-query>
</operation>
<query id="customerAddressSQL">
<sql>select * from Customers where contactLastName = ? and contactFirstName = ?</sql>
<result element="customer-addresses" rowName="customer-address">
<element name="customer-number" column="customerNumber" />
<element name="contact-last-name" column="contactLastName" />
<element name="contact-first-name" column="contactFirstName" />
<element name="address-line1" column="addressLine1" />
<element name="address-line2" column="addressLine2" />
<element name="city" column="city" />
<element name="state" column="state" />
<element name="postal-code" column="postalCode" />
<element name="country" column="country" />
</result>
<param name="lastName" sqlType="STRING" /> <!- This maps to the $input_format variable ->
<param name="firstName" sqlType="STRING" />
</query>
Scenario2: With Nested Queries
//first build the nested query
$nested_query_input_format = array("customerNumber", "INTEGER");
$nested_query_output_format = array(
"resultElement" => "Customers",
"rowElement" => "Customer",
"elements" => array(
"Name" => "CUSTOMERNAME")
);
$nested_query_sql = "select c.CUSTOMERNAME from WSO2WSAS.CUSTOMERS c where c.CUSTOMERNUMBER = ?"
$nested_query_input_mapping = array("customerNumber" => "customerNumber");
$nested_query = array("inputFormat" => $nested_query_input_format,
"outputFormat" => $nested_query_output_format,
"sql" => $sql,
"inputMapping" => $nested_query_input_mapping);
//then build the operation
$input_format = NULL; //no input param is set
$output_format = array(
"resultElement"=>"Orders",
"rowElement"=>"Order",
"elements"=>array(
"Order-Number"=>"ORDERNUMBER",
"Last-Name"=>"LASTNAME",
"First-Name"=>"FIRSTNAME"),
"quires" => array($nested_query)
);
$sql = "select o.ORDERNUMBER,o.ORDERDATE, o.STATUS,o.CUSTOMERNUMBER from WSO2WSAS.ORDERS o";
$operation = array("inputFormat" => $input_format, "outputFormat" => $output_format,
"sql" => $sql);
The above part in the Java is defined as following,
<operation name="customerOrders">
<call-query href="customerOrdersSQL" />
</operation>
<query id="customerOrdersSQL">
<sql>select o.ORDERNUMBER,o.ORDERDATE, o.STATUS,o.CUSTOMERNUMBER from WSO2WSAS.ORDERS o</sql>
<result element="Orders" rowName="Order">
<element name="Order-number" column="ORDERNUMBER" />
<element name="Last-name" column="ORDERDATE" />
<element name="First-name" column="STATUS" />
<call-query href="customerNameSQL" >
<with-param name="customerNumber" query-param="customerNumber" />
</call-query>
</result>
</query>
<operation name="customerName">
<call-query href="customerNameSQL" >
<with-param name="customerNumber" query-param="customerNumber" />
</call-query>
</operation>
<query id="customerNameSQL">
<sql>select c.CUSTOMERNAME from WSO2WSAS.CUSTOMERS c where c.CUSTOMERNUMBER = ?</sql>
<result element="Customers" rowName="Customer">
<element name="Name" column="CUSTOMERNAME" />
</result>
<param name="customerNumber" sqlType="INTEGER" />
</query>