This post describes how to create new relation by usin jQuery Autocompleter widget in Symfony project.
Recently, I’ve had a situation where I had to create a form for entering a product details, with a dropdown or autocomplete input for selecting product’s brand. If a user entered a non-existing brand, it had to be automatically created and assigned to product that the user was editing.
Creating an autocomplete input is done by inserting this in a form class:
// lib/form/doctrine/your_form.class.php $this->widgetSchema['brand_id'] = new sfWidgetFormDoctrineJQueryAutocompleter(array( 'url' => '/autocomplete_brand', 'model' => 'Brand', 'value_callback' => 'findOneById', 'config' => "{max: 20}" )); $this->setValidator('brand_id', new sfValidatorPass()); |
It is important to add a sfValidatorPass validator for brand_id field, so that Symfony won’t return an error when entering a name of non-existant brand in database.
For URL /autocomplete_brand create an action that handles jquery queries:
// apps/frontend/modules/autocomplete/actions.class.php function executeAutocompletebrand(sfWebRequest $request) { $limit = $request->getParameter('limit', 20); $result = BrandTable::findBrandByName($request['q'], $limit); $output = array(); foreach($result as $r) $output[$r['id']] = $r['name']; return $this->renderText(json_encode($output)); } |
After searching for items that match the entered input, check if there are results to display. If there are none, insert a result that will tell your application to create a new Brand relation:
// lib/model/doctrine/BrandTable.class.php public static function findBrandByName($name, $limit = 20) { $results = self::getInstance()->createQuery('b') ->where('b.name LIKE \'%'.$name.'%\'') ->limit($limit) ->execute(array(),Doctrine_Core::HYDRATE_ARRAY); if(count($results) == 0) return array(array('id'=>'new--'.$name, 'name'=>''.$name)); // If no brands are found, return this as a new Brand. else return $results; } |
Finally, create a post validator in which you’ll create the new Brand relation if field brand_id has been filled with a prefix new–. Your form class should now look like something like this:
// lib/form/doctrine/your_form.class.php public function configure() { ... $this->widgetSchema['brand_id'] = new sfWidgetFormDoctrineJQueryAutocompleter(array( 'url' => '/autocomplete_brand', 'model' => 'Brand', 'value_callback' => 'findOneById', 'config' => "{max: 20}" )); $this->setValidator('brand_id', new sfValidatorPass()); $this->validatorSchema->setPostValidator( new sfValidatorCallback(array('callback' => array($this, 'productPostValidate'))) ); ... } /** Helper function for determening if a new brand has been entered **/ private function has_new_brand($brand_id) { return strpos($brand_id, 'ew--') == 1; } /** Helper function for extracting the new brand name from brand_id field value **/ private function get_new_brand_name($brand_id) { $tmp = explode('new--', $brand_id); return(end($tmp)); } function productPostValidate($validator, $values) { $tainted_vals = $this->getTaintedValues(); $brand_id = $tainted_vals['brand_id']; if($this->has_new_brand($tainted_vals['brand_id'])) { // Create new brand $brand = new Brand(); $brand->setName($this->get_new_brand_name($tainted_vals['brand_id'])); $brand->save(); // Set brand id $values['brand_id'] = $brand->getId(); } ... } |
This should enable you to automatically create a new brand entry if an entered name does not exist in your database.