Изменения в Yii 1.1: CActiveDataProvider

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

yii data provider

Я всё-таки решил продолжить обсуждение фреймворка Yii. Но на этот раз затяжных циклов я не планирую 🙂 , просто хочу рассказать о некоторых нововведениях в версиии 1.1.

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

Но есть момент, который может вызвать вопросы у тех, кто начинает знакомство с фреймворком с версии 1.1. Речь о классе CActiveDataProvider. Как несложно догадаться по названию, он предназначен для работы с базой данных. И если вы создадите сайт с помощью консольной утилиты yiic, то этот класс будет использован в методах actionIndex и actionAdmin контроллеров.

Класс очень удобный и позволяет сократить объём кода. Проблема в том, что документация немного отстаёт от жизни. В разделе о работе с базой данных подробно рассказывается об использовании классов CActiveRecord, CDbCriteria и других, но информация о CActiveDataProvider есть только в API.

Итак, что представляет собой этот класс.

По большому счёту это обёртка вокруг четырёх классов CActiveRecord, CDbCriteria, CPagination и CSort. Именно с их помощью выполняется основная работа с базой данных и очень часто все эти классы используются одновременно. Поэтому вполне логично, что разработчики решили создать общую оболочку для работы с ними.

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

В версиях 1.0.х для вывода записей какой-нибудь таблицы с разбивкой на страницы (пагинацией) использовался следующий код.

$criteria=new CDbCriteria;

$pages=new CPagination(Screenshots::model()->count($criteria));
$pages->pageSize=self::PAGE_SIZE;
$pages->applyLimit($criteria);

$sort=new CSort('Screenshots');
$sort->applyOrder($criteria);

$models=Screenshots::model()->findAll($criteria);

$this->render('admin',array(
	'models'=>$models,
	'pages'=>$pages,
	'sort'=>$sort,
));

Обратите внимание, что здесь создаются экземпляры CDbCriteria, CPagination и CSort. При этом с помощью CDbCriteria мы можем изменять SQL запросы, которые создаёт модель (Screenshots). CPagination используется для настройки пагинации, а CSort – сортировки записей. Массив с объектами CActiveRecord возвращает метод findAll модели. Обратите внимание, что для настройки, которые хранятся в CDbCriteria используются всеми остальными классами.

Теперь взгляните на то, как решается эта же задача в версии 1.1.

$dataProvider=new CActiveDataProvider('Screenshots', array(
	'pagination'=>array(
		'pageSize'=>self::PAGE_SIZE,
	),
));

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

Правда, значительно лучше выглядит? 😉 Хотя, на самом деле, работает код точно также. Просто все экземпляры объектов CPagination, CSort и CDbCriteria находятся внутри CActiveDataProvider.

Обратите внимание, что в первом параметре конструктора мы передаём название модели, данные которой нужно получить. CActiveDataProvider сам вызовет соответствующие методы find() или findAll().

Тут возникает вполне резонный вопрос: «Каким образом настраивать поиск, разбивку на страницы и т.п.?» Всё очень просто, нужно передать массив с соответствующими параметрами в конструкторе. Например.

$dataProvider=new CActiveDataProvider('Post', array(
	'criteria'=>array(
		'condition'=>'status=1 AND tags LIKE :tags',
		'params'=>array(':tags'=>$_GET['tags']),
		'with'=>array('author'),
	),
	'pagination'=>array(
		'pageSize'=>20,
	),
));

Как видите, настройки указываются точно так же, как и в предыдущих версиях (по большому счёту это они и есть). Т.е. вы можете спокойно использовать документацию по CDbCriteria при настройке CActiveDataProvider.

Кроме того, если возникнет необходимость, можно использовать методы getCriteria(), setCriteria(), getSort(), setSort() для чтения и установки соответствующих объектов.

Передача данных в представление.

Как несложно заметить по листингам, в версиях 1.0.х необходимо было передавать три параметра: с данными, настройками пагинации и сортировки. В новом варианте передаётся только один – сам объект CActiveDataProvider.

При этом если вы будете вручную вытягивать из него данные, код получится довольно объёмным. Но в версию 1.1 входят компоненты zii, один из которых (zii.widgets.grid.CGridView) специально разработан для использования вместе с CActiveDataProvider. Т.е. вы передаёте ему экземпляр CActiveDataProvider и заголовки столбцов, а он на основе этих данных формирует таблицу.

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

Интересно почитать

Раскрутка сайта занятие увлекательное, но ошибки обойдутся недёшево.

  • xandeadx

    а можно результат работы виджета zii.widgets.grid.CGridView в виде скрина?

  • xandeadx

    а можно результат работы виджета zii.widgets.grid.CGridView в виде скрина?

  • Sam

    http://www.zhex.net/wp-content/uploads/2009/12/GRIDVIEW1.JPG

    Кстати, text-align: justify тут выглядит ужасно!

  • Sam

    http://www.zhex.net/wp-content/uploads/2009/12/GRIDVIEW1.JPG

    Кстати, text-align: justify тут выглядит ужасно!

  • Yii весьма… неоднозначный фреймворк. Особенно после CI 🙂
    Но понравился очень. Быстрый, удобный, красивый. Правда для себя отметил два «но».
    Во-первых очень своеобразные конфиги. Непривычно писать конфиг в виде return.
    А второе, поправьте меня если я не прав, но Yii это фреймворк сам в себе. Поясню. Мне очень нравится как написаны контроллеры, чуть меньше нравится работа представлений, но вот модель… Даже не сама модель, а часть работы с базой мне не нравится. Чуть усложнишь базу и нативная модель даёт сбой. Приходится подключать Doctrine. В этом плане нравится symfony, где работа с моделью сразу ведётся средствами Doctrine или Proper на выбор.
    В частности пришлось писать свой класс DoctrineDataProvider (к счастью не очень сложно). А с учётом того что в версии 1.1.1 планируется класс CActiveFrom… Опять они очень сильно специализируются…

    • По большому счету, они нигде не говорили, что их библиотека для работы с БД — аналог Doctrine или Propel.

      Конфиги — дело привычки. Настройки хранятся в виде массива, а используется return или нет — дело десятое (на мой взгляд).

      • Аналог, не аналог, но и то и другое и третье — ORM с различным уровнем возможностей. Что-то удобнее, что-то… менее удобнее. Но это, как и конфиги, вы правильно выразались — дело привычки. И я бы ещё добавил — особенности проектируемого приложения. В одном у меня замечально работаеит их нативый AR. Но для другого — он не подходит и понадобился Doctrine.

        • Расскажите для каких задач не подходит их AR?

        • Полностью согласен насчет особенностей проектируемого приложения.

          Но насчет терминологии поспорю. Active Record, Data Gateway, Object Relational Mapping — это все разные паттерны. И если всех их считать производными от ORM, то тогда проще их просто назвать «библиотеками для работы с БД» и вообще не морочить себе голову с названиями.

          И, если можно, вопрос. На ваш взгляд, человеку, который вообще не работал с ORM, какой из них будет понятнее (Propel, Doctrine или что-нибудь попроще, вроде RedBean)? Я планирую пост на эту тему, но нужен хороший пример, не хочется что бы получилась просто очередная инструкция «как подключить ORM».

  • Yii весьма… неоднозначный фреймворк. Особенно после CI 🙂
    Но понравился очень. Быстрый, удобный, красивый. Правда для себя отметил два «но».
    Во-первых очень своеобразные конфиги. Непривычно писать конфиг в виде return.
    А второе, поправьте меня если я не прав, но Yii это фреймворк сам в себе. Поясню. Мне очень нравится как написаны контроллеры, чуть меньше нравится работа представлений, но вот модель… Даже не сама модель, а часть работы с базой мне не нравится. Чуть усложнишь базу и нативная модель даёт сбой. Приходится подключать Doctrine. В этом плане нравится symfony, где работа с моделью сразу ведётся средствами Doctrine или Proper на выбор.
    В частности пришлось писать свой класс DoctrineDataProvider (к счастью не очень сложно). А с учётом того что в версии 1.1.1 планируется класс CActiveFrom… Опять они очень сильно специализируются…

    • По большому счету, они нигде не говорили, что их библиотека для работы с БД — аналог Doctrine или Propel.

      Конфиги — дело привычки. Настройки хранятся в виде массива, а используется return или нет — дело десятое (на мой взгляд).

      • Аналог, не аналог, но и то и другое и третье — ORM с различным уровнем возможностей. Что-то удобнее, что-то… менее удобнее. Но это, как и конфиги, вы правильно выразались — дело привычки. И я бы ещё добавил — особенности проектируемого приложения. В одном у меня замечально работаеит их нативый AR. Но для другого — он не подходит и понадобился Doctrine.

        • Расскажите для каких задач не подходит их AR?

        • Полностью согласен насчет особенностей проектируемого приложения.

          Но насчет терминологии поспорю. Active Record, Data Gateway, Object Relational Mapping — это все разные паттерны. И если всех их считать производными от ORM, то тогда проще их просто назвать «библиотеками для работы с БД» и вообще не морочить себе голову с названиями.

          И, если можно, вопрос. На ваш взгляд, человеку, который вообще не работал с ORM, какой из них будет понятнее (Propel, Doctrine или что-нибудь попроще, вроде RedBean)? Я планирую пост на эту тему, но нужен хороший пример, не хочется что бы получилась просто очередная инструкция «как подключить ORM».

  • гост

    Если использовать CSort и в модель добавить relations связь со второй таблицей, то при сортировке по полю, имеющему такое же имя полей в 1-ой и во 2-ой выдает ошибку 1052. Как с этим бороться еще не решил.
    Надеюсь доступно объяснил)

    • гост

      Нашел такое решение. для сортировки по значению из первой таблицы:
      таблица 1, поля: page_id,title,type,created
      таблица 2, поля: type_id,title,…
      Метод relations в модели.

      public function relations()
      {
      return array('page_type' =>
      array(self::BELONGS_TO, 'Page_type', 'type','select'=>'page_type.title'));
      }

      Выборка в контроллере:

      $criteria=new CDbCriteria;
      $pages=new CPagination(Page::model()->count($criteria));
      $sort=new CSort('Page');
      $sort->applyOrder($criteria);
      $criteria->with=array('page_type');
      $criteria->order='t.'.$criteria->order;

      $models=Page::model()->findAll($criteria);

      Насколько данный метод правильный?

      • Я не сталкивался с такой ситуацией, поэтому точно не отвечу. Но выглядит вполне нормально. В документации так и сказано, если имена полей одинаковы, то к ним нужно добавлять имя таблицы. Вы так и сделали 😉

        • гост

          Я думал, что можно где то в метода прописать что нужно и yii автоматом все сделает:)
          Во всяком случае, если найду более красивое решение — отпишусь.
          Спасибо)

  • гост

    Если использовать CSort и в модель добавить relations связь со второй таблицей, то при сортировке по полю, имеющему такое же имя полей в 1-ой и во 2-ой выдает ошибку 1052. Как с этим бороться еще не решил.
    Надеюсь доступно объяснил)

    • гост

      Нашел такое решение. для сортировки по значению из первой таблицы:
      таблица 1, поля: page_id,title,type,created
      таблица 2, поля: type_id,title,…
      Метод relations в модели.

      public function relations()
      {
      return array('page_type' =>
      array(self::BELONGS_TO, 'Page_type', 'type','select'=>'page_type.title'));
      }

      Выборка в контроллере:

      $criteria=new CDbCriteria;
      $pages=new CPagination(Page::model()->count($criteria));
      $sort=new CSort('Page');
      $sort->applyOrder($criteria);
      $criteria->with=array('page_type');
      $criteria->order='t.'.$criteria->order;

      $models=Page::model()->findAll($criteria);

      Насколько данный метод правильный?

      • Я не сталкивался с такой ситуацией, поэтому точно не отвечу. Но выглядит вполне нормально. В документации так и сказано, если имена полей одинаковы, то к ним нужно добавлять имя таблицы. Вы так и сделали 😉

        • гост

          Я думал, что можно где то в метода прописать что нужно и yii автоматом все сделает:)
          Во всяком случае, если найду более красивое решение — отпишусь.
          Спасибо)

  • Anton

    «В версиях 1.1.х для вывода записей какой-нибудь таблицы с разбивкой на страницы (пагинацией) использовался следующий код»

    Опечатка? В версиях 1.0.*

  • Вы правы, исправил.
    Спасибо!

  • Рамиль

    Просто спасибо за статью

  • Pernik

    здравствуйте, подскажите пожалуйста как вывести на экран строку запроса которая получится ???

    • Самый удобный вариант — включить журналирование.

      'routes'=>array(
      array(
      'class'=>'CWebLogRoute',
      'levels'=>'trace, info, error, warning',
      ),
      ...

      Этот код нужно добавить в config/main.php. В результате лог будет показан внизу станицы. Также можно сохранить лог в файле.

      Подробнее здесь.

      В Yii2 для этих целей сделали специальный тулбар.