Starting from version 1.1.2, Yii is equipped with a Web-based code generation tool called Gii. It supercedes the previous yiic shell
generation tool which runs on command line. In this section, we will
describe how to use Gii and how to extend Gii to increase our
development productivity.
Gii is implemented in terms of a module and must be used within an existing Yii application. To use Gii, we first modify the application configuration as follows:
[php] return array( ...... 'modules'=>array( 'gii'=>array( 'class'=>'system.gii.GiiModule', 'password'=>'pick up a password here', // 'ipFilters'=>array(...a list of IPs...), // 'newFileMode'=>0666, // 'newDirMode'=>0777, ), ), );
In the above, we declare a module named gii
whose class is [GiiModule]. We also specify a password for the module which we will be prompted for when accessing Gii.
By default for security reasons, Gii is configured to be accessible only on localhost. If we want to make it accessible on other trustable computers, we can configure the [GiiModule::ipFilters] property as shown in the above code.
Because Gii may generate and save new code files in the existing application, we need to make sure that the Web server process has the proper permission to do so. The above [GiiModule::newFileMode] and [GiiModule::newDirMode] properties control how the new files and directories should be generated.
Note: Gii is mainly provided as a development tool. Therefore, it should only be installed on a development machine. Because it can generate new PHP script files in the application, we should pay sufficient attention to its security measures (e.g. password, IP filters).
We can now access Gii via the URL http://hostname/path/to/index.php?r=gii
. Here we assume http://hostname/path/to/index.php
is the URL for accessing the existing Yii application.
If the existing Yii application uses path
-format URLs (see URL management), we can access Gii via the URL http://hostname/path/to/index.php/gii
. We may need to add the following URL rules to the front of the existing URL rules:
[php] 'components'=>array( ...... 'urlManager'=>array( 'urlFormat'=>'path', 'rules'=>array( 'gii'=>'gii', 'gii/<controller:\w+>'=>'gii/<controller>', 'gii/<controller:\w+>/<action:\w+>'=>'gii/<controller>/<action>', ...existing rules... ), ), )
Gii comes with a few default code generators. Each code generator is responsible for generating a specific type of code. For example, the controller generator generates a controller class together with a few action view scripts; the model generator generates an ActiveRecord class for the specified database table.
The basic workflow of using a generator is as follows:
Preview
button to preview the code to be
generated. You will see a table showing a list of code files to be
generated. You can click on any of them to preview the code;Generate
button to generate the code files;Note: when generating models make sure to review and correct
rules
method since database structure often doesn't reflect data validation requirements.
While the default code generators coming with Gii can generate very powerful code, we often want to customize them or create new ones to fit for our taste and needs. For example, we may want the generated code to be in our own favorite coding styles, or we may want to make the code to support multiple languages. All these can be done easily with Gii.
Gii can be extended in two ways: customizing the code templates of the existing code generators, and writing new code generators.
A code generator is stored under a directory whose name is treated as the generator name. The directory usually consists of the following content:
model/ the model generator root folder ModelCode.php the code model used to generate code ModelGenerator.php the code generation controller views/ containing view scripts for the generator index.php the default view script templates/ containing code template sets default/ the 'default' code template set model.php the code template for generating model class code
Gii looks for available generators in a set of directories specified by the [GiiModule::generatorPaths] property. When customization is needed, we can configure this property in the application configuration as follows,
[php] return array( 'modules'=>array( 'gii'=>array( 'class'=>'system.gii.GiiModule', 'generatorPaths'=>array( 'common.gii', // a path alias ), ), ), );
The above configuration instructs Gii to look for generators under the directory aliased as common.gii
, in addition
to the default locations system.gii.generators
and application.gii
.
It is possible to have two generators with the same name but under different search paths. In this case, the generator under the path specified earlier in [GiiModule::generatorPaths] will take precedence.
This is the easiest and the most common way of extending Gii. We use an example to explain how to customize code templates. Assume we want to customize the code generated by the model generator.
We first create a directory named protected/gii/model/templates/compact
. Here model
means that we are going to override the default model generator. And templates/compact
means we will add a new code template set named compact
.
We then modify our application configuration to add application.gii
to [GiiModule::generatorPaths], as shown in the previous sub-section.
Now open the model code generator page. Click on the Code Template
field. We should see a dropdown list which contains our newly created template directory compact
.
However, if we choose this template to generate the code, we will see
an error. This is because we have yet to put any actual code template
file in this new compact
template set.
Copy the file framework/gii/generators/model/templates/default/model.php
to protected/gii/model/templates/compact
. If we try generating again with the compact
template, we should succeed. However, the code generated is no different from the one generated by the default
template set.
It is time for us to do the real customization work. Open the file protected/gii/model/templates/compact/model.php
to edit it. Remember that this file will be used like a view script,
which means it can contain PHP expressions and statements. Let's modify
the template so that the attributeLabels()
method in the generated code uses Yii::t()
to translate the attribute labels:
[php] public function attributeLabels() { return array( <?php foreach($labels as $name=>$label): ?> <?php echo "'$name' => Yii::t('application', '$label'),\n"; ?> <?php endforeach; ?> ); }
In each code template, we can access some predefined variables, such as $labels
in the above example. These variables are provided by the corresponding
code generator. Different code generators may provide different set of
variables in their code templates. Please read the description in the
default code templates carefully.
In this sub-section, we show how to create a new generator that can generate a new widget class.
We first create a directory named protected/gii/widget
. Under this directory, we will create the following files:
WidgetGenerator.php
: contains the WidgetGenerator
controller class. This is the entry point of the widget generator.WidgetCode.php
: contains the WidgetCode
model class. This class has the main logic for code generation.views/index.php
: the view script showing the code generator input form.templates/default/widget.php
: the default code template for generating a widget class file.WidgetGenerator.php
The WidgetGenerator.php
file is extremely simple. It only contains the following code:
[php] class WidgetGenerator extends CCodeGenerator{ public $codeModel='application.gii.widget.WidgetCode'; }
In the above code, we specify that the generator will use the model class whose path alias is application.gii.widget.WidgetCode
. The WidgetGenerator
class extends from [CCodeGenerator] which implements a lot of
functionalities, including the controller actions needed to coordinate
the code generation process.
WidgetCode.php
The WidgetCode.php
file contains the WidgetCode
model class that has the main logic for generating a widget class based
on the user input. In this example, we assume that the only input we
want from the user is the widget class name. Our WidgetCode
looks like the following:
[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); } }
The WidgetCode
class extends from [CCodeModel]. Like a normal model class, in this class we can declare rules()
and attributeLabels()
to validate user inputs and provide attribute labels, respectively.
Note that because the base class [CCodeModel] already defines some rules
and attribute labels, we should merge them with our new rules and
labels here.
The prepare()
method prepares the code to be generated.
Its main task is to prepare a list of [CCodeFile] objects, each of which
represent a code file being generated. In our example, we only need to
create one [CCodeFile] object that represents the widget class file
being generated. The new widget class will be generated under the protected/components
directory. We call [CCodeFile::render] method to generate the actual
code. This method includes the code template as a PHP script and returns
the echoed content as the generated code.
views/index.php
Having the controller (WidgetGenerator
) and the model (WidgetCode
), it is time for us to create the view views/index.php
.
[php] <h1>Widget Generator</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"> Widget class name must only contain word characters. </div> <?php echo $form->error($model,'className'); ?> </div> <?php $this->endWidget(); ?>
In the above, we mainly display a form using the [CCodeForm] widget.
In this form, we display the field to collect the input for the className
attribute in WidgetCode
.
When creating the form, we can exploit two nice features provided by the [CCodeForm] widget. One is about input tooltips. The other is about sticky inputs.
If you have tried any default code generator, you will notice that
when setting focus in one input field, a nice tooltip will show up next
to the field. This can easily achieved here by writing next to the input
field a div
whose CSS class is tooltip
.
For some input fields, we may want to remember their last valid values so that the user can save the trouble of re-entering them each time they use the generator to generate code. An example is the input field collecting the controller base class name default controller generator. These sticky fields are initially displayed as highlighted static text. If we click on them, they will turn into input fields to take user inputs.
In order to declare an input field to be sticky, we need to do two things.
First, we need to declare a sticky
validation rule for
the corresponding model attribute. For example, the default controller
generator has the following rule to declare that baseClass
and actions
attributes are sticky:
[php] public function rules() { return array_merge(parent::rules(), array( ...... array('baseClass, actions', 'sticky'), )); }
Second, we need to add a CSS class named sticky
to the container div
of the input field in the view, like the following:
[php] <div class="row sticky"> ...input field here... </div>
templates/default/widget.php
Finally, we create the code template templates/default/widget.php
.
As we described earlier, this is used like a view script that can
contain PHP expressions and statements. In a code template, we can
always access the $this
variable which refers to the code model object. In our example, $this
refers to the WidgetModel
object. We can thus get the user-entered widget class name via $this->className
.
[php] <?php echo '<?php'; ?> class <?php echo $this->className; ?> extends CWidget { public function run() { } }
This concludes the creation of a new code generator. We can access this code generator immediately via the URL http://hostname/path/to/index.php?r=gii/widget
.