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

Начиная с версии 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.