Виджеты — это некоторые компоненты, которые предназначены в первую очередь для реализации элементов интерфейса и которые можно использовать многократно. Классическим примером такого элемента может быть меню. Меню используется практически на каждой странице сайта и от страницы к странице остается неизменным. То есть, на каждой странице нужно повторять один и тот же код для получения меню и его вывода. Но благодаря виджетам можно избежать этой утомительной процедуры и создать меню только один раз, а затем просто вызывать его.
Размещаются виджеты в папке components, которую нужно создать в корне приложения. Для создания виджета необходимо унаследовать класс yii\base\Widget. Внутри класса виджета нужно переопределить методы init() и run(). Также можно создавать собственные методы. Метод init(), как правило, используется для нормализации свойств виджета (установка значений), а метод run() возвращает результат рендеринга.
В Yii2 уже имеется большое количество различных виджетов: для создания форм, меню, jQuery UI и другие. Кроме того, фреймворк позволяет создавать собственные виджеты. Давайте создадим простой виджет, который будет приветствовать посетителей сайта.
Создание виджета
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <?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(). Метод принимает массив настроек для инициализации виджета и возвращает результат его рендеринга.
1 2 3 4 5 6 7 | <?php /* @var $this yii\web\View */ use app\components\HelloWidget; ?> <div class = "page-index" > <?= HelloWidget::widget([ 'message' => 'Доброе утро!' ]); ?> </div> |
Пока что метод run() возвращает короткую строку. Но что делать, если нужно возвращать большой объем html-кода? Нужно создать рядом с файлом класса виджета директорию views, в которую и поместить представление для виджета. Само представление можно получить с помощью метода render().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | <?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:
1 2 3 4 5 6 7 8 | <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() можно получить внутри класса виджета и как-то обработать:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | <?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> |