If all of the best practices for deploying a Yii application are applied and you still do not have the performance you want, then most probably there are some bottlenecks with the application itself. The main principle while dealing with these bottlenecks is that you should never assume anything and always test and profile the code before trying to optimize it.
In this recipe, we will try to find bottlenecks in the Yii2 mini application.
Create a new yii2-app-basic
application using the Composer package manager, as described in the official guide at http://www.yiiframework.com/doc-2.0/guide-start-installation.html.
<?php use yiidbMigration; class m160308_093233_create_example_tables extends Migration { public function up() { $tableOptions = null; if ($this->db->driverName === 'mysql') { $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE=InnoDB'; } $this->createTable('{{%category}}', [ 'id' => $this->primaryKey(), 'name' => $this->string()->notNull(), ], $tableOptions); $this->createTable('{{%article}}', [ 'id' => $this->primaryKey(), 'category_id' => $this->integer()->notNull(), 'title' => $this->string()->notNull(), 'text' => $this->text()->notNull(), ], $tableOptions); $this->createIndex('idx-article-category_id', '{{%article}}', 'category_id'); $this->addForeignKey('fk-article-category_id', '{{%article}}', 'category_id', '{{%category}}', 'id'); } public function down() { $this->dropTable('{{%article}}'); $this->dropTable('{{%category}}'); } }
<?php namespace appcommands; use appmodelsArticle; use appmodelsCategory; use FakerFactory; use yiiconsoleController; class DataController extends Controller { public function actionInit() { $db = Yii::$app->db; $faker = Factory::create(); $transaction = $db->beginTransaction(); try { $categories = []; for ($id = 1; $id <= 100; $id++) { $categories[] = [ 'id' => $id, 'name' => $faker->name, ]; } $db->createCommand()->batchInsert(Category::tableName(), ['id', 'name'], $categories)->execute(); $articles = []; for ($id = 1; $id <= 100; $id++) { $articles[] = [ 'id' => $id, 'category_id' => $faker->numberBetween(1, 100), 'title' => $faker->text($maxNbChars = 100), 'text' => $faker->text($maxNbChars = 200), ]; } $db->createCommand() ->batchInsert(Article::tableName(), ['id', 'category_id', 'title', 'text'], $articles)->execute(); $transaction->commit(); } catch (Exception $e) { $transaction->rollBack(); throw $e; } } }
And execute it:
./yii data/init
ArticleController
class as follows:<?php namespace appcontrollers; use Yii; use appmodelsArticle; use yiidataActiveDataProvider; use yiiwebController; class ArticleController extends Controller { public function actionIndex() { $query = Article::find(); $dataProvider = new ActiveDataProvider([ 'query' => $query, ]); return $this->render('index', [ 'dataProvider' => $dataProvider, ]); } }
views/article/index.php
view as follows:<?php use yiihelpersHtml; use yiiwidgetsListView; /* @var $this yiiwebView */ /* @var $dataProvider yiidataActiveDataProvider */ $this->title = 'Articles'; $this->params['breadcrumbs'][] = $this->title; ?> <div class="article-index"> <h1><?= Html::encode($this->title) ?></h1> <?= ListView::widget([ 'dataProvider' => $dataProvider, 'itemOptions' => ['class' => 'item'], 'itemView' => '_item', ]) ?> /div>
Then add views/article/_item.php
:
<?php use yiihelpersHtml; /* @var $this yiiwebView */ /* @var $model appmodelsArticle */ ?> <div class="panel panel-default"> <div class="panel-heading"><?= Html::encode($model->title); ?></div> <div class="panel-body"> Category: <?= Html::encode($model->category->name) ?> </div> </div>
Follow these steps to profile an application with Yii:
views/article/index.php
file and add profiler calls before and after the ListView
widget:<div class="article-index"> <h1><?= Html::encode($this->title) ?></h1> <?php Yii::beginProfile('articles') ?> <?= ListView::widget([ 'dataProvider' => $dataProvider, 'itemOptions' => ['class' => 'item'], 'itemView' => '_item', ]) ?> <?php Yii::endProfile('articles') ?> </div>
Now refresh the page.
Now examine the Profiling report:
We can see that our articles block has taken close to 40 milliseconds.
category
relation as follows:class ArticleController extends Controller { public function actionIndex() { $query = Article::find()->with('category'); $dataProvider = new ActiveDataProvider([ 'query' => $query, ]); return $this->render('index', [ 'dataProvider' => $dataProvider, ]); } }
Right now the articles listing has taken close to 25 milliseconds because the application makes fewer SQL queries with eager loading of related models.
You can enclose any fragment of source code with Yii::beginProfile
and Yii::endProfile
calls:
Yii::beginProfile('articles'); // ... Yii::endProfile('articles');
After executing the page, you can see the report with all timings on the Profiling page of the debug module.
Also, you can use nested profiling calls as follows:
Yii::beginProfile('outer'); Yii::beginProfile('inner'); // ... Yii::endProfile('inner'); Yii::endProfile('outer');
18.188.175.182