mirror of
https://github.com/yiisoft/yii.git
synced 2026-03-08 09:06:53 +01:00
223 lines
15 KiB
Plaintext
223 lines
15 KiB
Plaintext
Создание расширений
|
||
===================
|
||
|
||
Поскольку создание расширений подразумевает их использование сторонними разработчиками,
|
||
процесс создания требует дополнительных усилий. Ниже приведены основные правила, которые необходимо соблюдать
|
||
при создании расширений:
|
||
|
||
* расширение должно быть самодостаточным, т.е. зависимость от внешних ресурсов должна быть минимальна. Очень неудобно, когда для работы
|
||
расширения требуется устанавливать дополнительные пакеты, классы и иные файлы ресурсов;
|
||
* все файлы расширения должны быть собраны в одной папке, имя которой должно совпадать с именем расширения;
|
||
* классы расширения должны начинаться с префикса, чтобы избежать конфликтов имен с классами других расширений;
|
||
* расширение должно включать подробную документацию по API и порядку установки, чтобы сократить время, необходимое для изучения и работы с расширением;
|
||
* расширение должно использовать подходящую лицензию. Если вы хотите, чтобы ваше расширение могло быть использовано как открытыми, так и закрытыми проектами,
|
||
вы можете воспользоваться лицензиями BSD, MIT и др., но не GPL, т.к. последняя требует, чтобы код, где было использовано ваше расширение, также был открыт.
|
||
|
||
Ниже мы расскажем о том, как создать новое расширение в соответствии с классификацией,
|
||
приведенной в [обзоре](/doc/guide/extension.overview). Пояснения в равной степени распространяются и на
|
||
расширения, используемые исключительно в собственных проектах.
|
||
|
||
Компонент приложения
|
||
---------------------
|
||
|
||
[Компонент приложения](/doc/guide/basics.application#application-component) должен реализовывать интерфейс [IApplicationComponent]
|
||
или расширять класс [CApplicationComponent]. Основной метод, который необходимо реализовать, — [IApplicationComponent::init].
|
||
В этом методе компонент инициализируется. Метод вызывается после того, как компонент создан и получены начальные значения,
|
||
обозначенные в [конфигурации приложения](/doc/guide/basics.application#application-configuration).
|
||
|
||
По умолчанию, компонент приложения создается и инициализируется только в момент первого обращения к нему в ходе обработки запроса.
|
||
Если необходимо принудительно создавать компонент сразу после создания экземпляра приложения, то нужно добавить идентификатор
|
||
этого компонента в свойство [CApplication::preload].
|
||
|
||
Поведение
|
||
---------
|
||
|
||
Для того, чтобы создать поведение необходимо реализовать интерфейс [IBehavior].
|
||
Для удобства в Yii имеется класс [CBehavior], реализующий этот интерфейс и
|
||
предоставляющий некоторые общие методы. Наследуемые классы могут реализовать
|
||
дополнительные методы, которые будут доступны в компонентах, к которым прикреплено
|
||
поведение.
|
||
|
||
При разработке поведений для [CModel] и [CActiveRecord] можно наследовать
|
||
[CModelBehavior] и, соответственно, [CActiveRecordBehavior]. Эти базовые классы
|
||
предоставляют дополнительные возможности, специально созданные для [CModel] и [CActiveRecord].
|
||
К примеру, класс [CActiveRecordBehavior] реализует набор методов для обработки
|
||
событий жизненного цикла ActiveRecord. Наследуемый класс, таким образом, может
|
||
перекрыть эти методы и выполнить код, участвующий в жизненном цикле AR.
|
||
|
||
Следующий код демонстрирует пример поведения ActiveRecord. Если это поведение
|
||
назначено объекту AR и вызван метод `save()`, атрибутам `create_time` и
|
||
`update_time` будет автоматически выставлено текущее время.
|
||
|
||
~~~
|
||
[php]
|
||
class TimestampBehavior extends CActiveRecordBehavior
|
||
{
|
||
public function beforeSave($event)
|
||
{
|
||
if($this->owner->isNewRecord)
|
||
$this->owner->create_time=time();
|
||
else
|
||
$this->owner->update_time=time();
|
||
}
|
||
}
|
||
~~~
|
||
|
||
Виджет
|
||
------
|
||
|
||
[Виджет](/doc/guide/basics.view#widget) должен расширять класс [CWidget] или производные от него.
|
||
|
||
Наиболее простой способ создать виджет — расширить существующий виджет и переопределить его методы или заменить значения по умолчанию.
|
||
Например, если вы хотите заменить CSS-стиль для [CTabView], то, используя виджет, нужно сконфигурировать свойство [CTabView::cssFile]. Можно также расширить класс
|
||
[CTabView], чтобы при использовании виджета не требовалась постоянная конфигурация, следующим образом:
|
||
|
||
~~~
|
||
[php]
|
||
class MyTabView extends CTabView
|
||
{
|
||
public function init()
|
||
{
|
||
if($this->cssFile===null)
|
||
{
|
||
$file=dirname(__FILE__).DIRECTORY_SEPARATOR.'tabview.css';
|
||
$this->cssFile=Yii::app()->getAssetManager()->publish($file);
|
||
}
|
||
parent::init();
|
||
}
|
||
}
|
||
~~~
|
||
|
||
Выше мы переопределяем метод [CWidget::init] и, если свойство [CTabView::cssFile] не установлено,
|
||
присваиваем ему значение URL нового CSS-стиля по умолчанию. Файл нового CSS-стиля необходимо поместить
|
||
в одну папку с файлом класса `MyTabView`, чтобы их можно было упаковать как расширение. Поскольку CSS-стиль не доступен из веб, его
|
||
необходимо опубликовать как ресурс.
|
||
|
||
Чтобы написать виджет с нуля, нужно, как правило, реализовать два метода: [CWidget::init] и [CWidget::run].
|
||
Первый метод вызывается, когда мы используем конструкцию `$this->beginWidget` для вставки виджета в представление,
|
||
а второй — когда используется конструкция `$this->endWidget`.
|
||
Если необходимо получить и обработать некоторый контент между вызовами этих двух функций
|
||
можно запустить [буферизацию вывода](http://php.net/manual/en/book.outcontrol.php) в
|
||
[CWidget::init] и получать сохраненный вывод для дальнейшей обработки в методе [CWidget::run].
|
||
|
||
На странице, где используется виджет, он обычно подключает CSS-стили, JavaScript файлы и прочие файлы ресурсов.
|
||
Файлы такого рода называются *ресурсы*, поскольку хранятся с файлом класса виджета и, как правило, недоступны веб-пользователям.
|
||
Для того, чтобы дать к ним доступ, их необходимо опубликовать, используя [CWebApplication::assetManager], как показано в фрагменте кода выше.
|
||
Помимо этого, если необходимо подключить файлы CSS-стиля или JavaScript на текущей странице, их необходимо зарегистрировать посредством [CClientScript]:
|
||
|
||
~~~
|
||
[php]
|
||
class MyWidget extends CWidget
|
||
{
|
||
protected function registerClientScript()
|
||
{
|
||
// …подключаем здесь файлы CSS или JavaScript…
|
||
$cs=Yii::app()->clientScript;
|
||
$cs->registerCssFile($cssFile);
|
||
$cs->registerScriptFile($jsFile);
|
||
}
|
||
}
|
||
~~~
|
||
|
||
Виджет также может иметь собственные файлы представлений. В этом случае необходимо создать папку `views`
|
||
в папке с файлом класса виджета и поместить в нее все файлы представлений. Чтобы отрендерить представления виджета в его классе используется
|
||
конструкция `$this->render('ViewName')`, аналогично использованию в контроллере.
|
||
|
||
Действие
|
||
------
|
||
|
||
[Действие](/doc/guide/basics.controller#action) должно расширять класс [CAction] или производные от него. [IAction::run] — основной метод, который необходимо
|
||
реализовать для действия.
|
||
|
||
Фильтр
|
||
------
|
||
|
||
Фильтр должен расширять класс [CFilter] или производные от него. Основными методами, которые необходимо реализовать,
|
||
являются [CFilter::preFilter] и [CFilter::postFilter]. Первый вызывается до выполнения действия, второй — после.
|
||
|
||
~~~
|
||
[php]
|
||
class MyFilter extends CFilter
|
||
{
|
||
protected function preFilter($filterChain)
|
||
{
|
||
// применяется до выполнения действия
|
||
return true; // значение false возвращается, если действие не должно выполняться
|
||
}
|
||
|
||
protected function postFilter($filterChain)
|
||
{
|
||
// применяется после завершения выполнения действия
|
||
}
|
||
}
|
||
~~~
|
||
|
||
Параметр `$filterChain` — экземпляр класса [CFilterChain], содержащий информацию о действии, к которому применяются фильтры в настоящий момент.
|
||
|
||
|
||
Контроллер
|
||
----------
|
||
|
||
[Контроллер](/doc/guide/basics.controller), предлагаемый как расширение, должен наследовать класс [CExtController], а не класс [CController].
|
||
Основной причиной этого является то, что в случае класса [CController] предполагается, что файлы представлений располагаются
|
||
в `application.views.ControllerID`, а в случае класса [CExtController] считается, что файлы представлений находятся в папке `views`,
|
||
расположенной в папке с файлом класса этого контроллера. Очевидно, что расширение-контроллер удобнее распространять, когда все
|
||
файлы расширения собраны в одном месте.
|
||
|
||
Валидатор
|
||
---------
|
||
|
||
Валидатор должен расширять класс [CValidator] и реализовывать его метод [CValidator::validateAttribute].
|
||
|
||
~~~
|
||
[php]
|
||
class MyValidator extends CValidator
|
||
{
|
||
protected function validateAttribute($model,$attribute)
|
||
{
|
||
$value=$model->$attribute;
|
||
if($value has error)
|
||
$model->addError($attribute,$errorMessage);
|
||
}
|
||
}
|
||
~~~
|
||
|
||
Команда консоли
|
||
---------------
|
||
|
||
[Консольная команда](/doc/guide/topics.console) должна расширять класс [CConsoleCommand]
|
||
и реализовывать его метод [CConsoleCommand::run]. При желании можно переопределить
|
||
метод [CConsoleCommand::getHelp], который отвечает за информационную справку по команде.
|
||
|
||
~~~
|
||
[php]
|
||
class MyCommand extends CConsoleCommand
|
||
{
|
||
public function run($args)
|
||
{
|
||
// $args — массив аргументов, переданных с командой
|
||
}
|
||
|
||
public function getHelp()
|
||
{
|
||
return 'Usage: how to use this command';
|
||
}
|
||
}
|
||
~~~
|
||
|
||
Модуль
|
||
------
|
||
Информация о порядке использования и создания модулей представлена в разделе [Модуль](/doc/guide/basics.module#using-module).
|
||
|
||
Если сформулировать требования в общем виде, то модуль должен быть самодостаточным, файлы ресурсов (CSS, JavaScript, изображения), используемые модулем, должны распространяться вместе с модулем, а сам модуль должен публиковать ресурсы, чтобы они были доступны для веб-пользователей.
|
||
|
||
|
||
Компонент общего вида
|
||
-----------------
|
||
|
||
Разработка компонента общего вида аналогична написанию класса. Компонент, как и модуль,
|
||
должен быть самодостаточен и удобен для использования разработчиками.
|
||
|
||
|
||
|
||
<div class="revision">$Id: extension.create.txt 1423 2009-09-28 01:54:38Z qiang.xue $</div> |