![]() |
|
Code snippets for symfony 1.x |
|
Form widget for symfony 1.4 extending sfWidgetFormDoctrineChoice or sfWidgetFormPropelChoice. The new feature is the possibility to indicate a column which values will be used to group all the fields with that column value within optgroup HTML elements.
You can get the code in this link.
The regular use of forward is causing double content rendering and it took me a while to find the proper solution so here it is:
<?php class SiteFilter extends sfFilter { public function execute($filterChain) { $context = $this->getContext(); if($this->isFirstCall()){ if(true){ // put some condition in here $context->getController()->forward('module', 'action'); throw new sfStopException(); // the solution } } $filterChain->execute(); } }
As you can see sfStopException() is the key.
Sometimes, you may have to know, at the controller level what are these informations, it can be easily achieved with the following regexp: (inside a controller action function)
$matches = array(); $controller = $this->getRequest()->attributes->get('_controller'); preg_match('/(.*)\\\Bundle\\\(.*)\\\Controller\\\(.*)Controller::(.*)Action/', $controller, $matches); $request = $this->getRequest(); $request->attributes->set('namespace', $matches[1]); $request->attributes->set('bundle', $matches[2]); $request->attributes->set('controller', $matches[3]); $request->attributes->set('action', $matches[4]);
These informations are now available in the action attributes:
For example:
echo 'namespace: '. $request->attributes->get('namespace'). PHP_EOL; echo 'bundle: '. $request->attributes->get('bundle'). PHP_EOL; echo 'controller: '. $request->attributes->get('controller'). PHP_EOL; echo 'action: '. $request->attributes->get('action'). PHP_EOL;
Will output:
namespace: COil bundle: ToolsBundle controller: Home action: index
See you. COil
PS: This can be useful in a preExecute() like function.
PS2: The namespace here is the parent directory where is stored the bundle.
Example:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE xliff PUBLIC "-//XLIFF//DTD XLIFF//EN" "http://www.oasis-open.org/committees/xliff/documents/xliff.dtd"> <xliff version="1.0"> <file original="global" source-language="en" datatype="plaintext" date="2009-08-12T12:32:00Z"> <header/> <body> ... <trans-unit> <source>from %from_date%<br />to %to_date%</source> <target>de %from_date%<br />até %to_date%</target> </trans-unit> </body> </file> </xliff>
Use '<' and '>' to replace '<' and '>'. respectively.
This will allow you to use multiple themes for symfony, each with it's own templates and stylesheets.
Grab the code at GitHub: https://github.com/acetous/Symfony-Themed
The templates have to be stored in sf_root_dir/themes/application/themename/module. A theme named default has to be present. Layouts have to be placed in sf_root_dir/themes/themename/application.
Example:
sf_root_dir/themes/default/frontend/layout.php is the new place for your layoutsf_root_dir/themes/default/frontend/post/indexSuccess.php is the new place for the success-template when calling the index-action on the post-moduleCreate stylesheets for your theme. You'll need to create one for each application.
Example:
default-frontend.cssdefault-backend.cssPlease include this stylesheets in your themes root directory. They need to be copied into the /web/css folder.
All themes (except default) will load the default-templates if a custom one is not found. This way you just need to create the template files you want to alter.
Apart from that the default-stylesheet will be included if a custom one cannot be found.
lib directory (the task to lib/task and filter to lib/filter).filters.yml. Add one filter for the themes logic:
<pre>rendering: ~
security: ~
themes:
class: ThemedFilter
cache: ~
execution: ~
</pre>Just run the ./symfony themed:setup task to setup all themes or ./symfony themed:setup name to setup a specific theme.
$c->addAscendingOrderByColumn( 'NAME COLLATE utf8_bin' );
quite obvious
ReUse of: Conditional object actions for the admin generator By Georg Sorst
The list view of the admin generator currently always displays all defined object actions for each object. There is no way to display an object action for an object only if some condition on this object itself is met.
In order to extend the admin generator with this functionality only a small enhancement is required. You can either apply this change per module or create a new admin generator theme as described in the Symfony book.
The templates/_list_td_actions.php has to be extended to look roughly like this, depending on whether you already have your own modifications in there.
I use sf v1.4.11, for make this enhancement...
First, you should find the partial _list_td_actions.php found in
lib/vendor/symfony/lib/plugins/sfDoctrinePlugin/data/generator/sfDoctrineModule/admin/template/templates/_list_td_actions.php
And modify the original code like this:
<td>
<?php if ($this->configuration->getValue('list.object_actions')): ?>
<ul class="sf_admin_td_actions">
<?php foreach ($this->configuration->getValue('list.object_actions') as $name => $params): ?>
<?php if ( isset( $params['condition'] ) ): ?>
[?php if ( <?php echo ( isset( $params['condition']['invert'] ) && $params['condition']['invert'] ? '!' : '') . '$' . $this->getSingularName( ) . '->' . $params['condition']['function'] ?>( <?php echo ( isset( $params['condition']['params'] ) ? $params['condition']['params'] : '' ) ?> ) ): ?]
<?php endif; ?>
<?php if ('_delete' == $name): ?>
<?php echo $this->addCredentialCondition('[?php echo $helper->linkToDelete($'.$this->getSingularName().', '.$this->asPhp($params).') ?]', $params) ?>
<?php elseif ('_edit' == $name): ?>
<?php echo $this->addCredentialCondition('[?php echo $helper->linkToEdit($'.$this->getSingularName().', '.$this->asPhp($params).') ?]', $params) ?>
<?php elseif ('_show' == $name): ?>
<?php echo $this->addCredentialCondition('[?php echo $helper->linkToShow($'.$this->getSingularName().', '.$this->asPhp($params).') ?]', $params) ?>
<?php else: ?>
<li class="sf_admin_action_<?php echo $params['class_suffix'] ?>">
<?php echo $this->addCredentialCondition($this->getLinkToAction($name, $params, true), $params) ?>
</li>
<?php endif; ?>
<?php if ( isset( $params['condition'] ) ): ?>
[?php endif; ?]
<?php endif; ?>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</td>
Second, in the generator.yml you should add aditional lines like
actionName:
label: actionLabel
action: executeAction
#Now the enhancement =D
condition:
# function is the name of function in the model::functionName and must return boolean
function: functionName
# params are the params to send to functionName
params: "$model->getDbField(), $sf_user, 'test'"
# invert is used if you need invert the result of functionName
invert: false
Third and final step, go to
lib/model/doctrine/model.class.php
and create the functionName like:
/** * $param1 is $model->getDbField() * $param2 is $sf_user * $param3 is 'test' */ public function functionName($param1, $param2, $param3){ //code personal return boolean; }
Final, important!
php symfony cache:clear
Well that's all, sorry for my bad English =S Thanks Georg Sorst and Google Translator
Remember to register your hydrator first.
public function executeIndex(sfWebRequest $request) { $manager = Doctrine_Manager::getInstance(); $manager->registerHydrator('xml', 'Doctrine_Hydrator_XmlDriver'); }
See http://www.doctrine-project.org/documentation/manual/1_2/en/data-hydrators
<?php class Doctrine_Hydrator_XmlDriver extends Doctrine_Hydrator_ArrayDriver { public function hydrateResultSet($stmt) { $array = parent::hydrateResultSet($stmt); return $this->arrayToXml($array); } /** * <pre>Converts an Doctrine array graph of the form: * .array * . 0 => * . array * . 'id' => '1' * . 'PersonaDomicilio' => * . array * . 0 => * . array * . 'id' => '1' * To a XML representation. * @param array $array * @return DOMDocument <pre> The XML with following structure: * . <result> * . <rootTable_Collection> * . <rootTable> * . <id></id> * . <field1></field1> * . <relatedTable_Collection> * . <relatedTable> * . </relatedTable> * . <relatedTable> * . </relatedTable> * . :: * . </relatedTable_Collection> * . </rootTable> * . <rootTable> * . :: * . </rootTable> * . :: * . </rootTable_Collection> * . <result> * </pre> */ public function arrayToXml(array $array) { $result = new DOMDocument(); $rootNode = $result->createElement('result'); $result->appendChild($rootNode); $iterator = new RecursiveIteratorIterator( new RecursiveArrayIterator($array), RecursiveIteratorIterator::SELF_FIRST ); $prevLvl = 0; $component[$prevLvl] = $this->_queryComponents[$this->_rootAlias]['table'] ->getComponentName(); $obj = $result->createElement($component[$prevLvl] . '_Collection'); $rootNode->appendChild($obj); foreach ($iterator as $k => $val) { $depth = $iterator->getDepth(); if ($depth < $prevLvl) { for ($i = 0; $i < ($prevLvl - $depth); $i++) { $obj = $obj->parentNode; } } if (! is_array($val)) { $son = $result->createElement($k, $val); $obj->appendChild($son); } else { if (is_numeric($k)) { $son = $result->createElement($component[$depth]); } else { $component[$depth + 1] = $k; $son = $result->createElement($k . '_Collection'); } $obj->appendChild($son); !empty($val) && $obj = $son; } $prevLvl = $depth; } return $result; } }
Sometimes you'd like to use partial from auto-generated module, like list partial, to keep list presentation unified.
But, before using partial, you add to be sure that cache has been generated or your application will be broken.
This little method (you can place in your tools.class.php) check that cache exists, and if not, generate it.
public static function generateAdminGeneratorModuleCache($module, $configuration) { if(!($configuration instanceof sfApplicationConfiguration)) { throw new sfConfigurationException('You must provide a valid application configuration object'); } if(!file_exists(sfConfig::get('sf_app_module_dir').DIRECTORY_SEPARATOR.$module)) { throw new sfConfigurationException('The module specified does not exists for this application'); } if(!file_exists(sfConfig::get('sf_app_module_dir').DIRECTORY_SEPARATOR.$module.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'generator.yml')) { throw new sfConfigurationException('This module does not seems to be a valid admin generator module'); } if(!file_exists(sfConfig::get('sf_module_cache_dir').DIRECTORY_SEPARATOR.$module.DIRECTORY_SEPARATOR.'actions'.DIRECTORY_SEPARATOR.'actions.class.php')) { // Init generator manager $generator_manager = new sfGeneratorManager($configuration); $config = sfYaml::load(sfConfig::get('sf_app_module_dir').DIRECTORY_SEPARATOR.$module.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'generator.yml'); $config['generator']['param']['moduleName'] = $module; $generator_manager->generate($config['generator']['class'], $config['generator']['param']); } }
Now you can call it from any actions with
myTools::generateAdminGeneratorModuleCache('my_module',$this->getContext()->getConfiguration());
<?php /** * This validator accepts either a four-member array such as * array( 'deg' => 12, 'min' => 34, 'sec' => 56, 'dir' => 'N' ) * or a numeric value such as 12.3456 */ class ynValidatorLatLong extends sfValidatorBase { protected function configure($options = array(), $messages = array()) { parent::configure( $options, $messages ); $this->addRequiredOption('axis'); // 'latitude' || 'longitude' $this->addMessage('invalid_axis', 'The selected axis is invalid.'); } protected function doClean( $value ) { $max = 0; switch ( $this->getOption('axis') ) { case 'latitude': $max = 90; break; case 'longitude': $max = 180; break; default: throw new sfValidatorError($this, 'invalid_axis'); } if ( is_array( $value ) ) { $value = $this->arrayToInt( $value ); } if ( ! is_numeric( $value ) ) { throw new sfValidatorError($this, 'invalid'); } if ( abs( $value ) > $max ) { throw new sfValidatorError($this, 'invalid'); } return $value; } protected function arrayToInt( array $value ) { if ( count( $value ) != 4 ) { throw new sfValidatorError($this, 'invalid'); } if ( ! isset( $value['deg'] ) || ! isset( $value['min'] ) || ! isset( $value['sec'] ) || ! isset( $value['dir'] ) ) { throw new sfValidatorError($this, 'invalid'); } if ( preg_match( '/\D/', $value['deg'].$value['min'].$value['sec'] ) ) { // non-integer component throw new sfValidatorError($this, 'invalid'); } $multiplier = 1; switch ( strtolower( $value['dir'] ) ) { case 'n': $axis = 'latitude'; break; case 's': $axis = 'latitude'; $multiplier = -1; break; case 'e': $axis = 'longitude'; break; case 'w': $axis = 'longitude'; $multiplier = -1; break; default: throw new sfValidatorError($this, 'invalid'); } if ( $axis != $this->getOption('axis') ) { throw new sfValidatorError($this, 'invalid'); } return ( $value['deg'] + $value['min']/60 + $value['sec']/60/60 ) * $multiplier; } } /* * UNIT TEST */ <?php require_once dirname(__FILE__).'/../../bootstrap/unit.php'; $t = new lime_test(); $lat_validator = new ynValidatorLatLong( array( 'axis' => 'latitude' ) ); $lon_validator = new ynValidatorLatLong( array( 'axis' => 'longitude' ) ); $lat_good = array( array( 'deg' => 50, 'min' => 12, 'sec' => 14, 'dir' => 'N' ), array( 'deg' => 50, 'min' => 12, 'sec' => 14, 'dir' => 'S' ), 50.456789, 50, -50.456789, -50 ); $lat_bad = array( 'three members' => array( 'deg' => 50, 'min' => 12, 'sec' => 14 ), 'invalid n/s 1' => array( 'deg' => 50, 'min' => 12, 'sec' => 14, 'dir' => 'F' ), 'invalid n/s 2' => array( 'deg' => 50, 'min' => 12, 'sec' => 14, 'dir' => 'E' ), 'non-integer member' => array( 'deg' => 50, 'min' => 12, 'sec' => 14.2, 'dir' => 'N' ), 'out of range' => array( 'deg' => 100, 'min' => 12, 'sec' => 14, 'dir' => 'N' ), ); $lon_good = array( array( 'deg' => 150, 'min' => 12, 'sec' => 14, 'dir' => 'E' ), array( 'deg' => 150, 'min' => 12, 'sec' => 14, 'dir' => 'W' ), 50.456789, 50, -50.456789, -50 ); $lon_bad = array( 'three members' => array( 'deg' => 150, 'min' => 12, 'sec' => 14 ), 'invalid e/w 1' => array( 'deg' => 150, 'min' => 12, 'sec' => 14, 'dir' => 'F' ), 'invalid e/w 2' => array( 'deg' => 150, 'min' => 12, 'sec' => 14, 'dir' => 'N' ), 'non-integer member' => array( 'deg' => 150, 'min' => 12, 'sec' => 14.2, 'dir' => 'E' ), 'out of range' => array( 'deg' => 200, 'min' => 12, 'sec' => 14, 'dir' => 'E' ), ); foreach ( $lat_good as $value ) { try { $clean = $lat_validator->clean( $value ); $t->ok( is_numeric( $clean ), 'is numeric' ); if ( is_array( $value ) ) { switch( $value['dir'] ) { case 'E': $multiplier = 1; break; case 'W': $multiplier = -1; break; } $t->ok( abs($clean) >= $value['deg'] && abs($clean) <= $value['deg']+1, "value $clean makes sense" ); $t->ok( $clean * $multipler >= 0, 'direction correct' ); } else { $t->is( $value, $clean, 'value makes sense' ); } } catch( sfValidatorError $e ) { $t->fail( $e->getMessage() ); } } foreach ( $lat_bad as $message => $value ) { try { $lat_validator->clean( $value ); $t->fail( $message ); } catch( sfValidatorError $e ) { $t->pass( $message ); } } foreach ( $lon_good as $value ) { try { $clean = $lon_validator->clean( $value ); $t->ok( is_numeric( $clean ), 'is numeric' ); if ( is_array( $value ) ) { switch( $value['dir'] ) { case 'E': $multiplier = 1; break; case 'W': $multiplier = -1; break; } $t->ok( abs($clean) >= $value['deg'] && abs($clean) <= $value['deg']+1, "value $clean makes sense" ); $t->ok( $clean * $multipler >= 0, 'direction correct' ); } else { $t->is( $value, $clean, 'value makes sense' ); } } catch( sfValidatorError $e ) { $t->fail( $e->getMessage() ); } } foreach ( $lon_bad as $message => $value ) { try { $lon_validator->clean( $value ); $t->fail( $message ); } catch( sfValidatorError $e ) { $t->pass( $message ); } }