This document discusses building web services using the Zend Framework. It introduces key components for building SOAP, XML-RPC, and RESTful services, including the Zend_Soap, Zend_XmlRpc, and Zend_Rest libraries. It provides an example of building a timesheet API and exposing it through different protocols, demonstrating how to define methods, handle requests and responses, and implement clients. Documentation of the API using docblocks is also covered.
2. WARNING
Used Environment
PHP 5.3.2
Zend Framework 1.10.4
MySQL 5.1.32
Examples might not work on another environment
3. Michelangelo van Dam
⢠Independent Consultant
⢠Zend CertiďŹed Engineer (ZCE)
- PHP 4 & PHP 5
- Zend Framework
⢠Co-Founder of PHPBenelux
⢠Shepherd of âelephpantâ herds
4. What is Zend Framework
A loosly-coupled framework
with a ďŹexible architecture that
lets you easily build modern
web applications and web
services
Matthew Weier OâPhinney - Chief Architect Zend Framework
5. Why a service ?
â˘- opening up existing API
internal applications
- external applications
- additional functionality
⢠support to more devices
⢠portability and ďŹexibility
7. Example service
â˘- Time registration
list time sheets
- add a new task
- edit an existing task
- delete an existing task
8. MVC approach
â˘- time module
controllers (for displaying listing and forms)
- actions (for listing, adding, editing and deleting)
- models (for access to database)
- forms (for ďŹltering, validation and rendering forms)
9. Common behavior
⢠all âlogicâ is put in the controller
⢠actions = API interface
â˘- downside
not ďŹexible towards other clients
- difďŹcult to provide maintenance
- not reusable design
10. Time Module
Time_IndexController
indexAction editAction
- lists registered tasks - displays a form
- links to the form - for adding task
- for adding task - for editing task
- for editing task
- for deleting task
registerAction deleteAction
- processes form data - deletes task
- validating data
- storing data in db
11. API Design
â˘- moving logic
out the controller
- in an API class
⢠structures the application
⢠is better testable
⢠is better maintainable
12. Time API
My_Api_Timesheet
listTasks registerNewTask
- lists registered tasks - for adding task
editExistingTask deleteExistingTask
- for modifying task - deletes task
19. listTasks
/**
* List all tasks for a given user
*
* @param int $user
* @param int $limit
* @return array
*/
public function listTasks($user, $limit = null)
{
$array = array ();
$timesheet = new Time_Model_Timesheet();
if (null !== ($result = $timesheet->fetchAll(array (
'user_id = ?' => $user,
), array ('date DESC', 'start_time ASC'), $limit))) {
foreach ($result as $entry) {
$array[] = $entry->toArray();
}
}
return $array;
}
20. registerNewTask
/**
* Register a new task
*
* @param int $user The ID of the user
* @param int $customer The ID of the customer
* @param int $task The ID of the task
* @param string $date A date formatted as YYYY-mm-dd
* @param string $start The starting time as HH:mm:ss
* @param string $end The ending time as HH:mm:ss
* @param string $description A short description
* @return bool TRUE if registration succeeded
* @throws My_Api_Timesheet_Exception
*/
21. registerNewTask
public function registerNewTask(
$user, $customer, $task, $date, $start, $end, $description)
{
$timesheet = new Time_Model_Timesheet();
$timesheet->setUserId($user)
->setCustomerId($customer)
->setTaskId($task)
->setDate($date)
->setStartTime($start)
->setEndTime($end)
->setDescription($description);
$validator = $this->_validate($timesheet);
if (false === $validator) {
require_once 'My/Api/Timesheet/Exception.php';
throw new My_Api_Timesheet_Exception('Invalid data provided');
}
$timesheet->save();
return true;
}
22. editExistingTask
/**
* Modify an existing task
*
* @param int $id The ID of an existing Task
* @param int $user The ID of the user
* @param int $customer The ID of the customer
* @param int $task The ID of the task
* @param string $date A date formatted as YYYY-mm-dd
* @param string $start The starting time as HH:mm:ss
* @param string $end The ending time as HH:mm:ss
* @param string $description A short description
* @return bool TRUE if registration succeeded
* @throws My_Api_Timesheet_Exception
*/
23. editExistingTask
public function editExistingTask(
$id, $user, $customer, $task, $date, $start, $end, $description)
{
$timesheet = new Time_Model_Timesheet();
$timesheet->setId($id)
->setUserId($user)
->setCustomerId($customer)
->setTaskId($task)
->setDate($date)
->setStartTime($start)
->setEndTime($end)
->setDescription($description);
$validator = $this->_validate($timesheet);
if (false === $validator) {
require_once 'My/Api/Timesheet/Exception.php';
throw new My_Api_Timesheet_Exception('Invalid data provided');
}
$timesheet->save();
return true;
}
24. deleteExistingTask
/**
* Removes an existing task
*
* @param int $id The ID of an existing Task
* @param int $user The ID of the user
* @return bool TRUE if registration succeeded
*/
public function deleteExistingTask($id, $user)
{
$timesheet = new Time_Model_Timesheet();
$timesheet->setId($id)
->setUserId($user);
$validator = $this->_validate($timesheet);
if (false === $validator) {
require_once 'My/Api/Timesheet/Exception.php';
throw new My_Api_Timesheet_Exception('Invalid data provided');
}
$timesheet->delete(array (
'id = ?' => $timesheet->getId(),
'user_id = ?' => $timesheet->getUserId(),
));
return true;
}
25. _validate
/**
* Private validation method
*
* @param Time_Model_Timesheet $timesheet
* @return bool TRUE if validated, FALSE if invalid
*/
private function _validate(Time_Model_Timesheet $timesheet)
{
$result = true;
$validator = new Time_Form_Register();
$customer = new Time_Model_Customer();
$task = new Time_Model_Task();
$validator->getElement('customer_id')->setMultiOptions($customer->toSelect());
$validator->getElement('task_id')->setMultiOptions($task->toSelect());
if (!$validator->isValid($timesheet->toArray())) {
$result = false;
}
return $result;
}
37. REST Server
<?php
class Time_RestController extends Zend_Controller_Action
{
public function indexAction()
{
$this->_helper->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
$server = new Zend_Rest_Server();
$server->setClass('My_Api_Timesheet');
$server->handle();
}
}
38. REST Client
<?php
class Time_RestClientController extends Zend_Controller_Action
{
protected $_client;
public function init()
{
$this->_helper->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
$this->_client = new Zend_Rest_Client('http://www.demo.local/Time/rest');
}
public function indexAction()
{
$this->_client->listTasks(2);
Zend_Debug::dump($this->_client->get());
}
}
41. Recommended reading
Web Services Essentials
Ethan Cerami
OâReilly
Programming Web Services with XML-RPC
Simon St. Laurent, Joe Johnston, Edd Dumbill,
Dave Winer
OâReilly
42. Recommended reading
RESTful Web Services
Leonard Richardson, Sam Ruby
OâReilly
Programming Web Services with XML-RPC
James Snell, Doug Tidwell, Pavel Kulchenko
OâReilly
44. Feedback is important
â˘- ďŹnd a bug ?
test it
- report it
- send a patch/ďŹx
⢠need a non-existing component
- submit proposal
⢠like Zend Framework
- blog about it
- talk about it
⢠translations
45. ZF Bug hunt days
Zend Framework Bughuntdays
every 3rd Thursday and Friday of the month
http://framework.zend.com/issues
IRC (irc.freenode.net) #zftalk.dev
prizes:
recognition and appreciation of the community
Free subscription for 1 year on php|Architect magazine
Zend Framework t-shirt