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

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

Використання Gii
----------------

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

~~~
[php]
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] містять права,
з якими будуть створюватися файли і директорії.

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

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

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

~~~
[php]
'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. Переглянути журнал генерації коду.

Розширення Gii
--------------

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

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

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

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

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

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

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

~~~
[php]
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()` для перекладу заголовків полів:

~~~
[php]
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` вкрай простий. Він містить лише наступний код:

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

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

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

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

~~~
[php]
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`:

~~~
[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`:

~~~
[php]
public function rules()
{
	return array_merge(parent::rules(), array(
		…
		array('baseClass, actions', 'sticky'),
	));
}
~~~

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

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

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

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

~~~
[php]
<?php echo '<?php'; ?>

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

	}
}
~~~

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