Yii PHP framework: создание запросов с условием IN

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

yii in condition

Последнее время мне довольно часто приходится работать с фреймворком Yii. И иногда возникают вопросы, ответы на которые не очевидны (во всяком случае для меня).

Попробую объяснить. Сам фреймворк, на мой взгляд, достаточно удобный и документация подробная. Но классов много, и формат передачи данных их методам часто сильно влияет на результат.

Рассмотрим небольшой пример – использование оператора IN.

Т.е. нужно сформировать примерно такой запрос.

SELECT * FROM tbl_users WHERE id IN (1, 2, 3)


В Полном руководстве на эту тему информации я не нашел, а копаться в методах CActiveRecord времени не было.

Вообще, я очень не люблю такие ситуации. С одной стороны, понятно как нужно написать запрос, с другой – хочется сделать это красиво, используя встроенную библиотеку. Копаться в документации не хочется, ведь на это уходит время, а запрос можно составить за считанные минуты. Но не все так просто.

Сначала покажу решение «в лоб».

$ids = array(1, 2, 3);

$dataProvider=new CActiveDataProvider('User',array(
	'criteria'=>array(
		'condition'=>'id IN ('.implode(',', $ids).')',
	)
));

$this->render('admin',array(
	'model'=>$dataProvider,
));

Тут мы формируем массив со значениями, которые будут перечислены после IN. А затем, с помощью функции implode преобразуем его в строку и просто вставляем в запрос.

Недостатки такого подхода очевидны.

Во-первых, выглядит код не очень красиво.

Во-вторых, в реальном приложении значения для массива $ids будут откуда-то поступать, возможно, из параметров запроса. И если массив $ids окажется пустым, то при выполнении запроса возникнет ошибка.

В-третьих, полученные значения нужно проверить. А написать что-то вроде

'params'=>array(':id'=>implode(', ', $ids)),

не получится, т.к. результат работы функции implode будет взят в кавычки. Т.е. проверку необходимо сделать предварительно.

Как видите, возникают проблемы практически на пустом месте. Нужно написать код проверок, решить, что делать с пустым массивом, затем протестировать этот код. И все ради решения тривиальной задачи.

Решение с помощью библиотеки Yii.

Оно выглядит гораздо проще.

$model = User::model()->findAllByAttributes(array('id'=>array(1, 2, 3)));
$dataProvider=new CActiveDataProvider('User');
$dataProvider->setData($model);

$this->render('index',array(
	'dataProvider'=>$dataProvider,
));

Метод findAllByAttributes класса CActiveRecord позволяет искать записи в БД по названию поля и значению. При этом если значение является массивом, то используется оператор IN.

Этот же самый код можно записать немного иначе.

$dataProvider=new CActiveDataProvider('User');
$dataProvider->criteria->addInCondition('id', array(1,2,3));

$this->render('index',array(
	'dataProvider'=>$dataProvider,
));

Тут уже используется метод addInCondition класса CDbCriteria. Если вы работаете с CActiveDataProvider, то получить доступ к объекту CDbCriteria можно с помощью свойства criteria.

Теперь проведем несколько экспериментов.

Попробуем в массиве передать строку.

$dataProvider->criteria->addInCondition('id', array(1,2,'test'));

В результате будет выполнен следующий запрос.

SELECT * FROM 'tbl_user' WHERE "id" IN (1, 2, 0)

Т.е. текстовое значение было заменено нулем, что вполне логично, т.к. поле id имеет тип INT.

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

$dataProvider->criteria->addInCondition('id', array(1, 'admin','user'));

сформирует такой запрос

SELECT * FROM 'tbl_user' WHERE "id" IN ('1','admin', 'user')

А если массив окажется пуст, то фреймворк гарантирует, что из таблицы не будет выбрана ни одна запись.

$dataProvider->criteria->addInCondition('id', array());
SELECT * FROM 'tbl_user' WHERE 0=1

Как видите, использование встроенных библиотек всё-таки оправдывает время, потраченное на их изучение 😉

Только не забывайте при работе с документацией Yii учитывать один момент. Классы библиотеки тесно связаны между собой, поэтому искать нужно не только в методах класса, с которым вы непосредственно работаете, но и в методах объектов, которые использует этот класс.

Так CActiveDataProvider использует CDbCriteria для формирования условий, CPagination – для разбивки на страницы, CSort – для сортировки и т.д.

Вообще, я заметил, чем больше работаешь с фреймворком, тем логичнее кажутся решения разработчиков и тем проще ориентироваться в документации 🙂

Удачи!

  • К yii нужно достаточно долго привыкать, поначалу мне казалось все непонятно, потом все очень даже логично

  • AmdY

    к сожалению, документация очень часто устаревает и приходится копаться в коде и даже phpdoc зачастую не спасает. Поэтому блоги наше всё, из них узнаёшь кучу тонкостей.

  • блин, такие очевидные вещи.
    вот лучше бы углубленную документацию CGridView
    а то там некоторые вещи совсем не понятны (

  • вот там разрыв мозга

  • Вы читаете мои мысли 🙂 (о CGridView). Я как раз планирую пост об изменении дизайна и разметки этого компонента.

  • может на неделе напишу пост на хабре о некоторых неочевидностях

  • Alexy4b

    Проверь пожалуйста мейл на gala.net, там как вопрос об использовании CGridView.

  • Александр

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

  • хорошо, учту

  • Agree with Landlord sit.Various of online games provided are dynamic sense and free indefinitely.We are looking forward to you to here share the experience of
    exciting game competition! Dior homme shoes

  • поначалу было не понятно, но это естественный процесс. Как разобрался скажу «это гениально ! «

    когда разбирался с zf (нужно было по работе) преимущество yii для меня было очевидным, причем во всем. Возможно я не прав и это дело «вкуса»

  • Мне тоже ZF показался «навороченным». Хотя широкое использование паттернов — это хорошо.
    Думаю ZF лучше подходит для больших проектов, со сложной архитектурой. Для решения простых задач приходиться писать много «лишнего» кода.

  • Дмитрий

    Отличная статья, спасибо
    Тут както разбирался с одной задачей, пока не могу решить, имеется в базе поле допустим cat_id в котором могут храниться ID раздела или разделов и выглядеть могут примерно так 1,2 или 33,2 или 123,65,32,78 или 34
    Тоесть в данном поле могут храниться не только один ID, и могут несколько чередуясь через запятую. Вопрос, если мне потребуется вывести раздел у которого ID допустим 65 как составить запрос?

  • Использовать в запросе оператор LIKE

  • endo

    А как в запрос where in добавить order by id(…)

    • $dataProvider=new CActiveDataProvider('User', array(
      'criteria'=>array(
      'condition'=>'id IN ('.implode(',', $ids).')',
      'order'=>'create_time DESC',
      ),
      ));