Retrieving Data with the OData SDK for PHP
[Upadting this post to incorporate some fixes in the OData SDK for PHP. Also adding this link to a related post: CRUD Operations with the OData SDK for PHP.]
What is OData?
I’ll start with an over simplification: OData is a protocol for creating data services that make it possible to retrieve and edit data using HTTP. More specifically, OData enables you to define a data model that lets clients address data as resources by using URIs. Data is retrieved and updated by using the HTTP actions of GET, POST, PUT, DELETE and MERGE. For example, consider a database represented by the diagram below and containing the data shown:
For a service that implements the OData protocol, this URI, http://MyDomain/MyService.svc/Customers, would return XML similar in structure to this:
<?xml version="1.0" encoding="utf-8"?>
Note: This is only an example. This is not the actual Atom feed returned by a service that implements the OData protocol. Here’s an example of a real feed (I’m just trying to keep things simple for now): http://services.odata.org/Northwind/Northwind.svc/Customers. If you can’t see the XML for this feed in IE, clear the “Turn on feed reading view” option in Tools|Internet Options|Content|Settings, or just view the source.
Similarly, http://MyDomain/MyService.svc/Orders would return predictably structured XML containing all data for orders, http://MyDomain/MyService.svc/Customer('2') would return information about customer with CustomerID=2, http://MyDomain/MyService.svc/Customer('1')/Orders would return only order information for customer with CustomerID=1, and so on.
Digging a little deeper into OData (which you can do here: http://www.odata.org/developers/protocols/overview), you’ll see that…
At the core of OData are feeds, which are Collections of typed Entries. Each entry represents a structured record with a key that has a list of Properties of primitive or complex types.
In order to help clients discover the shape of an OData service…an OData service may expose a Service Metadata Document. OData metadata documents describe the Entity Data Model (EDM) for a given service, which is the underlying abstract data model used by OData services to formalize the description of the resources it exposes.
Personally, I find the best way to get my head around a new idea like this is to write some code, so I’ll do that next.
Consuming an OData Feed
Technically, you don't need an SDK to consume an OData feed, you just need some functionality for consuming XML (which PHP has). However, the OData SDK for PHP generates classes that make it easy to write clean, easy to read, maintainable code for consuming an OData feed. At the heart of the OData SDK is a command line tool (called PHPDataSvcUtil.php) that generates these classes. When you point PHPDataSvcUtil.php at a particular data service, it generates classes based on the formal description of the service (i.e. the metadata in the Service Metadata Document mentioned above). In the example above, a Customers class with Name and Phone properties would be generated, along with methods for getting related information and creating new customers. A similar class for Orders would be generated. The generated classes also contain logic for creating, updating, and deleting data, and saving those changes to the database. You can point PHPDataSvcUtil.php at any OData service (like the Netflix service, http://blogs.msdn.com/interoperability/archive/2010/03/16/odata-interoperability-with-net-java-php-iphone-and-more.aspx). Here, I'll use the public, read-only Northwind service for demonstration purposes.
Generating Proxy Classes with the OData SDK for PHP
First, I need to generate classes that allow me to write clean code to consume the service. To do that, I follow these steps:
1. Download the OData SDK for PHP here: http://odataphp.codeplex.com/.
2. Follow the directions in the Installation and Configuration section of the User_Guide.htm file (in the doc directory of the SDK download).
3. Don’t forget to re-start your Web server after making changes to your php.ini file.
4. (Optional) Add your PHP installation directory to your PATH environment variable. This will allow you to run PHP scripts from any directory.
Now I can generate a file (NorthwindProxies.php) that contains my classes with the PHPDataSvcUtil.php tool from the command line (without the line breaks):
Note: If you are behind a proxy, the /ph= and pp= options are required. See the user guide in the SDK download for more information.
This will generate the NorthwindProxies.php file in the C:\PHPLib\odataphp directory. It is probably worth spending some time looking at the classes that are generated, but I’ll leave the majoity of that to you and I'll move on to actually using the classes. The one class I will note, however, is the NorthwindEntities class. The NorthwindEntities class manages all the in-memory objects that represent tables, rows, relationships, etc. in the database and it contains the logic for saving changed (or new) data to the database. Understanding a bit about this class becomes more important when we start creating, modifying, and deleting data (which I'll save for another post).
Using the Generated Classes
Now for the easy part. The generated classes let me write code like this (which returns all customers):
$proxy = new NorthwindEntities();
$response = $proxy->Execute("Customers");
foreach($response->Result as $customer)
echo $customer->CompanyName.": ".$customer->ContactName."</br>";
The generated classes provide several query options (expand, filter, orderby, skip, top, inlinecount, count, skiptoken, select) that allow me to filter a query based on a CustomerID, expand it to include the customers orders, and display the number of orders:
$proxy = new NorthwindEntities();
$response = $proxy->Customers()
->Filter("CustomerID eq 'ALFKI'")
$customer = $response->Result;
There are many different ways to compose queries, and even complex queries start to become intuitive with a little study of the API. The user guide (User_Guide.htm) that comes with the SDK is full of examples for retrieving data, so I’ll leave further exploration to you for now.
I’ve really only scratched the surface of the API here. What I like about the generated classes is that they allow me to write clean, easy to understand, maintainable code. Hopefully this will be even more evident in a future post which will look at insert, update, and delete operations. Oh, and did I mention that my PHP code never has to have a database connection? That was all taken care of by the data service.
I would definitely be interested in understanding what areas of the API you would be most interested in learning more about…please let me know!