Yii2 provides a powerful tool to generate models, controllers, and CRUD (create, read, update, and delete) actions, forms, modules, and extensions: Gii.
At the bottom of the basic/config/web.php
file, placed in the basic standard configuration, there is a block of code that enables Gii:
if (YII_ENV_DEV) { // configuration adjustments for 'dev' environment $config['bootstrap'][] = 'debug'; $config['modules']['debug'] = 'yiidebugModule'; $config['bootstrap'][] = 'gii'; $config['modules']['gii'] = 'yiigiiModule'; }
Verify that these lines are present, otherwise append them at the bottom of the web.php
file before the return $config
statement. The last check is in basic/web/index.php
. Verify that YII_ENV
is dev
, with this line:
defined('YII_ENV') or define('YII_ENV', 'dev');
Now, we can point our browser to http://hostname/basic/web/gii
, and we should see this error page:
This page will be displayed since access to Gii is locked by a password.
We need to add extra configuration to the gii
module, passing other allowed IPs. Gii's configuration has an attribute named allowedIPs
, which consents to specify which IP addresses can access the Gii page:
'allowedIPs' => ['127.0.0.1', '::1', '192.168.178.20']
In this extract, Gii will accept access from a localhost (in the IPv4 form with 127.0.0.1 and IPv6 form with ::1
) and from 192.168.178.20
, which should be our IP address in private network.
If the Yii2 application is running on an external hosting, we will set our IP public address in this list of allowed IPs. For example, if our IP is 66.249.64.76
, this entry will be appended to existent (if we want maintain other permitted access points):
'allowedIPs' => ['127.0.0.1', '::1', '192.168.178.20', '66.249.64.76']
To allow access from everywhere (useful in the development stage), we can add *
in this list, which means that the Gii page can be accessed from every IP address:
'allowedIPs' => ['127.0.0.1', '::1', '192.168.178.20', '*']
Consequently, the content of gii]['gii'] = 'yiigiiModule'
is:
$config['modules']['gii'] = [ 'class' => 'yiigiiModule', 'allowedIPs' => ['127.0.0.1', '::1', '192.168.178.20', '*'] ]; configuration in basic/config/web.php will be: if (YII_ENV_DEV) { // configuration adjustments for 'dev' environment $config['bootstrap'][] = 'debug'; $config['modules']['debug'] = 'yiidebugModule'; $config['bootstrap'][] = 'gii'; //$config['modules' }
Now, we are able to access to Gii from any IP.
Refresh the browser by clicking on the page http://hostname/basic/web/gii
and we can finally see its content:
Now, click on the Start button of Model Generator; we will have a form of Model Generator where Table Name is the unique field to fill in. When we start to type the table name, auto-suggestion will display the possible choices. After doing this, when we move to the Model Class field, this will be automatically filled in by a framework. The other fields can be left with the default settings.
Type room
in Table Name and then click on the Model Class field. This field will be filled with Room, which is the filename in the models
folder.
Clicking on the Preview button will display the path where the file will be created and the action will be applied (it should be the overwrite value because we created it in the previous chapter).
Finally, click on the Generate button to complete this action. A response message will give us information about the execution of this operation.
This is the form with a successful result:
Repeat this operation for the other two tables: reservations and customers.
Now, we have three models in the basic/models
folder: Room.php
, Reservation.php
, and Customer.php
.
Let's explain what Gii has done. Open the basic/models/Room.php
file, and we have three methods:
tableName()
rules()
attributeLabels()
The first method, tableName()
, simply returns the name of table to which this model is linked:
public static function tableName() { return 'room'; }
The second method, rules()
, is important because it contains rules validation to be checked when the validate()
method is launched (it is launched automatically in the save()
method) or a massive attributes assignment as:
$model->attributes = arrayWithData;
This is the content of the rules()
method:
public function rules() { return [ [['floor', 'room_number', 'has_conditioner', 'has_tv', 'has_phone', 'available_from'], 'required'], [['floor', 'room_number', 'has_conditioner', 'has_tv', 'has_phone'], 'integer'], [['available_from'], 'safe'], [['price_per_day'], 'number'], [['description'], 'string'] ]; }
The first rule specifies that the fields floor
, room_number
, has_condition
, has_tv
, and avaiable_from
are mandatory because they are passed to the required validator. Moreover, they must be an integer, as required by the second rule.
The fourth rule specifies that the price_per_day
field is a number, while the last rule states that description
is a string.
The last method attributeLabels()
specifies the representation of fields in the display view as a form, grid, and so on.
This is the content of attributeLabels()
:
public function attributeLabels() { return [ 'id' => 'ID', 'floor' => 'Floor', 'room_number' => 'Room Number', 'has_conditioner' => 'Has Conditioner', 'has_tv' => 'Has Tv', 'has_phone' => 'Has Phone', 'available_from' => 'Available From', 'price_per_day' => 'Price Per Day', 'description' => 'Description', ]; }
Yii2 reports—in the model—any relationship between the tables present in a database. We have the Reservation
model that has links to Room
and Customer
.
Follow these instructions to make the framework able to create a relationship in the model:
Reservation
table, add two indexes, respectively for the room_id
and customer_id
fields:ALTER TABLE `reservation` ADD INDEX ( `room_id` ) ; ALTER TABLE `reservation` ADD INDEX ( `customer_id` ) ;
Reservation
table, add two constraints to the room
and customer
tables:ALTER TABLE `reservation` ADD FOREIGN KEY ( `room_id` ) REFERENCES `room` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT ; ALTER TABLE `reservation` ADD FOREIGN KEY ( `customer_id` ) REFERENCES `customer` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT ;
In these constraints, we used RESTRICT
for DELETE
and UPDATE
operations. RESTRICT
avoids the deletion of reservations that refer to customers or rooms that we are trying to delete. Therefore, to delete a customer or room that figures in reservations, we will be required to first delete the reservations.
This behavior ensures that important data such as reservations is never deleted automatically (in a cascade) when deleting a room or a customer. An error message will be displayed when you try to do this to a reservation linked to the customer or room.
In other contexts, a commonly used keyword is CASCADE
, which removes all data that refers to linked tables.
Open Gii again and navigate to http://hostname/basic/web/gii
, then click on the Start button in Model Generator and type room
in Table Name. Click on the Preview button at the bottom of the page and this time you will see that models/Room.php
exists and the action is overwrite, unflagged.
Click on the check near 'overwrite' and then on the Generate button. In this way, we have forced to overwrite the Room
model with the relational data from the Room
table.
Now, basic/models/Room.php
contains a new method named getReservations
at the bottom, with this content:
/** * @return yiidbActiveQuery */ public function getReservations() { return $this->hasMany(Reservation::className(), ['room_id' => 'id']); }
This method returns an ActiveQuery instance, which is used to build a query to be dispatched to the database.
When called as a property, this method will return the list of reservations linked to the model.
You might encounter the case where $model
is an instance of the Room
class for example: $reservationsList = $model->reservations;
In this case, fill the $reservationsList
variables with a list of reservations related to this Room
model.
This is not surprising, although the hasMany
method returns an ActiveQuery
object.
If we explore the __get()
method of BaseActiveRecord
(which is the base class of ActiveRecord) that handles the property requirements, we can see these lines of code:
$value = parent::__get($name); if ($value instanceof ActiveQueryInterface) { return $this->_related[$name] = $value->findFor($name, $this); } else { return $value; }
This returns linked results when the $value
content is an instance of ActiveQueryInterface
(which is an interface implemented by the ActiveQuery
class).
18.119.105.239