Виджеты — Yii2

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

Размещаются виджеты в папке 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>

Leave a Comment