I was searching long time for a good auto completer widget, but I couldn’t find one. So I created it by my self.
I did the same with the build in symfony auto completer widget. When I have time I’ll post it, too.
I hope I could help someboby with this small tutorial. Please give me feedback if there are any questions or suggestions.
Now lets start:
1. Copy the dojo package to the js folder
Resulting path to dojo.js: web/js/dojoToolkit/dojo/dojo.js
2. Enable dojo.js
If not yet happend elsewhere:
Modify the view.yml: apps/[YOUR_APP]/config/view.yml
default: javascripts: [dojoToolkit/dojo/dojo.js]
3. Create the widget
lib/widget/itWidgetFormInputTextDijitAutocomplete.php
class itWidgetFormInputTextDijitAutocomplete extends sfWidgetForm { /** * Constructor. * * Available options: * * * model: Name of the Model * * searchfield: Table field in which will be searched * * action: Optional: request action or url * * value_method: Optional: getter for form field value * * output_method: Optional: getter for form field name * * criteria_method: Optional: getter for additional cirteria * * add_empty: Optional: add emtpy value option to the list * * choices: An array of possible choices (n/a) * * formatter: A callable to call to format the checkbox choices * * @see sfWidgetForm */ protected function configure($options = array(), $attributes = array()) { $this->addRequiredOption('model'); $this->addRequiredOption('search_field'); $this->addOption('action', 'default/ajaxAutocomplete'); $this->addOption('value_method', 'getPrimaryKey'); $this->addOption('output_method', 'getPrimaryKey'); $this->addOption('choices', array()); $this->addOption('criteria_method', ''); $this->addOption('formatter', array($this, 'formatter')); $this->addOption('add_empty', false); } /** * Renders the widget as HTML. * * @param string The name of the HTML widget * @param mixed The value of the widget * @param array An array of HTML attributes * @param array An array of errors * * @return string A HTML representation of the widget * * @see sfWidgetForm */ public function render($name, $value = null, $attributes = array(), $errors = array()) { $choices = $this->getOption('choices'); if ($choices instanceof sfCallable) { $choices = $choices->call(); } $baseAttributes = array( 'action' => $this->getOption('action'), 'name' => $name, 'type' => 'text', 'value' => $value, 'id' => $id = $this->generateId($name, self::escapeOnce($name)), ); return call_user_func($this->getOption('formatter'), $this, $baseAttributes); } /** * Renders the input fields as HTML. * * @param widget * @param attributes * * @return string A HTML representation of the input fields */ public function formatter($widget, $attributes) { sfLoader::loadHelpers(array('Form', 'Url')); $request = sfContext::getInstance()->getRequest(); $field_id = $attributes['id']; $field_name = $attributes['name']; $ajax_field_id = 'ajax_'.$attributes['id']; $out_field_id = 'out_'.$attributes['id']; $object = @call_user_func(array($this->getOption('model').'Peer', 'retrieveByPk'), $attributes['value']); $default_out = $request->getParameter('ajax_'.$attributes['name'], is_object($object) ? call_user_func(array(&$object, $this->getOption('output_method'))) : ''); $default_value = $request->getParameter($attributes['name'], is_object($object) ? $attributes['value'] : ''); $html = ''; $ajaxParams=array( 'model' => $this->getOption('model'), 'value_method' => $this->getOption('value_method'), 'output_method' => $this->getOption('output_method'), 'search_field' => $this->getOption('search_field'), 'criteria_method' => $this->getOption('criteria_method'), 'field_name' => 'ajax_'.$attributes['name'] ); if($this->getOption('add_empty')===false) $ajaxParams['add_empty'] = 'false'; elseif($this->getOption('add_empty')===true) $ajaxParams['add_empty'] = 'true'; else $ajaxParams['add_empty'] = $this->getOption('add_empty'); $requestUrl = url_for($attributes['action'].'?'.http_build_query($ajaxParams), true); $requestUrl = str_replace('frontend_dev.php/', '', $requestUrl); $requestUrl = str_replace('backend_dev.php/', 'backend.php/', $requestUrl); $html .= << <script type="text/javascript"> dojo.require("dojo.data.ItemFileReadStore"); dojo.require("dijit.form.FilteringSelect"); </script> <div dojoType="dojo.data.ItemFileReadStore" jsId="{$field_id}Store" url="{$requestUrl}"></div> EOF; $input_text = << <input dojoType="dijit.form.FilteringSelect" store="{$field_id}Store" class="medium" id="{$field_id}" name="{$field_name}" value="{$default_value}" hasDownArrow="true", invalidMessage="", ignoreCase="true" /> }
4. Create action for the AJAX request
apps/[APP_NAME]/modules/default/actions/actions.class.php
<?php class defaultActions extends sfActions { public function executeAjaxAutocomplete($request) { $model = $request->getParameter('model'); $criteria_method = $request->getParameter('criteria_method'); $add_empty = $request->getParameter('add_empty', 'false'); $this->value_method = $request->getParameter('value_method'); $this->output_method = $request->getParameter('output_method'); $peer_object = $model.'Peer'; if($criteria_method!='') $criteria = call_user_func(array($peer_object, $criteria_method)); else $criteria = new Criteria(); $search_field = $request->getParameter('search_field'); $field_name = $request->getParameter('field_name'); $var = $request->getParameter($field_name); $j_object = array('identifier' => 'abbreviation', 'items'=>array()); $search_field = strtoupper($search_field); $sql_field = constant($peer_object.'::'.$search_field); $c = $criteria; $c->add($sql_field, $var . '%', Criteria::LIKE); $this->list = call_user_func(array($peer_object,'doSelect'), $c); if($add_empty!='false') { $j_object['items'][] = array( 'name' => $add_empty=='true' ? '' : $add_empty, 'label' => '', 'abbreviation' => '' ); } foreach($this->list AS $v) { $j_object['items'][] = array( 'name' => call_user_func(array(&$v, $this->output_method)), 'label' => call_user_func(array(&$v, $this->output_method)), 'abbreviation' => call_user_func(array(&$v, $this->value_method)) ); } $this->j_object = $j_object; } }
5. Create template for the AJAX request
apps/[APP_NAME]/modules/default/templates/ajaxAutocompleteSuccess.php
<?php decorate_with(false); ?> <?php print_r(json_encode($j_object)); ?>
6. Insert the new widget into your sfForm
apps/[APP_NAME]/modules/default/templates/ajaxAutocompleteSuccess.php
... # $widgets['user_id'] = new sfWidgetFormInput(); $widgets['user_id'] = new stWidgetFormInputTextDijitAutocomplete(array( 'model' => 'User', 'search_field' => 'username', 'value_method' => 'getId', 'output_method' => 'getUsername', 'criteria_method' => 'getCriteriaRegisteredUsers' ));
6. Clear symfony cache
Links:
- http://api.dojotoolkit.org/jsdoc/1.3.2/dijit.form.FilteringSelect