Overview arrow_sm.gif Architecture arrow_sm.gif User Guide arrow_sm.gif FAQ arrow_sm.gif Contributors

User Guide

Introduction

OData SDK for PHP is designed to make it easier for PHP applications to consume data services created using the WCF Data Services framework.
The goal is to provide the same functionality that is available in the libraries available to .NET developers. The library includes support for accessing and
modifying the data exposed by the WCF Data Services.

Protocol documentation and the list of publicly available OData services can be found on the OData web site .

Requirements

This toolkit requires the following frameworks to be installed and enabled
PHP XSL Extension
PHP CURL Extension

Package Content

\Doc:
  • Toolkit User guide
\Samples\WCFDataServices:

A Visual Studio 2008 project for three sample WCF Data Services used by the sample applications included in the PHP Toolkit. The Visual Studio project creates three services:
  • NorthWindDataService: Service used by the SimpleApplications and can be used to test the WCFDataServicesEditor.
  • ACSNorthWindDataService: This service is same as ‘NorthWindDataService’, expect access to this service requires ACS authentication. Two applications in the SimpleApplication use this service.
  • GameStoreDataService: Service used by the Game Store sample Application.
\Sample\PHPApplications:
  • WCFDataServicesEditor: A PHP application that can be used to connect, browse, read and write data exposed by a WCF Data Services.
  • SimpleApplications: A list of simple HelloWorld-type applications that show how WCF Data Services queries can be used to retrieve data from a server, how to use library APIs to perform Add/Delete/Update operations on entity instances, and Add/Delete links between entity instances.
  • VideoGameStoreApplication: a sample application that shows how WCF Data Services can be used to access a remote data source.
\framework:
  • Contains the source code for the tools used to generate the proxy class and to communicate with data service.

Installation and Configuration

The assumption is that PHP is already installed and configured on the machine where the WCF Data Services toolkit is installed.
The toolkit does not have any dependency on the host OS so it can run on Windows, Linux or Mac OSX machines.

To Install and Configure:
  • Create a folder named 'odataphp' eg: C:\PHPLib\odataphp
  • Copy the files and folders in the framework folder to the folder created above.
  • Add the path to the folder created in step 1 to the 'include_path' directive in php.ini. e.g.
   include_path = ".;C:\PHPLib\odataphp"

  • Create a variable called 'odataphp_path' in the php.ini file and set it to the path where the PHP Toolkit was installed (step 1). Open php.ini and search for 'Paths and Directories' section. Just below the definition of 'include_path' directive, add the following two lines:
   ;OData SDK for PHP Library Path
   odataphp_path = "C:\PHPLib\odataphp"

  • On linux platform, make sure you have the php-xml module installed. This can be installed using yum as follows,
   yum install php-xml

  • Enable php_xsl.dll in php.ini. Search for 'extension=php_xsl.dll' in the php.ini file and remove the semicolon (;) in front.
  • Enable php_curl.dll in php.ini. Search for 'extension=php_curl.dll' in the php.ini file and remove the semicolon (;) in front.
  • Optional: The content of the PHPApplications directory can be deployed on a web server to provide access to the sample applications from a single web page. The PHPApplications directory contains some php files, template and CSS files that are used to build this Sample Web Page. All Samples must be configured before they can be run from the Web Page. Refer to the readme files in the WCFDataServiceEditor, SimpleApplications and GameStoreApplication directories for setup instructions. The PHP samples use the services in sample WCFDataServices directory. For more information, refer to the readme file in WCFDataServices to configure these sample services.

Creating Proxy Class

The first step is to create a proxy class that is used to connect to the WCF Data service. The PHP client library provides a PHPDataSvcUtil.php file which creates the proxy class.
The command to create the proxy class is:
php <path of Client Libary>PHPDataSvcUtil.php /uri=<data service Uri> | /metadata=<service metadata file>  [/out=<output file path>]
[/auth=windows|acs /u=username /p=password [/sn=servicenamespace /at=applies_to] ] [/ph=proxy-host /pp=proxy-port 
[/pu=proxy-user /ppwd=proxy-password] [/ups=yes|no] ]

<Path of Toolkit Library> is the path where the toolkit library files are installed (See the installation instructions).
Note: In order to use this utility, the configuration option {'odataphp_path'} should be set in php.ini configuration file. (Refer Installation and Configuration section for more details).

Command-line parameters

Option Definition Note
/config Path to the configuration file. The PHPDataSvcUtil.php can be used in two ways, either you can specify required options (described below) through command-line or you can define the same options in a configuration file and use /config option to specify path to the config file.
/uri OData Service Uri The PHPDataSvcUtil.php uses this Uri to retrieve the service metadata. e.g. http://localhost:13985/NorthWindDataService.svc
/metadata Path to service meta data file. If you already have the service metadata file saved on your local machine, you can use this option to specify the path to this metadata file.
/out Output directory or file path The output path where the generated proxy file is to be saved. If file name is not specified, then the Entity Container name (defined in the WCF Data Service) is used as filename. If this parameter is absent then proxy file will be generated in the current directory.
/auth Type of authentication. Possible values are: 'windows', 'acs' This parameter is needed if access to the service requires authentication. Currently PHPDataSvcUtil supports two type of authentication, 'Windows Authentication' and 'Azure Access Control Service Authentication'.
/u Windows username or acs scope If /auth is of type ‘winodws’, then /u will be the windows username in the form domain/username. If /auth is of type ‘acs’, then /u will be the acs scope name.
/p Windows password or acs issuer key If /auth is of type ‘windows, then /u will be the windows password. If /auth is of type ‘acs’, then /u will be the acs issuer key
/sn acs service namesapce If /auth is of type ‘acs’, then /sn will be the acs service namespace.
/at acs applies to If /auth is of type ‘acs’, then /at will be the acs ‘applies to’ value
/ph Http Proxy Host
/pp Http Proxy port If you are running behind a proxy, then /ph and /pu parameters are required.
/pu Http Proxy username
/ppwd Http Proxy password Use /pu and /ppwd, if your proxy requires authentication
/ups Use proxy settings for service request. Possible values are: 'yes', 'no' By default the user specified proxy settings will be used while requesting metadata from OData Service. If you are using ACS auth and access to your service not require any proxy settings (e.g. service running locally) then set this flag to no, /ups=no

Example usage:

Generating proxy for a service running locally which is configured for anonymous access, using /uri option.

C:\PHPLib\ odataphp>php PHPDataSvcUtil.php /uri=http://localhost:13986/NorthWindDataServices.svc /out=D: \samples\SimpleApplication

PHPDataSvcUtil will connect to http://localhost:13986/NorthWindDataServices.svc, retrieves the metadata and generate the proxy file in D: \samples\SimpleApplication with name same as container name in the metadata.

Generating proxy from local metadata file using /metadata option

C:\PHPLib\ odataphp>php PHPDataSvcUtil.php
/metadata=D: \samples\SimpleApplication\NorthWindMeta.xml /out=D: \samples\SimpleApplication

PHPDataSvcUtil will use the metadata from the local disk and generate the proxy file in D: \samples\SimpleApplication with name same as container name in the metadata.

Generating proxy for remote service if you are running behind http proxy, using /ph and /pp options

C:\PHPLib\ odataphp>php PHPDataSvcUtil.php
/uri=http://netflix.cloudapp.net/catalog.svc /out=D: \samples\SimpleApplication /ph=itgproxy.redmond.corp.microsoft.com /pp=80

PHPDataSvcUtil will use the proxy settings specified with /ph and /pp option to connect to the remote service and generates the proxy file in D: \samples\SimpleApplication with name same as container name in the metadata.

Generating proxy for a service in the same domain but configured to require windows authentication, using /auth, /u and /p options

C:\PHPLib\ odataphp>php PHPDataSvcUtil.php
/uri= http://mflasko-dev/_vti_bin/listdata.svc /out=D: \samples\SimpleApplication /auth=windows /u=redmond\odphp /p=passw0rd!

PHPDataSvcUtil will use windows credential provided over /u and /p options to connect to the service and generates the proxy file in D: \samples\SimpleApplication with name same as container name in the metadata.

Generating proxy for a service running locally but configured to require ACS authentication, using /auth, /u, /p, /sn and /at options

C:\PHPLib\ odataphp>php PHPDataSvcUtil.php
/uri= http://localhost:13985/ACSNorthWindDataService.svc /out=D: \samples\SimpleApplication /auth=acs /u=infocorp
/p=qfimKj6Vm6HKAJryTRno5USPpvz0c8bBzFhlqKex6lQ= /sn=wcfdataservice 
/at=http://localhost/WCFNorthWindService/ /ups=no /ph=itgproxy.redmond.corp.microsoft.com /pp=80

PHPDataSvcUtil will use acs credential provided over /u, /p, /sn and /at options to connect to the service and generates the proxy file in D: \samples\SimpleApplication with name same as container name in the metadata.

Using /config option

In the above example, there are too many options passed using command line. Alternatively you can define all these options in a config file and can use /config option.

[Data service Url]

/uri=http://localhost:13985/ACSNorthWindDataService.svc/

[Out Directory]

/out= D: \samples\SimpleApplication


[ACS Auth Information]

/auth=acs

/u=infocorp

;Note that issuer key should be inside double quotes as it contain '=' symbol

/p="qfimKj6Vm6HKAJryTRno5USPpvz0c8bBzFhlqKex6lQ="

/sn=wcfdataservice

/at=http://localhost/WCFNorthWindService/
 

[Proxy Settings to acess ACS]

/ph=itgproxy.redmond.corp.microsoft.com

/pp=80
 

[Want to use the above proxy for getting metadata]

; No because the service is running locally

/ups=no


C:\PHPLib\ odataphp>php PHPDataSvcUtil.php  /config=D:/myApp/settings.ini

Using Proxy Class

After the proxy class for the WCF Data Service has been created, it can be used in the application to access the data on the server. The proxy class generated by the PHPDataSvcUtil needs to be included first:
require_once 'MyProxyClass.php';

The MyProxyClass.php file contains the definition of a class that can be use to execute various queries using the Execute method.
The following code snippet shows an example on how to use the proxy class to access a customer record for a service that exposes the NorthWind database.
The goal is to retrieve the record for the Costumer with CustomerID=’ALFKI’
$proxy = new NorthwindEntities();
$response = $proxy->Execute(“Customers(‘ALFKI’)”);
$customer = $response->Result[0];
echo ($customer->CompanyName);

This code sample uses the proxy class to connect to the WCF Data Service, retrieves a customer, and prints the CompanyName field using the customers class defined in the myproxyclass.php file.
Note: The syntax EntitySet(<key>) is used to select an entity instance from the data service. Here EntitySet can be mapped to the database table, <key> to the primary key and entity instance to table record.

Data Service Query Expressions

The data service query expressions are conditions that determine what data is retrieved from the WCF Data Service. Using query expressions we can perform traditional query operations against resources (entity sets), such as filtering, sorting, and paging. Query expressions are composed of query options, query operators, and query function. There are nine Data Service Query options supported by the OData SDK for PHP.
  • expand
  • filter
  • orderby
  • skip
  • top
  • inlinecount
  • count
  • skiptoken
  • select
Complete documentation on the Data Service Query expressions can be found on the MSDN site

Running Query Expressions

The OData SDK for PHP exposes two APIs which can be used for running query expressions against WCF Data Service servers.
  • ObjectContext::Execute
  • DataServiceQuery::Execute
The ObjectContext: :Execute accepts query expression URI as parameter, this method will use HTTP GET request to retrieve the entity set (in Atom format) addressed by the query expression URI, create a collection of entity objects by parsing the atom response and returns DataServiceResponse object which includes this collection.

An instance of DataServiceQuery class represents single query request to a data service, this class exposes few methods which helps to reduce the difficulty in building the query expression URI.

DataServiceQuery Class

OData SDK for PHP includes a class that represents a single query request to a data service.

The DataServiceQuery class exposes the following member function: the 'Query Options in Detail' section contains examples on how to use the DataServiceQuery functions
API Prototype Parameters Description
AddQueryOption($name, $value) $name: The string value that contains the name of the query string option (filter, orderby, skip, top, select, expand) to add. $value: The string that contains the value of the query string option. The query options are added to the resultant query expression URI using ?name=value&name2=value2… syntax where the name maps directly to the $name parameter and the value is from $value parameter.
Expand($relatedEntities) $relatedEntities: The related entities separated by comma, which is to be embedded in the result. Add the $expand option in the resultant query expression URI.
Filter($expression) $expression: The expression to be applied to restrict the entities to be embedded in the result. Add the $filter option in the resultant query expression URI.
Select($properties) $properties: Properties of the entity separated by comma. Add the $select option in the resultant query expression URI.
Top($count) $count: The maximum number of entities to be embedded in the result. Add the $top option in the resultant query expression URI.
Skip(count) $count: The number of rows to be skipped when returning results. Add the $skip option in the resultant query expression URI.
IncludeTotalCount() None Requests to include the number of entities returned by the query expression URI along with the query result.
Count() None Returns only count of entities satisfying the resultant query URI expression.
RequestUri() None Returns the resultant query URI expression.
Execute() None Executes the query expression and returns QueryOperationResponse object which holds the results as a collection.

QueryOperationResponse Class

OData SDK for PHP includes a class that represents the return type of ObjectContext::Execute and DataServiceQuery::Execute APIs.

The QueryOperationResponse class exposes the following member functions:
API Prototype Parameters Description
TotalCount() None Returns total number of entities in the entity set, if user asked for row count (using DataServiceQuery::IncludeInlineCount or inlinecount=allpages option in the query expression).
getQuery() None Returns the query expression URI that generates the QueryOperationResponse item.
getStatusCode() None Returns the HTTP response code associated with HTTP response for the query expression URI.
getHeaders() None Returns an associative array which contains the HTTP response headers associated with HTTP response for the query expression URI.
getError() None Gets error thrown by the data service query operation.
GetContinuation($collection) $collection: The collection of related objects being loaded Returns 'DataServiceQueryContinuation' object that contains the URI that is used to retrieve the next page of related entities in the specified collection. See Server Side Paging Section for more details.

This class includes the following member variable:

Member variable Description
Result The result of execution of query, expression URI as a collection.

OData Exception Classes

OData SDK for PHP supports 4 types of exception.

DataServiceRequestException class

Exception class representing any exception that can occur, while building the query expression URI (using DataServiceRequest member APIs) or running the query.

Member variable Description
Response Type: QueryOperationResponse. The Response::getError(), Response::getStatusCode(), and Response::getHeaders can be used for getting error details.


This exception class includes one member variable of type QueryOperationResponse, Response. Please see the samples in Query Options in Details section to understand how to handle this exception.

ODataServiceException class

Exception class representing any error returned from the WCF Data services, while processing the requests (expect query request) from the client.

The ODataServiceException class exposes following functions:

API Prototype Parameters Parameters
getError() None Returns short description of error.
getDetailedError() None Returns detailed description of error.
getStatusCode() None Returns the HTTP response code associated with HTTP response representing the error.
getHeaders() None Returns an associative array which contains the HTTP response headers associated with HTTP response representing the error.

InvalidOperation class

Exception class representing any exception that can occur, due to the invalid usage of the APIs by the client application. For example if client application try to add same entity instance twice, then the context tracking logic will throw this exception with message ‘The context is already tracking the entity’.

The InvalidOperation class exposes following functions

API Prototype Parameters Parameters
getError() None Returns short description of error.
getDetailedError() None Returns detailed description of error.

ACSUtilException class

Exception class representing any error that occurred while trying to retrieve ACS token from Azure. This error can occur while using ACSUtil class or ACSCredential class.

The ACSUtilException class exposes following functions

API Prototype Parameters Parameters
getError() None Returns description of error from ACS.
getStatusCode() None Returns the HTTP response code associated with HTTP response representing the error.
getHeaders() None Returns an associative array which contains the HTTP response headers associated with HTTP response representing the error.

Query Options in Detail

This section explains the query options in detail and the samples use DataServiceQuery class for building and executing query expressions.

Expand

The ‘expand’ option allows you to embed one or more sets of related entities in the results. For example, if you want to display a customer and its sales orders, you could execute two requests, one for Customers(‘ALFKI’) and one for Customers(‘ALFKI’) Orders. The ‘expand’ option on the other hand allows you to return the related entities in-line with the response of the parent in a single HTTP request.

You may specify multiple navigation properties to expand by separating them with commas, and you may traverse more than one relationship by using a dot to jump to the next navigation property. The benefit of using the Expand option is that data can be retrieved with only one server invocation.

Example:
  try

{

   //customer ALFKI with related sales orders

   $query = new DataServiceQuery(‘Customers’, $proxy)
                ->Filter(“CustomerID eq ‘ALFKI’”)
                ->Expand(‘Orders’);
   $response = $query->Execute();
   $customer = $response->Result[0];
   echo count($customer-Orders); 

   //In the proxy class, for each entity set, there will be one function (with name same as name of entity set) 
   //that returns DataServiceQuery object for that particular entity set.

   //customer ALFKI with related sales orders and employee information related to those orders

   $response = $proxy->Customers()
                        ->Filter( “CustomerID eq ‘ALFKI’”)
                        ->Expand(‘Orders/Employees’)->Execute();

   $customer = $response->Result[0]
   echo count($customer->Orders(10248)->Employees); 

  //Orders entity with id 10248 with related employees information and related shipper information

  $response = $proxy->Orders()
                       ->Filter( ‘OrderID eq 10248’)
                       ->Expand(‘Employees,Shippers’)->Execute();

  $order = $response->Result[0];
  echo count($order->Employees(5)->Shippers);
  echo count($order->Employees);
}
catch(DataServiceRequestException $exception)
{
  echo $exception->Response->getError(); 
}

Instead of using DataServiceQuery class, you can also use the Execute() API of ObjectContext class. For example,
    $query = new DataServiceQuery(‘Customers’, $proxy)
                ->Filter(“CustomerID eq ‘ALFKI’”)
                ->Expand(‘Orders’);
    $response = $query->Execute();

Can be replaced using ObjectContext::Execute API, in this case the correct query strings need to be built:

    $response = $proxy->Execute(“Customers(‘ALFKI’)?/$expand=Orders”);

Filter

Restrict the entities returned from a query by applying the expression specified in this operator to the entity set identified by the last segment of the URI path.

Example:
try
{

  //all customers in London
  $response = $proxy->Customers()
                        ->Filter( “City eq ‘London’”)
                        ->Execute();

  $customers = $response->Result; 

  //Match all Customers with the value of the property ‘fullname’ equal to ‘Wayne, John’

  $response = $proxy->Customers()
                        ->Filter( “'Wayne, John' eq insert(ContactName, length(lastname), ',')”)
                        ->Execute();
  $customers = $response->Result;

}
catch(DataServiceRequestException $exception)
{
   echo $exception->Response->getError(); 
}


Conditions that can be used in the filter option are:

Condition Description
Eq Equals
Ne Not equals
Lt Less than
Le Less than or equal
Gt Greater than
Ge Greater than or equal

OrderBy

Sort the results by the criteria given in this value. Multiple properties can be indicated by separating them with a comma. The sort order can be controlled by using the “asc” (default) and “desc” modifiers.

Example:
try
{

    $response  = $proxy->Customers()->OrderBy(‘City’)->Execute();
    $customers = $response->Result;
    $response  = $proxy->Customers()->OrderBy(‘City desc’)            
                                   ->Execute();

    $customers = $response->Result;
    $response  = $proxy->Customers()->OrderBy(‘City desc,CompanyName asc’)->Execute();
    $customers = $response->Result;
}
catch(DataServiceRequestException $exception)
{
    echo $exception->Response->getError(); 
}

Skip

Skip the number of rows given in this parameter when returning results. This is useful in combination with “top” to implement paging (e.g. suppose there are 40 total entities, if using 10-entity pages, saying $skip=30&top=$10 would return the fourth page).

NOTE: if an orderby option is included, ‘skip’ will skip entities in the order given by that option. If no orderby option is given, ‘skip’ will sort the entities by primary key and then perform the skip operation.

Example:
try
{
   //return all customers except the first 10
   $response = $proxy->Customers()->Skip( ‘10’)->Execute();
   $customers = $response->Result; 

   //return the 4th page, in 10-row pages
   $response = $proxy->Customers()->Skip( ‘30’) 
                                  ->Top(‘$top’, ‘10’)
				  ->Execute();
   $customers = $response->Result;
}
catch(DataServiceRequestException $exception)
{
   echo $exception->Response->getError(); 
}

Top

Restrict the maximum number of entities to be returned. This option is useful both by itself and in combination with skip, where it can be used to implement paging as discussed in the description of ‘skip’.

Example:
try
{

   //top 5 sales orders
   $response = $proxy->Orders()->Top(‘5’)->Execute();
   $orders = $response->Result; 

   //top 5 sales orders with the highest TotalDue
   $response = $proxy->Orders()->Orderby(‘TotalDue’)
			       ->Top(‘5’)->Execute();
   $sorders = $response->Result;
}
catch(DataServiceRequestException $exception)
{
   echo $exception->Response->getError(); 
}

inlinecount

This option directs the service to include the count of all the entities along with the entities that are returned by a URI. This count is executed as a separate query, and the returned value is independent of any defined page size limits (concept of page size limit will be discussed in Server Side Paging section).

Example:
try
{
  //all customers in London
  $response = $proxy->Customers()
                        ->Filter(“City eq ‘London’”)
                        ->IncludeTotalCount()       
                        ->Execute();
  $customers = $response->Result;

  echo ‘Total Number of Customers:’. $response->TotalCount();
  echo ‘Number of Customers in London:’ . count($customers);
}
catch(DataServiceRequestException $exception)
{
  echo $exception->Response->getError(); 
}

Count

The count query option can be added to any entity sets on the server, the result is a plaintext response that represents the count of entities in that set.

Example:
try
{
  //all customers in London
  $query = $proxy->Customers()
                 ->Filter(“City eq ‘London’”);                       
  echo ‘Number of Customers with Lodon as City:’ . $query->Count();
}
catch(DataServiceRequestException $exception)
{
   echo $exception->Response->getError(); 
}

Detailed discussion of inlinecount and count can be found on this blog .

Select

The projections (select) feature of WCF Data Services allows working with a subset of an entity’s properties. The projections feature allows clients to address only the relevant properties to a specific request. By only requesting the applicable subset of an entity’s properties for a particular query, client applications can optimize for bandwidth consumption.

The following example shows how to use this feature:

Example:
try
{
     $svc = new NorthwindEntities();
     $query = $svc->Customers()->filter("Country eq 'USA'")
                               ->Select('CustomerID,CompanyName');
     $customersResponse = $query->Execute();
    

     foreach($customersResponse->Result as $customer)
     {

         echo "CustomerID:" . $customer->CustomerID . "<br/>";
         echo "Company Name:" . $customer->CompanyName . "<br/>";
         echo "Country:" .$customer->Country . "(This will be null as we selected 
						only CustomerID and CompanyName)" . "<br/>";
         echo "----------" . "<br/>";
     }
}
catch(DataServiceRequestException $ex)
{
        echo 'Error: while running the query ' . $ex->Response->getQuery();
        echo "<br/>";
        echo $ex->Response->getError();
}

Server Driven Paging

The Server-Driven Paging feature allows a service author to set the number of entities returned for each request. In addition to limiting the number of entities returned per request, the server provides the client a "next link" which is simply a URI specifying how to continue retrieving the rest of the entities in the collection not returned by the first request.

Please refer this blog to understand how to configure a service with Server-Driven Paging feature.

The QueryOperationResponse::GetContinuation() API can be used to get the “next link”. The following sample shows how to use QueryOperationResponse::GetContinuation().


Example:
try
{
     $svc = new NorthwindEntities(NORTHWIND_SERVICE_URL);
     $query = $svc->Customers()->Filter(”Country eq 'USA'”);
     $customersResponse = $query->Execute();    

     echo "CustomerID" . "<br/>";
     echo "----------" . "<br/>"; 

     $nextCustomerToken = null;
  

     do
      {

        if($nextCustomerToken != null)
        {
            $customersResponse = $svc->Execute($nextCustomerToken);
        }
 

        foreach($customersResponse->Result as $customer)
        {
             echo $customer->CustomerID . "<br/>";
        }


      }while(($nextCustomerToken = $customersResponse->GetContinuation()) != null);

}
catch(DataServiceRequestException $exception)
{
    echo $exception->Response->getError(); 
}

Please refer the samples provided in the next section to understand how to use QueryOperationResponse::GetContinuation(..) API to load related entities.

Loading Related Entities using expand option and LoadProperty API

When you execute a query using ObjectContext::Execute or DataServiceQuery::Execute, only the entities in the addressed entity set are returned (in QueryOperationResponse::Result). For example, when a query against the Northwind data service returns Customers entities, by default the related Orders entities are not returned, even though there is a relationship between Customers and Orders. There are two ways to load related entities in a single call to the server:
  • Using expand query option to request that the query return entities that are related by an association to the entity set that the query requested, this is known as eager loading. The following sample, List all customer’s ID in NorthWind DB with USA as Country and associated Order's ID using expand option. This sample application also shows how to use QueryOperationResponse::GetContinutation(..) API to continue retrieving the rest of the related entities in the collection if the WCF service implements server side paging:

try
{
     $svc = new NorthwindEntities(NORTHWIND_SERVICE_URL); 
     $query = $svc->Customers()
                     ->Filter(”Country eq 'USA'”)
                     ->Expand('Orders');
     $customerResponse = $query->Execute(); 

     $nextCustomerToken = null;

     do
     { 

        if($nextCustomerToken != null)
        {
            $customerResponse = $svc->Execute($nextCustomerToken);
        }


        foreach($customerResponse->Result as $customer)
        {
             echo '<br/>CustomerID: ' . $customer->CustomerID . "<br/>";

             $nextOrderToken = null;
             $firstTime = true;
             $ordersResponse; 

             echo "<br/>Associcated Orders <br/>";
             echo "-----------------------<br/>";
            

             do
             {
                $orders = array();
                if(!$firstTime)
                {
                    $ordersResponse = $svc->Execute($nextOrderToken);
                    $orders = $ordersResponse->Result;
                    $nextOrderToken = $ordersResponse->GetContinuation();
                }

 
                if($firstTime)
                {
                    $orders = $customer->Orders;
                    $nextOrderToken = $customerResponse->GetContinuation($customer->Orders);
                    $firstTime = false;
                }
 

                foreach($orders as $order)
                {
                     echo "     " . $order->OrderID . "<br/>";
                } 

             }while($nextOrderToken != null);
        }

     }while(($nextCustomerToken = $customerResponse->GetContinuation()) != null);
}
catch(DataServiceRequestException $exception)
{
    echo $exception->Response->getError(); 
}

  • You can call the LoadProperty method of the ObjectContext class to explicitly load related entities. Each call to the LoadProperty method creates a separate request to the data service. The following sample, List all cutomer's ID in NorthWind DB with USA as Country and associated Order's ID using LoadProperty API. This sample application also shows how to use QueryOperationResponse::GetContinutation(..) API to continue retrieving the rest of the related entities in the collection:
try
{
       $svc = new NorthwindEntities(NORTHWIND_SERVICE_URL);
       $query = $svc->Customers()->Filter("Country eq 'USA'");
       $customerResponse = $query->Execute();
       $nextCustomerToken = null;

      do
      {
         if($nextCustomerToken != null)
         {
             $customerResponse = $svc->Execute($nextCustomerToken);
         }

         foreach($customerResponse->Result as $customer)
         {
              echo '<br/>CustomerID: ' . $customer->CustomerID . "<br/>";
              $nextOrderToken = null;
              echo "<br/>Assoicated Orders <br/>";
              echo "-----------------------<br/>";

              do
              {
                 $ordersResponse = $svc->LoadProperty($customer, 'Orders', $nextOrderToken);
                 foreach($customer->Orders as $order)
                 {
                     echo "     " . $order->OrderID . "<br/>";
                 }     
            }while(($nextOrderToken = $ordersResponse->GetContinuation()) != null);            
        } 
     }while(($nextCustomerToken = $customerResponse->GetContinuation()) != null);
 }
 catch(DataServiceRequestException $exception)
 {
     echo $exception->Response->getError(); 
 }

Creating new instance in data service

In this scenario a new object is created on the client and then made persistent by saving it on the database exposed by the WCF Data Service.

To create a new instance in the data service, first a new php object needs to be created and the required properties populated, then by calling AddObject and passing the object and the target entity-set, the object is added to the collection of objects in the database. A final call to the SaveChanges method makes the actual call to make the changes permanent in the database.

The Following code snippet shows how to use this functionality.

require_once 'MyProxyClass.php';

try
{
   $proxy = new NorthwindEntities();

   //Create a Customer php Object 
   $customer = Customers::CreateCustomers('CHAN9', 'channel9');

   //inserting Customers object context tracking system 
   $proxy->AddObject('Customers', $customer);

   //SaveChange insert the object into data service
   $proxy->SaveChanges();
}
catch(ODataServiceException $exception)
{
   echo $exception->getError();
}

Updating an existing instance in data service

To update an existing instance in the data service, first an instance from data service needs to be retrieved using the ObjectContext::Execute or LoadProperty or DataServiceQuery::Execute methods. After the object has been retrieved and the properties set with the new values a call to the UpdateObject applies the changes to the table(s) in the database. The following code snippet shows how to use UpdateObject API.
require_once 'MyProxyClass.php';

try
{
    $proxy = new NorthwindEntities();
    $response = $proxy->Execute("Customers('CHAN9')");
    $customer = $response->Result[0];

    //update the CompanyName property
    $customer->CompanyName = "Channel8";

    //add the object to the list of objects that needs to be updated in the database
    $proxy->UpdateObject($customer);

    //SaveChanges updates the object in the data service
    $proxy->SaveChanges();
}
catch(DataServiceRequestException $e)
{
   echo $exception->Response->getError();
}
catch(ODataServiceException $exception)
{
   echo $exception->getError();
}

Deleting an existing instance from data service

Similar to the Update scenario, to delete an existing instance in the data service, first an instance needs to be retrieved using ObjectContext::Execute or LoadProperty or DataServiceQuery::Execute methods, then a call to the DeleteObject methods adds the objects to the list of objects that needs to be deleted in the database. . The following code snippet shows how to use DeleteObject API.
require_once 'MyProxyClass.php';

try
{
    $proxy = new NorthwindEntities();
    $response = $proxy->Execute ("Customers('CHAN9')");
    $customer = $response->Result[0];

    //The object is added to the list of objects to delete in the database
    $proxy->DeleteObject($customer);

    //SaveChanges persists the changes in the database 
    $proxy->SaveChanges();
}
catch(DataServiceRequestException $e)
{
   echo $exception->Response->getError();
}
catch(ODataServiceException $exception)
{
   echo $exception->getError();
}

Creating link between instances

If you have a relationship between two entities in the data service, then you can use AddLink to create relationship between the corresponding entity instances. The following code snippet shows how to create link between a Customers instance and Orders instances.
require_once 'MyProxyClass.php';

try
{
    $proxy = new NorthwindEntities();

   //Create a Customers instance 
   $customer = new Customers();
   $customer ->CustomerID = 'CHAN9';
   $customer ->CompanyName = 'channel9';
   $proxy->AddToCustomers($customer);

   //Create an Orders instance
   $Order = new Orders();

   //Create link between Customer and Order.
   $proxy->AddLink($cust, "Orders", $Order);
   $proxy->SaveChanges();
}
catch(ODataServiceException $exception)
{
   echo $exception->getError();
}

Deleting link between instances

If you have two related entity instances in data service, then you can use DeleteLink to remove the relationship between these two. The following code snippet shows how to delete the link.
require_once 'MyProxyClass.php';

try
{
    $proxy = new NorthwindEntities();

    //Retrive the Customers entity and related orders with key ‘CHAN9’ 
    $response = $proxy->Filter("CustomerID eq 'CHAN9'")
                      ->Expand('Orders')
                      ->Execute();
    $customers = $response->Result;

    foreach($customers as $customer)
    {
        foreach($customer->Orders as $o)
        {
            $proxy->DeleteLink($customer, 'Orders', $o);
        }
    }

    $proxy->SaveChanges();
}
catch(DataServiceRequestException $e)
{
   echo $exception->getError();
}
catch(ODataServiceException $exception)
{
   echo $exception->getError();
}

Batch and Non-Batch mode of the SaveChanges Operation

When you call the SaveChanges API, all changes that the context tracks are translated into REST-based operations, by default, these changes are packed in a batch and sent in a single request message. To require that changes to be sent in separate HTTP requests, you must set the Save Change mode to non-batch.

$svc->SetSaveChangesOptions(SaveChangesOptions::None);

To reset to batch mode:

$svc->SetSaveChangesOptions(SaveChangesOptions::Batch);

Enhanced BLOB Support

The V1of of the oData protocol allows defining entities with property of type BLOB. The issue with this implementation was you could not be able to defer the loading of BLOB property, for example if you have an Employee entity definition with a BLOB property that represent employee's photo, then when you request for a specific set of employees, the resultant payload from data service will be huge as each entity includes BLOB stream.

The V2.0of of the oData protocol enhances the BLOB support provided in Version 1 to enable data services to stream arbitrarily large BLOBs, store binary content separate from its metadata this helps to easily defer the loading of BLOB content when its metadata is requested, these type of entities are known as Media Link Entry (MLE) Entity.

OData SDK for PHP exposes the following APIs to support the enhanced BLOB feature of oData protocol V2.0.

API Prototype Parameters Expected Exceptions Description
GetReadStream($entity, $args = null) entity: The entity that has the binary property to retrieve. args: values can be null, string or object of DataServiceRequestArgs. Throws InvalidOperation if args is not null, string or instance of DataServiceRequestArgs, exception will throw if entity is not a media link entry. If WCF Data Service returns any error library will throw ODataServiceException Synchronously requests a data stream that contains the binary property of requested Media Link Entry $entity. The $args argument can be null, a string representing Accept HTTP header or instance of DataServiceRequestArgs class which contains settings for the HTTP request message (Slug, Accept, Content-Type etc..)
GetReadStreamUri($entity) $entity: The entity that has the binary property to retrieve. Throws InvalidOperation, if entity is not currently tracked by the context. Gets the URI that is used to return binary property data as a data stream.
SetSaveStream($entity, $stream, $closeStream, $contentType, $slug) $entity: The entity that has the binary property to set. $stream: The binary stream to set. $contentType: The type of stream. $slug: The default path and name to be given to the stream once it is uploaded. Throws InvalidOperation, if any one of the following criteria is not satisfied. $contentType cannot be null, $slug cannot be null, $stream cannot be null and Context should track the entity. Sets a new data stream as the binary property of an entity, with the specified settings in the request messages

Getting Stream associated with MLE entity using GetReadStream

The following code snippet uses GetReadStream API to retrieve the stream associated with MLE entity set SharedDocuments of SharePoint 2010 site.

require_once 'TeamSiteDataContext.php';

try
{
    $queryResponse = $context->SharedDocuments()->Execute();
    $sharedDocItem = $queryResponse->Result[0];
    $responseStream = $context->GetReadStream($sharedDocItem);
    $stream = $responseStream->getStream();

    if (!$handle = fopen("download.xlsx", 'wb'))
    {
        echo "Cannot open file for writing output";
        exit;
    } 

    if (fwrite($handle, $stream) === FALSE)
    {
        echo "Cannot write to file";
        exit;
    }       

    fclose($handle);
 }
catch (ODataServiceException $e)
{       
        echo $e->getDetailedError();
        echo $e->getError();
}
catch(DataServiceRequestException $e)
{
    echo $e->Response->getError();
}

The above sample connects to the Shared Document library on a SharePoint site and downloads the first document that is then saved as download.xslx

Creating a Media Link Entry and associate a stream with it using SetSaveStream

require_once 'TeamSiteDataContext.php';

try
{       
    $stream = null;
    //make sure that WSR.docx should present in current working directory
    if (!$handle = fopen("WSR.docx", 'rb'))
    {
        echo "Cannot open file for reading  ";
        exit;
    }                  
    
    if (($stream = fread($handle, filesize("WSR.docx"))) === FALSE)
    {
        echo "Cannot read to file";
        exit;

    }                

    $doc = new SharedDocumentsItem();
    
    $doc->Name = "WSR1.docx";
    $doc->ContentType = ContentType::MS_OFFICE_DOCX;
    $context->AddToSharedDocuments($doc);

    $context->SetSaveStream($doc,
                            $stream,
                            false,
                            ContentType::MS_OFFICE_DOCX,
                            "/Shared Documents/WSR2.docx");                          
    $context->SaveChanges(); 
}
catch (ODataServiceException $e)
{      
    echo $e->getDetailedError();
    echo $e->getError();
}
catch(DataServiceRequestException $e)
{
    echo $e->Response->getError();
}

Working with Azure Storage Tables

The following basic operations are supported on azure storage tables and entities
  • Create a table or entity
  • Retrieve a table or entity, with filters
  • Update an entity (but not a table)
  • Delete a table or entity.

Operations against azure storage tables requires user to specify azure credentials. The constructor of AzureTableCredential class accepts azure account name and key.
$proxy->Credential = new AzureTableCredential(account-name, account-key);

Create Azure Storage Table

The following code snippet shows how to create a table with name Employees in azure.
require_once 'Context/ObjectContext.php';
define AZURE_SERVICE_URL("http://<Account>.table.core.windows.net");
define AZURE_ACCOUNT_NAME(specify your account name here);
define AZURE_ACCOUNT_KEY(specify your account key here);

try
{
   $svc = new ObjectContext(AZURE_SERVICE_URL);
   
   //specify azure credentials
   $svc->Credential = new AzureTableCredential(AZURE_ACCOUNT_NAME, AZURE_ACCOUNT_KEY);      
   //Create a table entity and add it to the context

   $table = new Tables();
   $table->TableName = 'Employees';
   $svc->AddObject('Tables', $table);
   $svc->SaveChanges();
}
catch(ODataServiceException $e)
{
    echo $e->getError();
}

Inserting a blog entity into azure table

The following code snippet shows how to insert an entity into the table created in the above sample. Here we have to define the entity definition (table entry metadata) for the Employee entities to be stored in the Employees table. The important points to be noted while defining table entry metadata are:
  • Table entry must be derived from TableEntry class
  • All the properties which is to be stored in the azure table must have the attribute @Type:EntityProperty
  • The table entry must have the following attributes
    • @Class:name of table Entry class
    • @Key:PartitionKey
    • @Key:RowKey
require_once 'Context/ObjectContext.php';
define AZURE_SERVICE_URL(“http://<Account>.table.core.windows.net”);
define AZURE_ACCOUNT_NAME(specify your account name here);
define AZURE_ACCOUNT_KEY(specify your account key here);

/**
 *@Class:Employees
 *@Key:PartitionKey
 *@Key:RowKey
 */
class Employees extends TableEntry
{
    /**
     *@Type:EntityProperty
     */
     Public $Name;

    /**
     *@Type:EntityProperty
     */
     Public $Age;
     
    /**
     *@Type:EntityProperty
     */
     Public $Visible;   
}

try
{
    $svc = new ObjectContext(AZURE_SERVICE_URL);
    
    //specify azure credentials
    $svc->Credential = new AzureTableCredential(AZURE_ACCOUNT_NAME, AZURE_ACCOUNT_KEY);

    //Create a employees entity and add it to the context
    $tableEntity = new Employees();
    $tableEntity->PartitionKey = 'Partition1';
    $tableEntity->RowKey = 'Row1';
    $tableEntity->Name = 'ALKFI';
    $tableEntity->Age = 26;
    $tableEntity->Visible = true;

    $svc->AddObject('Employees', $tableEntity);
    $svc->SaveChanges();
}
catch(ODataServiceException $e)
{
   echo $e->getError();
}

Updating Table entry by adding a new property

The following code snippet shows how to update the previously inserted table entity by adding address property. Here we have to update the definition of entity (table entry metadata) for the Employee entities with address property.
require_once 'Context/ObjectContext.php';
define AZURE_SERVICE_URL("http://<Account>.table.core.windows.net");
define AZURE_ACCOUNT_NAME(specify your account name here);
define AZURE_ACCOUNT_KEY(specify your account key here);

/**
 *@Class:Employees
 *@Key:PartitionKey
 *@Key:RowKey
 */
class Employees extends TableEntry
{
    /**
     *@Type:EntityProperty
     */
     Public $Name;

    /**
     *@Type:EntityProperty
     */
     Public $Age;

    /**
     *@Type:EntityProperty
     */
     Public $Visible;

    /**
     *@Type:EntityProperty
     */
     Public $Address;   
}

try
{
    $svc = new ObjectContext(AZURE_SERVICE_URL);
    
    //specify azure credentials
    $svc->Credential = new AzureTableCredential(AZURE_ACCOUNT_NAME, AZURE_ACCOUNT_KEY);
    
    //Create query object to fetch the record
    $query = new DataServiceQuery('\Employees', $svc);
    $response = $query-> Filter("Name eq 'ALFKI'")->Execute();
    $tableEntity = $response->Result[0];

    //Update the table entry by adding Address Property
    $tableEntity->Address = 'Line No1';
    $svc->UpdateObject($tableEntity);
    $svc->SaveChanges();
}
catch(ODataServiceException $e)
{
   echo $e->getError();
}
catch(DataServiceRequestException $e)
{
   echo $e->Response->getError();
}

Updating Table entry by removing an existing property

he following code snippet shows how to update the previously updated table entity by removing address property. Here we have to update the definition of entity (table entry metadata) for the Employee entities by removing address property. Since we require a replace (removal of address property) while update, we have to set the replaceOnUpdate flag to true by using the following API.

$svc->ReplaceOnUpdate(true);

require_once 'Context/ObjectContext.php';
define AZURE_SERVICE_URL("http://<Account>.table.core.windows.net");
define AZURE_ACCOUNT_NAME(specify your account name here);
define AZURE_ACCOUNT_KEY(specify your account key here);
 

/**
 *@Class:Employees
 *@Key:PartitionKey
 *@Key:RowKey
 */
class Employees extends TableEntry
{
    /**
     *@Type:EntityProperty
     */
     Public $Name;

    /**
     *@Type:EntityProperty
     */
     Public $Age;

    /**
     *@Type:EntityProperty
     */
     Public $Visible;
}

try
{
    $svc = new ObjectContext(AZURE_SERVICE_URL);
    
    //Set ReplaceOnUpdate flag true
    $svc->ReplaceOnUpdate(true);

    //specify azure credentials
    $svc->Credential = new AzureTableCredential(AZURE_ACCOUNT_NAME, AZURE_ACCOUNT_KEY);
    //Create query object to fetch the record
    $query = new DataServiceQuery('\Employees', $svc);
    $response = $query->Filter("Name eq 'ALFKI'")->Execute();
    $tableEntity = $response->Result[0];

    //Update the table entry    
    $svc->UpdateObject($tableEntity);
    $svc->SaveChanges();
}
catch(ODataServiceException $e)
{
   echo $e->getError();
}
catch(DataServiceRequestException $e)
{
   echo $e->Response->getError();
}

Delete Table entry and Table

The following code snippet shows how to delete table entry from table and table from azure. For this, we will fetch the table entry and table, mark it as deleted (using DeleteObject API) and then save the changes.
require_once 'Context/ObjectContext.php';
define AZURE_SERVICE_URL("http://<Account>.table.core.windows.net");
define AZURE_ACCOUNT_NAME(specify your account name here);
define AZURE_ACCOUNT_KEY(specify your account key here);

/**
 *@Class:Employees
 *@Key:PartitionKey
 *@Key:RowKey
 */
class Employees extends TableEntry
{
    /**
     *@Type:EntityProperty
     */
     Public $Name;

    /**
     *@Type:EntityProperty
     */
     Public $Age;

    /**
     *@Type:EntityProperty
     */
     Public $Visible;
}

try
{
    $svc = new ObjectContext(AZURE_SERVICE_URL);

    //specify azure credentials
    $svc->Credential = new AzureTableCredential(AZURE_ACCOUNT_NAME, AZURE_ACCOUNT_KEY);

    //Create query object to fetch the record
    $query = new DataServiceQuery('\Employees', $svc);
    $response = $query->Filter("Name eq 'ALFKI'")->Execute();
    $tableEntity = $response->Result[0];

    //mark the table entry as deleted   
    $svc->DeleteObject($tableEntity);
    $svc->SaveChanges();
    
    $query = new DataServiceQuery("\Tables", $svc);
    $response = $query->Filter("TableName eq 'Employees'")->Execute();
    $table= $response->Result[0];

    //mark the table as deleted   
    $svc->DeleteObject($table);
    $svc->SaveChanges();
}
catch(ODataServiceException $e)
{
   echo $e->getError();
}
catch(DataServiceRequestException $e)
{
   echo $e->Response->getError();
}

Using Call-Back Feature

OData library allows client application to register call back functions. The library exposes two APIs for registering call-backs.
  • ObjectConext::OnBeforeRequest
  • ObjectContext::OnAfterResponse
OnBeforeRequest API can be used to register a function which is to be invoked before making actual request to the WCF Data Service by the library. The library makes service request in 3 cases:
  • When client executes any query using DataServiceQuery::Execute, DataServiceRequest::Count, ObjectContext::LoadProperty or ObjectContext:Execute APIs.
  • When client retrieves media stream associated with a Media Link entry (MLE) using ObjectContex::GetReadStream API.
  • When client invokes ObjectContext::SaveChanges API to save the entity instances and relationships (links) in the context to the underlying WCF Data Service.

Once the client application register a call-back function using OnBeforeRequest API, before making any request to the WCF Data Service, the oData library will invoke the registered function by passing a reference to the HttpRequest object which will be used to make the actual service request. Client application can manipulate this HttpRequest object, for e.g. client can view and change the request body, the http method and the headers set by the library, add additional headers, specifying credential and proxy settings.

The HttpRequest exposes the following methods:
API Prototype Parameters Return Value Description
getBody() None string To get raw body of the http request.
getHTMLFriendlyBody() None string To get the body of the http request as HTML friendly string.
setBody($body) $body: string representing http request body. None To set the body of the http request.
getMethod() None HttpVerb: The enum representing the possible http methods. The HttpVerb enum has the following members: HttpVerb::DELETE, HttpVerb::GET, HttpVerb::MERGE, HttpVerb::POST and HttpVerb::PUT
setMethod(httpVerb::) httpVerb:: HttpVerb enum None See the description section of getMethod() function for possible values of HttpVerb enum.
getUri() None string To get the Uri of http request.
setUri($uri) $uri: String representing the uri None To set the Uri of http request.
getCredential() None Credential instance reference To get the reference to ObjectContext::Credential member variable. The reference can be one of the following type: WindowsCredential, AzureTableCredential or ACSCredential
setCredential($credential) $credential:Reference to Credential instance. None To set the credential to be used for request. The possible types are: WindowsCredential, AzureTableCredential or ACSCredential
getProxy() none Reference to HttpProxy instance To get the reference to ObjectContext::HttpProxy member variable
setProxy($proxy) $proxy: HttpProxy instance. None To set the http proxy to be used for request


The ‘Headers’ Member variable of HttpRequest give access to the request headers. The methods exposed by this member variable are:
Prototype Parameters Return Value Description
Add($key, $value) $key: header name $value: header value None To add a new key-value (headername-value) pair to the HttpRequest::Headers collection. If an item with key exists then overwrite it.
Remove($key) $key: header name None To remove key-value (headername-value) pair from the HttpRequest::Headers collection identified by $key
TryGetValue($key, &$value) $key: header name $value out: header value boolean If a headername-value pair exists with header name as $key, set the out parameter $value with header value and return true. If not found return false.
HasKey($key) $key: header name boolean Returns true if a headername-value pair exists with headername as $key, else false.
GetAllKeys() None array To get the all header name as an array.
GetAll() None array To get all headername-value pair as array.
Clear() None None To remove all headername-value pairs.
CopyFrom($srcArray) $srcArray: array holding headername-value paris. None To merge the contents of $srcArray with existing headername-value pairs.

The following sample shows how to use the call-back feature to add authorization header to the http request headers before making request to an ACS protected WCF Data Service.

Note: Refer Samples/WCFDataServices/ACSNorthWindDataServices to see how to configure a service with acs authentication.

define("ACS_NORTHWIND_SERVICE_URL", "http://localhost:13985/ACSNorthWindDataService.svc");
define("SERVICE_NAMESPACE", 'wcfdataservice');
define("ACS_USER", 'infocorp');
define("ACS_PWD", 'qfimKj6Vm6HKAJryTRno5USPpvz0c8bBzFhlqKex6lQ=');
define("ACS_APPLIESTO", 'http://localhost/WCFNorthWindService/');

/**
 * CallBack function, this will be invoked before making request to
 * OData service.
 */
function OnBeforeCallBack($httpRequest)
{
    $proxy = new HttpProxy(PROXY_HOST, PROXY_PORT);
    //Use the ACSUtil helper class of toolkit
    $acsutil = new ACSUtil(SERVICE_NAMESPACE,
                               ACS_USER,
                               ACS_PWD,
                               ACS_APPLIESTO,
                               array(),
                               $proxy);
    try
    {       
        //Get the ACS Token
        $token = $acsutil->GetACSToken();
        //Format the token in the way DataService Expects
        $authHeaderValue = 'WRAP access_token="' . urldecode($token) . '"';
        //Add the acs auth header
        $httpRequest->Headers->Add('authorization', $authHeaderValue);
    }
    catch(ACSUtilException $ex)
    {
        echo 'Failed to get the ACS token' . "<br/>";
        echo $ex->getError();
        exit;
    }
}

try
{
     $svc = new NorthwindEntities1(ACS_NORTHWIND_SERVICE_URL);
     //Regitser the call-back
     $svc->OnBeforeRequest('OnBeforeCallBack', null);
     $query = $svc->Customers()->filter("CustomerID eq 'ALFKI'");
     $customersResponse = $query->Execute();

     if(count($customersResponse->Result))
     {
        echo "<br/><br/>Customer Information <br/><br/>";
        $customer = $customersResponse->Result[0];
        echo 'CustomerID: ' . $customer->CustomerID . "<br/>";
        echo 'CompanyName: ' . $customer->CompanyName . "<br/>";
     }
     else
     {
         echo 'Failed to retrieve the Customer with ID \'ALFKI\'';
     }
}
catch(DataServiceRequestException $ex)
{
        echo 'Error: while running the query ' . $ex->Response->getQuery();
        echo "<br/>";
        echo $ex->Response->getError();
}
catch (ODataServiceException $e)
{
    echo "Error:" . $e->getError() . "<br>"  
         . "Detailed Error:"  
         . $e->getDetailedError();
}

In this above sample we used ACSUtil class to retrieve ACS token, then added this token as value of http authorization header. You can also use ACSCredential class and HttpRequest::Credential instead of ACSUtil as in the call-back follows:
function OnBeforeCallBack($httpRequest)
{
    $proxy = new HttpProxy(PROXY_HOST, PROXY_PORT);
     $acsCredential = new ACSCredential(SERVICE_NAMESPACE,
                                         ACS_USER,
                                         ACS_PWD,
                                         ACS_APPLIESTO,
                                         array(), /*additional claims*/
                                         $proxy   /*proxy required to access ACS
                                                    note that we will not set $svc->HttpProxy
                                                    because the service is running locally
                                                  */
                                         );
    $httpRequest->setCredential($acsCredential);
}


OnAfterRepsonse API can be used to register a function which is to be invoked after receiving response from the WCF Data Service and before converting the response to entity instances in the context. Once the client application register a call-back function using OnAfterResponse API, after receiving the response from the WCF Data Service, the oData library will invoke the registered function by passing a reference to the HttpResponse object which contains the response information.

The HttpResponse exposes the following methods:

Prototype Parameters Return Value Description
getBody() None string To get the raw body of the http response.
getHTMLFriendlyBody() None string To get the body of the http response as a HTML friendly string.
getHeaders() None array To get the HTTP response headers as array of headername-value paris.
getHeader($key) $key: string string To get value of header identified by the header name $key
getMessage() None string To get the Http message associated with the response. e.g. ‘Created’, ‘Not Found’ etc..
getCode() None integer To get the http code associated with the response.

The following sample shows how to use the call-back feature to print the response body for query from server.
function OnAfterCallBack($httpResponse)
{
    echo '<h1>The HTML Friendly Response body</h1>'  . "<br/>";
    echo $httpResponse->getHTMLFriendlyBody();
    exit;
}

$svc = new NorthwindEntities();
try
{  
    $svc = new NorthwindEntities();         
    $svc->OnAfterResponse('OnAfterCallBack', null);       
    $query = $svc->Customers()->filter("Country eq 'USA'");
    $customersResponse = $query->Execute();
}
catch(DataServiceRequestException $exception)
{
    echo 'Error during running the query:';
    echo "<br/>";
    echo $exception->Response->getError();
    exit;
}

Note: The library will throw InvalidOperation exception if the definition of call-back function not found while trying to invoke them.

Authentication in the Client Library

This toolkit can work against WCF Data Service hosted on IIS with Windows (or anonymous) authentication or Azure Storage Tables with Azure Authentication or WCF Data Services with ACS authentication. By default, the client library assumes the WCF Data Service uses an anonymous Windows connection so user credentials don't need to be specified. However, you can set the Credentials property in the proxy object to point to an object holding the credential information. The Credential property will be used if the WCF Data Service requires the user to be authenticated.

Windows Authentication:

$proxy->Credential = new WindowsCredential(domain\user-name, password); 

Azure Table Authentication:

$proxy->Credential = new AzureTableCredential(account-name, account-key);

ACS Authentication:

$proxy->Credential = new ACSCredential(service_namespace, wrap_name, wrap_password, wrap_scope, 
			claims /*array of claims*/, proxy /*HttpProxy instance*/); 

Note that one can pass proxy settings as an argument of ACSCredential class. This is required if your service is running in localmachine with acs authentication, In this case proxy information is required to retrieve token from acs, but not required for accessing the service (so no need to set HttpProxy member variable of generated proxy class)

Custom authentication Support

In more advanced scenario, the client application might need to set custom headers; registering call-back using OnBeforeRequest API will help in this case.

Connecting to Data Service through proxy server

If connecting to data service requires proxy server, you can set the Proxy property in the Http proxy object to point to an object holding the proxy server information.
$proxy->HttpProxy = new HttpProxy(proxy-server-name, proxy-server-port, proxy-user-name, proxy-password); 

The proxy-user-name and proxy-password are optional, which are needed only if the proxy requires authentication.

Reference Section

List of APIs supported by PHP Client Library.
API Prototype Parameters Return Value Description
Execute (query-expression) The query to be executed against data service. Please refer ‘Data Service Queries’ section for more details Returns result of query as a collection. If any error occurs (ex: malformed query), then client library will throw DataServiceRequestException exception with proper message. To execute queries against the data service
AddObject(TargetEntitySet, Object) TargetEntitySet: The name of entity set to which Object belongs to. Object: PHP Object represents the instance of a data service entity to be inserted. If any error occurs (ex: if entity instance already exists), then client library will throw InvalidOperation exception with proper message To create a new instance in the data service
UpdateObject(Object) Object: PHP Object represents the existng instance of a data service entity, which is to be updated. If any error occurs (ex: if entity instance not exists), then client library will throw InvalidOperation exception with proper message. To modify an existing entity instance.
DeleteObject(Object) Object: PHP Object represents the existing instance of a data service entity, which is to be deleted. If any error occurs (ex: if entity instance not exists), then client library will throw InvalidOperation exception with proper message. To delete an entity instance.
AddLink(Source, SourceProperty, Target) Source: The source object in the link. SourceProperty: The name of the property on the source object that represents the source in the link between the source and the target. Target: The target object involved in the link that is bound to the source object. If any error occurs, then client library will throw InvalidOperation exception with proper message. To add a link between two entity instances, assuming that there is a relationship between Two entities
DeleteLink(Source, SourceProperty, Target) Source: The source object in the link. SourceProperty: The name of the property on the source object that represents the source in the link between the source and the target. Target: The target object involved in the link that is bound to the source object. If any error occurs, then client library will throw InvalidOperation exception with proper message. To delete link between two entity instances.
AddHeader(Header-Name, Header-Value) Header-Name: The name of custom HTTP header Header-Value: The value of custom HTTP header. None To add custom headers
RemoveHeaders() None None To Remove all custom headers aded by user
SaveChanges() None Throws InvalidOperation exception if any error occurred while generating atom pub xml to be send to the service. Thows ODataServiceException exception with proper message if WCF Data Service returns any error. To save the changes made to the entities in the context to data service.
SetSaveChangesOptions($scOptions) $scOptions: SaveChanges Option. Possible values are SaveChangesOptions::None and SaveChangesOptions::Batch Throws InvalidOperation, if user passes invalid parameter. To set the SaveChanges mode, if mode is Batch all the changes will be packed in a single HTTP request, otherwise framework will generate separate HTTP request for each change. The Default mode is Batch.
SetReplaceOnUpdate($rOnUpdate) $rOnUpdate: boolean None Used to decide the HTTP verb used for updation. If this flag is true framework will use PUT verb, else MERGE verb.
GetReadStream($entity, $args = null) entity: The entity that has the binary property to retrieve $args:null, string or object of DataServiceRequestArgs. Throws InvalidOperation if args is not null, string or instance of DataServiceRequestArgs, exception will throw if entity is not a media link entry. If WCF Data Service returns any error this API will throw ODataServiceException. Synchronously requests a data stream that contains the binary property of requested Media Link Entry $entity. The $args argument can be null, a string representing Accept message header or instance of DataServiceRequestArgs class which contains settings for the HTTP request message (Slug, Accept, Content-Type etc..)
GetReadStreamUri($entity) $entity: The entity that has the binary property to retrieve Throws InvalidOperation, if entity is not currently tracked by the context. Gets the URI that is used to return binary property data as a data stream
SetSaveStream($entity, $stream, $closeStream, $contentType, $slug) $entity: The entity that has the binary property to set. $stream: The binary stream to set. $contentType: The type of stream. $slug: The default path and name to be given to the stream once it is uploaded. Throws InvalidOperation, if any one of the following criteria is not satisfied: $contentType cannot be null, $slug cannot be null, $stream cannot be null or context should track the entity. Sets a new data stream as the binary property of an entity, with the specified settings in the request message
OnBeforeReqest($functionname, $classinstance) $functionname: The name of the function to be registered as call-back. $classinstance: If the above function is a member function of a class, then this will be the reference to that class else null. None To register call-back function to be invoked before sending any request to the service.
OnAfterResponse(($functionname, $classinstance) $functionname: The name of the function to be registered as call-back. $classinstance: If the above function is a member function of a class, then this will be the reference to that class else null. None To register call-back function to be invoked after receiving any response from the service.

Last edited Mar 14, 2010 at 4:18 PM by anu_chandy, version 123

Comments

No comments yet.