Автоматична генерація коду

Починаючи з версії 1.1.2, до складу Yii входить веб-інструмент для генерації коду, який називається Gii. Він замінює консольний генератор yiic shell, що існував до цього. У даному розділі описано, як використовувати Gii і як розширити його для прискорення розробки.

1. Використання Gii

Gii є модулем і повинен бути використаний у складі існуючого додатка Yii. Для використання Gii необхідно відредагувати файл конфігурації додатка наступним чином:

return array('modules'=>array(
        'gii'=>array(
            'class'=>'system.gii.GiiModule',
            'password'=>'задайте свій пароль',
            // 'ipFilters'=>array(…перелік IP…),
            // 'newFileMode'=>0666,
            // 'newDirMode'=>0777,
        ),
    ),
);

Вище ми оголосили модуль з імʼям gii і класом GiiModule. Також ми встановили пароль, який буде використовуватися для доступу до Gii.

За умовчанням, у цілях безпеки, Gii доступний тільки для localhost. Якщо необхідно надати доступ до нього з інших компʼютерів, потрібно встановити властивість GiiModule::ipFilters як показано в коді вище.

Так як Gii буде генерувати і зберігати нові файли з кодом в існуючий додаток, необхідно переконатися в тому, що процес веб-сервера має на це права. Показані вище властивості GiiModule::newFileMode та GiiModule::newDirMode містять права, з якими будуть створюватися файли і директорії.

Примітка: Gii є інструментом розробника. Тому він повинен бути встановлений виключно на компʼютері або сервері розробника. Так як він може генерувати нові скрипти PHP, необхідно приділити особливу увагу безпеці (пароль, IP фільтри).

Тепер можна запустити Gii по URL http://hostname/path/to/index.php?r=gii, де http://hostname/path/to/index.php - URL вашого додатку.

Якщо існуючий додаток використовує формат URL path (див. гарні адреси URL), ми можемо запустити Gii по URL http://hostname/path/to/index.php/gii. Може знадобитися додати наступні правила URL перед вже існуючими:

'components'=>array('urlManager'=>array(
        'urlFormat'=>'path',
        'rules'=>array(
            'gii'=>'gii',
            'gii/<controller:\w+>'=>'gii/<controller>',
            'gii/<controller:\w+>/<action:\w+>'=>'gii/<controller>/<action>',
            …існуючі правила…
        ),
    ),
)

У складі Gii є готовий набір генераторів коду. Кожен генератор відповідає за свій тип коду. Наприклад, генератор контролера створює клас контролера разом з декількома шаблонами представлення; генератор моделі створює клас ActiveRecord для певної таблиці БД.

Послідовність роботи з генератором наступна:

  1. Зайти на сторінку генератора;
  2. Заповнити поля, які задають параметри генерованого коду. Приміром, для генерації модуля необхідно вказати його ID;
  3. Натиснути кнопку Preview для попередньої оцінки генерованого коду. Ви побачите таблицю файлів, які будуть згенеровані і зможете переглянути їх код;
  4. Натиснути кнопку Generate для створення файлів;
  5. Переглянути журнал генерації коду.

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

2. Розширення Gii

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

Gii можна розширювати двома способами: змінюючи існуючі шаблони кодогенераторів та створюючи свої генератори.

Структура кодогенератора

Генератор коду розміщується в директорії, чиє імʼя є імʼям генератора. Директорія зазвичай містить:

model/                       коренева директорія генератора моделі
   ModelCode.php             модель, яка використовується для генерації коду
   ModelGenerator.php        контролер кодогенератора
   views/                    представлення генератора
      index.php              шаблон за замовчуванням
   templates/                шаблони коду
      default/               набір шаблонів 'default'
         model.php           шаблон для генерації класу моделі

Шлях пошуку генераторів

Gii шукає генератори у списку директорій, зазначених у властивості GiiModule::generatorPaths. У тому випадку, якщо необхідно додати свої генератори, слід налаштувати додаток таким чином:

return array(
    'modules'=>array(
        'gii'=>array(
            'class'=>'system.gii.GiiModule',
            'generatorPaths'=>array(
                'common.gii',   // псевдонім шляху
            ),
        ),
    ),
);

Наведені вище налаштування змушують Gii шукати генератори в директорії із псевдонімом common.gii на додаток до стандартного system.gii.generators та application.gii.

Можливо мати кілька однойменних генераторів, якщо у них різні шляхи пошуку. У цьому випадку буде використовуватися генератор, шлях пошуку якого зазначений вище в GiiModule::generatorPaths.

Зміна шаблонів коду

Зміна шаблонів коду - найпростіший і найпоширеніший шлях розширення Gii. Ми будемо використовувати приклади для того, щоб описати, як змінити шаблони коду. Припустимо, нам необхідно змінити код, що створюється генератором моделі.

Спочатку ми створюємо директорію protected/gii/model/templates/compact. Тут model означає, що ми збираємося перекрити генератор моделі за замовчуванням. А templates/compact - що ми додаємо новий набір шаблонів коду compact.

Після цього ми додаємо в налаштування програми у властивість GiiModule::generatorPaths значення application.gii, як показано у попередньому підрозділі.

Тепер відкриваємо сторінку генератора моделі. Клацаємо на поле Code Template. Ви повинні побачити список, що випадає, який містить нашу щойно створену директорію шаблонів compact. Проте, якщо ми оберемо цей шаблон, буде виведена помилка. Відбувається це тому, що у наборі compact ще немає самих шаблонів коду.

Скопіюємо файл framework/gii/generators/model/templates/default/model.php в protected/gii/model/templates/compact. Якщо спробувати згенерувати код з набором compact ще раз, генерація повинна пройти успішно. Тим не менш, генерований код нічим не відрізняється від коду, який одержується з набору default.

Час зробити деякі зміни. Відкриємо файл protected/gii/model/templates/compact/model.php. Даний файл буде використано як шаблон представлення, що означає, що він може містити вирази і код PHP. Змінимо шаблон таким чином, що метод attributeLabels() генерованого коду буде використовувати Yii::t() для перекладу заголовків полів:

public function attributeLabels()
{
    return array(
<?php foreach($labels as $name=>$label): ?>
            <?php echo "'$name' => Yii::t('application', '$label'),\n"; ?>
<?php endforeach; ?>
    );
}

У кожному шаблоні коду у нас є доступ до деяких визначених змінних, таких як, наприклад, $labels. Ці змінні задаються відповідним генератором коду. Різні генератори можуть надавати шаблонам різні набори змінних. Варто уважно вивчити опис шаблонів коду за замовчуванням.

Створення нових генераторів

У цьому підрозділі ми покажемо, як реалізувати новий генератор, який зможе створювати нові класи віджетів.

Спочатку створимо директорію protected/gii/widget. У ній створимо такі файли:

  • WidgetGenerator.php: містить клас контролера WidgetGenerator, який є вхідною точкою генератора віджетів.
  • WidgetCode.php: містить клас моделі WidgetCode, який відповідає за логіку генерації коду.
  • views/index.php: відображення, що містить форму введення генератора.
  • templates/default/widget.php: шаблон коду за замовчуванням для генерації класу віджета.

Реалізація WidgetGenerator.php

Файл WidgetGenerator.php вкрай простий. Він містить лише наступний код:

class WidgetGenerator extends CCodeGenerator
{
    public $codeModel='application.gii.widget.WidgetCode';
}

Тут ми описуємо, що генератор буде використовувати клас моделі, чий псевдонім шляху application.gii.widget.WidgetCode. Клас WidgetGenerator успадковується від CCodeGenerator, що реалізує велику кількість функцій, включаючи дії контролера, необхідні для координації процесу генерації коду.

Реалізація WidgetCode.php

Файл WidgetCode.php містить клас моделі WidgetCode, у якому реалізована логіка генерації класу віджету на основі отриманих від користувача параметрів. У даному прикладі будемо вважати, що єдине, що вводить користувач — імʼя класу віджета. WidgetCode виглядає наступним чином:

class WidgetCode extends CCodeModel
{
    public $className;
 
    public function rules()
    {
        return array_merge(parent::rules(), array(
            array('className', 'required'),
            array('className', 'match', 'pattern'=>'/^\w+$/'),
        ));
    }
 
    public function attributeLabels()
    {
        return array_merge(parent::attributeLabels(), array(
            'className'=>'Widget Class Name',
        ));
    }
 
    public function prepare()
    {
        $path=Yii::getPathOfAlias('application.components.' . $this->className) . '.php';
        $code=$this->render($this->templatepath.'/widget.php');
 
        $this->files[]=new CCodeFile($path, $code);
    }
}

Клас WidgetCode успадковується від CCodeModel. Як і в звичайному класі моделі, в даному класі ми реалізуємо методи rules() і attributeLabels() для валідації введення і генерації підписів полів відповідно. Варто зазначити, що так як базовий клас CCodeModel вже описує деяку кількість правил валідації і назв підписів, то ми повинні обʼєднати їх з нашими правилами і підписами.

Метод prepare() готує код до генерації. Головне завдання методу - підготувати список обʼєктів CCodeFile, кожен з яких представляє майбутній файл з кодом. У нашому прикладі необхідно створити всього один обʼєкт CCodeFile, що представляє клас віджету, який буде згенеровано в директорії protected/components. Для безпосередньої генерації коду використовується метод CCodeFile::render. Даний метод містить PHP-шаблон коду і повертає згенерований код.

Реалізація views/index.php

Після реалізації контролера (WidgetGenerator) і моделі (WidgetCode) саме час зайнятися представленням views/index.php:

<h1>Генератор віджета</h1>
 
<?php $form=$this->beginWidget('CCodeForm', array('model'=>$model)); ?>
 
    <div class="row">
        <?php echo $form->labelEx($model,'className'); ?>
        <?php echo $form->textField($model,'className',array('size'=>65)); ?>
        <div class="tooltip">
            Клас віджета повинен містити тільки літери.
        </div>
        <?php echo $form->error($model,'className'); ?>
    </div>
 
<?php $this->endWidget(); ?>

У даному коді ми відображаємо форму, використовуючи віджет CCodeForm. У цій формі ми показуємо поле для введення атрибута className моделі WidgetCode.

При створенні форми ми можемо використовувати дві чудові можливості CCodeForm. Одна - підказки для полів. Друга - запамʼятовування введених значень.

Якщо ви використали один із стандартних генераторів коду, ви могли помітити гарні спливаючі підказки, що зʼявляються поруч із полем при отриманні ним фокусу. Використовувати дану можливість дуже легко: достатньо після поля вкласти div з CSS класом tooltip.

Для деяких полів корисно запамʼятати останнє вірне значення і тим самим дозволивши користувачеві не вводити значення повторно щоразу, коли він використовує генератор. Прикладом може служити поле введення базового класу контролера у стандартному генераторі. Такі поля спочатку відображаються як підсвічений статичний текст. При натисканні вони перетворюються в поля введення.

Для того, щоб запамʼятовувати ці дані, необхідно зробити дві речі.

По-перше, потрібно описати правило валідації sticky для відповідного атрибута моделі. Приміром, для стандартного генератора контролера використовується наведене нижче правило для запамʼятовування атрибутів baseClass і actions:

public function rules()
{
    return array_merge(parent::rules(), array(array('baseClass, actions', 'sticky'),
    ));
}

По-друге, у відображенні необхідно додати CSS клас sticky контейнеру div поля вводу:

<div class="row sticky">
    …поле введення…
</div>

Реалізація templates/default/widget.php

Нарешті, ми створюємо шаблон коду templates/default/widget.php. Як було описано раніше, він використовується як PHP-шаблон представлення. У шаблоні коду ми завжди можемо звернутися до змінної $this, яка містить екземпляр моделі коду. У нашому прикладі $this містить обʼєкт WidgetModel. Таким чином, ми можемо отримати введений користувачем клас віджету через $this->className.

<?php echo '<?php'; ?>
 
class <?php echo $this->className; ?> extends CWidget
{
    public function run()
    {
 
    }
}

На цьому реалізація генератора коду завершена. Звернутися до нього можна по URL http://hostname/path/to/index.php?r=gii/widget.

Be the first person to leave a comment

Please to leave your comment.