Types of requests
PIE has sensible conventions for handling all these requests, and more. You can, of course, override these conventions (if you need to), but it's much more convenient to just use what PIE provides.
Slots and Layouts
Typically, PIE apps generate output in a two-step process:
- First, they fill some slots (such as dashboard, content, title)
- Then, they render a layout, where the contents of those slots are variables (such as $dashboard, $content and $title)
Internally, slots are filled by a call to Pie_Response::fillSlot($slot_name). If the slot has already been filled — e.g. with a call to Pie_Response::setSlot($slot_name, $content) — then fillSlot just returns its contents. Otherwise, it invokes the slot's handler, to get the slot's content.
This is done as follows: let's say the URI "foo/bar" was requested. To fill a slot named "content", you would implement a handler for the event "foo/bar/response/content". Its return value is used to fill the slot. That is what we did when were writing our first app.
Slots are a really useful feature in PIE. They let your app fill multiple content areas in a single request. This will become very useful when we talk about AJAX. You can define additional, custom slots with arbitrary names. For example, an AJAX call might want the server to fill and return the "votes" slot, or "updated_profile_html" slot. These custom slots can return strings, arrays, or anything else.
As you know, PIE splits the various stages of handling a request into separate events. For example, "pie/post" would be used for making changes to the server state. All the output generation should happen in the "pie/response" handler, and this is when you fill the slots named in Pie_Request::slotNames().
PIE supports a Model-View-Controller (MVC) architecture. As a result, you will typically keep separate files (in the APP_DIR/views folder) which are easy for web designers to work with.
By convention, if a view is meant to be rendered in a certain slot, it should be placed in a folder named after that slot. Thus, for the content slot, the views are in the APP_DIR/views/$app/content/ folder.
The layout is actually just a view, and the default "pie/response" handler selects the layout based on Pie_Request::isAjax() and Pie_Request::accepts($mime_type). For example, if an HTML document is requested by a browser, the APP_DIR/views/$app/layout/html.php layout is used.
The Pie_Html class
When rendering output, you would do well to make use of Pie_Html functions as much as possible. They do a lot of things on behalf of your app, that not only let you interoperate with the rest of PIE (such as doing automatic un-routing), but also help you write standards-compliant code more easily.
Here is a partial list of useful functions from Pie_Html. In the list below, note that $href, $action, etc. don't have to be URLs, but can also be URI strings, such as "youMixer/welcome".
- Pie_Html::text($str) is used to escape literal text in HTML
- Pie_Html::a($href, $attributes = array()) outputs a link
- Pie_Html::form($action, $method = 'post', $attributes = array()) outputs a form
- Pie_Html::img($src, $alt='image', $attributes = array()) outputs an image
- Pie_Html::div($id, $class, $attributes = array()) outputs a div
- Pie_Html::tag($tagname, $attributes = array()) outputs an arbitrary HTML tag
Most of the tags above output an opening tag, which you would then follow by some HTML and then a closing tag. For example:
<?php echo Pie_Html::a('youMixer/welcome') ?>cool</a>However, you can usually pass the content of the tag as its final parameter
<?php echo Pie_Html::a('youMixer/welcome', null, 'cool') ?>You can also output a self-closing tag like this:
<?php echo Pie_Html::a('script', null, true)
PIE has support for dynamic theming. For example, your app can give each user the ability to customize the appearance of the site. The way this is achieved is through a "theme cascade".
To add a theme folder to the cascade at runtime, you call Pie_Html::pushThemeUrl($theme_url). From then on, the function Pie_Html::themedUrl($src) checks this folder before trying previous folders. If the specified $src exists relative to a particular theme folder, the resulting URL is used for the src attribute.
Let's illustrate with some examples:
Pie_Html::pushThemeUrl(Pie_Request::baseUrl()); Pie_Html::pushThemeUrl(Pie_Request::baseUrl().'/themes/red'); // red theme Pie_Html::img('img/foo.jpg'); // if themes/red/img/foo.jpg exists, we use that // otherwise, img/foo.jpg Pie_Response::addScript('js/foo.js'); // if themes/red/js/foo.js exists, use that // otherwise, use js/foo/js
Scripts and stylesheets
Pie_Response::addStylesheet('css/foo.js'); Pie_Response::addScript('js/foo.js'); Pie_Response::addScript('http://yahoo.com/yui/CoolBar.js');PIE translates strings like "js/foo.js" into absolute urls using Pie_Html::getThemedUrl($local).
Pie_Response::setStyle('p, div.foo', array('background' => 'red')); Pie_Response::addScriptLine("document.location = $new_location_json;");
When to print output
The Pie_Dispatcher::dispatch() function, which dispatches requests, fires the following events in order:
- pie/validate - validate the input for correctness
- pie/objects - time to create any objects from the input
- pie/reroute - may change URI, e.g. to login page
- pie/post - called if the $_POST array is not empty; you can change server state here
- pie/analytics - this is a good time to record something in the logs
- pie/response - finally, the event handler for this should do
Out of all of these events, the only one where you should print output is pie/response (just use echo or print). That is what gets returned to the browser. If an uncaught exception ever escapes the pie/response handler, you will see an exception dump on the front page, that you need to fix. If an exception is thrown in some other event (such as pie/post), PIE attempts to render a regular page (by calling pie/response anyway) but with the errors reported in the errors slot.
To redirect to another URL or URI, call Pie_Response::redirect($uri). For example, you can do
Pie_Response::redirect('youMixer/welcome')You should typically do this before the "pie/response" event -- which is expected to start printing output -- since headers can't be set after that.
A typical time to redirect is when a POST request succeeded. (That way, refreshing the page will not cause the browser to attempt the POST request again.) However, PIE does this automatically through a convention: the $_REQUEST['_']['onSuccess'] field is automatically checked by the default "pie/response" handler -- and if it not empty, PIE redirects there (if the Pie_Response::getErrors() array was empty). This is explained further in the "Forms and Tools" article.
By default, PIE buffers your output. You can buffer handler to gzip the output by calling Pie_Response::isBuffered('gzip'). Or you can turn it off altogether calling Pie_Response::isBuffered(false). Turning output buffering off disables the use of layouts, but may be useful for pages that are designed to load slowly over time. For example, "Comet" is a technique that uses long-polling for pushing information over HTTP, and can benefit from this.
Complete reference to PHP ON PIE
TODO: include an iframe with PHPDoc-generated reference