четверг, 22 марта 2012 г.

Работа с flash-сообщениями в Yii Framework

Предисловие

Как-то захотелось выводить в приложении сообщения о результате каких-либо действий пользователя или после выполнения определённых транзакций выводить результат операции.

Оказалось, что в Yii уже заложен простой функционал вывода подобных сообщений. Работа с этим уже описана, например в [1]. Также можно заглянуть в описание API на yiiframework.com. Тем не менее разобравшись один раз, захотелось записать для себя, а может и ещё для кого-нибудь, более подробнее как это работает.

Работа с flash-сообщениями

Можно задать сколько угодно flash-сообщений. Для работы с ними используются методы класса CWebUser: setFlash, getFlash, hasFlash, hasFlashes.

Для обращения к методам необходимо воспользоваться свойством user объекта-приложения, возвращающего экземпляр класса CWebUser:

Yii::app()->user

Метод setFlash

Метод setFlash устанавливает сообщение, которое сохраняется в сессии пользователя ($_SESSION) и определено следующим образом:

setFlash($key,$value,$defaultValue=null)
где:
$key - идентификатор сообщения;
$value - собственно текст сообщения;
$defaultValue - судя по имени, сообщение по умолчанию. Но на самом деле особого значения не имеет. Как описано в API, если значение $defaultValue такое же, как и $value, то сообщение с идентификатором $key будет удалено. То есть для удаления сообщения можно при вызове функции указать значение $value как null для идентификатора $key, который необходимо удалить.

Примеры использования:

//Установка сообщения с идентификатором 'error'
setFlash('error','Ошибка выполнения операции')

//Сообщение с идентификатором 'error' будет удалено
setFlash('error',null)

Метод hasFlash

Метод hasFlash является функцией, проверяющей наличие уже установленного сообщения с идентификатором $key и возвращающая boolean значение в зависимости от результата. Синтаксис:

bool hasFlash($key)

Пример использования:

// Будет выведено сообщение с идентификатором 'error',
// если оно существует
if (Yii::app()->user->hasFlash('error'))
{
    echo Yii::app()->user->getFlash('error');
}

Метод getFlash и вывод сообщения

Метод getFlash:

string getFlash($key, $defaultValue=null, $delete=true)

Позволяет получить текст сообщения по заданному идентификатору $key. Так же можно задать необязательные параметры $defaultValue и $delete.

$defaultValue задаёт значение, которое будет выводиться, если сообщение с указанным идентификатором не существует. По умолчанию равно null.

$delete определяет будет ли удалено сообщение после вызова функцией getFlash или нет. По умолчанию равно true. То есть после вызова сообщения оно будет удалено, если явно не задать параметр $delete при вызове функции равным false.

// После вызова сообщение НЕ будет удалено
// и может быть вызвано повторно
Yii::app()->user->getFlash('error', 'Операция проведена успешно!', false); 

// После вызова сообщение будет удалено
Yii::app()->user->getFlash('error', 'Операция проведена успешно!'); 

На самом деле, функция getFlash возвращает всего лишь текст сообщения. Следовательно, если просто выводить его, например, инструкцией echo, то этот текст ничем не будет отличаться от остального.

Для визуализации flash-сообщения используются классы стилей оформления блока <div>, определённые в таблице стилей [каталог_приложения]/css/main.css:

flash-error - вывод информации об ошибке;
flash-notice - вывод уведомления, предупреждения;
flash-success - вывод информации об успешной операции.

Примерно в таком виде:

<div class="flash-error">
    <?php echo Yii::app()->user->getFlash('error'); ?>
</div>

Что выведет оформленный соостветствующим образом блок с сообщением об ошибке.

Используя main.css можно описать свой стиль оформления какого-нибудь сообщения, например, flash-info, для чего определить описание для соответствующего селектора (в данном случае div.flash-info) в таблице стилей подобно описанию существующих стилей блоков-сообщений.

Метод getFlashes

Метод getFlashes возвращает ассоциативный массив всех определённых в сеансе пользователя flash-сообщений в формате "идентификатор" => "сообщение":

array getFlashes($delete=true)

При вызове метода можно указать boolean-значение $delete, указывающее, надо ли удалять все сообщения. По умолчанию после вызова функции все сообщения будут удалены, если явно не указан параметр false при вызове функции.

Когда метод может быть полезен? Например, при выполнении транзакции, включающей в себя несколько операций, таких как, проверка на валидность введенных данных, запрос к БД, обработка результатов запроса и т.д., на каждом этапе может возникнуть исключение. Можно перехватывая эти исключения устанавливать соответствующие flash-сообщения, например 'check_error', 'db_error' и т.д. с текстом ошибки исключения (Exception::getMesage()). Затем после завершения транзакции, чтобы не перебирать все сообщения можно воспользоваться методом getFlashes:

$flashes = Yii::app()->user->getFlashes();
if ($flashes)
{
    echo "<div class=\"flash-error\">\n";
    echo "При выполнении операции возникли исключения:\n";
    foreach ($flashes as $error_id => $error_text)
    {
        echo "$error_id: $error_text<br /><br />\n";
    }
    echo "</div>\n";
}

Ссылки на использованные ресурсы

  1. Alexander Makarov. Yii 1.1 Application Development Cookbook. - PACKT Publishing, 2011
  2. Русскоязычное сообщество Yii
  3. Официальная страница Yii Framework

среда, 7 декабря 2011 г.

Некоторые моменты создания и работы модуля в Yii

Предисловие

Данный материал не претендует на оригинальность, скорее всего это просто ход мыслей при освоении работы с модулями в Yii Framework. Всё дальнейшее можно воспринимать как попытку расширить некоторые моменты, описанные сухим языком справочной системы.

Материал предназначен для людей только начавших осваивать Yii Framework.


Создание модуля

Если структура приложения Yii была создана с использованием утилиты yiic, то самый удобный и простой способ создать модуль в Yii — воспользоваться кодогенератором Gii, который в общем случае может быть вызван по URL

http://[сервер]/[путь_к_приложению]/index.php?r=gii

При этом модуль Gii должен быть подключен и настроен в конфигурационном файле приложения main.php. Для генерации модуля в меню на странице кодогенератора выбирается Module Generator, затем вводится уникальный идентификатор, он же — имя, модуля.

Идентификатор модуля должен содержать только буквенные символы, при этом он зависит от регистра символов, то есть «example» не то же самое, что «Example». Это написано во всплывающей подсказке при установке курсора в поле Module ID на странице создания модуля.

Структура каталогов будущего модуля, которая будет сгенерирована, видна уже после нажатия Preview. Если, например, имя модуля «example», структура формируется следующая:

 modules
 |-example
   |-ExampleModule.php
   |
   |-components
   |-controllers
   | |-DefaultController.php
   |
   |-messages
   |-models
   |-views
     |-default
     | |-index.php
     |
     |-layouts
где:
  • modules — стандартный каталог приложения;
  • example — каталог с именем модуля (то, что было введено в Module ID);
  • ExampleModule.php — файл класса модуля, имя которого зависит от имени модуля, только с заглавной буквы;
  • components - может содержать компоненты пользователя;
  • controllers - содержит файлы классов контроллеров;
  • DefaultController.php – контроллер по умолчанию при создании модуля;
  • models - содержит файлы классов моделей;
  • views - содержит файлы представлений контроллера и макетов;
  • default - содержит файлы представлений для контроллера по умолчанию (DefaultController);
  • index.php — начальное представление для модуля;
  • layouts — содержит файлы макетов.

После нажатия Generate будет сгенерирована структура модуля, о чём появится соответствующее сообщение. Будет так же сообщено о том, что для того, чтобы модуль заработал его нужно прописать в конфигурационном файле приложения main.php ([путь_к_приложению]/protected/config/main.php).

Если модуль Gii по каким-то причинам недоступен, то создать структуру модуля можно даже вручную воспользовавшись описанием структуры, приведённой выше или взятой из описания модуля в справочной системе.

Чтобы прописать модуль в main.php необходимо в массив modules добавить значение '[имя модуля]':

<?php
...
  return array(
  ...
    'modules' => array(
      'example'
    ),
  ...
  );

При начальной генерации приложения утилитой yiic массив modules конфигурационного файла уже содержит что-то вроде следующего

'modules' => array(
  //uncomment the following to enable the Gii tool
  'gii' => array(
    'class' => 'system.gii.GiiModule',
    'password' => '********',
    //If removed, Gii defaults to localhost only. Edit carefully to taste.
    'ipFilters' => array('127.0.0.1','::1'),
  ),
),

То есть, как минимум один модуль уже прописан в приложении (Gii). Таким образом, после добавления информации о модуле 'example' код в этом случае вероятно будет таким:

'modules' => array(
  //uncomment the following to enable the Gii tool
  'gii' => array(
    'class' => 'system.gii.GiiModule',
    'password' => '********',
    //If removed, Gii defaults to localhost only. Edit carefully to taste.
    'ipFilters' => array('127.0.0.1','::1'),
  ),
  'example',
),

Теперь можно отобразить первую страницу модуля (index.php) используя URL: http://[сервер]/[путь_к_приложению]/index.php?r=example


Контроллеры и действия

При обращении к модулю, созданному по стандартной классической схеме, следующие записи в URL равнозначны:

.../index.php?r=example

.../index.php/?r=example/defalut

.../index.php/?r=example/default/index

В первом случае Yii в модуле example обращается к контроллеру по умолчанию ('default'), а в контроллере по умолчанию выполняется действие по умолчанию ('index').
При второй записи выполняется действие по умолчанию ('index') в указанном явно контроллере ('default').
В третьей записи контроллер и действие указаны явно в стандартом для Yii формате записи r=[модуль]/[контроллер]/[действие].

Если контроллер и (или) действие отличаются от определённых по умолчанию, то их всегда необходимо указывать явно (предположим, что существуют действие 'run' в контроллере по умолчанию 'default', а также контроллер 'worker' и действие в нём 'go'):

.../index.php?r=example/default/run

.../index.php?r=example/worker

.../index.php?r=example/worker/go

В первом случае вызывается действие, отличное от по умолчанию ('run'), в контроллере по умолчанию ('default');
Во втором — действие по умолчанию 'index' в контроллере 'worker', отличном от по умолчанию;
В третьем — действие, отличное от по умолчанию 'go', в контроллере, отличном от по умолчанию 'worker'.

При этом следует понимать, что контроллер по умолчанию, задаваемый для модуля, и действие по умолчанию, задаваемое для контроллера могут быть переопределены. Логично, что контроллер по умолчанию необходимо переопределить в классе модуля (ExampleModule в данном случае), а действия по умолчанию — в классах контроллеров (если основываться на предыдущих примерах, то это DefaultController и WorkerController). Класс модуля унаследован от стандартного класса Yii CWebModule, в котором определён контроллер по умолчанию:

public $defaultController = 'default';

Таким образом, чтобы переопределить контроллер по умолчанию в собственном модуле, необходимо просто изменить значение переменной $defaultController в классе модуля:

class ExampleModule extends CWebModule
{
  public $defaultController = 'worker';

  ...
}

После этого при использовании в URL .../index.php/?r=example будет происходить обращение к контроллеру worker (класс WorkerController) и вызываться в нём действие по умолчанию index (метод actionIndex), если, конечно, таковое существует в контроллере или не переопределено в контроллере.
Если действие не существует, или если действие по умолчанию не переопределялось и при этом действие 'index' не существует, то при обращении к контроллеру выходит ошибка 404 с сообщением о невозможности найти запрашиваемое действие.

Аналогично контроллеру по умолчанию для модуля, определяется действие по умолчанию в контроллере. В родительском классе для всех контроллеров CController это прописано в переменной $defaultAction:

public $defaultAction='index';

Таким образом, переписав в классе контроллера значение переменной $defaultAction, можно переопределить действие по умолчанию:

class WorkerController extends Controller
{
  public $defaultAction = 'go';
  ...
}

После переопределения контроллера по умолчанию и действия по умолчанию в этом контроллере, как приведено в примерах выше, следующие фрагменты URL приведут к одному и тому же результату:

.../index.php?r=example

.../index.php/?r=example/worker

.../index.php/?r=example/worker/go


Представления

Правила работы с представлениями в модуле, аналогичны правилам работы с представлениями в приложении. То есть представления, к которым обращается метод render() из контроллера, должны находиться в каталоге views/[имя_контроллера] модуля, а имена представлений должны совпадать с именем файла php представления. Так, если продолжать предыдущий пример с созданием контроллера worker, то файлы представлений, к которым обращаются из методов этого контроллера должны располагаться в каталоге modules/views/worker. И если разместить в указанном каталоге файл представления, например, go_page.php, то код вызова этого представления может быть следующим:

class WorkerController extends Controller
{
  public function actionGo()
  {
    $this->render('go_page');
  }
}

Начальные значения свойств модуля

При указании информации о модуле в файле настройки приложения main.php можно указать начальные значения свойств модуля. Например, в модуле gii заданы начальные значения свойств password и ipFilters. По этой же аналогии могут указываться начальные значения своего модуля:

'modules' => array(
  'example' => array(
    'headline' => 'Модуль для примера',
  ),
)

Само свойство headline при этом должно быть объявлено в классе модуля:

class ExampleModule extends CwebModule
{
  public $headline = '';
  ...
}

Обращаться к свойству модуля можно через свойство module контроллера. Например, в представлении, вызванном из контроллера модуля можно вывести заголовок модуля, указанный в main.php:

<?php echo $this->module->headline; ?>

Псевдоним пути для модуля

Yii определяет псевдоним пути при создании модуля соответствующий идентификатору модуля. Так, например, в примере с модулем example можно обращаться к каталогам и файлам внутри модуля при помощи псевдонима example, тогда псевдоним

example.controllers

будет соответствовать пути

[путь_к_приложению]\protected\modules\example\controllers

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

Yii::import('example.models.ExampleForm');

Это подключит файл класса модели формы (ExampleForm.php), при наличии такового в подкаталоге models модуля, при первом обращении к этому классу.

При создании модуля с использованием кодогенератора Gii в методе init класса модуля Yii автоматически импортирует все классы в подкаталогах models и components модуля:

public function init()
{
  $this->setImport(
    array(
      'example.models.*',
      'example.components.*',
    )
  );
}

Ссылки на использованные ресурсы

  1. Русскоязычное сообщество Yii
  2. Официальная страница Yii Framework