![]() |
|
Snippets |
|
For the record, partials are wonderful. Self-sustained pieces of gui code, with access to all the symfony templating tools you need.
Sometimes however, even partials tend to be overkill. And sometimes, too, you have an annoying little piece of gui code that ought to go into your main template (indexSuccess.php or what have you), but you can't bear to put it there, because it would disturb the flow of an otherwise beautifully crafted template.
The truth is, every reuseable piece of gui code that isn't a full blown template really fits the partial paradigm, but sometimes it's just too much overhead, too much trouble, or just not necessary. Consider the following:
<table> <?php foreach ($vars as $key => $value): ?> <tr> <th><?php include(fragment_name('label')) ?></th> <td><?php include(fragment_name('input')) ?></td> </tr> <?php endforeach; ?> </table>
<table> <?php foreach ($vars as $key => $value): ?> <tr> <th><?php include(fragment_path('somemodule/label')) ?></th> <td><?php include(fragment_path('anothermodule/input')) ?></td> </tr> <?php endforeach; ?> </table>
The _label.php file could contain a few if statements... enough to generate the label for our input. It would look quite a bit uglier to place the code right in the th tag, but that's what we're really going for. _label.php would immediately have access to $key and $value, and any other variables already in scope, including typical templating variables provided by symfony. Additionally, there's no overhead to deal with, which helps if that foreach loop will execute 200 times (well, maybe that's a stretch). Finally, it just looks symfony-esque (at least to me)!
<?php /** * Get the path to a fragment. * The syntax is similar to that of include_partial. * To be used in conjunction with php's include function. * Variables in scope in the caller will be available in the execution of the fragment. * * <strong>Example</strong> * <code> * include(fragment_path('mypartial')) * </code> * * <strong>Example</strong> * <code> * include(fragment_path('mymodule/mypartial')) * </code> * * @param string fragment name * @return string path to the fragment * @see get_fragment, include_fragment, include_partial */ function fragment_path($templateName) { // partial is in another module? if (false !== ($sep = strpos($templateName, '/'))) { $moduleName = substr($templateName, 0, $sep); $templateName = substr($templateName, $sep + 1); } else { $moduleName = sfContext::getInstance()->getActionStack()->getLastEntry()->getModuleName(); } $fragmentName = fragment_name($templateName); // test for global fragment if ($moduleName === 'global') { $path = sfConfig::get('sf_app_template_dir'); } // if not global, or the global fragment doesn't exist if ($moduleName !== 'global' || !is_readable($path.'/'.$fragmentName)) { // get the full path to the file $path = sfConfig::get('sf_app_module_dir').'/'.$moduleName.'/'.sfConfig::get('sf_app_module_template_dir_name'); } // return the full path to the partial return $path.'/'.$fragmentName; } /** * Translates a nice fragment name into the filename adhering to the naming convention for partials. * * <strong>Example</strong> * <code> * include(fragment_name('mypartial')) * </code> * * @param string fragment name * @return string fragment filename * @see fragment_path */ function fragment_name($templateName) { return '_'.$templateName.'.php'; }
Remember: you can place this in myproject/apps/myapp/lib/helper/PartialPlusHelper.php
Then you can use it with <?php use_helper('PartialPlus') ?>
Some applications want to offer various look-and-feels for each page. The online documentation already explains how to change the CSS from the server, and a bit of JavaScript will allow you to do it from the client side.
But sometimes this is not enough, and the need arises to have several sets of templates for each action. Something like:
mymodule/
templates/
indexSuccess.php # Template for index action, default theme
theme1/
indexSuccess.php # Template for index action, theme1
theme2/
indexSuccess.php # Template for index action, theme2
Thanks to the MVC architecture, this is very easy to do.
We'll suppose that theme is an attribute of the sfUser object. It contains a simple string. The way to determine the preferred theme for a given user will be left to your sagacity, as well as the way to extend the sfUser class to deliver this attribute. The interesting thing is to modify the view class to have it use the theme when the time comes to look for templates and layouts.
Create a myView.class.php file in the lib/ directory of your application, and write in:
<?php class myView extends sfPHPView { public function configure() { parent::configure(); // Grab the theme from the user (of from anywhere else) $theme = $this->getContext()->getUser()->getTheme(); // If there is a theme and if the theme feature is enabled if($theme && sfConfig::get('app_theme')) { // Look for templates in a $theme/ subdirectory of the usual template location if (is_readable($this->getDirectory().'/'.$theme.'/'.$this->getTemplate())) { $this->setDirectory($this->getDirectory().'/'.$theme); } // Look for a layout in a $theme/ subdirectory of the usual layout location if (is_readable($this->getDecoratorDirectory().'/'.$theme.'/'.$this->getDecoratorTemplate())) { $this->setDecoratorDirectory($this->getDecoratorDirectory().'/'.$theme); } } } }
To force symfony to use this view class instead of the default sfPHPView, create a module.yml in the application's config/ directory, and write in it:
all: view_class: my
The new view will look for themes only if you enabled the feature in your app.yml:
all: theme: on
Clear the cache, and the theme feature is ready.
It works like this: when a user has a defined theme, symfony will look for templates and layouts in a subdirectory named by this theme. For instance, if a user has a foobar theme, and that it requests the mymodule/myaction action, symfony will look for the template in:
apps/myapp/modules/mymodule/templates/foobar/myActionSuccess.php
and for the layout in:
apps/myapp/templates/foobar/layout.php
The beauty of this modification is that if the themed template doesn't exist, symfony will use the normal template as a fallback.
It must be pretty easy to package this into a plugin, so if you feel like doing it...
A common modification to a CRUD module is filtering records on the list page. There are probably several nice ways to do this, including drop-downs, forms, etc. depending on the situation. While some of these approaches are nice, they are often not minimal... We like minimal!
Say you have a book table, with a related author and genre. You want to filter the list of books by those related tables with links, paginate your list, and make it sortable via links in the header of the table.
To do this traditionally, you'll need lots of nasty little if statements in your template code, to check for all of these parameters (author_id, genre_id, title, num_pages, page, etc.) and combine them all together into a meaningful uri for your link_to function (something like "book/list?author_id=10&genre_id=5&sort=title&page=5"). We still want that link in the end, but generating it... How about a better way!
<!-- listSuccess.php --> <?php use_helpers('Filter', 'Pagination') ?> <h2>Books</h2> <h3>Filter By</h3> <table class="filters"> <tbody> <tr> <th>Author:</th> <td><?php echo filter_navigation(objects_for_filter($authors), 'author_id', $author_id) ?></td> </tr> <tr> <th>Genre:</th> <td><?php echo filter_navigation(objects_for_filter($genres), 'genre_id', $genre_id) ?></td> </tr> </tbody> </table> <hr /> <table class="list"> <thead> <tr> <th><?php link_to_unless($sort == 'id', 'Id', filter_url('sort', 'id')) ?></th> <th><?php link_to_unless($sort == 'title', 'Title', filter_url('sort', 'title')) ?></th> <th><?php link_to_unless($sort == 'author_id', 'Author', filter_url('sort', 'author_id')) ?></th> <th><?php link_to_unless($sort == 'genre_id', 'Genre', filter_url('sort', 'genre_id')) ?></th> </tr> </thead> <tbody> <?php foreach ($pager->getResults() as $book): ?> <tr> <td><?php echo $book->getId() ?></td> <td><?php echo $book->getTitle() ?></td> <td><?php echo $book->getAuthor()->getName() ?></td> <td><?php echo $book->getGenre()->getName() ?></td> </tr> <?php endforeach; ?> </tbody> </table> <?php echo pager_navigation($pager) ?>
NOTE: pager_navigation is covered at http://www.symfony-project.com/snippets/snippet/4, but needs to guess the uri, which is done at http://www.symfony-project.com/snippets/snippet/59.
NOTE: filter_navigation returns an unordered list, so you'll need css to display the <li> tags inline, and remove <ul> padding, margin, etc.
I'll leave it as an exercise to the reader to create the controller (action) code for this template, but it should be obvious.
<?php /** * Generate a url using the current internal uri, but replaces a param with a new value. * If the param is not in the current uri's query string, it is added instead. * * This is useful for a page that uses several filters to record sets, * and needs all the filters to work together, instead of blasting each * other away when a new link is clicked. * * <strong>Examples:</strong> * <code> * // with current uri => mymodule/myaction?author=10&genre=3 * * echo link_to('new author', filter_url('author', 5)); * // uri when clicked => mymodule/myaction?author=5&genre=3 * * echo link_to('new genre', filter_url('genre', 1)); * // uri when clicked => mymodule/myaction?author=10&genre=1 * * // with current uri => mymodule/myaction * * echo link_to('an author', filter_url('author', 10)); * // uri when clicked => mymodule/myaction?author=10 * </code> * * @param string the name of the parameter to replace * @param string the value to replace the current value with * @param boolean use route name * @return string the url with the parameter replaced * @see link_to */ function filter_url($param, $new_value, $with_route_name = false) { // fetch params from query string $params = _get_params(_get_query_string()); // replace param with new value $params[$param] = $new_value; return _get_uri($with_route_name) . '?' . _build_query_string($params); } /** * Removes a parameter from the current uri and returns the resulting url. * * @see filter_url */ function remove_filter_url($param, $with_route_name = false) { // fetch params from query string $params = _get_params(_get_query_string()); // remove param unset($params[$param]); return _get_uri($with_route_name) . '?' . _build_query_string($params); } /** * Generates an unordered list of links to filter the current record set by. * Multiple sets of filter_navigation links will work together, using the current uri. * * <strong>Examples:</strong> * <code> * echo filter_navigation(array(10=>'Jones', 12=>'Smith, J.', 13=>'Darby'), 'author_id', 13); * echo filter_navigation(objects_for_filter($authors), 'author_id', 13); * </code> * * @param array list of key=>value pairs of ids and strings * @param string the name of the parameter for this filter * @param string the selected id (or null, if none selected) * @param string the text to use for the "all" link * @see filter_url */ function filter_navigation($list, $param, $selected = null, $all_text = 'All') { $html = ''; $html .= content_tag('li', link_to_unless($selected === null, $all_text, remove_filter_url($param))); foreach ($list as $key => $value) { $html .= content_tag('li', link_to_unless($selected == $key, $value, filter_url($param, $key))); } return content_tag('ul', $html); } /** * Generates a simple list from a record set of propel objects. * Expects a getId function and a toString function. * * @param array objects to be converted to a list * @see filter_navigation */ function objects_for_filter($objects) { $list = array(); foreach ($objects as $object) { $list[$object->getId()] = $object->toString(); } return $list; } function _get_uri($with_route_name = false) { $internal_uri = sfRouting::getInstance()->getCurrentInternalUri($with_route_name); $ar = explode('?', $internal_uri); return ($with_route_name ? '@' : '') . $ar[0]; } function _get_query_string() { $internal_uri = sfRouting::getInstance()->getCurrentInternalUri(); $ar = explode('?', $internal_uri); return isset($ar[1]) ? $ar[1] : ''; } function _get_params($query_string) { // parse query string into associative array $params = array(); if ($query_string != '') { foreach (explode('&', $query_string) as $kvpair) { list($key, $value) = explode('=', $kvpair); $params[$key] = $value; } } return $params; } function _build_query_string($params) { // build list of key=value strings $ar = array(); foreach ($params as $key => $value) { $ar[] = $key . '=' . $value; } return implode('&', $ar); }
NOTE: Place this in apps/myapp/lib/helper/FilterHelper.php.
<?php echo link_to('new author', filter_url('author_id', 10)) ?> <?php echo url_for(filter_url('author_id', 10)) ?> <?php echo link_to_if($condition, 'new author', filter_url('author_id', 10)) ?> <?php echo link_to_unless($condition, 'new author', filter_url('author_id', 10)) ?> <?php echo button_to('new author', filter_url('author_id', 10)) ?>
<?php /** * Shortcut combining link_to and filter_url into single function. * * @see link_to * @see filter_url */ function link_to_filter($name, $param, $new_value, $options = array()) { return link_to($name, filter_url($param, $new_value), $options); } /** * Shortcut combining url_for and filter_url into single function. * * @see url_for * @see filter_url */ function filter_url_for($param, $new_value) { return url_for(filter_url($param, $new_value)); } /** * Shortcut combining link_to_if and filter_url into single function. * * @see link_to_if * @see filter_url */ function link_to_filter_if($condition, $name, $param, $new_value, $options = array()) { return link_to_if($condition, $name, filter_url($param, $new_value), $options); } /** * Shortcut combining link_to_unless and filter_url into single function. * * @see link_to_unless * @see filter_url */ function link_to_filter_unless($condition, $name, $param, $new_value, $options = array()) { return link_to_unless($condition, $name, filter_url($param, $new_value), $options); } /** * Shortcut combining button_to and filter_url into single function. * * @see button_to * @see filter_url */ function button_to_filter($name, $param, $new_value, $options = array()) { return button_to($name, filter_url($param, $new_value), $options); }
Here are some extra helpers that I don't think are in symfony anywhere. They would fit rather nicely in the 'Number' helper group.
<!-- someTemplateSuccess.php --> <?php use_helper('NumberPlus') ?> <table> <thead> <tr> <th>Name</th> <th>Phone</th> <th>Credit Card</th> <th>Exp.</th> <th>Income</th> <th>SSN</th> </tr> </thead> <tbody> <?php foreach ($users as $user): ?> <tr> <td><?php echo $user->getName() ?></td> <td><?php echo format_phone($user->getPhone()) ?></td> <td><?php echo mask_credit_card($user->getCc()) ?></td> <td><?php echo format_exp($user->getExp()) ?></td> <td><?php echo format_usd($user->getIncome()) ?></td> <td><?php echo format_ssn($user->getSsn()) ?></td> </tr> <?php endforeach; ?> </tbody> </table>
<?php /** * Formats a number by injecting nonnumeric characters in a specified format * into the string in the positions they appear in the format. * * <strong>Example:</strong> * <code> * echo format_string('1234567890', '(000) 000-0000'); * // => (123) 456-7890 * * echo format_string('1234567890', '000.000.0000'); * // => 123.456.7890 * </code> * * @param string the string to format * @param string the format to apply * @return the formatted string */ function format_string($string, $format) { if ($format == '' || $string == '') return $string; $result = ''; $fpos = 0; $spos = 0; while ((strlen($format) - 1) >= $fpos) { if (is_alphanumeric(substr($format, $fpos, 1))) { $result .= substr($string, $spos, 1); $spos++; } else { $result .= substr($format, $fpos, 1); } $fpos++; } return $result; } /** * Transforms a number by masking characters in a specified mask format, * and ignoring characters that should be injected into the string without * matching a character from the original string (defaults to space). * * <strong>Example:</strong> * <code> * echo mask_string('1234567812345678', '************0000'); * // => ************5678 * * echo mask_string('1234567812345678', '**** **** **** 0000'); * // => **** **** **** 5678 * * echo mask_string('1234567812345678', '**** - **** - **** - 0000', ' -'); * // => **** - **** - **** - 5678 * </code> * * @param string the string to transform * @param string the mask format * @param string a string (defaults to a single space) containing characters to ignore in the format * @return string the masked string */ function mask_string($string, $format, $ignore = ' ') { if ($format == '' || $string == '') return $string; $result = ''; $fpos = 0; $spos = 0; while ((strlen($format) - 1) >= $fpos) { if (is_alphanumeric(substr($format, $fpos, 1))) { $result .= substr($string, $spos, 1); $spos++; } else { $result .= substr($format, $fpos, 1); if (strpos($ignore, substr($format, $fpos, 1)) === false) $spos++; } $fpos++; } return $result; } /** * Formats a phone number. * * @param string the unformatted phone number to format * @param string the format to use, defaults to '(000) 000-0000' * @return string the formatted string * * @see format_string */ function format_phone($string, $format = '(000) 000-0000') { return format_string($string, $format); } /** * Formats a variable length phone number, using a standard format. * * <strong>Example:</strong> * <code> * echo smart_format_phone('1234567'); * // => 123-4567 * * echo smart_format_phone('1234567890'); * // => (123) 456-7890 * * echo smart_format_phone('91234567890'); * // => 9 (123) 456-7890 * * echo smart_format_phone('123456'); * // => 123456 * </code> * * @param string the unformatted phone number to format * * @see format_string */ function smart_format_phone($string) { switch (strlen($string)) { case 7: return format_string($string, '000-0000'); case 10: return format_string($string, '(000) 000-0000'); case 11: return format_string($string, '0 (000) 000-0000'); default: return $string; } } /** * Formats a U.S. Social Security Number. * * <strong>Example:</strong> * <code> * echo format_ssn('123456789'); * // => 123-45-6789 * </code> * * @param string the unformatted ssn to format * @param string the format to use, defaults to '000-00-0000' * * @see format_string */ function format_ssn($string, $format = '000-00-0000') { return format_string($string, $format); } /** * Formats a credit card expiration string. Expects 4-digit string (MMYY). * * @param string the unformatted expiration string to format * @param string the format to use, defaults to '00-00' * * @see format_string */ function format_exp($string, $format = '00-00') { return format_string($string, $format); } /** * Formats (masks) a credit card. * * @param string the unformatted credit card number to format * @param string the format to use, defaults to '**** **** **** 0000' * * @see mask_string */ function mask_credit_card($string, $format = '**** **** **** 0000') { return mask_string($string, $format); } /** * Formats a USD currency value with two decimal places and a dollar sign. * * @param string the unformatted amount to format * @param string the format to use, defaults to '$%0.2f' * * @see sprintf */ function format_usd($money, $dollar = true, $format = '%0.2f') { return ($dollar ? '$' : '') . sprintf($format, $money); } /** * Determines if a string has only alpha/numeric characters. * * @param string the string to check as alpha/numeric * * @see is_numeric * @see preg_match */ function is_alphanumeric($string) { return preg_match('/[0-9a-zA-Z]/', $string); }
NOTE: Place this in something like myproj/apps/myapp/lib/helper/NumberPlusHelper.php
I wanted to add created_by and updated_by columns to my Doctrine schema. But the existing templates doesn't support that, so i've rewritten the Timestampable template.
Now Doctrine can automatically update these fields to the specific username oder userid.
ExampleUser:
tableName: example_user
columns:
id:
primary: true
autoincrement: true
type: integer(4)
name:
type: string(255)
password:
type: string(255)
actAs:
Userid:
created:
name: created_by
type: integer
updated:
name: updated_by
type: string
If you are writing integer as type the template will call $sf_user->getId(), if you write string it will call $sf_user->getUsername().
So the above example will write the ID to created_by column and the username to updated_by column.
<?php /* * $Id$ * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals * and is licensed under the LGPL. For more information, see * <http://www.phpdoctrine.org>. */ /** * Doctrine_Template_Userid * * Easily add created and updated by ids or usernames to your doctrine records * * @package Doctrine * @subpackage Template * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.phpdoctrine.org * @since 1.0 * @version $Revision$ * @author Thomas Boerger <tb@mosez.net> */ class Doctrine_Template_Userid extends Doctrine_Template { /** * Array of userid options * * @var string */ protected $_options = array( 'created' => array( 'name' => 'created_by', 'type' => 'integer', 'disabled' => false, 'expression' => false, 'options' => array() ), 'updated' => array( 'name' => 'updated_by', 'type' => 'integer', 'disabled' => false, 'expression' => false, 'onInsert' => true, 'options' => array() ) ); /** * __construct * * @param string $array. * @return void */ public function __construct(array $options) { $this->_options = Doctrine_Lib::arrayDeepMerge($this->_options, $options); } /** * setTableDefinition * * @return void */ public function setTableDefinition() { if(!$this->_options['created']['disabled']) { $this->hasColumn( $this->_options['created']['name'], $this->_options['created']['type'], null, $this->_options['created']['options'] ); } if(!$this->_options['updated']['disabled']) { $this->hasColumn( $this->_options['updated']['name'], $this->_options['updated']['type'], null, $this->_options['updated']['options'] ); } $this->addListener(new Doctrine_Template_Listener_Userid($this->_options)); } }
<?php /* * $Id$ * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals * and is licensed under the LGPL. For more information, see * <http://www.phpdoctrine.org>. */ /** * Doctrine_Template_Listener_Userid * * @package Doctrine * @subpackage Template * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.phpdoctrine.org * @since 1.0 * @version $Revision$ * @author Thomas Boerger <tb@mosez.net> */ class Doctrine_Template_Listener_Userid extends Doctrine_Record_Listener { /** * Array of userid options * * @var string */ protected $_options = array(); /** * __construct * * @param string $options. * @return void */ public function __construct(array $options) { $this->_options = $options; } /** * preInsert * * @param object $Doctrine_Event. * @return void */ public function preInsert(Doctrine_Event $event) { if(!$this->_options['created']['disabled']) { $createdName = $this->_options['created']['name']; $event->getInvoker()->$createdName = $this->getUserId('created'); } if(!$this->_options['updated']['disabled'] && $this->_options['updated']['onInsert']) { $updatedName = $this->_options['updated']['name']; $event->getInvoker()->$updatedName = $this->getUserId('updated'); } } /** * preUpdate * * @param object $Doctrine_Event. * @return void */ public function preUpdate(Doctrine_Event $event) { if( ! $this->_options['updated']['disabled']) { $updatedName = $this->_options['updated']['name']; $event->getInvoker()->$updatedName = $this->getUserId('updated'); } } /** * getUserId * * Gets the userid or username depending on field format * * @param string $type. * @return void */ public function getUserId($type) { $options = $this->_options[$type]; if ($options['expression'] !== false && is_string($options['expression'])) { return new Doctrine_Expression($options['expression']); } elseif (!class_exists("pakeApp")) { switch($options['type']) { case 'integer': if (class_exists('sfGuardUser')) { return sfContext::getInstance()->getUser()->getAttribute('user_id', null, 'sfGuardSecurityUser'); } else { return sfContext::getInstance()->getUser()->getId(); } break; case 'string': return sfContext::getInstance()->getUser()->getUsername(); break; default: return 'n/a'; break; } } } }
I recently had several template fragments which each had some conditional css that needed to be defined in the template itself. This is a perfect application for slots.
However, since there were several of these fragments/partials being executed, I would have to use several slots. That seemed unnecessary. Consider the following:
/* _fragment.php */ <?php append_to_slot('style') ?> <style> .class { font-size: 12px; } </style> <? end_slot() ?>
/* _anotherfragment.php */ <?php append_to_slot('style') ?> <style> .anotherclass { <?php if ($bool): ?> font-size: 24px; <?php else: ?> font-size: 26px; <?php endif; ?> } </style> <?php end_slot() ?>
Those were just an example, so don't laugh. They do illustrate, however, the use of a helper which continually appends your content to the existing slot, which may or may not already contain content; in this case the "style" slot.
Then of course, you can use the slot in your layout.php (in the head tag):
... <head> ... <?php if (has_slot('style')) echo get_slot('style') ?> </head>
Note: while the append_to_slot function can be ended by either end_slot() or end_append_to_slot(), the prepend_to_slot function must be ended by end_prepend_to_slot()!
<?php /** * Begins the capturing of a slot, * but echoes the current value of a slot with the same name, * if one exists, before capturing the rest of the slot content. * * <strong>Example</strong> * <code> * // first myslot * slot('myslot'); * echo 'something to put in "myslot"'; * end_slot(); * // second myslot * append_to_slot('myslot'); * echo 'some stuff to append to existing "myslot" slot'; * end_slot(); * </code> * * @param string slot name * @return void * @see end_append_to_slot, slot, end_slot */ function append_to_slot($name) { // check for the existence of a slot with the same name $content = ''; if (has_slot($name)) $content = get_slot($name); // regardless, begin capturing the slot slot($name); // echo either a blank string, or the previous value of the slot with the same name echo $content; } /** * Stops the content capture and save the content in the slot. * This function is interchangeable with end_slot, and is not necessary. * It is useful only for consistency's sake. * * @return void * @see append_to_slot, slot, end_slot */ function end_append_to_slot() { // simply end the current slot end_slot(); } /** * Begins the capturing of a slot, * but saves the current value of a slot with the same name, * if one exists, and then proceeds to capture the slot content. * * @param string slot name * @return void * @see end_prepend_to_slot, slot, end_slot */ function prepend_to_slot($name) { $context = sfContext::getInstance(); $response = $context->getResponse(); // check for the existence of a slot with the same name $content = ''; if (has_slot($name)) $content = get_slot($name); // save the current slot value in a temporary location $response->setParameter('old_slot', $content, 'symfony/view/sfView/slot'); // begin capturing the slot slot($name); } /** * Retrieves the old value of a slot which was backed up in prepend_to_slot, * if one exists, and echoes this value, * and then proceeds to stop the content capture and save the content in the slot. * * @return void * @see prepend_to_slot, slot, end_slot */ function end_prepend_to_slot() { $context = sfContext::getInstance(); $response = $context->getResponse(); // retrieve the old value for this slot $content = $response->getParameter('old_slot', '', 'symfony/view/sfView/slot'); // remove the old value $response->getParameterHolder()->remove('old_slot', 'symfony/view/sfView/slot'); // echo the old value of the slot, or a blank string if no slot existed echo $content; // end the current slot end_slot(); }
Remember: you can place this in myproject/apps/myapp/lib/helper/PartialPlusHelper.php
Then you can use it with <?php use_helper('PartialPlus') ?>
I'm not sure how useful these will be, but I came up with the idea, so I figured I'd post them as well.
<table> <?php foreach ($vars as $key => $value): ?> <tr> <th><?php include_fragment('label', array('id' => $key, 'object' => $value))) ?></th> <td><?php include_fragment('input', array('id' => $key, 'object' => $value))) ?></td> </tr> <?php endforeach; ?> </table>
These helpers use the fragment idea from http://www.symfony-project.com/snippets/snippet/124 and take it a step further. There's a difference here, however, that's important to note:
Note: Unlike partials, these fragments will have absolutely no variables available, unless explicitly passed in. Templating variables provided by symfony will not be available, due to php's scope handling when inside of functions. That's why my previous snippet used include(fragment_name('myfragment')) instead, which gave the fragment access to all currently in-scope variables.
<?php /** * Includes a template fragment. * This function is similar in nature to include_partial, * but does not use sfView, and therefore has virtually no overhead. * * <strong>Example</strong> * <code> * include_fragment('mypartial'); * </code> * * @param string fragment name * @return void * @see include_partial, get_fragment, fragment_path, fragment_name */ function include_fragment($templateName, $vars = array()) { echo get_fragment($templateName, $vars); } /** * Evaluates and returns a template fragment. * The syntax is similar to that of include_fragment. * * <strong>Example</strong> * <code> * echo get_fragment('mypartial'); * </code> * * @param string fragment name * @return string result of a fragment include * @see include_partial, include_fragment, fragment_path, fragment_name */ function get_fragment($templateName, $vars = array()) { // start the output buffer ob_start(); ob_implicit_flush(0); // extract the variables in the scope of this function extract($vars); // include the file include(fragment_path($templateName)); // clear the output buffer, and return the content return ob_get_clean(); }
Remember: you can place this in myproject/apps/myapp/lib/helper/PartialPlusHelper.php
Then you can use it with <?php use_helper('PartialPlus') ?>