Виджеты — это некоторые компоненты, которые предназначены в первую очередь для реализации элементов интерфейса и которые можно использовать многократно. Классическим примером такого элемента может быть меню. Меню используется практически на каждой странице сайта и от страницы к странице остается неизменным. То есть, на каждой странице нужно повторять один и тот же код для получения меню и его вывода. Но благодаря виджетам можно избежать этой утомительной процедуры и создать меню только один раз, а затем просто вызывать его.
Размещаются виджеты в папке components, которую нужно создать в корне приложения. Для создания виджета необходимо унаследовать класс yii\base\Widget. Внутри класса виджета нужно переопределить методы init() и run(). Также можно создавать собственные методы. Метод init(), как правило, используется для нормализации свойств виджета (установка значений), а метод run() возвращает результат рендеринга.
В Yii2 уже имеется большое количество различных виджетов: для создания форм, меню, jQuery UI и другие. Кроме того, фреймворк позволяет создавать собственные виджеты. Давайте создадим простой виджет, который будет приветствовать посетителей сайта.
Создание виджета
<?php
namespace app\components;
use yii\base\Widget;
use yii\helpers\Html;
class HelloWidget extends Widget
{
public $message;
public function init() {
parent::init();
if ($this->message === null) {
$this->message = 'Добрый день!';
}
}
public function run() {
return '<h1>'.Html::encode($this->message).'</h1>';
}
}
Теперь попробуем вызвать созданный виджет в представлении. Для этого необходимо обратиться к созданному классу и вызвать статичный метод widget(). Метод принимает массив настроек для инициализации виджета и возвращает результат его рендеринга.
<?php
/* @var $this yii\web\View */
use app\components\HelloWidget;
?>
<div class="page-index">
<?= HelloWidget::widget(['message' => 'Доброе утро!']); ?>
</div>
Пока что метод run() возвращает короткую строку. Но что делать, если нужно возвращать большой объем html-кода? Нужно создать рядом с файлом класса виджета директорию views, в которую и поместить представление для виджета. Само представление можно получить с помощью метода render().
<?php
namespace app\components;
use yii\base\Widget;
class HelloWidget extends Widget
{
public $message;
public function init() {
parent::init();
if (!is_null($this->message)) {
return;
}
$hour = date('G');
if ($hour >= 0 && $hour < 6) {
$this->message = 'Доброй ночи!';
} elseif ($hour >= 6 && $hour < 12) {
$this->message = 'Доброе утро!';
} elseif ($hour >= 12 && $hour < 18) {
$this->message = 'Добрый день!';
} else {
$this->message = 'Добрый вечер!';
}
}
public function run() {
return $this->render('hello', ['message' => $this->message]);
}
}
<?php
/*
* Файл components/views/hello.php
*/
use yii\helpers\Html;
?>
<h1 class="hello"><?= Html::encode($message); ?></h1>
<?php
/* @var $this yii\web\View */
use app\components\HelloWidget;
?>
<div class="page-index">
<?= HelloWidget::widget(); ?>
</div>
Методы begin() и end()
Не все виджеты вызываются с посмощью метода widget(), иногда у них бывает внутреннее содержимое и тогда виджет вызывается методами begin() и end(). Типичный пример — базовый виджет ActiveForm:
<div class="page-feedback">
<?php $form = ActiveForm::begin(['id' => 'feedback-form']); ?>
<?= $form->field($message, 'name')->textInput(); ?>
<?= $form->field($message, 'email')->textInput(); ?>
<?= $form->field($message, 'body')->textarea(['rows' => 5]); ?>
<?= Html::submitButton('Отправить', ['class' => 'btn btn-primary']); ?>
<?php ActiveForm::end(); ?>
</div>
Этот виджет сгенерирует открывающий и закрывающий тэги <form> в местах вызова методов begin() и end() соответственно. При этом, содержимое, расположенное между вызовами указанных методов будет выведено без каких-либо изменений.
При вызове метода begin() будет создан новый экземпляр виджета, при этом вызов метода init() произойдет сразу после выполнения остального кода в конструкторе виджета. При вызове метода end(), будет вызван метод run(), а возвращенное им значение будет выведено методом end().
Содержимое между вызовами методов begin() и end() можно получить внутри класса виджета и как-то обработать:
<?php
namespace app\components;
use yii\base\Widget;
class HelloWidget extends Widget
{
public $message;
public function init() {
parent::init();
if (is_null($this->message)) {
$this->message = 'Добрый день!';
}
ob_start();
}
public function run() {
$content = ob_get_clean();
return $this->render(
'hello',
[
'message' => $this->message,
'content' => $content
]
);
}
}
<?php
/*
* Файл components/views/hello.php
*/
use yii\helpers\Html;
?>
<h1 class="hello"><?= Html::encode($message); ?></h1>
<p><?= Html::encode($content); ?></p>
<?php
/* @var $this yii\web\View */
use app\components\HelloWidget;
?>
<div class="page-index">
<?php HelloWidget::begin(); ?>
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
<?php HelloWidget::end(); ?>
</div>
<div class="page-index">
<h1 class="hello">Добрый день!</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
</div>