Yii: Множественный автокомплит с помощью CJuiAutoComplete (автокомплит нескольких значений в одно поле)

Yii: Множественный автокомплит с помощью CJuiAutoComplete (автокомплит нескольких значений в одно поле) В развитие одиночного автокомплита, когда в поле подставляется лишь одно значение, выбираемое из другой модели, научимся делать мульти автокомплит, используя всё тот же CJuiAutoComplete.

Как делать обычный автокомплит, мы описали в статье Yii: Автокомплит CJuiAutoComplete для выбора значения поля из другой модели и добавления этого значения в поле. Для того, чтобы сделать мульти автокомплит, нужно немного усложнить javascript и распарсить строку из нескольких значений.

В представление /protected/views/song/_form.php добавляем виджет CJuiAutoComplete для поля movies (этого атрибута нет в таблице БД, там есть movie_ids, потому что в базу данных хотим сохранять id фильмов из модели Movie; впрочем если требуется созранять сами значения, то вместо movies ставьте сразу movie_ids - тогда и дополнительные преобразования при сохранении модели Song не потребуются) выбора фильмов, в которых звучит эта песня. Песня может звучать в нескольких фильмах, поэтому и делаем множественный автокомплит.

...
<div class="row">
 <?php echo CHtml::script("
     function split(val) {
      return val.split(/,\s*/);
     }
     function extractLast(term) {
      return split(term).pop();
     }
   ")?>
 <?php $this->widget('zii.widgets.jui.CJuiAutoComplete', array(
   'model'=>$model,
   'attribute'=>'movies', 
   'source'=>"js:function(request, response) {
      $.getJSON('".$this->createUrl('suggest')."', {
        term: extractLast(request.term)
      }, response);
      }",
   'options'=>array(
     'delay'=>300,
     'minLength'=>2,
     'showAnim'=>'fold',
     'select'=>"js:function(event, ui) {
         var terms = split(this.value);
         // remove the current input
         terms.pop();
         // add the selected item
         terms.push( ui.item.value );
         // add placeholder to get the comma-and-space at the end
         terms.push('');
         this.value = terms.join(', ');
         return false;
       }"
   ),
   'htmlOptions'=>array(
     'size'=>'40'
   ),
  ));?>
 <?php echo $form->labelEx($model,'movie_ids'); ?>
 <?//php echo $form->hiddenField($model,'movie_ids',array('rows'=>6, 'cols'=>50)); ?>
 <?php echo $form->error($model,'movie_ids'); ?>
</div>
...

Здесь мы асинхронно вызвали действие 'suggest', оно в контроллере SongController.php

public function actionSuggest(){
 if (Yii::app()->request->isAjaxRequest && isset($_GET['term'])) {
  $models = Movie::model()->suggestTag($_GET['term']);
  $result = array();
  foreach ($models as $m)
   $result[] = array(
     'label' => $m->title_en,
     'value' => $m->title_en,
     'id' => $m->movie_id,
   );

  echo CJSON::encode($result);
 }
} 

Не забываем этот экшн разрешить в rules() этого контроллера (или в фильтрах, если используете их).

Видим, что этот экшн обращается к методу suggestTag модели Movie.php

 public function suggestTag($keyword){
 $tags=$this->findAll(array(
   'condition'=>'title_en LIKE :keyword',
   'params'=>array(
     ':keyword'=>'%'.strtr($keyword,array('%'=>'\%', '_'=>'\_', '\\'=>'\\\\')).'%',
   )
 ));
 return $tags;
} 

Теперь важный момент: мы получили и подставили значения в поле с атрибутом movies (сейчас там английские названия фильмов, соответствующих песне - просто в строку через запятую), но в базу данных будем сохранять их id (тоже строка id через запятую). Поэтому делаем постобработку в afterFind() и beforeSave() в модели Song.php

В afterFind() добавим

// преобразовать movie_ids -> movies  
$movies=array();
foreach(self::string2array($this->movie_ids) as $movie_id)
$movies[]=Movie::model()->findByPk($movie_id)->title_en;
$this->movies = self::array2string($movies);

В beforeSave()

 // преобразовать movies -> movie_ids
$movie_ids=array();
foreach(self::string2array($this->movies) as $movie)
$movie_ids[]=CHtml::encode(Movie::model()->findByAttributes(array('title_en' => $movie))->movie_id);
$this->movie_ids = self::array2string($movie_ids);   

 

В помощь помимо статьи, указанной в источнике (она наиболее помогла воплотить задуманное), ещё полезно глянуть:

 

almix
Разработчик Loco, автор статей по веб-разработке на Yii, CodeIgniter, MODx и прочих инструментах. Создатель Team Sense.

Вы можете почитать все статьи от almix'а.



Другие статьи по этой теме:

Комментарии (0)     Подпишитесь на RSS комментариев к этой статье.