Yii фреймворк: создание кнопок с помощью CButtonColumn

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

yii php CButtonColumn

Тему этого поста подсказал мне читатель по имени Alex, за что ему большое спасибо.

Речь о компонентах zii, которые, начиная с версии 1.1, входят в состав фреймворка, и активно используются утилитой yiic при генерации кода.

К сожалению, документация по этим компонентам есть только в виде API (комментарии к исходникам) и её явно недостаточно.

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

Рассмотрим такую ситуацию. Для одной из таблиц в БД вы создали стандартный набор CRUD операций. И вам нужно в таблицу с перечнем записей добавить дополнительную кнопку. На первый взгляд, задача довольно простая, т.к. таблица генерируется с помощью виджета 'zii.widgets.grid.CGridView' и среди компонентов zii есть CButtonColumn, который специально предназначен для создания колонок с кнопками. Т.е. задача заключается в настройке этого компонента.

Чтобы было понятнее, рассмотрим небольшой пример.

Создаём новое приложение
yiic webapp .
По-умолчанию в нём есть sqlite база с одной таблицей (данными пользователей)

Создаём модель
yiic shell
model User tbl_user

и CRUD интерфейс
crud User

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

При этом страница управления записями (index.php?r=user/admin) содержит таблицу с перечнем записей и столбцов с кнопками «View», «Update», «Delete». Я сделал скриншот (несколько колонок убрал, чтобы уменьшить ширину таблицы).

initial grid

Взгляните на код, который создаёт стандартную таблицу с тремя кнопками.

<?php $this->widget('zii.widgets.grid.CGridView', array(
	'dataProvider'=>$dataProvider,
	'columns'=>array(
		'id',
		'username',
		'password',
		'email',
		array(
			'class'=>'CButtonColumn',
		),
	),
)); ?>

В массиве columns перечисляем названия столбцов таблицы. Если имя столбца совпадает с полем таблице, то оно будет заполнено соответствующими данными. В последнем элементе указан массив с одним элементом 'class'=>'CButtonColumn'. Этого достаточно для создания трёх стандартных кнопок.

Попробуем добавить ещё один столбец с кнопкой «AJAX запрос». Для этого добавим ещё один элемент в массив 'columns'

array(
	'class'=>'CButtonColumn',
	'buttons'=>array(
		'preview'=>array(
			'label'=>'AJAX запрос',
			'url'=>'…',
			'click'=>'…',
		),
	),
	'template'=>'{preview}',
),

Принцип следующий. В элементе 'buttons' нужно указать массив с новыми кнопками. При этом ключ каждого элемента этого массива является названием кнопки. Его мы должны использовать в элементе 'template' для того, чтобы показать кнопку в таблице. Название необходимо заключит в фигурные скобки. При этом можно добавить в одну ячейку сразу несколько кнопок, например, так:
'{view} {update} {delete}'.

Для каждой кнопки необходимо указать массив с параметрами.
'label' – содержит текст, который будет отображаться на кнопке.
'url'PHP выражение, которое сформирует ссылку для данной кнопки.
'click'JS функция, которая будет назначена в качестве обработчика клика по кнопке.

Кроме того, можно указать картинку и массив с html атрибутами с помощью параметров 'imageUrl' и 'options'. Но сейчас речь не о них. У меня больше всего вопросов вызвали 'url' и 'click'.

Расписывать свои ковыряния в исходниках я не буду. Лучше сразу покажу решение.

В параметре 'url' нужно записать PHP выражение, которое сформирует URL, в виде строки, т.е. в кавычках. Например,

'url'=>'Yii::app()->createUrl("user/getuser")'

При этом будут доступны две переменные: $row и $data. Первая содержит номер строки, вторая — объект с данными текущей записи. Т.е. добавить в запрос GET параметр с email’ом пользователя можно так:

'url'=>'Yii::app()->createUrl("user/getuser", array("email"=>$data->email))'

Для того, чтобы проверить отправку AJAX запросов я добавил в контроллер метод actionGetuser. Он ищет пользователя по его email'у.

public function actionGetuser() {
	if (isset($_GET['email'])) {
		$user = User::model()->find('email=:email', array(':email'=>$_GET['email']));
		if (null !== $user) {
			echo $user->username;
		} else {
			echo 'unknown';
		}
	} else {
		echo 'unknown';
	}
}

Напишем функцию, которая будет выполнять отправку запроса. Я добавил её прямо в представление.

<?php
$js_preview =<<< EOD
function() {
	var url = $(this).attr('href');
	$.get(url, function(response) {
		alert(response);
	});
	return false;
}
EOD;
?>

<?php $this->widget('zii.widgets.grid.CGridView', array(
	'dataProvider'=>$dataProvider,
	'columns'=>array(
		'id',
		'username',
		'password',
		'email',
		array(
			'class'=>'CButtonColumn',
		),
		array(
			'class'=>'CButtonColumn',
			'buttons'=>array(
				'preview'=>array(
					'label'=>'AJAX запрос',
					'url'=>'Yii::app()->createUrl("user/getuser", array("email"=>$data->email))',
					'click'=>$js_preview,
				),
			),
			'template'=>'{preview}',
		),
	),
)); ?>

Как видите, текст JS функции присвоен переменной $js_preview. Эту переменную мы и указываем в параметре 'click'.

Принцип работы следующий.

1) Получаем URL данной кнопки (с помощью $(this).attr('href')). Он уже содержит email в качестве GET параметра.

2) Отправляем AJAX запрос (с помощью $.get).

3) Показываем результат (alert(response)).

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

Если есть замечания или вопросы, пишите, обсудим 😉

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

Дейтинг — хорошая возможность заработать на своих интернет-проектах.

Спастись от жары не сложно. Достаточно купить сплит-системы.

  • webs

    Очень интересно и вовремя, спасибо. Вопросов и замечаний пока нет .

  • webs

    Очень интересно и вовремя, спасибо. Вопросов и замечаний пока нет .

  • Нужно будет попробовать. Спасибо.

  • Нужно будет попробовать. Спасибо.

  • С первого раза не разобрался, но тема хорошая.

  • С первого раза не разобрался, но тема хорошая.

  • тема хорошая !!!

  • тема хорошая !!!

  • Полезный материал, пригодится в работе. Спасибо за инфу!

  • Полезный материал, пригодится в работе. Спасибо за инфу!

  • Действительно полезная информация! И пример очень понятный!

  • Действительно полезная информация! И пример очень понятный!

  • Понятным языком написано, разобрался сразу!

  • Понятным языком написано, разобрался сразу!

  • А можно как-то вынести настройки кнопок в глобальную конфигурацию? Я пробовал так:
    'params' => array(
    'class'=>'CButtonColumn',
    'deleteButtonLabel'=>'Hallo',
    ),
    но при инициализации выдаётся ошибка

  • Попробуйте так
    'params' => array(
    'button1'=>array(
    'class'=>'CButtonColumn',
    'deleteButtonLabel'=>'Hallo',
    )
    ),

  • отличная идея

  • Oleg

    Привет! Спасибо за статью во-первых) Во-вторых я столкнулся с проблемой — запрос выполняется дважды по одному клику, в чем может быть причина?

    • Попробуйте так
      $js_preview =<<< EOD
      function(e) {
      e.stopPropagation();
      var url = $(this).attr('href');
      $.get(url, function(response) {
      alert(response);
      });
      return false;
      }
      EOD;

      • Oleg

        Владимир, вы замечательный человек) Мало того что статьи хорошие, так еще и отвечаете очень быстро. Но не помогло к сожалению. Поподробнее распишу, может быть у меня где-то критический недочет, а для вас будет очевидно:
        Шаблон:
        widget('application.components.adminMenu'); ?>

        Отсюда не аяксом я запрашиваю экшн:
        public function actionBaner() {
        $pic = Mbaner::model()->findAllByAttributes(array('act'=>'1'));
        $dataProvider=new CActiveDataProvider('Mbaner');
        $this->render('baner',array('dataProvider'=>$dataProvider),false,true);
        }

        View baner содержит следущее:
        <?php
        $js_preview =<<widget('zii.widgets.grid.CGridView', array(
        'id'=>'mainBaners',
        'dataProvider'=>$dataProvider,
        'filter'=>$pic,
        'columns'=>array(
        'id',
        'name',
        'image',
        'date',
        'act',
        array(
        'class'=>'CButtonColumn',
        'buttons'=>array(
        'preview'=>array(
        'label'=>'Удалить',
        'url'=>'Yii::app()->createUrl(«admin/delban», array(«id»=>$data->id))',
        'click'=>$js_preview,
        'imageUrl'=>Yii::app()->request->baseUrl.'/images/system/cross.png',
        ),
        ),
        'template'=>'{preview}',
        ),
        ),
        ));
        ?>
        По клику отправляется сразу два запроса.

        • Oleg

          эхо content во втором диве шаблна заскринился

        • Спасибо )
          Но так просто найти ошибку не получилось. Может вы сможете где-нибудь выложить ваше приложение, чтобы я мог firebug'ом посмотреть?

        • Oleg

          Спасибо за желание помочь, но я разобрался. Причиной было дублирующий вызов jQuery в хеде. Я прописывал его руками, yii добавлял свой. Убрал, исчезли ошибки и повторная обработка события.

        • Очень рекомендую пользоваться встроенными средствами Yii для подключения JS кода — http://www.simplecoding.org/yii-php-framework-sozdayom-igrovoj-sajt-chast-7-rabota-s-javascript-i-stranicy-igr.html

        • N-professor

          Здравствуйте. А подскажите пожалуйста, как сделать что бы строка над которой провели действие(нажав на кнопку) исчезла. Как при удалении.

        • Установите для каждой кнопки обработчик события click.

          Код удаления строки будет примерно такой
          $(this).parent().parent().remove;
          (если используется библиотека jQuery)

        • Sonik2k

          Владимир, а есть ли форум или иной тип связи с Вами, кроме комментов к записям, мне бы узнать такую вещь, как капча-екстендет, которая не хочет работать с модулем user, при чем страница логина нормально с ним работает, а вот регистрация говорит, что капча не верно введена, 3 часа маялся, комментарии на моей странице  с капчей этой работают, логин страница, добавление новостей, все в порядке, а вот рега пользователя никак, капча не верна и все тут…

        • Личного форума у меня нет 🙂 Можете написать письмо (vova_33[@]gala.net). Но, хочу сразу предупредить, что капча-екстендет не пользовался, поэтому советы будут только общие — проверить отладчиком браузера параметры запроса, поискать где капча-екстендет хранит значение капчи.

        • Sonik2k

          А в чем разница, встроенными ли средствами добавлять скрипты или самому в layout/main.php?

        • Встроенные средства позволяют исключить повторное подключение одних и тех же скриптов. Пока приложение простое, контролировать подключенные скрипты не сложно. Но когда вы начинаете пользоваться компонентами и/или модулями, которые тоже могут подключать скрипты, то задача усложняется.

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

    • Данные находятся в обычной html таблице. Т.е. после обновления вы можете просто вставить в неё строку (с помощью JavaScript).

      Но, в зависимости от требований, возможно будет лучше обновить таблицу полностью (в этом случае ajax запрос на обновление данных должен вернуть всю таблицу целиком).

  • Alisher Davronov

    а почему js функция повторно объявляется для каждой строки?
    не можно просто один раз объявить, а потом в clickах указать ее (т.е. токо названия)

    спасибо

    • Честно говоря, мне сложно сейчас ответить. Редко пользуюсь этой возможностью. Помню, что в то время когда я писал эту статью, были какие-то проблемы. Нужно проверить в последних версиях фреймворка.

  • Егор Чабанюк

    В API написано что в 'imageUrl' если не установлено то используется текстовая ссылка, но когда я оставляю пустым, то остается только рамочка (возможной фотки) вывести просто ссылку [delete]?
    'class'=>'CButtonColumn',
    'template' => '{delete}
    'buttons' => array(
    'postview' => array(
    'label'=>'…', // text label of the button

    'imageUrl'=>'…', // image URL of the button. If not set or false, a text link is used

    • А почему вы использовали троеточие вместо false?
      'imageUrl'=>false,

      • Егор Чабанюк

        Нет, у меня стоит false)) не оттуда скинул, при false маленькая пиктограмма, а мне нужно чтобы был просто текст [delete] вместо иконки

        • Я понял. Честно говоря, править исходники фреймворка не совсем правильно. Иконка у вас появлялась из-за того, что вы использовали шаблон {delete}. В данном случае лучше создать свой шаблон, что-то вроде {text_delete} точно также как в статье http://www.yiiframework.com/wiki/106/using-cbuttoncolumn-to-customize-buttons-in-cgridview/ описано создание шаблона для кнопки email

  • Андрей

    а нафига вставлять JS ф-ю в виде безымянной ф-и ч/з переменную $js_preview, да еще выводить ее ч/з PHP если можно просто прописать ее имя а объявить традиционным способом.

    • Потому что статья о настройке CButtonColumn, а не о JavaScript вообще.

      Кроме того, если количество JS кода небольшое, то удобнее когда все настройки собраны в одном месте (в данном случае — в представлении).

  • Hennadii

    спасибо, Дядь! помогло.