Створення розширень

Оскільки створення розширень передбачає їх використання сторонніми розробниками, процес створення вимагає додаткових зусиль. Нижче наведені основні правила, яких необхідно дотримуватися при створенні розширень:

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

Компонент додатка

Компонент додатка повинен реалізовувати інтерфейс IApplicationComponent або розширювати клас CApplicationComponent. Основний метод, який необхідно реалізувати, - IApplicationComponent::init. У цьому методі відбувається ініціалізація компонента. Метод викликається після того, як компонент створений і отримані початкові значення, зазначені в конфігурації додатка.

За замовчуванням, компонент додатку створюється і ініціалізується тільки в момент першого звернення до нього в ході обробки запиту. Якщо необхідно примусово створювати компонент відразу після створення екземпляра додатку, то потрібно додати ідентифікатор цього компонента у властивість CApplication::preload.

Поведінка

Для того, щоб створити поведінку необхідно реалізувати інтерфейс 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();
    }
}

Віджет

Віджет повинен розширювати клас 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'), аналогічно використанню в контролері.

Дія

Дія має розширювати клас CAction або похідні від нього. IAction::run - основний метод, який необхідно реалізувати для дії.

Фільтр

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

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

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

Контролер

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

Валідатор

Валідатор повинен розширювати клас CValidator і реалізовувати його метод CValidator::validateAttribute.

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

Команда консолі

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

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

Модуль

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

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

Загальний компонент

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