Archive for the ‘Frameworks’ Category
Building a RESTful Web API with PHP and Apify
Apify is a small and powerful open source library that delivers new levels of developer productivity by simplifying the creation of RESTful architectures. You can see it in action here. Web services are a great way to extend your web application, however, adding a web API to an existing web application can be a tedious and time-consuming task. Apify takes certain common patterns found in most web services and abstracts them so that you can quickly write web APIs without having to write too much code.
Apify exposes similar APIs as the Zend Framework, so if you are familiar with the Zend Framework, then you already know how to use Apify. Take a look at the UsersController class.
Building a RESTful Web API
In Apify, Controllers handle incoming HTTP requests, interact with the model to get data, and direct domain data to the response object for display. The full request object is injected via the action method and is primarily used to query for request parameters, whether they come from a GET or POST request, or from the URL.
Creating a RESTful Web API with Apify is easy. Each action results in a response, which holds the headers and document to be sent to the user’s browser. You are responsible for generating the response object inside the action method.
class UsersController extends Controller
{
public function indexAction($request)
{
// 200 OK
return new Response();
}
}
The response object describes the status code and any headers that are sent. The default response is always 200 OK, however, it is possible to overwrite the default status code and add additional headers:
class UsersController extends Controller
{
public function indexAction($request)
{
$response = new Response();
// 401 Unauthorized
$response->setCode(Response::UNAUTHORIZED);
// Cache-Control header
$response->setCacheHeader(3600);
// ETag header
$response->setEtagHeader(md5($request->getUrlPath()));
// X-RateLimit header
$limit = 300;
$remaining = 280;
$response->setRateLimitHeader($limit, $remaining);
// Raw header
$response->addHeader('Edge-control: no-store');
return $response;
}
}
Content Negotiation
Apify supports sending responses in HTML, XML, RSS and JSON. In addition, it supports JSONP, which is JSON wrapped in a custom JavaScript function call. There are 3 ways to specify the format you want:
- Appending a format extension to the end of the URL path (.html, .json, .rss or .xml)
- Specifying the response format in the query string. This means a format=xml or format=json parameter for XML or JSON, respectively, which will override the Accept header if there is one.
- Sending a standard Accept header in your request (text/html, application/xml or application/json).
The acceptContentTypes method indicates that the request only accepts certain content types:
class UsersController extends Controller
{
public function indexAction($request)
{
// only accept JSON and XML
$request->acceptContentTypes(array('json', 'xml'));
return new Response();
}
}
Apify will render the error message according to the format of the request.
class UsersController extends Controller
{
public function indexAction($request)
{
$request->acceptContentTypes(array('json', 'xml'));
$response = new Response();
if (! $request->hasParam('api_key')) {
throw new Exception('Missing parameter: api_key', Response::FORBIDDEN);
}
$response->api_key = $request->getParam('api_key');
return $response;
}
}
Request
GET /users.json
Response
Status: 403 Forbidden
Content-Type: application/json
{
"code": 403,
"error": {
"message": "Missing parameter: api_key",
"type": "Exception"
}
}
Resourceful Routes
Apify supports REST style URL mappings where you can map different HTTP methods, such as GET, POST, PUT and DELETE, to different actions in a controller. This basic REST design principle establishes a one-to-one mapping between create, read, update, and delete (CRUD) operations and HTTP methods:
| HTTP Method | URL Path | Action | Used for |
| GET | /users | index | display a list of all users |
| GET | /users/:id | show | display a specific user |
| POST | /users | create | create a new user |
| PUT | /users/:id | update | update a specific user |
| DELETE | /users/:id | destroy | delete a specific user |
If you wish to enable RESTful mappings, add the following line to the index.php file:
try {
$request = new Request();
$request->enableUrlRewriting();
$request->enableRestfulMapping();
$request->dispatch();
} catch (Exception $e) {
$request->catchException($e);
}
The RESTful UsersController for the above mapping will contain 5 actions as follows:
class UsersController extends Controller
{
public function indexAction($request) {}
public function showAction($request) {}
public function createAction($request) {}
public function updateAction($request) {}
public function destroyAction($request) {}
}
By convention, each action should map to a particular CRUD operation in the database.
Building a Web Application
Building a web application can be as simple as adding a few methods to your controller. The only difference is that each method returns a view object.
class PostsController extends Controller
{
/**
* route: /posts/:id
*
* @param $request Request
* @return View|null
*/
public function showAction($request)
{
$id = $request->getParam('id');
$post = $this->getModel('Post')->find($id);
if (! isset($post->id)) {
return $request->redirect('/page-not-found');
}
$view = $this->initView();
$view->post = $post;
$view->user = $request->getSession()->user
return $view;
}
/**
* route: /posts/create
*
* @param $request Request
* @return View|null
*/
public function createAction($request)
{
$view = $this->initView();
if ('POST' !== $request->getMethod()) {
return $view;
}
try {
$post = new Post(array(
'title' => $request->getPost('title'),
'text' => $request->getPost('text')
));
} catch (ValidationException $e) {
$view->error = $e->getMessage();
return $view;
}
$id = $this->getModel('Post')->save($post);
return $request->redirect('/posts/' . $id);
}
}
The validation is performed inside the Post entity class. An exception is thrown if any given value causes the validation to fail. This allows you to easily implement error handling for the code in your controller.
Entity Class
You can add validation to your entity class to ensure that the values sent by the user are correct before saving them to the database:
class Post extends Entity
{
protected $id;
protected $title;
protected $text;
// sanitize and validate title (optional)
public function setTitle($value)
{
$value = htmlspecialchars(trim($value), ENT_QUOTES);
if (empty($value) || strlen($value) < 3) {
throw new ValidationException('Invalid title');
}
$this->title = $title;
}
// sanitize text (optional)
public function setText($value)
{
$this->text = htmlspecialchars(strip_tags($value), ENT_QUOTES);
}
}
Routes
Apify provides a slimmed down version of the Zend Framework router:
$routes[] = new Route('/posts/:id',
array(
'controller' => 'posts',
'action' => 'show'
),
array(
'id' => '\d+'
)
);
$routes[] = new Route('/posts/create',
array(
'controller' => 'posts',
'action' => 'create'
)
);
HTTP Request
GET /posts/1
Incoming requests are dispatched to the controller “Posts” and action “show”.
Feedback
- If you encounter any problems, please use the issue tracker.
- For updates follow @fedecarg on Twitter.
- If you like Apify and use it in the wild, let me know.
Implementing Dynamic Finders and Parsing Method Expressions
Most ORMs support the concept of dynamic finders. A dynamic finder looks like a normal method invocation, but the method itself doesn’t exist, instead, it’s generated dynamically and processed via another method at runtime.
A good example of this is Ruby. When you invoke a method that doesn’t exist, it raises a NoMethodError exception, unless you define “method_missing”. Rails ActiveRecord::Base class implements some of its magic thanks to this method. For example, find_by_title(title) and find_by_title_and_date(title, date) are turned into:
find(:first, :conditions => ["title = ?", title]) find(:first, :conditions => ["title = ? AND date = ?", title, date])
What’s nice about Ruby is that the language allows you to define methods dynamically using the “define_method” method. That’s how Rails defines each dynamic finder in the class after it is first invoked, so that future attempts to use it do not run through the “method_missing” method.
Method Expressions
GORM, Grails ORM library, introduces the concept of dynamic method expressions. A method expression is made up of the prefix such as “findBy” followed by an expression that combines one or more properties. Grails takes advantage of Groovy features to provide dynamic methods:
findByTitle("Example")
findByTitleLike("Exa%")
Method expressions can also use a boolean operator to combine two criteria:
findAllByTitleLikeAndDateGreaterThan("Exampl%", '2010-03-23')
In this case we are using AND in the middle of the query to make sure both conditions are satisfied, but you could equally use OR:
findAllByTitleLikeOrDateGreaterThan("Exampl%", '2010-03-23')
Parsing Method Expressions
MethodExpressionParser is a PHP library for parsing method expressions. It’s designed to quickly and easily parse method expressions and construct conditions based on attribute names and arguments.
Description
[finderMethod]([attribute][expression][logicalOperator])?[attribute][expression]
Expressions
- LessThan: Less than the given value
- LessThanEquals: Less than or equal a give value
- GreaterThan: Greater than a given value
- GreaterThanEquals: Greater than or equal a given value
- Like: Equivalent to a SQL like expression
- NotEqual: Negates equality
- IsNotNull: Not a null value (doesn’t require an argument)
- IsNull: Is a null value (doesn’t require an argument)
Examples
findByTitleAndDate('Example', date('Y-m-d'));
SELECT * FROM book WHERE title = ? AND date = ?
findByTitleOrDate('Example', date('Y-m-d'))
SELECT * FROM book WHERE title = ? OR date = ?
findByPublisherOrTitleAndDate('Name', 'Example', date('Y-m-d'))
SELECT * FROM book WHERE publisher = ? OR (title = ? AND date = ?)
findByPublisherInAndTitle(array('Name1', 'Name2'), 'Example')
SELECT * FROM book WHERE publisher IN (?, ?) AND date = ?
findByTitleLikeAndDateNotNull('Examp%')
SELECT * FROM book WHERE title LIKE ? AND date NOT NULL
findByIdOrTitleAndDateNotNull(1, 'Example')
SELECT * FROM book WHERE (id = ?) OR (title = ? AND date NOT NULL)
Example 1:
findByTitleLikeAndDateNotNull('Examp%');
Outputs:
array
0 =>
array
0 =>
array
'attribute' => string 'title'
'expression' => string 'Like'
'format' => string '%s LIKE ?'
'placeholders' => int 1
'argument' => string 'Examp%'
1 =>
array
'attribute' => string 'date'
'expression' => string 'NotNull'
'format' => string '%s IS NOT NULL'
'placeholders' => int 0
'argument' => null
Example 2:
findByTitleAndPublisherNameOrTitleAndPublisherName('Title', 'a', 'Title', 'b');
Outputs:
array
0 =>
array
0 =>
array
'attribute' => string 'title'
'expression' => string 'Equals'
'format' => string '%s = ?'
'placeholders' => int 1
'argument' => string 'Title'
1 =>
array
'attribute' => string 'publisher_name'
'expression' => string 'Equals'
'format' => string '%s = ?'
'placeholders' => int 1
'argument' => string 'a'
1 =>
array
0 =>
array
'attribute' => string 'title'
'expression' => string 'Equals'
'format' => string '%s = ?'
'placeholders' => int 1
'argument' => string 'Title'
1 =>
array
'attribute' => string 'publisher_name'
'expression' => string 'Equals'
'format' => string '%s = ?'
'placeholders' => int 1
'argument' => string 'b'
See more examples: Project Wiki
Usage
class EntityRepository
{
private $methodExpressionParser;
// Return a single instance of MethodExpressionParser
public function getMethodExpressionParser() {
}
// Finder methods
public function findBy($conditions) {
var_dump($conditions);
}
public function findAllBy($conditions) {
var_dump($conditions);
}
// Invoke finder methods
public function __call($method, $args) {
if ('f' === $method{0}) {
try {
$result = $this->getMethodExpressionParser()->parse($method, $args);
$finderMethod = key($result);
$conditions = $result[$finderMethod];
} catch (MethodExpressionParserException $e) {
$message = sprintf('%s: %s()', $e->getMessage(), $method);
throw new EntityRepositoryException($message);
}
return $this->$finderMethod($conditions);
}
$message = 'Invalid method call: ' . __METHOD__;
throw new BadMethodCallException($message);
}
}
Performance
PHP doesn’t allow you to define methods dynamically, this means that every time you invoke a finder method the parser has to search, extract and map all the attribute names and expressions. To avoid introducing this performance overhead you can cache the attribute names. For example:
class EntityRepository
{
private $methodExpressionParser;
private $classMetadata;
// Return a single instance of MethodExpressionParser
public function getMethodExpressionParser() {
}
// Return a single instance of ClassMetadata
public function getClassMetadata() {
}
// Invoke finder methods
public function __call($method, $args) {
if ('f' === $method{0}) {
$parser = $this->getMethodExpressionParser();
$classMetadata = $this->getClassMetadata();
try {
$finderMethod = $parser->determineFinderMethod($method);
if ($classMetadata->hasMissingMethod($method)) {
$attributes = $classMetadata->getMethodAttributes($method);
$conditions = $parser->map($args, $attributes);
} else {
$expressions = substr($method, strlen($finderMethod));
$attributes = $this->extractAttributeNames($expressions);
$conditions = $parser->map($args, $attributes);
$classMetadata->setMethodAttributes($method, $attributes);
}
} catch (MethodExpressionParserException $e) {
$message = sprintf('%s: %s()', $e->getMessage(), $method);
throw new EntityRepositoryException($message);
}
return $this->$finderMethod($conditions);
}
$message = 'Invalid method call: ' . __METHOD__;
throw new BadMethodCallException($message);
}
}
The Expression objects are lazy-loaded, depending on the expressions found in the method name.
Extensibility
The MethodExpressionParser class was designed with extensibility in mind, allowing you to add new Expressions to the library.
abstract class Expression {
}
class EqualsExpression extends Expression {
}
Source Code
http://fedecarg.com/repositories/show/expressionparser
Check out the current development trunk with:
$ svn checkout http://svn.fedecarg.com/repo/Zf/Orm
Review: Zend Framework 1.8 Web Application Development
Zend Framework is one of the most popular and hottest open-source frameworks being used today. The number of books about Web development using Zend Framework has increased over the last couple of years.
Packt Publishing sent me a copy of the book Zend Framework 1.8 Web Application Development by Keith Pope to review. I found this book to be a good introduction to the topics that a Zend Framework developer will need to know when developing enterprise Web applications. The book is also aimed at advanced users, considering there were a couple of things that I learned about this framework from reading the book. The book not only shows you how the Zend Framework works, but also how to write an application for real-world usage. The book covers access control, performance optimization, testing, debugging and application design. The writing is clear, the code examples are good and Keith does an excellent job of walking you through the life-cycle of a request, explaining how things work and how you can extend the framework to fit your needs.
Here’s an example of the Storefront application:
http://code.google.com/p/zendframeworkstorefront/source/browse
Conclusion
The book is very well-written, nicely structured and full of highly practical advice. Overall, I’m happy to say that Zend Framework 1.8 Web Application Development fulfilled my expectations.
Other Reviews
“The content of the book is delivered in a fluent, very enthusiastic and ‘knowledge-pillowed’ writing tone. By implementing or working through the Storefront application seasoned web developers using older versions of the Framework will get a good blue sheet on new components like Zend_Application and it’s implication in the bootstrapping process; while new developers tending towards picking up the Zend Framework will get a current and well compiled guide, which might first start off with a steep learning-curve but will turn into profund knowledge once hanging in there.”
“The flow of this book is heavily inspired by the famous Ruby on Rails book, Agile Web Development with Rails, where the author invites you to join the process of building a demo application, which in both cases is a shopping cart system. Judging by the feedback of the Rails book, most people feel quite comfortable learning a framework this way, some don’t. I guess if you are not a fan of following a defined learning structure, this book probably isn’t for you.”
Testing Zend Framework Action Controllers With Mocks
In this post I’ll demonstrate a unit test technique for testing Zend Framework Action Controllers using Mock Objects. Unit testing controllers independently has a number of advantages:
- You can develop controllers test-first (TDD).
- It allows you to develop and test all of your controller code before developing any of the view scripts.
- It helps you quickly identify problems in the controller, rather than problems in one of the combination of Model, View and Controller.
The Action Controller I’m going to test has only one method, profileAction():
tests/application/controllers/UserController.php
class UserController extends Zend_Controller_Action
{
public function profileAction()
{
$this->view->userId = $this->_getParam('user_id');
return $this->render();
}
}
tests/application/ControllerTestCase.php
class ControllerTestCase extends Zend_Test_PHPUnit_ControllerTestCase
{
public $application;
public function setUp()
{
$this->application = new Zend_Application(
APPLICATION_ENV,
APPLICATION_PATH . '/config/application.ini'
);
$this->bootstrap = array($this, 'bootstrap');
parent::setUp();
}
public function tearDown()
{
Zend_Controller_Front::getInstance()->resetInstance();
$this->resetRequest();
$this->resetResponse();
$this->request->setPost(array());
$this->request->setQuery(array());
}
public function bootstrap()
{
$this->application->bootstrap();
}
}
tests/application/controllers/UserControllerTest.php
require_once TESTS_PATH . '/application/ControllerTestCase.php';
require_once APPLICATION_PATH . '/controllers/UserController.php';
class UserControllerTest extends ControllerTestCase
{
public function testStubRenderMethodCall()
{
$request = $this->getRequest()
->setRequestUri('/user/profile/1')
->setParams(array('user_id'=>1))
->setPathInfo(null);
$response = $this->getResponse();
$this->getFrontController()
->setRequest($request)
->setResponse($response)
->throwExceptions(true)
->returnResponse(false);
$controller = $this->getMock(
'UserController',
array('render'),
array($request, $response, $request->getParams())
);
$controller->expects($this->once())
->method('render')
->will($this->returnValue(true));
$this->assertTrue($controller->profileAction());
$this->assertTrue($controller->view->user_id == 1);
}
}
You can go further making both the tests and the implementation more sophisticated. The main point is that you can build and test a controller in a way that doesn’t require a view script to be written to do so.
Zend Framework Known Issues
By default Zend_Test_PHPUnit_ControllerTestCase sets the redirector exit value to false, leading to unexpected behavior when unit testing your code. For that reason, make sure you always add a return statement after calling a utility method:
class UserController extends Zend_Controller_Action
{
public function profileAction()
{
if (null == $this->_getParam('user_id', null) {
return $this->_redirect('/');
}
return $this->render();
}
}
If you want the Front Controller to throw exceptions, you have no other choice than to overwrite the dispatch method and pass a boolean TRUE to the throwExceptions() method:
class ControllerTestCase extends Zend_Test_PHPUnit_ControllerTestCase
{
...
public function dispatch($url = null)
{
// redirector should not exit
$redirector = Zend_Controller_Action_HelperBroker::getStaticHelper('redirector');
$redirector->setExit(false);
// json helper should not exit
$json = Zend_Controller_Action_HelperBroker::getStaticHelper('json');
$json->suppressExit = true;
$request = $this->getRequest();
if (null !== $url) {
$request->setRequestUri($url);
}
$request->setPathInfo(null);
$this->getFrontController()
->setRequest($request)
->setResponse($this->getResponse())
->throwExceptions(true)
->returnResponse(false);
$this->getFrontController()->dispatch();
}
...
}
The Dispatcher not only violates the DRY principle but also suffers from amnesia. The problem is that it doesn’t store the instance of the Action Controller, instead, it destroys it (Zend_Controller_Dispatcher_Standard Line 305). You can easily get around this issue by extending the standard dispatcher and overwriting the dispatch() method:
class Zf_Controller_Dispatcher_Standard extends Zend_Controller_Dispatcher_Standard
{
...
public function dispatch($url = null)
{
...
Zend_Registry::set('Zend_Controller_Action', $controller);
// Destroy the page controller instance and reflection objects
$controller = null;
}
This will allow you to access the view object after dispatching the request:
class ExampleControllerTest extends ControllerTestCase
{
public function testDefaultActionRendersViewObject()
{
$this->dispatch('/');
$controller = Zend_Registry::get('Zend_Controller_Action');
$this->assertEquals('ExampleController', get_class($controller));
$this->assertTrue(isset($controller->view));
}
Links
PHPUnit: Testing Zend Framework Controllers
PHPUnit: Mock Objects
Symfony 1.3 Web Application Development
Packt Publishing recently sent me a copy of the book “Symfony 1.3 Web Application Development” to review.
This book is not a reference guide, but an example driven tutorial that takes you through the process of building Model-View-Controller-based web applications. You will learn how to create and develop a simple online store application. It also covers best practices for better and quicker application development.
The book is based on the latest version of the Symfony Framework, and does a great job telling you what you get out of the box and how it works, which is perfect for hitting the ground running. During the development you are introduced to the concepts and features of the MVC framework. However, for those who want to know more about the framework, the book doesn’t explain how things work under the covers. This book is more for beginners who want to get started with Symfony 1.3.
One thing I didn’t like about this book is that it uses Propel instead of Doctrine as the default ORM framework. Apart from that, it does a great job explaining and demonstrating with practical examples how to build a Web application from scratch.
Overall, and considering that some of the topics in this book have already been covered in Practical Symfony 1.3, I rate this book 4 out of 5.
Database Replication Adapter for Zend Framework Applications
Last updated: 21 Feb, 2010
Database replication is an option that allows the content of one database to be replicated to another database or databases, providing a mechanism to scale out the database. Scaling out the database allows more activities to be processed and more users to access the database by running multiple copies of the databases on different machines.
The problem with monolithic database designs is that they don’t establish an infrastructure that allows for rapid changes in business requirements. Here is where database replication comes into play. Replication can be used effectively for many different purposes, such as separating data entry and reporting, distributing load across servers, providing high availability, etc.
Zf_Orm_DataSource is a Zend Framework Replication Adapter class flexible enough to support the most commonly used replication scenarios:
Single-Master Replication
In the simplest replication scenario, the master copy of directory data is held in a single read-write replica on one server called the supplier server. The supplier server also maintains changelog for this replica. On another server, called the consumer server, there can be multiple read-only replicas.
Configuration array:
$config = array(
'adapter' => 'Pdo_Mysql',
'driver_options' => array(PDO::ATTR_TIMEOUT=>5),
'username' => 'root',
'password' => 'root',
'dbname' => 'test',
'master_servers' => 1,
'servers' => array(
array('host' => 'db.master-1.com'),
array('host' => 'db.slave-1.com'),
array('host' => 'db.slave-2.com')
)
);
// or ...
$config = array(
'adapter' => 'Pdo_Mysql',
'driver_options' => array(PDO::ATTR_TIMEOUT=>5),
'dbname' => 'test',
'master_servers' => 1,
'servers' => array(
array('host' => 'db.master-1.com', 'username' => 'user1', 'password'=>'pass1'),
array('host' => 'db.slave-1.com', 'username' => 'user2', 'password' => 'pass2'),
array('host' => 'db.slave-2.com', 'username' => 'user3', 'password' => 'pass3')
)
);
In the setup above, all writes will go to the master connection and all reads will be randomly distributed across the available slaves.
Multi-Master Replication
This type of configuration can work with any number of consumer servers. Each consumer server holds a read-only replica. The consumers can receive updates from all the suppliers. The consumers also have referrals defined for all the suppliers to forward any update requests that the consumers receive.
$config = array(
'adapter' => 'Pdo_Mysql',
'driver_options' => array(PDO::ATTR_TIMEOUT=>5),
'username' => 'root',
'password' => 'root',
'dbname' => 'test',
'master_servers' => 2,
'master_read' => true,
'servers' => array(
array('host' => 'db.master-1.com'),
array('host' => 'db.master-2.com')
)
);
Using a distributed memory caching system
Database connections are expensive and it’s very inefficient for an application to try to connect to a server that is down or not responding. A distributed memory caching system can help alleviate this problem by keeping a list of all the failed connections in memory, sharing that information across multiple servers and allowing the application to access it before attempting to open a connection.
To enable this option, you have to pass an instance of the Memcached adapter class:
class Bootstrap extends Zend_Application_Bootstrap_Base
{
protected function _initCache()
{
...
}
protected function _initDatabase()
{
$config = include APPLICATION_PATH . '/config/database.php';
$cache = $this->getResource('cache');
$dataSource = new Zf_Orm_DataSource($config, $cache, 'cache_tag');
Zend_Registry::set('dataSource', $dataSource);
}
}
And here is a short example of how the Replication Adapter might be used in a ZF application:
class TestDao
{
public function fetchAll()
{
$db = Zend_Registry::get('dataSource')->getConnection('slave');
$query = $db->select()->from('test');
return $db->fetchAll($query);
}
public function insert($data)
{
$db = Zend_Registry::get('dataSource')->getConnection('master');
$db->insert('test', $data);
return $db->lastInsertId();
}
}
Source Code:
https://github.com/fedecarg/zf-replication-adapter
Adding theme support to your Zend Framework application
This is a brief explanation on how to add theme support to your Zend Framework application and how to ensure those themes are self-contained, easy to distribute and install.
Themes are very powerful and extremely easy to develop. They allow you to quickly switch between layouts and change the look and feel of your application. You can use themes to show, for example, a mobile friendly version of your site.
Making a Zend Framework application theme-able is a three-step process.
First, modify your directory structure:
application/
controllers/
library/
public/
themes/
default/
css/
images/
templates/
custom/
css/
images/
templates/
Then, edit your Bootstrap class:
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initView()
{
$theme = 'default';
if (isset($this->config->app->theme)) {
$theme = $this->config->app->theme;
}
$path = PUBLIC_PATH.'/themes/'.$theme.'/templates';
$layout = Zend_Layout::startMvc()
->setLayout('layout')
->setLayoutPath($path)
->setContentKey('content');
$view = new Zend_View();
$view->setBasePath($path);
$view->setScriptPath($path);
return $view;
}
}
And finally, copy your view scripts and layouts to the templates directory:
application/
library/
public/
themes/
full-site/
css/
images/
templates/
error/
index/
partials/
layout.phtml
mobile-site/
css/
images/
templates/
Voila, mission accomplished.
Zend Framework DAL: DAOs and DataMappers
A Data Access Layer (DAL) is the layer of your application that provides simplified access to data stored in persistent storage of some kind. For example, the DAL might return a reference to an object complete with its attributes instead of a row of fields from a database table.
A Data Access Objects (DAO) is used to abstract and encapsulate all access to the data source. The DAO manages the connection with the data source to obtain and store data. Also, it implements the access mechanism required to work with the data source. The data source could be a persistent store like a database, a file or a Web service.
And finally, the DataMapper pattern is used to move data between the object and a database while keeping them independent of each other. The DataMapper main responsibility is to transfer data between the two and also to isolate them from each other.
Here’s an example of the DataMapper pattern:
Database Table Structure
CREATE TABLE `user` ( `id` int(11) NOT NULL auto_increment, `first_name` varchar(100) NOT NULL, `last_name` varchar(100) NOT NULL, PRIMARY KEY (`id`) )
The User DAO
The DAO pattern provides a simple, consistent API for data access that does not require knowledge of an ORM interface. DAO does not just apply to simple mappings of one object to one relational table, but also allows complex queries to be performed and allows for stored procedures and database views to be mapped into data structures.
A typical DAO design pattern interface is shown below:
interface UserDao
{
public function fetchRow($id);
public function fetchAll();
public function insert($data);
public function update($id, $data);
public function delete($id);
}
class UserDatabaseDao implements UserDao
{
public function fetchRow($id)
{
$dataSource = Zf_Orm_Manager::getInstance()->getDataSource();
$db = $dataSource->getConnection('slave');
$query = $db->select()->from('user')->where('id = ?', $id);
return $db->fetchRow($query);
}
public function fetchAll()
{
$dataSource = Zf_Orm_Manager::getInstance()->getDataSource();
$db = $dataSource->getConnection('slave');
$query = $db->select()->from('user');
return $db->fetchAll($query);
}
public function insert($data)
{
$dataSource = Zf_Orm_Manager::getInstance()->getDataSource();
$db = $dataSource->getConnection('master');
$db->insert('user', $data);
return $db->lastInsertId();
}
public function update($id, $data)
{
$dataSource = Zf_Orm_Manager::getInstance()->getDataSource();
$db = $dataSource->getConnection('master');
$condition = $db->quoteInto('id = ?', $id);
return $db->update('user', $data, $condition);
}
public function delete($id)
{
$dataSource = Zf_Orm_Manager::getInstance()->getDataSource();
$db = $dataSource->getConnection('master');
$condition = $db->quoteInto('id = ?', $id);
return $db->delete('user', $condition);
}
}
The User Entity
An Entity is anything that has continuity through a life cycle and distinctions independent of attributes that are important to the application’s user.
class User
{
protected $id;
protected $firstName;
protected $lastName;
public function setId() {
}
public function getId() {
}
public function setFirstName() {
}
public function getFirstName() {
}
public function setLastName() {
}
public function getLastName() {
}
public function toArray() {
}
}
The User DataMapper
class UserDataMapper extends Zf_Orm_DataMapper
{
public function __construct()
{
$this->setMap(
array(
'id' => 'id',
'first_name' => 'firstName',
'last_name' => 'lastName'
)
);
}
}
Source Code: http://fedecarg.com/repositories/show/datamapper
The User Repository
Repositories play an important part in DDD, they speak the language of the domain and act as mediators between the domain and data mapping layers. They provide a common language to all team members by translating technical terminology into business terminology.
Lets create a UserRepository class to isolate the domain object from details of the DAO:
class UserRepository
{
private $databaseDao;
public funciton setDatabaseDao(UserDao $dao)
{
$this->databaseDao = $dao;
}
public function getDatabaseDao()
{
if (null === $this->databaseDao) {
$this->setDatabaseDao(new UserDatabaseDao());
}
return $this->databaseDao;
}
public function find($id)
{
$row = $this->getDatabaseDao()->fetchRow($id);
$mapper = new UserDataMapper();
$user = $mapper->assign(new User(), $row);
return $user;
}
}
The User Controller
class UserController extends Zend_Controller_Action
{
public function viewAction()
{
$userRepository = new UserRepository();
$user = $userRepository->find($this->_getParam('id'));
if ($user instanceof User) {
$id = $user->getId();
$firstName = $user->getFirstName();
$lastName = $user->getLastName();
// get an array of key value pairs
$row = $user->toArray();
}
}
}
That’s all, I hope you’ve found this post useful.
Increase speed and reduce bandwidth usage
Apache’s mod_deflate module provides the DEFLATE output filter that allows output from your server to be compressed before being sent to the client over the network.
There are two ways of enabling gzip compression:
- Using Apache’s mod_deflate
- Using output buffering
Encoding the output and setting the appropriate headers manually makes the code more portable. Keep in mind that there are hundreds of Linux distributions, each slightly different to significantly different. To allow portability the application should not make assumptions about the OS or config involved.
Using Apache
1. Enable mod_deflate
Debian/Ubuntu:
$ a2enmod deflate $ /etc/init.d/apache2 force-reload
2. Configure mode_deflate
$ nano /etc/apache2/mods-enabled/deflate.conf # # mod_deflate configuration # <IfModule mod_deflate.c> AddOutputFilterByType DEFLATE text/plain AddOutputFilterByType DEFLATE text/html AddOutputFilterByType DEFLATE text/xml AddOutputFilterByType DEFLATE text/css AddOutputFilterByType DEFLATE application/xml AddOutputFilterByType DEFLATE application/xhtml+xml AddOutputFilterByType DEFLATE application/rss+xml AddOutputFilterByType DEFLATE application/javascript AddOutputFilterByType DEFLATE application/x-javascript DeflateCompressionLevel 9 BrowserMatch ^Mozilla/4 gzip-only-text/html BrowserMatch ^Mozilla/4\.0[678] no-gzip BrowserMatch \bMSIE !no-gzip !gzip-only-text/html DeflateFilterNote Input instream DeflateFilterNote Output outstream DeflateFilterNote Ratio ratio </IfModule>
Using output buffering
Create a gzip compressed string in your bootstrap file:
try {
$frontController = Zend_Controller_Front::getInstance();
if (@strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false) {
ob_start();
$frontController->dispatch();
$output = gzencode(ob_get_contents(), 9);
ob_end_clean();
header('Content-Encoding: gzip');
echo $output;
} else {
$frontController->dispatch();
}
} catch (Exeption $e) {
if (Zend_Registry::isRegistered('Zend_Log')) {
Zend_Registry::get('Zend_Log')->err($e->getMessage());
}
$message = $e->getMessage() . "\n\n" . $e->getTraceAsString();
/* trigger event */
}
Reference
Is this the best open source CMS ever created?
Meet TYPOlight, a powerful Web content management system that specializes in accessibility (back end and front end) and uses XHTML and CSS to generate W3C/WAI compliant pages.
Accessibility
A growing number of countries around the world have introduced legislation which either directly addresses the need for websites to be accessible to people with disabilities, or which addresses the more general requirement for people with disabilities not to be discriminated against. TYPOlight does not treat accessibility as just an additional feature and is thoroughly accessible.
Web 2.0
PHP 5 and Ajax are modern “Web 2.0″ technologies that you can find in a lot of contemporary applications. TYPOlight has a solid codebase built on the new object-oriented programming features of PHP 5 and can therefore be considered a future-proof software. To ensure back end accessibility, every Ajax feature includes a graceful fallback in case JavaScript is disabled.
Page features
- Different page types
- Multiple websites in one tree
- Manual or timed publication
- Hidden pages
- Password protect pages
Editing features
- Clipboard feature
- Edit multiple records
- Built-in rich text editor (TinyMCE)
- Different content elements and modules
- Multilingual spellchecker
- Insert tags (similar to server side includes)
- Manual or timed publication
File manager
- Multiple file uploads
- Image thumbnails and file preview
- Edit uploaded files with the source editor
- File operation permissions
- Copy, move, rename files or folders
- Delete folders recursively
Form generator
- Automatic input validation
- Store uploaded files on the server
- Send form data via e-mail
- Send uploaded files as e-mail attachment
Search engine
- Automatic page indexing
- Search indexing on protected pages
- Phrase search, wildcard search, AND/OR search
- Search result caching and pagination
Full feature list
- Intuitive user interface
- Accessible XHTML strict output
- Meets W3C/WAI requirements
- Web 2.0 support (mootools-based)
- Live update service
- Accessible administration area
- Multiple back end languages and themes
- Generates search engine friendly URLs
- Multi-language support
- Powerful permission system
- Versioning and undo management
- Advanced search and sorting options
- Front end output 100% template based
- Automatic e-mail encryption (spam protection)
- Supports SMTP in addition to PHP’s mail function
- Supports multiple websites in one tree
- Supports GZip compression
- Print articles as PDF
System features
- Open Source (LGPL)
- Web-based administration
- Platform independent
- Over 150 third party extensions
- Multilingual documentation