Виджет для symfony. Выборка нескольких элементов из списка.
Для работы понадобилось написать виджет, я встречал видео с таким виджетом(который мне необходим) - но автор не выложил его кода(или я не нашёл) - по этому написал свой. И покажу как это сделал - для того чтоб вы меня поправили если я в чём то не прав или можно было сделать проще - или же сами чему то научились бы. Приступим.
Для начала суть: необходима выборка из списка нескольких элементов, по умолчанию symfony генерирует форму с sfWidgetFormPropelChoice он довольно не удобен, по этому его лучше заменить на sfWidgetFormPropelChoice. Однако заказчику он не нравился и был его явный минус - если список большой то выводить все его элементы на страницу - не приемлемо. Так что добавим строку с автодополнением. Так же надо учесть что в выпадающий список не должны попадать уже добавленные категории. А чтоб минимизировать его место на основной форме - вынесем его в отдельное окно
Так как я уже сделал его то чтоб удобнее было понять о чём идёт речь вот скришоты:
По клику "показать список" - появляется форма на которой можно выбрать категории товаров или же добавить новую.
Схема бд:
Виджет будет использоваться для таблици contrctors_goods_buy, он будет выводится при добавления "контрактора".
Если у вас девственно чистый проект - то необходимо создать папку widget в lib, в ней создадим файл который будет классом виджета. Далее будут приводить его код с комментариями:
Наследуем виджет от существующего sfWidgetFormSelectCheckbox - соответственно чекбоксы будем формировать с такой же структурой как и в нём. Первоначально наш список пуст для этого мы определяем опцию choices и далее вызываем родительский конструктор.
Теперь переопределяем функцию configure указываем что опции model, addurl и jsonurl должны быть обязательно определены. В первой будет указана модель(CategoryOfGoods) по которой осуществлять поиск а вторая линк к добавлению категории товара и в третьей обработка запроса по поиску.
Необходимо определить внешний вид виджета и прицепить скрипты, для этого определяем переменные: template.html и template.javascript. Во второй описываем функцию добавление элемента: appli{div.id}, привязываем к "Добавить" аяксовый запрос, устанавливаем параметры диалога и автодополнения
Для работы автозоавершения необходимо добавить js-скрипты, а для красоты выпадающего списка css всё взято с http://jqueryui.com
И так один из сложных моментов функция render. В начале генерируем id и имена классов это делаем за тем чтоб они были индивидуальны - что позволит использовать виджет несколько раз на странице. Далее если мы находимся в добавлении нового элемента то $value будет пустым - однако в случае с редактированием нам надо добавить уже существующие элементы для этого и служит данный if. $value - храни айдишники категорий товаров привязанных к данному "контрактору" по этому используем Criteria::IN. Далее просто наполняем опцию {checkbox.lists} содержимым.
Виджет описан однако это ещё не всё нам необходимо обработать запрос по поиску ссылка для запроса генерируется с учётом привязки экшена к текущему классу(contractors). Логичнее было бы повесить на категории товаров но я не генерировал для них модуля
мы указываем в каком формате следует отослать ответ после чего вызываем функцию selectForAutoComplite с строкой в которой хранятся введёны буквы и список уже добавленных, далее возвращаем полученный результат. (из моего названия функции можно определить что ссылка на запрос будет оканчиваться на : contractors/searchgoods )
Теперь определим функцию selectForAutoComplite:
Реализация такая же как в плагине sfWidgetFormPropelJQueryAutocompleter. Тут думаю всё ясно массиве указывается имя и id.
Ещё добавим стиль чтоб список выводился без точек и скрывался диалог:
Теперь остаётся только определить виджет для необходимого нам поля:
Генерировать так url - не верно - однако другого способа я не знаю. Буду благодарен если подскажете.
Для начала суть: необходима выборка из списка нескольких элементов, по умолчанию symfony генерирует форму с sfWidgetFormPropelChoice он довольно не удобен, по этому его лучше заменить на sfWidgetFormPropelChoice. Однако заказчику он не нравился и был его явный минус - если список большой то выводить все его элементы на страницу - не приемлемо. Так что добавим строку с автодополнением. Так же надо учесть что в выпадающий список не должны попадать уже добавленные категории. А чтоб минимизировать его место на основной форме - вынесем его в отдельное окно
По клику "показать список" - появляется форма на которой можно выбрать категории товаров или же добавить новую.
Схема бд:
Виджет будет использоваться для таблици contrctors_goods_buy, он будет выводится при добавления "контрактора".
Если у вас девственно чистый проект - то необходимо создать папку widget в lib, в ней создадим файл который будет классом виджета. Далее будут приводить его код с комментариями:
<?php class sfWidgetFormManyJQueryChoise extends sfWidgetFormSelectCheckbox { public function __construct($options = array(), $attributes = array()) { $options['choices'] = array(); parent::__construct($options, $attributes); }
Наследуем виджет от существующего sfWidgetFormSelectCheckbox - соответственно чекбоксы будем формировать с такой же структурой как и в нём. Первоначально наш список пуст для этого мы определяем опцию choices и далее вызываем родительский конструктор.
public function configure($options = array(), $attributes = array()) { $this->addRequiredOption('model'); $this->addRequiredOption('jsonurl');$this->addRequiredOption('addurl');
Теперь переопределяем функцию configure указываем что опции model, addurl и jsonurl должны быть обязательно определены. В первой будет указана модель(CategoryOfGoods) по которой осуществлять поиск а вторая линк к добавлению категории товара и в третьей обработка запроса по поиску.
Необходимо определить внешний вид виджета и прицепить скрипты, для этого определяем переменные: template.html и template.javascript. Во второй описываем функцию добавление элемента: appli{div.id}, привязываем к "Добавить" аяксовый запрос, устанавливаем параметры диалога и автодополнения
template.html
<input type="button" value="Показать список" id="{show.id}" /> <br> <br> <div id="{div.id}" class="{div.class}" > <ul class="checkbox_list" id="{checkbox.list.id}"> </ul> </div> <div id="{dialog.div.id}" class="{div.class}" title="{div.id}"> <input type="text" id="{input.add.id}" /> <input type="button"
id="{add.button.id}" class="{div.class}" value="Добавить" /> <br> <ul class="checkbox_list" id="{dialog.checkbox.list.id}"> {checkbox.lists} </ul> </div>
template.javascript
<script type="text/javascript"> function appli{div.id}(name, id) { jQuery("#{dialog.checkbox.list.id}").append("<li><input name="{checkbox
.name}" type="checkbox" value=""+id+"" id="{div.id}_"+id+"" checked> <label
for="{div.id}_"+id+"">"+name+"</label></li><br>"); } jQuery("#{add.button.id}").click(function(){ $.ajax({ url: "{link.add}", dataType: "json", data: { name: jQuery("#{input.add.id}").val() }, success: appli{div.id}(data.name, data.name) }); jQuery("#{add.button.id}").addClass("{div.class}"); jQuery("#{input.add.id}").val(""); }); jQuery("#{show.id}").click(function() { jQuery("#{dialog.div.id}").dialog({ width: 300, zIndex: 100, modal: true, close: function() { jQuery("#{checkbox.list.id}").html(jQuery("#{dialog.checkbox.list.id}").html()); } }); }); var setA{show.id} = jQuery("#{input.add.id}").autocomplete({ source: function(request, response) { $.ajax({ url: "{link.sourse}", dataType: "json", data: { term : request.term, already: function(){ var arr = new Array(); jQuery("#{dialog.checkbox.list.id} input:checkbox").each(function(index) { arr.push(jQuery(this).val()); }); return arr; } }, success: function(data) { if (data.length == 0) jQuery("#{add.button.id}").removeClass("{div.class}"); else jQuery("#{add.button.id}").addClass("{div.class}"); response(data); } }); }, minLength: 2, delay: 200, select: function( event, ui ) { appli{div.id}(ui.item.label, ui.item.value); setA{show.id}; }, close: function( event, ui ) { jQuery("#{input.add.id}").val(""); }, open: function(){ $(this).autocomplete("widget").css("z-index", 300); return false; } }); </script>
public function getJavascripts() { return array( 'jquery-1.9.1.js', 'jquery-ui.js' ); } public function getStylesheets() { return array( 'http://code.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.css' => 'screen'
'sfWidgetFormManyJQueryChoise.css' => 'screen'); }
Для работы автозоавершения необходимо добавить js-скрипты, а для красоты выпадающего списка css всё взято с http://jqueryui.com
public function render($name, $value = null, $attributes = array(), $errors = array()) { $template_vars = array( '{div.id}' => $this->generateId($name), '{dialog.div.id}' => $this->generateId($name.'[dialog]'), '{div.class}' => 'class_hide_div', '{input.add.id}' => $this->generateId($name.'[add]'), '{add.button.id}' => $this->generateId($name.'[add_button]'), '{checkbox.list.id}' => $this->generateId($name.'[checkbox]'), '{dialog.checkbox.list.id}' => $this->generateId($name.'[dialog_checkbox]'), '{show.id}' => $this->generateId($name.'[show]'), '{checkbox.lists}' => "", '{link.sourse}' => $this->getOption('jsonurl'), '{checkbox.name}' => $name.'[]', ); if ($value !== null) { $class = constant($this->getOption('model').'::PEER'); $c = new Criteria(); $c->add($class::ID, $value, Criteria::IN); foreach ($class::doSelect($c) as $elem) { $template_vars['{checkbox.lists}'] .= "<li><input name="".$name."[]" type
="checkbox" value="".$elem->getId()."" id="".$template_vars['{div.id}']."_".$elem
->getId()."" checked> <label for="".$template_vars['{div.id}']."_".$elem->ge
tId()."">".(string)$elem."</label></li><br>"; } } return strtr( $this->getOption('template.html').$this->getOption('template.javascript'), $template_vars ); }
И так один из сложных моментов функция render. В начале генерируем id и имена классов это делаем за тем чтоб они были индивидуальны - что позволит использовать виджет несколько раз на странице. Далее если мы находимся в добавлении нового элемента то $value будет пустым - однако в случае с редактированием нам надо добавить уже существующие элементы для этого и служит данный if. $value - храни айдишники категорий товаров привязанных к данному "контрактору" по этому используем Criteria::IN. Далее просто наполняем опцию {checkbox.lists} содержимым.
Виджет описан однако это ещё не всё нам необходимо обработать запрос по поиску ссылка для запроса генерируется с учётом привязки экшена к текущему классу(contractors). Логичнее было бы повесить на категории товаров но я не генерировал для них модуля
class contractorsActions extends autoContractorsActions { public function executeSearchgoods($request) { $this->getResponse()->setContentType('application/json'); $goods = CategoryOfGoods::selectForAutoComplite($request->getParameter('term'), $request->getParameter('already')); return $this->renderText(json_encode($goods)); } public function executeAddgoods($request) { $this->getResponse()->setContentType('application/json'); $good = new CategoryOfGoods(); $good->setName($request->getParameter('name')); $goodId = $good->save(); $res = array('name' => $request->getParameter('name'), 'id' => $goodId); return $this->renderText(json_encode($res)); } }
мы указываем в каком формате следует отослать ответ после чего вызываем функцию selectForAutoComplite с строкой в которой хранятся введёны буквы и список уже добавленных, далее возвращаем полученный результат. (из моего названия функции можно определить что ссылка на запрос будет оканчиваться на : contractors/searchgoods )
Теперь определим функцию selectForAutoComplite:
static public function selectForAutoComplite($query, $already) { $criteria = new Criteria(); $criteria->add(CategoryOfGoodsPeer::NAME, '%'.$query.'%', Criteri
a::LIKE); $criteria->add(CategoryOfGoodsPeer::ID, explode(",",$already), Criteria::NOT_IN); $criteria->addAscendingOrderByColumn(CategoryOfGoodsPeer::NAME); $criteria->setLimit(3); $goods = array(); foreach (CategoryOfGoodsPeer::doSelect($criteria) as $good) { $goods[$good->getId()] = array('label' => (string) $good, 'valu
e' => $good->getId()); } return $goods; }
Реализация такая же как в плагине sfWidgetFormPropelJQueryAutocompleter. Тут думаю всё ясно массиве указывается имя и id.
Ещё добавим стиль чтоб список выводился без точек и скрывался диалог:
ul li { list-style-type:none; }
.class_hide_div { display: none; }
Теперь остаётся только определить виджет для необходимого нам поля:
$this->setWidget('contractors_goods_buy_list', new sfWidgetFormManyJQueryChoise( array('model' => 'CategoryOfGoods', 'jsonurl' => sfContext::getInstance()->getController()->genUrl('contractors/searchgoods'), 'addurl' => sfContext::getInstance()->getController()->genUrl('contractors/addgoods'))));
Генерировать так url - не верно - однако другого способа я не знаю. Буду благодарен если подскажете.
Комментарии
Отправить комментарий