Создание расширений

Поскольку создание расширений подразумевает их использование сторонними разработчиками, процесс создания требует дополнительных усилий. Ниже приведены основные правила, которые необходимо соблюдать при создании расширений:

  • расширение должно быть самодостаточным, т.е. зависимость от внешних ресурсов должна быть минимальна. Очень неудобно, когда для работы расширения требуется устанавливать дополнительные пакеты, классы и иные файлы ресурсов;
  • все файлы расширения должны быть собраны в одной директории, имя которой должно совпадать с названием расширения;
  • классы расширения должны начинаться с префикса, чтобы избежать конфликтов имён с классами других расширений;
  • расширение должно включать подробную документацию по его установке и API, чтобы сократить время, затрачиваемое другими разработчиками на его подключение и изучение;
  • расширение должно использовать надлежащую лицензию. Если вы хотите, чтобы ваше расширение могло быть использовано как открытыми, так и закрытыми проектами, вы можете воспользоваться лицензиями BSD, MIT и др., но не GPL, т.к. последняя требует, чтобы код, в котором используется ваше расширение, также был открыт.

Ниже мы расскажем о том, как создать новое расширение в соответствии с классификацией, приведённой в обзоре. Пояснения в равной степени распространяются и на расширения, используемые исключительно в собственных проектах.

1. Компонент приложения

Компонент приложения должен реализовывать интерфейс IApplicationComponent или расширять класс CApplicationComponent. Основной метод, который необходимо реализовать, — IApplicationComponent::init. В этом методе происходит инициализация компонента. Метод вызывается после того, как компонент создан и установлены начальные значения, указанные в конфигурации приложения.

По умолчанию компонент приложения создаётся и инициализируется только в момент первого обращения к нему в ходе обработки запроса. Если необходимо принудительно создавать компонент сразу после создания экземпляра приложения, то нужно добавить идентификатор этого компонента в свойство CApplication::preload.

2. Поведение

Для того чтобы создать поведение, необходимо реализовать интерфейс IBehavior. Для удобства в Yii имеется класс CBehavior, реализующий этот интерфейс и предоставляющий некоторые общие методы. Наследуемые классы могут реализовать дополнительные методы, которые будут доступны в компонентах, к которым прикреплено поведение.

При разработке поведений для CModel и CActiveRecord можно наследовать классы CModelBehavior и CActiveRecordBehavior соответственно. Эти базовые классы предоставляют дополнительные возможности, специально созданные для CModel и CActiveRecord. К примеру, класс CActiveRecordBehavior реализует набор методов для обработки событий жизненного цикла ActiveRecord. Наследуемый класс, таким образом, может перекрыть эти методы и выполнить код, участвующий в жизненном цикле AR.

Следующий код демонстрирует пример поведения ActiveRecord. Если это поведение прикреплено к объекту AR и вызван метод save(), атрибутам create_time и update_time будет автоматически присвоено текущее время.

class TimestampBehavior extends CActiveRecordBehavior
{
    public function beforeSave($event)
    {
        if($this->owner->isNewRecord)
            $this->owner->create_time=time();
        else
            $this->owner->update_time=time();
    }
}

3. Виджет

Виджет должен расширять класс CWidget или производные от него.

Наиболее простой способ создать виджет — расширить существующий виджет и переопределить его методы или заменить значения по умолчанию. Например, если вы хотите заменить CSS стиль для CTabView, то, используя виджет, нужно установить свойство CTabView::cssFile. Можно также расширить класс CTabView, чтобы при использовании виджета не требовалась постоянная конфигурация, следующим образом:

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. Если необходимо получить и обработать содержимое, заключённое между вызовами этих двух функций, то можно запустить буферизацию вывода в CWidget::init и получать сохранённый вывод для дальнейшей обработки в методе CWidget::run.

Когда виджет используется на странице, он обычно подключает CSS стили, JavaScript файлы и прочие файлы ресурсов. Файлы такого рода называются ресурсы, поскольку они хранятся вместе с файлом класса виджета и, как правило, не доступны веб-пользователям. Для того чтобы они стали доступны пользователям, их необходимо опубликовать, используя CWebApplication::assetManager, как показано во фрагменте кода выше. Помимо этого, если необходимо подключить файлы CSS стилей или JavaScript на текущей странице, их необходимо зарегистрировать посредством CClientScript:

class MyWidget extends CWidget
{
    protected function registerClientScript()
    {
        // …подключаем здесь файлы CSS или JavaScript…
        $cs=Yii::app()->clientScript;
        $cs->registerCssFile($cssFile);
        $cs->registerScriptFile($jsFile);
    }
}

Виджет также может иметь собственные файлы представлений. В этом случае необходимо создать директорию views внутри директории, содержащей файл класса виджета, и поместить в неё все файлы представлений. Для подключения представлений виджета в его классе используется конструкция $this->render('ViewName'), аналогичная соответствующему методу контроллера.

4. Действие

Действие должно расширять класс CAction или производные от него. IAction::run — основной метод, который необходимо реализовать для действия.

5. Фильтр

Фильтр должен расширять класс CFilter или производные от него. Основными методами, которые необходимо реализовать, являются CFilter::preFilter и CFilter::postFilter. Первый вызывается до выполнения действия, второй — после.

class MyFilter extends CFilter
{
    protected function preFilter($filterChain)
    {
        // применяется до выполнения действия
        return true; // значение false возвращается, если действие не должно выполняться
    }
 
    protected function postFilter($filterChain)
    {
        // применяется после завершения выполнения действия
    }
}

Параметр $filterChain — экземпляр класса CFilterChain, содержащий информацию о действии, к которому применяются фильтры в настоящий момент.

6. Контроллер

Контроллер, распространяемый как расширение, должен наследовать класс CExtController, а не класс CController. Основной причиной этого является то, что для класса CController предполагается, что файлы представлений располагаются в application.views.ControllerID, а для CExtController считается, что файлы представлений находятся в директории views, расположенной в той же директории, что и файл класса этого контроллера. Очевидно, что расширение-контроллер удобнее распространять, когда все файлы расширения собраны в одном месте.

7. Валидатор

Валидатор должен расширять класс CValidator и реализовывать его метод CValidator::validateAttribute.

class MyValidator extends CValidator
{
    protected function validateAttribute($model,$attribute)
    {
        $value=$model->$attribute;
        if($value has error)
            $model->addError($attribute,$errorMessage);
    }
}

8. Команда консоли

Консольная команда должна расширять класс CConsoleCommand и реализовывать его метод CConsoleCommand::run. При желании можно переопределить метод CConsoleCommand::getHelp, который отвечает за информационную справку по команде.

class MyCommand extends CConsoleCommand
{
    public function run($args)
    {
        // $args — массив аргументов, переданных с командой
    }
 
    public function getHelp()
    {
        return 'Usage: how to use this command';
    }
}

9. Модуль

Информация о порядке создания и использования модулей представлена в разделе Модуль.

Если сформулировать требования в общем виде, то модуль должен быть самодостаточным, файлы ресурсов (CSS, JavaScript, изображения), используемые модулем, должны распространяться вместе с модулем, а сам модуль должен публиковать ресурсы, чтобы они были доступны для веб-пользователей.

10. Компонент общего вида

Разработка компонента общего вида аналогична написанию класса. Компонент, как и модуль, должен быть самодостаточен и удобен для использования другими разработчиками.

Be the first person to leave a comment

Please to leave your comment.