-
Book Overview & Buying
-
Table Of Contents
-
Feedback & Rating
Yii2 Application Development Cookbook - Third Edition
By :
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:
<?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);
}
}ShoppingCart in service locator as an application component in the config/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],
];
}
}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>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>['label' => 'Home', 'url' => ['/site/index']], ['label' => 'Cart', 'url' => ['/cart/index']], ['label' => 'About', 'url' => ['/site/about']], // …

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.
Change the font size
Change margin width
Change background colour