Yii PHP фреймворк: создаем поле с автозаполнением

Владимир | | MySQL, PHP, Web разработка, Yii.

yii php framework autocomplete

Приветствую всех!

Сегодня я покажу небольшой пример использования виджета CAutoComplete из фреймворка Yii. Как несложно догадаться, он создаёт текстовое поле, под которым может появляться список с вариантами подстановок.

Принцип создания такого поля довольно прост. На странице нужно поместить обычное текстовое поле и назначить событию onKeyUp обработчик, который будет отправлять AJAX запросы серверу. В этих запросах нужно передавать введённый посетителем текст. Сервер ищет совпадения с этим текстом в БД и возвращает результат браузеру. JavaScript обработчик создаёт список с вариантами, полученными от сервера, и показывает его под полем.

Как видите, алгоритм несложный, но рутинной работы много.

Теперь, давайте разберёмся, что сделает за вас виджет, а что придётся делать вам.

Для работы виджет использует плагин Autocomplete библиотеки jQuery, т.е. писать JS код от вас не потребуется (если, конечно, вы не хотите изменить стандартное поведение плагина).

Кроме того, виджет создаст текстовое поле и подключит все необходимые JS и CSS файлы. Вам нужно будет только указать некоторые настройки.

Основная часть вашей работы будет заключаться в написании метода, выполняющего обработку запросов и поиск данных в базе.

Теперь рассмотрим пример.

Допустим, у нас есть база данных с названиями стран. Таблица называется countries, а поле, в котором хранятся названия — c_name.

Создаём для этой таблицы модель.

yiic shell
model countries

Примечание. Тут я предполагаю, что у вас создано веб приложение и настроено подключение к базе данных.

Теперь нужно создать текстовое поле. CAutoComplete – это обычный виджет, поэтому поместить его можно где угодно. Если хотите, чтобы поле выводилось на отдельной странице – добавляйте его в соответствующее представление, хотите показывать на каждой странице – добавляйте в макет (layout).

Код вставки виджета выглядит так.

$this->widget('CAutoComplete',
	array(
		'model'=>'countries',
		'name'=>'country',
		'url'=>array('countries/autocomplete'),
		'minChars'=>2,
	)
);

Во втором параметре необходимо передать массив с параметрами. Обязательными являются первые два.
model – имя модели.
name – атрибут name текстового поля.
url – адрес скрипта, который будет обрабатывать AJAX запросы.
minChars – минимальное количество символов, при котором выполняется отправка запроса.

Естественно, это не все параметры. Подробный их перечень вы найдете на странице документации.

По мере ввода текста серверному скрипту будут отправляться AJAX запросы вида:

index.php?r=countries/autocomplete&q=%D0%B0%D0%BB&limit=10×tamp=1273944702297

В параметре q передаётся введённый посетителем текст, а в параметре limit – максимальное количество подстановок.

Рассмотрим метод, выполняющий обработку этого запроса.

public function actionAutoComplete() {
	
	if (isset($_GET['q'])) {
		
		$criteria = new CDbCriteria;
		$criteria->condition = 'c_name LIKE :country';
		$criteria->params = array(':country'=>$_GET['q'].'%');
		
		if (isset($_GET['limit']) && is_numeric($_GET['limit'])) {
			$criteria->limit = $_GET['limit'];
		}
		
		$countries = countries::model()->findAll($criteria);
		
		$resStr = '';
		foreach ($countries as $country) {
			$resStr .= $country->c_name."\n";
		}
		echo $resStr;
	}
}

Здесь мы проверяем, пришли ли параметры и формируем запрос.

Условие записываем с помощью класса CDbCriteria. В атрибуте condition указываем название поля и операцию сравнения (LIKE). В атрибуте params – текст, введенный пользователем. Обратите внимание, мы добавляем к нему знак %, т.е. найдены будут все страны, названия которых начинаются с указанных букв.

Затем добавляем параметр limit и выполняем запрос (findAll).

Из результатов поиска формируем строку с найденными названиями стран. Разделителем служит символ новой строки (это требование плагина). И отправляем результат браузеру.

Как видите, пользоваться виджетом совсем не сложно.

Все вопросы, советы и замечания пишите в комментариях.

Удачи!

  • SamDark

    У CDbCriteria есть метод addSearchCondition. С ним будет немного чище.

  • test10

    demo?

  • Спасибо!

    Получилось так

    $criteria->addSearchCondition('c_name', $_GET['q'].'%', false);

    если оставить с дефолтными настройками
    $criteria->addSearchCondition('c_name', $_GET['q']);
    то % будет добавлен с обоих сторон, а это не совсем то, что хотелось получить 🙂

  • Не вижу смысла. Все-равно код придется переделывать под конкретную БД.

  • Michael

    Спасибо, очень нужная вещь

  • По-моему из $_GET['q'] надо удалять %, иначе передадут % и будет ошибка.
    Вы еще не используете gii для кодогенерации?

  • Согласен, проверять полученные данные нужно обязательно.
    gii попробовал, но особых преимуществ пока не почувствовал.

  • Storm

    Спасибо! Статья как раз вовремя 🙂
    Попутно возник вопрос:
    не подскажете как можно в ajax-запросе передать кроме содержимого поля, значение выпадающего списка, расположенного на той же форме, где поле с автодополнением?

    У CAutoComplete есть параметр extraParams (http://www.yiiframework.com/doc/api/CAutoComple…), который позволяет в ajax-запросе передавать дополнительные параметры. А вот как в него загрузить значение <select> что-то не соображу 😐

  • Спасибо за интересный примерчик! Думаю, пригодится!

  • Судя по всему, CAutoComplete немного урезает возможности плагина Autocomplete.

    В документации сказано, что параметру extraParams можно передавать функцию. Но если передать функцию в настройках CAutoComplete, то она будет взята в кавычки, т.е. станет обычной строкой.

    Поэтому решение такое.

    Написать свой js скрипт с кодом

    jQuery(«#country»).setOptions({
    'extraParams':{'mypar':function() {return $(«#myselect»).val();}}}
    );

    country — id поля с автозавершением,
    myselect — id выпадающего списка.

    Подключить этот скрипт с помощью registerClientScript()

  • Storm

    Большое спасибо! Если бы сам и додумался, то еще не скоро 🙂

  • Jay

    Конечно круто кодить только на PHP не пользуясь JavaScript, но посмотрите на то что пишет Yii в JavaScript. Иногда гораздо легче реализовать простые Ajax запросы методом jQuery — $('#element').load('?r=controller/action',{var:value});

  • Я согласен. Но никто не запрещает использовать вместе с Yii свои скрипты, если код, который генерирует Yii вас не устраивает.

  • Pingback: Yii - CAutoComplete | Блог об интересах()

  • Roe-ru

    Сходу, не получилось..
    Владимир, уточните пожалуйста, может не так понял:
    в виджете: 'name'=>'test1', — это название html input(а)
    тогда в методе:
    $criteria->condition = 'cat_name LIKE :test1';
    $criteria->params = array(':test1'=>$_GET['q'].'%');
    Спасибо.

    • Roe-ru

      прошу прощения, все работает.

  • Refresh-2004

    Всё работает, НО появилась следующая проблема:
    Вводим кусочек слова, выдало нормальный список с вариантами! вводим что-то другое, выдаёт предыдущий результат, причём даже до контролера не доходит запрос!!! Где-то каким-то образом кэшируются данные!! вопрос где и какого лешего?! Проходит, ну не знаю, минуты 2-5, не засекал, вводим поиск и опять выдаётся нормальный результат… и всё повторяется заново!! Помогите плиз!!!

    • Попробуйте добавить GET параметр со случайным числом. На серверной стороне просто его игнорируйте.

  • Castortroy06

    у меня проблема. после того, как сработал autocomplete, yii ругается на незаполненное поле(то, которое работает с autocomplete)

    • Проверьте firebug'ом уходят ли данные на сервер. Дело в том, что autocomplete просто вставляет текст в поле, а при отправке формы не играет роли каким образом было заполнено поле, с клавиатуры, копипастом или с помощью autocomplete.

      • GrinXdrive

        Здравствуйте, у меня такая же проблема. Выборка данных для поля Autocomplete все работает хорошо, и данные вносятся в форму, но вот в базу данных заносится Null. Что можете посоветовать?

        • 1) Посмотрите, ушли ли данные на сервер (в инструментах chrome вкладка «сеть»).
          2) Если данные не ушли, проблема на клиентской стороне (скорее всего, с автокомплитом). В этом случае попробуйте отключить автокомплит и отправить форму.
          3) Если данные ушли. Лучше всего установить xDebug и посмотреть как заполнились данные модели перед сохранением (перед вызовом save) и есть ли ошибки после сохранения (параметр errors в модели). Причины ошибки могут быть разные, например, имя поле не указано в массиве rules модели.

        • GrinXdrive

          Благодарю за Вашу отзывчивость, а также за урок. Проблему решил. В представлении, заменил обращение не к модели countries, а к переменной $model. Теперь данные вносятся в БД.

          'model'=>$model, //'countries',

  • $criteria->limit = $_GET['limit'];
    А если я передам 1000?
    Лимит — нужно задать в скрипте.

    • Наверное, лучше поставить ограничение по максимальному значению.
      $criteria->limit = ($_GET['limit'] < 100) ? $_GET['limit'] : 100;

  • Denis

    Здравствуйте!Спасибо за отличную статью, очень помогла.
    Подскажите каким образом мне получить в поле Value значение id найденного в базе текстового поля. В моем случае пользователь ищет имя пользователя а дальше я работаю с id .

    • Значение id доступно после получения модели. Т.е. в цикл (строки 16-18) вы можете добавить
      $value = $country->id

  • Александр

    Скажите, отличается ли чем-то метод $criteria->compare('artist_name', $_GET['q'], true); от вышеперечисленных condition и addSearchCondition?

    • Вы можете использовать compare, но учтите, что оператор сравнения устанавливается исходя из первых символов значения ($_GET['q']). При этом если оператор не указан, а третий параметр равен true, то метод вернет все записи в которых $_GET['q'] является подстрокой. Т.е. запись
      $criteria->compare('artist_name', $_GET['q'], true);
      эквивалентна
      $criteria->addSearchCondition('c_name', '%'.$_GET['q'].'%', false);
      а не
      $criteria->addSearchCondition('c_name', $_GET['q'].'%', false);

      И кроме того, addSearchCondition позволяет указать дополнительные параметры. Например, использовать NOT LIKE вместо LIKE.