In this chapter we will cover how to install Yii Framework and about possible techniques of installation. We will introduce you to application templates: basic and advanced and their difference between them. Then you will learn about dependency injection container. This chapter contains info about model events, which trigger after some actions such as model saving, updating and others. We will learn how to use external code which will include ZendFramework, Laravel, or Symfony. We will also be learning about how to update your yii-1.x.x
based application to yii2
step-by-step.
Yii2 is a modern PHP framework provided as a Composer package. In this recipe, we will install the framework via the Composer package manager and configure the database connection for our application.
First of all, install the Composer package manager on your system.
Note
Note: If you use the OpenServer application on Windows, than the composer
command already exists in the OpenServer terminal.
In Mac or Linux download the installer from https://getcomposer.org/download/ and install it globally by using the following command:
sudo php composer-setup.php --install-dir=/usr/local/bin --filename=composer
In Windows without OpenServer download and run Composer-Setup.exe
from the https://getcomposer.org/doc/00-intro.md page.
If you do not have administrative privileges on the system then as an alternative you can just download the https://getcomposer.org/composer.phar raw file and use the php composer.phar
call instead of single the composer
command.
After installation run in your terminal:
composer
Or (if you just download archive) its alternative:
php composer.phar
When the installation succeeds you will see the following response:
______ / ____/___ ____ ___ ____ ____ ________ _____ / / / __ \/ __ '__ \/ __ \/ __ \/ ___/ _ \/ ___/ / /___/ /_/ / / / / / / /_/ / /_/ (__ ) __/ / \____/\____/_/ /_/ /_/ .___/\____/____/\___/_/ /_/ Composer version 1.2.0 2016-07-18 11:27:19
Right now you can install any package from the https://packagist.org repository.
You can install basic or advanced application templates. In order to learn about the differences between the templates see the Application templates recipe.
Note
Note that during installation the Composer package manager gets a lot of information from the GitHub site. GitHub may limit requests for anonymous users. In this case Composer asks you to input your access token. You should just register the https://github.com site and generate a new token via the https://github.com/blog/1509-personal-api-tokens guide.
Carry out the following steps for installing basic project template:
As the first step open your terminal and install Bower-to-Composer adapter:
composer global require "fxp/composer-asset-plugin:^1.2.0"
It provides a simple way to load related non-PHP packages (JavaScript and CSS) from the Bower repository.
Create a new application in the new
basic
directory:composer create-project --prefer-dist yiisoft/yii2-app-basic basic
Check that your PHP contains the required extensions:
cd basic php requirements.php
Create a new database (if it is needle for your project) and configure it in the
config/db.php
file.Try to run application via the following console command:
php yii serve
Check in your browser that the application works by the
http://localhost:8080
address:
For permanent working create a new host in your server (Apache, Nginx, and so on) and set the web
directory as a document root of the host.
Carry out the following steps for installing advanced project template:
As the first step open your terminal install Bower-to-Composer adapter:
composer global require "fxp/composer-asset-plugin:^1.2.0"
It provides a simple way to load related non-PHP packages (JavaScript and CSS) from the Bower repository.
Create a new application in the new
basic
directory:composer create-project --prefer-dist yiisoft/yii2-app-advanced advanced
The new application does not contains local configuration files and
index.php
entry scripts yet. To generate the files justinit
a working environment:cd advanced php init
During initialization select the Development environment.
Check that your PHP contains the required extensions:
php requirements.php
Create a new database and configure it in the generated
common/config/main-local.php
file.Apply the application migrations:
php yii migrate
This command will automatically create a
user
table in your database.Try to run a frontend application by the following console command:
php yii serve --docroot=@frontend/web --port=8080
Then run the backend in an other terminal window:
php yii serve --docroot=@backend/web --port=8090
Check in your browser that the application works via the
http://localhost:8080
andhttp://localhost:8090
addresses:
Create two new hosts for backend and frontend application in your server (Apache, Nginx, and so on) and set the backend/web
and frontend/web
directories as document roots of the hosts.
First of all, we installed the Composer package manager and the Bower asset plugin.
After we installed the application via the composer create-project
command, the command creates a new empty directory, clones the source code of application template and loads all its inner dependencies (framework and other components) into the vendor
subdirectory.
If needed, we will initialize application configuration and set up a new database.
We can check system requirements via running the requirements.php
script in console or browser mode.
And after cloning of the code we can configure our own PHP server to work with the web
directories as the server's document roots.
For more information about installing
yii2-app-basic
refer to, http://www.yiiframework.com/doc-2.0/guide-start-installation.html.Refer to, https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-installation.md for
yii2-app-advanced
.Refer to, https://getcomposer.org for the Composer package manager.
For creating a GitHub access token for Composer refer to https://github.com/blog/1509-personal-api-tokens.
Yii2 has two application templates for development: basic and advanced. What is the difference between basic and advanced templates?
The names are confusing. Some people in the end choose basic because advanced may sound repulsive. In this chapter we will look at the differences.
Please refer to the Installing the framework recipe's How to do it… section to understand and learn how to install different templates.
The advanced template has a custom system of configurations. It is developed so that a team can work together on a project but each developer can customize their own configurations for development, testing, and other environments.
Configuration environments can be complicated and normally aren't used when you develop alone.
The advanced template has frontend and backend folders for the frontend and backend parts of the web application accordingly. So you can configure a separate host for each folder and thereby isolate the frontend and backend part.
This is a simple way to organize files into directories and configure the web server. You can easily do the same thing in the basic template.
Neither front/back-end separation nor user management is on its own a good reason to choose the advanced template. It's better to adapt these features to your app—you'll learn more and won't get the difficult config problem.
If you will be working on the project with a team and you might need configuration flexibility, use different environments to develop and in this case a better choice would be the advanced application template. If you will be working alone and your project is simple you should choose the basic application template.
Dependency Inversion Principle (DIP) suggests we create modular low-coupling code with the help of extracting clear abstraction subsystems.
For example, if you want to simplify a big class you can split it into many chunks of routine code and extract every chunk into a new simple separated class.
The principle says that your low-level chunks should implement an all-sufficient and clear abstraction, and high-level code should work only with this abstraction and not low-level implementation.
When we split a big multitask class into small specialized classes, we face the issue of creating dependent objects and injecting them into each other.
If we could create one instance before:
$service = new MyGiantSuperService();
And after splitting we will create or get all dependent items and build our service:
$service = new MyService( new Repository(new PDO('dsn', 'username', 'password')), new Session(), new Mailer(new SmtpMailerTransport('username', 'password', host')), new Cache(new FileSystem('/tmp/cache')), );
Dependency injection container is a factory that allows us to not care about building our objects. In Yii2 we can configure a container only once and use it for retrieving our service like this:
$service = Yii::$container->get('app\services\MyService')
We can also use this:
$service = Yii::createObject('app\services\MyService')
Or we ask the container to inject it as a dependency in the constructor of an other service:
use app\services\MyService; class OtherService { public function __construct(MyService $myService) { … } }
When we will get the OtherService
instance:
$otherService = Yii::createObject('app\services\OtherService')
In all cases the container will resolve all dependencies and inject dependent objects in each other.
In the recipe we create shopping cart with storage subsystem and inject the cart automatically into controller.
Create a new application by using the Composer package manager, as described in the official guide at http://www.yiiframework.com/doc-2.0/guide-startinstallation.html.
Carry out the following steps:
Create a shopping cart class:
<?php namespace app\cart; use app\cart\storage\StorageInterface; class ShoppingCart { private $storage; private $_items = []; public function __construct(StorageInterface $storage) { $this->storage = $storage; } public function add($id, $amount) { $this->loadItems(); if (array_key_exists($id, $this->_items)) { $this->_items[$id]['amount'] += $amount; } else { $this->_items[$id] = [ 'id' => $id, 'amount' => $amount, ]; } $this->saveItems(); } public function remove($id) { $this->loadItems(); $this->_items = array_diff_key($this->_items, [$id => []]); $this->saveItems(); } public function clear() { $this->_items = []; $this->saveItems(); } public function getItems() { $this->loadItems(); return $this->_items; } private function loadItems() { $this->_items = $this->storage->load(); } private function saveItems() { $this->storage->save($this->_items); } }
It will work only with own items. Instead of built-in storing items to session it will delegate this responsibility to any external storage class, which will implement the
StorageInterface
interface.The cart class just gets the storage object in its own constructor, saves it instance into private
$storage
field and calls itsload()
andsave()
methods.Define a common cart storage interface with the required methods:
<?php namespace app\cart\storage; interface StorageInterface { /** * @return array of cart items */ public function load(); /** * @param array $items from cart */ public function save(array $items); }
Create a simple storage implementation. It will store selected items in a server session:
<?php namespace app\cart\storage; use yii\web\Session; class SessionStorage implements StorageInterface { private $session; private $key; public function __construct(Session $session, $key) { $this->key = $key; $this->session = $session; } public function load() { return $this->session->get($this->key, []); } public function save(array $items) { $this->session->set($this->key, $items); } }
The storage gets any framework session instance in the constructor and uses it later for retrieving and storing items.
Configure the
ShoppingCart
class and its dependencies in theconfig/web.php
file:<?php use app\cart\storage\SessionStorage; Yii::$container->setSingleton('app\cart\ShoppingCart'); Yii::$container->set('app\cart\storage\StorageInterface', function() { return new SessionStorage(Yii::$app->session, 'primary-cart'); }); $params = require(__DIR__ . '/params.php'); //…
Create the cart controller with an extended constructor:
<?php namespace app\controllers; use app\cart\ShoppingCart; use app\models\CartAddForm; use Yii; use yii\data\ArrayDataProvider; use yii\filters\VerbFilter; use yii\web\Controller; class CartController extends Controller { private $cart; public function __construct($id, $module, ShoppingCart $cart, $config = []) { $this->cart = $cart; parent::__construct($id, $module, $config); } public function behaviors() { return [ 'verbs' => [ 'class' => VerbFilter::className(), 'actions' => [ 'delete' => ['post'], ], ], ]; } public function actionIndex() { $dataProvider = new ArrayDataProvider([ 'allModels' => $this->cart->getItems(), ]); return $this->render('index', [ 'dataProvider' => $dataProvider, ]); } public function actionAdd() { $form = new CartAddForm(); if ($form->load(Yii::$app->request->post()) && $form->validate()) { $this->cart->add($form->productId, $form->amount); return $this->redirect(['index']); } return $this->render('add', [ 'model' => $form, ]); } public function actionDelete($id) { $this->cart->remove($id); return $this->redirect(['index']); } }
<?php namespace app\models; use yii\base\Model; class CartAddForm extends Model { public $productId; public $amount; public function rules() { return [ [['productId', 'amount'], 'required'], [['amount'], 'integer', 'min' => 1], ]; } }
Create the
views/cart/index.php
view:<?php use yii\grid\ActionColumn; use yii\grid\GridView; use yii\grid\SerialColumn; use yii\helpers\Html; /* @var $this yii\web\View */ /* @var $dataProvider yii\data\ArrayDataProvider */ $this->title = 'Cart'; $this->params['breadcrumbs'][] = $this->title; ?> <div class="cart-index"> <h1><?= Html::encode($this->title) ?></h1> <p><?= Html::a('Add Item', ['add'], ['class' => 'btn btn-success']) ?></p> <?= GridView::widget([ 'dataProvider' => $dataProvider, 'columns' => [ ['class' => SerialColumn::className()], 'id:text:Product ID', 'amount:text:Amount', [ 'class' => ActionColumn::className(), 'template' => '{delete}', ] ], ]) ?> </div>
Create the
views/cart/add.php
view:<?php use yii\helpers\Html; use yii\bootstrap\ActiveForm; /* @var $this yii\web\View */ /* @var $form yii\bootstrap\ActiveForm */ /* @var $model app\models\CartAddForm */ $this->title = 'Add item'; $this->params['breadcrumbs'][] = ['label' => 'Cart', 'url' => ['index']]; $this->params['breadcrumbs'][] = $this->title; ?> <div class="cart-add"> <h1><?= Html::encode($this->title) ?></h1> <?php $form = ActiveForm::begin(['id' => 'contact-form']); ?> <?= $form->field($model, 'productId') ?> <?= $form->field($model, 'amount') ?> <div class="form-group"> <?= Html::submitButton('Add', ['class' => 'btn btn-primary']) ?> </div> <?php ActiveForm::end(); ?> </div>
Add link items into the main menu:
['label' => 'Home', 'url' => ['/site/index']], ['label' => 'Cart', 'url' => ['/cart/index']], ['label' => 'About', 'url' => ['/site/about']], // …
Open the cart page and try to add rows:
In this case we have the main ShoppingCart
class with a low-level dependency, defined by an abstraction interface:
class ShoppingCart { public function __construct(StorageInterface $storage) { … } } interface StorageInterface { public function load(); public function save(array $items); }
And we have some an implementation of the abstraction:
class SessionStorage implements StorageInterface { public function __construct(Session $session, $key) { … } }
Right now we can create an instance of the cart manually like this:
$storage = new SessionStorage(Yii::$app->session, 'primary-cart'); $cart = new ShoppingCart($storage)
It allows us to create a lot of different implementations such as SessionStorage
, CookieStorage
, or DbStorage
. And we can reuse the framework-independent ShoppingCart
class with StorageInterface
in different projects and different frameworks. We must only implement the storage class with the interface's methods for needed framework.
But instead of manually creating an instance with all dependencies, we can use a dependency injection container.
By default the container parses the constructors of all classes and recursively creates all the required instances. For example, if we have four classes:
class A { public function __construct(B $b, C $c) { … } } class B { ... } class C { public function __construct(D $d) { … } } class D { ... }
We can retrieve the instance of class A
in two ways:
$a = Yii::$container->get('app\services\A') // or $a = Yii::createObject('app\services\A')
And the container automatically creates instances of the B
, D
, C
, and A
classes and injects them into each other.
In our case we mark the cart instance as a singleton:
Yii::$container->setSingleton('app\cart\ShoppingCart');
This means that the container will return a single instance for every repeated call instead of creating the cart again and again.
Besides, our ShoppingCart
has the StorageInterface
type in its own constructor and the container does know what class it must instantiate for this type. We must manually bind the class to the interface like this:
Yii::$container->set('app\cart\storage\StorageInterface', 'app\cart\storage\CustomStorage',);
But our SessionStorage
class has non-standard constructor:
class SessionStorage implements StorageInterface { public function __construct(Session $session, $key) { … } }
Therefore we use an anonymous function to manually creatie the instance:
Yii::$container->set('app\cart\storage\StorageInterface', function() { return new SessionStorage(Yii::$app->session, 'primary-cart'); });
And after all we can retrieve the cart object from the container manually in our own controllers, widgets, and other places:
$cart = Yii::createObject('app\cart\ShoppingCart')
But every controller and other object will be created via the createObject
method inside the framework. And we can use injection of cart via the controller constructor:
class CartController extends Controller { private $cart; public function __construct($id, $module, ShoppingCart $cart, $config = []) { $this->cart = $cart; parent::__construct($id, $module, $config); } // ... }
Use this injected cart object:
public function actionDelete($id) { $this->cart->remove($id); return $this->redirect(['index']); }
For more information about DIP refer to https://en.wikipedia.org/wiki/Dependency_inversion_principle
In order to learn more about dependency injection container refer to http://www.yiiframework.com/doc-2.0/guide-concept-di-container.html
Instead of manually creating instances of different shared services (application components) we can get them from a special global object, which contains configurations and instances of all components.
A service locator is a global object that contains a list of components or definitions, uniquely identified by an ID, and allow us to retrieve any needed instance by its ID. The locator creates a single instance of the component on-the-fly at the first call and returns a previous instance at the subsequent calls.
In this recipe, we will create a shopping cart component and will write a cart controller for working with it.
Create a new application by using the Composer package manager, as described in the official guide at http://www.yiiframework.com/doc-2.0/guide-start-installation.html.
Carry out the following steps to create a shopping cart component:
Create a shopping cart component. It will store selected items in a user session:
<?php namespace app\components; use Yii; use yii\base\Component; class ShoppingCart extends Component { public $sessionKey = 'cart'; private $_items = []; public function add($id, $amount) { $this->loadItems(); if (array_key_exists($id, $this->_items)) { $this->_items[$id]['amount'] += $amount; } else { $this->_items[$id] = [ 'id' => $id, 'amount' => $amount, ]; } $this->saveItems(); } public function remove($id) { $this->loadItems(); $this->_items = array_diff_key($this->_items, [$id => []]); $this->saveItems(); } public function clear() { $this->_items = []; $this->saveItems(); } public function getItems() { $this->loadItems(); return $this->_items; } private function loadItems() { $this->_items = Yii::$app->session->get($this->sessionKey, []); } private function saveItems() { Yii::$app->session->set($this->sessionKey, $this->_items); } }
Register the
ShoppingCart
in service locator as an application component in theconfig/web.php
file:'components' => [ … 'cart => [ 'class' => 'app\components\ShoppingCart', 'sessionKey' => 'primary-cart', ], ]
<?php namespace app\controllers; use app\models\CartAddForm; use Yii; use yii\data\ArrayDataProvider; use yii\filters\VerbFilter; use yii\web\Controller; class CartController extends Controller { public function behaviors() { return [ 'verbs' => [ 'class' => VerbFilter::className(), 'actions' => [ 'delete' => ['post'], ], ], ]; } public function actionIndex() { $dataProvider = new ArrayDataProvider([ 'allModels' => Yii::$app->cart->getItems(), ]); return $this->render('index', [ 'dataProvider' => $dataProvider, ]); } public function actionAdd() { $form = new CartAddForm(); if ($form->load(Yii::$app->request->post()) && $form->validate()) { Yii::$app->cart->add($form->productId, $form->amount); return $this->redirect(['index']); } return $this->render('add', [ 'model' => $form, ]); } public function actionDelete($id) { Yii::$app->cart->remove($id); return $this->redirect(['index']); } }
<?php namespace app\models; use yii\base\Model; class CartAddForm extends Model { public $productId; public $amount; public function rules() { return [ [['productId', 'amount'], 'required'], [['amount'], 'integer', 'min' => 1], ]; } }
Create the
views
/cart
/index.php
view:<?php use yii\grid\ActionColumn; use yii\grid\GridView; use yii\grid\SerialColumn; use yii\helpers\Html; /* @var $this yii\web\View */ /* @var $dataProvider yii\data\ArrayDataProvider */ $this->title = 'Cart'; $this->params['breadcrumbs'][] = $this->title; ?> <div class="site-contact"> <h1><?= Html::encode($this->title) ?></h1> <p><?= Html::a('Add Item', ['add'], ['class' => 'btn btn-success']) ?></p> <?= GridView::widget([ 'dataProvider' => $dataProvider, 'columns' => [ ['class' => SerialColumn::className()], 'id:text:Product ID', 'amount:text:Amount', [ 'class' => ActionColumn::className(), 'template' => '{delete}', ] ], ]) ?> </div>
Create the
views
/cart
/add.php
view:<?php use yii\helpers\Html; use yii\bootstrap\ActiveForm; /* @var $this yii\web\View */ /* @var $form yii\bootstrap\ActiveForm */ /* @var $model app\models\CartAddForm */ $this->title = 'Add item'; $this->params['breadcrumbs'][] = ['label' => 'Cart', 'url' => ['index']]; $this->params['breadcrumbs'][] = $this->title; ?> <div class="site-contact"> <h1><?= Html::encode($this->title) ?></h1> <?php $form = ActiveForm::begin(['id' => 'contact-form']); ?> <?= $form->field($model, 'productId') ?> <?= $form->field($model, 'amount') ?> <div class="form-group"> <?= Html::submitButton('Add', ['class' => 'btn btn-primary']) ?> </div> <?php ActiveForm::end(); ?> </div>
Add a link item into the main menu:
['label' => 'Home', 'url' => ['/site/index']], ['label' => 'Cart', 'url' => ['/cart/index']], ['label' => 'About', 'url' => ['/site/about']], // …
Open the cart page and try to add rows:
First of all we created our own class with a public sessionKey
option:
<?php namespace app\components; use yii\base\Component; class ShoppingCart extends Component { public $sessionKey = 'cart'; // … }
Secondly, we added the component definition into the components
section of the configuration file:
'components' => [ … 'cart => [ 'class' => 'app\components\ShoppingCart', 'sessionKey' => 'primary-cart', ], ]
Right now we can retrieve the component instance in two ways:
$cart = Yii::$app->cart; $cart = Yii::$app->get('cart');
And we can use this object in our own controllers, widgets, and other places.
When we call any component such as cart
:
Yii::$app->cart
We call the virtual property of the Application
class instance in the Yii::$app
static variable. But the yii\base\Application
class extends the yii\base\Module
class, which extends the yii\di\ServiceLocator
class with the __get
magic method. This magic method just calls the get()
method of the yii\di\ServiceLocator
class:
namespace yii\di; class ServiceLocator extends Component { private $_components = []; private $_definitions = []; public function __get($name) { if ($this->has($name)) { return $this->get($name); } else { return parent::__get($name); } } // … }
As a result it is an alternative to directly calling the service via the get
method:
Yii::$app->get('cart);
When we get a component from the get
method of service locator, the locator finds needed definition in its _definitions
list and if successful it creates a new object by the definition on the fly, registers it in its own list of complete instances _components
and returns the object.
If we get some component, multiplying the locator will always return the previous saved instance again and again:
$cart1 = Yii::$app->cart; $cart2 = Yii::$app->cart; var_dump($cart1 === $cart2); // bool(true)
It allows us to use the shared single cart instance Yii::$app->cart
or single database connection Yii::$app->db
instead of creating one large set from scratch again and again.
For more information about the service locator and about core framework components refer to http://www.yiiframework.com/doc-2.0/guide-concept-service-locator.html
The Configuring components recipe
The Creating components recipe in Chapter 8, Extending Yii
Yii2 provides the powerful module Gii to generate models, controllers, and views, which you can easily modify and customize. It's a really helpful tool for fast and quick development.
In this section we will explore how to use Gii and generate code. For example you have a database with one table named film
and you would like to create an application with CRUD operations for this table. It's easy.
Create a new application by using composer as described in the official guide at http://www.yiiframework.com/doc-2.0/guide-start-installation.html.
Download the Sakila database from http://dev.mysql.com/doc/index-other.html.
Execute the downloaded SQLs: first the schema then the data.
Configure the database connection in
config/main.php
to use the Sakila database.Run your web-server by
./yii serve
.
Go to
http://localhost:8080/index.php?r=gii
and select Model Generator.Fill out Table Name as
actor
and Model Class asActor
and press button Generate at the bottom of page.Return tothe main Gii menu by clicking the yii code generator logo on the header and choose CRUD Generator.
Fill out the Model Class field as
app\models\Actor
and Controller Class asapp\controllers\ActorController
.Press the Preview button at the bottom of page and then press green button Generate.
Check the result via
http://localhost:8080/index.php?actor/create
.
If you check your project structure you will see autogenerated code:

Firstly we've created an Actor
model. Gii automatically creates all model rules which depends on mysql
field types. For example, if in your MySQL actor
table's fields first_name
and last_name
have IS NOT NULL
flag then Yii automatically creates rule for it required
and sets max length 45
symbols because in our database max length of this field is set up as 45
.
public function rules() { return [ [['first_name', 'last_name'], 'required'], [['last_update'], 'safe'], [['first_name', 'last_name'], 'string', 'max' => 45], ]; }
Also Yii creates relationship between models automatically, based on foreign keys you added to your database. In our case two relations were created automatically.
public function getFilmActors() { return $this->hasMany(FilmActor::className(), ['actor_id' => 'actor_id']); } public function getFilms() { return $this->hasMany(Film::className(), ['film_id' => 'film_id'])->viaTable('film_actor', ['actor_id' => 'actor_id']); }
This relationship has been created because we have two foreign keys in our database. The film_actor
table has foreign key fk_film_actor_actor
which points to actor
table fields actor_id
and fk_film_actor_film
which points to film
table field film_id
.
Notice that you haven't generated FilmActor
model yet. So if you would develop full-app versus demo you had to generate Film
, FilmActor
models also. For the rest of the pieces, refer to http://www.yiiframework.com/doc-2.0/guide-start-gii.html.
Yii is a very customizable framework. Moreover, as in all customizable code, there should be a convenient way to set up different application parts. In Yii, this is provided through configuration files located at config
.
Create a new application by using the Composer package manager as described in the official guide at http://www.yiiframework.com/doc-2.0/guide-startinstallation.html.
If you have worked with Yii before, then you have probably configured a database connection:
return [ … 'components' => [ 'db' => [ 'class' => 'system.db.CDbConnection', 'dsn' => 'mysql:host=localhost;dbname=database_name', 'username' => 'root', 'password' => '', 'charset' => 'utf8', ], … ], … ];
This way of configuring components is used when you want to use a component across all application parts. With the preceding configuration, you can access a component by its name, such as Yii::$app->db
.
When you are using the Yii::$app->db
component for the first time directly or through an Active Record model, Yii creates a component and initializes its public properties with the corresponding values provided in db
array under the components
section of the application configuration file. In the preceding code, dsn
value will be assigned to yii\db\Connection::dsn
, username
will be assigned to Connection::username
, and so on.
If you want to find out what charset
stands for or want to know what else you can configure in the db
component, then you need to know its class. In the case of the db
component, the class is yii\db\Connection
. You can just open the class and look for its public properties, which you can set from config.
In the preceding code, the class
property is a bit special because it is used to specify the component class name. It does not exist in the yii\db\Connection
class. Therefore, it can be used to override a class as follows:
return [ … 'components' => [ 'db' => [ 'class' => app\components\MyConnection', … ], … ], … );
This way, you can override each application component; this is very useful whenever a standard component does not fit your application.
Now, let's find out which standard Yii application components you can configure. There are two application types bundled with Yii:
Web application (
yii\webApplication
)Console application (
yii\console\Application
)
Both are extended from yii\base\Application
, so both console and web applications share its components.
You can get the component names from the source code of the coreComponents()
application's method.
You can add your own application components (classes extended from yii\base\Component
) by simply adding new configuration items and pointing their class properties to your custom classes.
Both console and web application components are listed in the list at http://www.yiiframework.com/doc-2.0/guide-structure-application-components.html
For more information on creating your own components see:
The Service locator recipe
The Creating components recipe in Chapter 8, Extending Yii
Yii's events provide a simple implementation, which allows you to listen and subscribe to various events that occur in your web-application. For example, you may wish to send a notification about a new article to followers each time you publish new material.
Create a new application by using the Composer package manager, as described in the official guide at http://www.yiiframework.com/doc-2.0/guide-start-installation.html.
Execute the following SQL code on your server to create the
article
table:CREATE TABLE 'article' ( 'id' int(11) NOT NULL AUTO_INCREMENT, 'name' varchar(255) DEFAULT NULL, 'description' text, PRIMARY KEY ('id') ) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8;
Generate the
Article
model using Gii.Run your webserver by
./yii serve
command.
Add an action test to
\controllers\SiteController
:public function actionTest() { $article = new Article(); $article->name = 'Valentine\'s Day\'s coming? Aw crap! I forgot to get a girlfriend again!'; $article->description = 'Bender is angry at Fry for dating a robot. Stay away from our women. You've got metal fever, boy. Metal fever'; // $event is an object of yii\base\Event or a child class $article->on(ActiveRecord::EVENT_AFTER_INSERT, function($event) { $followers = ['john2@teleworm.us', 'shivawhite@cuvox.de', 'kate@dayrep.com' ]; foreach($followers as $follower) { Yii::$app->mailer->compose() ->setFrom('techblog@teleworm.us') ->setTo($follower) ->setSubject($event->sender->name) ->setTextBody($event->sender->description) ->send(); } echo 'Emails has been sent'; }); if (!$article->save()) { echo VarDumper::dumpAsString($article->getErrors()); }; }
Update the
config/web.php
componentmailer
using the following code.'mailer' => [ 'class' => 'yii\swiftmailer\Mailer', 'useFileTransport' => false, ],
Run this URL in your browser:
http://localhost:8080/index.php?r=site/test
.Also check
http://www.fakemailgenerator.com/inbox/teleworm.u
s/john2/
.
We've created an Article
model and added a handler for the ActiveRecord::EVENT_AFTER_INSERT
event to our Article
model. It means that every time we save a new article an event is triggered and our attached handler will be called.
In the real-world, we would like to notify our blog followers each time we publish a new article. In a real application we would have a follower
or user
table and with different blog sections not only single blog. In this example, after saving our model we notify our followers john2@teleworm.us
, shivawhite@cuvox.de
, and kate@dayrep.com
. In the last step we just prove that users have received our notifications, particularly john2
. You can create your own event with any name. In this example we use a built-in event called ActiveRecord::EVENT_AFTER_INSERT
, which is called after each insert to the database.
For example, we can create our own event. Just add a new actionTestNew
with the following code:
public function actionTestNew() { $article = new Article(); $article->name = 'Valentine\'s Day\'s coming? Aw crap! I forgot to get a girlfriend again!'; $article->description = 'Bender is angry at Fry for dating a robot. Stay away from our women. You've got metal fever, boy. Metal fever'; // $event is an object of yii\base\Event or a child class $article->on(Article::EVENT_OUR_CUSTOM_EVENT, function($event) { $followers = ['john2@teleworm.us', 'shivawhite@cuvox.de', 'kate@dayrep.com' ]; foreach($followers as $follower) { Yii::$app->mailer->compose() ->setFrom('techblog@teleworm.us') ->setTo($follower) ->setSubject($event->sender->name) ->setTextBody($event->sender->description) ->send(); } echo 'Emails have been sent'; }); if ($article->save()) { $article->trigger(Article::EVENT_OUR_CUSTOM_EVENT); } }
Also add the EVENT_OUR_CUSTOM_EVENT
constant to models/Article
as:
class Article extends \yii\db\ActiveRecord { CONST EVENT_OUR_CUSTOM_EVENT = 'eventOurCustomEvent'; … }
Run http://localhost:8080/index.php?r=site/test-new
.
You should see the same result and all notifications to followers will be sent again. The main difference is we used our custom event name.
After the save, we've triggered our event. Events may be triggered by calling the yii\base\Component::trigger()
method. The method requires an event name, and optionally an event object that describes the parameters to be passed to the event handlers.
For more information about events refer to http://www.yiiframework.com/doc-2.0/guide-concept-events.html
Package repositories, PSR standards, and social coding provide us with lots of high-quality reusable libraries and other components with free licenses. We can just install any external component in project instead of reengineering them from scratch. It improves development performance and makes for higher-quality code.
Create a new application by using the Composer package manager as described in the official guide at http://www.yiiframework.com/doc-2.0/guide-start-installation.html.
In this recipe we will try to attach some libraries manually and via Composer.
When you use NoSQL or other databases without autoincrement primary keys, you must generate unique identifiers manually. For example, you can use Universally Unique Identifier (UUID) instead of a numerical one. Let's do it:
Install https://github.com/ramsey/uuid component via Composer:
composer require ramsey/uuid
Create a demonstration console controller:
<?php namespace app\commands; use Ramsey\Uuid\Uuid; use yii\console\Controller; class UuidController extends Controller { public function actionGenerate() { $this->stdout(Uuid::uuid4()->toString() . PHP_EOL); $this->stdout(Uuid::uuid4()->toString() . PHP_EOL); $this->stdout(Uuid::uuid4()->toString() . PHP_EOL); $this->stdout(Uuid::uuid4()->toString() . PHP_EOL); $this->stdout(Uuid::uuid4()->toString() . PHP_EOL); } }
And just run it:
./yii uuid/generate
If successful, you'll see the following output:
25841e6c-6060-4a81-8368-4d99aa3617dd fcac910a-a9dc-4760-8528-491c17591a26 4d745da3-0a6c-47df-aee7-993a42ed915c 0f3e6da5-88f1-4385-9334-b47d1801ca0f 21a28940-c749-430d-908e-1893c52f1fe0
That's it! Now you can use the
Ramsey\Uuid\Uuid
class in your project.
We can install a library automatically when it is provided as a Composer package. In other cases we must install it manually.
For example, create some library examples:
Create the
awesome/namespaced/Library.php
file with the following code:<?php namespace awesome\namespaced; class Library { public function method() { return 'I am an awesome library with namespace.'; } }
Create the
old/OldLibrary.php
file:<?php class OldLibrary { function method() { return 'I am an old library without namespace.'; } }
Create a set of functions as an
old/functions.php
file:<?php function simpleFunction() { return 'I am a simple function.'; }
And now set up this file in our application:
Define the new alias for the
awesome
library namespace root in theconfig/web.php
file (inaliases
section):$config = [ 'id' => 'basic', 'basePath' => dirname(__DIR__), 'bootstrap' => ['log'], 'aliases' => [ '@awesome' => '@app/awesome', ], 'components' => [ // … ], 'params' => // … ];
or via the
setAlias
method:Yii::setAlias('@awesome', '@app/awesome');
Define a simple class file path at the top of the
config/web.php
file:Yii::$classMap['OldLibrary'] = '@old/OldLibrary.php';
Configure autoloading of the
functions.php
file incomposer.json
:"require-dev": { ... }, "autoload": { "files": ["old/functions.php"] }, "config": { ... },
And apply the changes:
composer update
And now create an example controller:
<?php namespace app\controllers; use yii\base\Controller; class LibraryController extends Controller { public function actionIndex() { $awesome = new \awesome\namespaced\Library(); echo '<pre>' . $awesome->method() . '</pre>'; $old = new \OldLibrary(); echo '<pre>' . $old->method() . '</pre>'; echo '<pre>' . simpleFunction() . '</pre>'; } }
If you want to use Yii2 framework code with other frameworks just add Yii2-specific parameters in composer.json
:
{ ... "extra": { "asset-installer-paths": { "npm-asset-library": "vendor/npm", "bower-asset-library": "vendor/bower" } } }
composer require yiisoft/yii2
Now open the entry script of your application (on ZendFramework, Laravel, Symfony, and many more), require the Yii2 autoloader, and create the Yii application instance:
require(__DIR__ . '/../vendor/autoload.php'); require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php'); $config = require(__DIR__ . '/../config/yii/web.php'); new yii\web\Application($config);
That's it! Now you can use Yii::$app instances, models, widgets and other components from Yii2.
In the first case we just install a new Composer package in our project and use it, because its composer.json
file defines all aspects of autoloading
library files.
But in the second case we did not have Composer packages and registered the files in the autoloading mechanism manually. In Yii2 we can use aliases and Yii::$classMap
for registering the roots of PSR-4 namespaces and for single files.
But as an alternative we can use Composer autoloader for all cases. Just define an extended autoload
section in the composer.json
file like this:
"autoload": { "psr-0": { "": "old/" }, "psr-4": {"awesome\\": "awesome/"}, "files": ["old/functions.php"] }
Apply the changes using this command:
composer update
Right now you can remove aliases and $classMap
definitions from your configuration files and ensure the example page still works correctly:

This example completely uses Composer's autoloader instead of the framework's autoloader.
For more information about integrating external code in Yii2 and framework code into our projects see the guide at http://www.yiiframework.com/doc-2.0/guide-tutorial-yii-integration.html
For more on aliases refer to http://www.yiiframework.com/doc-2.0/guide-concept-aliases.html
For more on the
autoload
section ofcomposer.json
refer to https://getcomposer.org/doc/01-basic-usage.md#autoloadingAnd also you can browse or search any Composer packages on https://packagist.org