Yii 2 Examples
Controls in Views
<?php
use yii\bootstrap\Carousel;
?>
 
<?= Carousel::widget([
    'items' => [
        // the item contains only the image
        '<img src="http://twitter.github.io/bootstrap/assets/img/bootstrap-mdo-sfmoma-01.jpg"/>',
        // equivalent to the above
        ['content' => '<img src="http://twitter.github.io/bootstrap/assets/img/bootstrap-mdo-sfmoma-02.jpg"/>'],
        // the item contains both the image and the caption
        [
            'content' => '<img src="http://twitter.github.io/bootstrap/assets/img/bootstrap-mdo-sfmoma-03.jpg"/>',
            //'caption' => '<a class="btn btn-lg btn-success" href="http://www.yiiframework.com">Learn More...</a>',
            'caption' => '<h4>This is title</h4><p>This is the caption text</p>',
            'options' => [...],
        ],
    ],
    'showIndicators' => true,
    //'controls' => ['&lsaquo;', '&rsaquo;'],
    'controls' => [
         '<span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>', 
         '<span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>'
    ],
]); >

DatePicker

  • run in your project dir:
    $ php composer.phar global require "fxp/composer-asset-plugin:~1.1.1"
    $ php composer.phar require --prefer-dist yiisoft/yii2-jui "*"
  • update composer: $ composer update
  • In your view file use use yii\jui\DatePicker;.
use yii\jui\DatePicker;
...
<?php 
  // Today 
  $model->publish_up = ($model->isNewRecord ? 
      date('Y-m-d', strtotime(date('Y-m-d'))) : 
      substr($model->publish_up, 0, 10) 
  );
?>
 
<?= DatePicker::widget([
    'model' => $model,
    'attribute' => 'publish_up',
    'language' => 'ru',
    'clientOptions' => [
        'dateFormat' => 'yyyy-mm-dd',
    ],
    'options'=> ['class'=>'form-control'],
]) ?> 
 
<?= $form->field($model, 'publish')->widget(DatePicker::classname(), [
           'language' => 'en',
           'clientOptions' => [
              'defaultDate' => '01-01-2014', 
              'dateFormat' => 'MM-dd-yyyy'
           ],
           'options'=> ['class'=>'form-control'],  // Bootstrap theme
       ]) ?>

Example:

<?php 
  // 3 days from today
  $model->ship_date = ($model->isNewRecord ? 
    date('Y-m-d', strtotime(date('Y-m-d')." +3 Days")) : 
    substr($model->ship_date, 0, 10) ); 
?> 
 
<?= $form->field($model,'ship_date', [
      'template' => '{label}<div class="input-group">
         <span class="input-group-addon glyphicon glyphicon-calendar" 
            aria-hidden="true" 
            onclick="document.getElementById(\'orderform-shipdate\').select();">
         </span>{input}</div>'
    ])->widget(\yii\jui\DatePicker::className(), [
        'dateFormat' => 'php:Y-m-d',  // 'php:Y-m-d' is the only supported format
        'value' => ($model->isNewRecord ? date("Y-m-d") : $model->date),
        'clientOptions' => [  // Options for JQuery UI widget
            'defaultDate' => '+7', //'2010-01-01',
            'currentText' => 'Today',
            //'dateFormat' => 'php:Y-m-d',  // 'php:Y-m-d' is the only supported format
            'language'   => 'US',
            'country'    => 'US',
            'showAnim'   => 'fold',
            'yearRange'  => 'c-20:c+0',
            'changeMonth'=> true,
            'changeYear' => true,
            'autoSize'   => true,
            'showButtonPanel' => true,
            //'showOn'     => "button",
            //'buttonImage'=> "images/calendar.gif",
            //'htmlOptions'=>[
            //    'style'=>'width:80px;',
            //    'font-weight'=>'x-small',
        ],
        'options' => [  // Options for HTML attributes
            'class' => 'form-control',  // Bootstrap theme
        ],
    ]) ?> 

See also: MaskedInput

A dropdownlist (or listbox) can be populated in several ways.

Method 1: Inline array

// View: [app]/views/order/_form.php
...
<?= $form->field($model, 'color')->dropDownList(
    ['red'=>'Red', 'blue'=>'Blue', 'green'=>'Green'],
    ['prompt'=>'--Select One--']    // options
) ?>

Method 2: Model attribute

// View: [app]/views/order/_form.php
...
<?= $form->field($model, 'color')->dropDownList(
    $colors,
    ['prompt'=>'--Select One--']    // options
) ?>

Where $colors is defined in the model:

//...
class Product extends \yii\db\ActiveRecord
{
   //...
   public function getColors()
   {
      return array('red'=>'Red', 'blue'=>'Blue', 'green'=>'Green');
   }
}   

Method 3: Data from another table (Lookup Table)

// View: [app]/views/order/_form.php
use yii\helpers\ArrayHelper;
use app\models\Customer;
...
<div class="order-form">
   ...
   <?= $form->field($model, 'customer_id')->label('Customer')->dropDownList(
                ArrayHelper::map(Customer::find()->all(), 'id', 'fullname'),
                // Or: ArrayHelper::map($model->customers, 'id', 'fullname'),
                // but requires to add the following function to the current model:
                // public function getCustomers()
                // {
                //    return Customer::find()->all();
                // }
                ['prompt'=>'--Select One--']    // options
            ) ?>
   ...
</div>            

Where fullname is a table field. If it is a virtual attribute, it needs to be defined in the model:

...
class Customer extends \yii\db\ActiveRecord
{
   ...
   // Virtual attributes
   public function getFullName()
   {
       return $this->first_name . " " . $this->last_name;
   }
}      

NOTE: If using ActiveForm, then the value of the model field will be used as the selected value. However, if not using ActiveForm and generating dropdown list with Html helper, then the dropDownList() function accepts parameter selection as well, which allows the value to default as selected. See more in the documentation.

Example:

public function actionCreate()
{
    $model = new Content();
 
    if ($model->load(Yii::$app->request->post()) && $model->save()) {
        return $this->redirect(['view', 'id' => $model->id]);
    } else {
        // set default values
        if (empty($model->show_hits))   $model->show_hits = 0;
        if (empty($model->show_rating)) $model->show_rating = 0;
        if (empty($model->featured))    $model->featured = 0;
        if (empty($model->ordering))    $model->ordering = 0;
        if (empty($model->created_by))  $model->created_by = Yii::$app->user->id;
 
        return $this->render('create', [
            'model' => $model,
        ]);
    }
}

Alternatively, define default rules in your model:

public function rules()
{
    return [
        ...
        [['created_at', 'modified_at'], 'safe'],
        [['created_at', 'modified_at'], 'default', 'value' => function(){
            return date('Y-m-d');
        }],
    ];
}

ListView

In view search, add widget ListView:

...
<?= yii\widgets\ListView::widget([
   'dataProvider' => $dataProvider,
   'itemView'     => '_listitem',
]) ?>

Add new partial view _listitem to define how each item in list will display:

<?php
use yii\helpers\Html;
use yii\helpers\HtmlPurifier;
?>
<div class="customer">
    <h2><?= Html::encode($model->id) ?></h2>
    <?= HtmlPurifier::process($model->name) ?>    
</div>

Alternatively, you can use an inline itemView:

<?= ListView::widget([
    'dataProvider' => $dataProvider,
    'options'   => [
        'tag'   => 'div',
        'class' => 'list-wrapper',
        'id'    => 'list-wrapper',
    ],
    'layout'   => "{pager}\n{items}\n{summary}",
    'itemView' => function ($model, $key, $index, $widget) {
        return $this->render('_list_item',['model' => $model]);
 
        // Alternatively, just do some echo...
        //return "{$model->title} posted by {$model->author}";
    },
]); ?>

In controller, the action should have a searchModel and dataProvider defined:

public function actionSearch()
{
    $searchModel = new CustomerSearch();
 
    if (empty(Yii::$app->request->queryParams)) {
        $dataProvider = new \yii\data\ArrayDataProvider();
    } else {
        $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
    }
 
    $dataProvider->pagination->pageSize = 0; // Set to no page
 
    return $this->render('search', [
        'searchModel'   => $searchModel,
        'dataProvider'  => $dataProvider,
    ]);
}

See more: Yii2 ListView Example

DetailView

<?= DetailView::widget([
    'model' => $model,
    'template' => '<tr><th width="150">{label}</th><td>{value}</td></tr>',  // row template
    'attributes' => [
        // using default values
        'title',             // title attribute (in plain text)
 
        // using formatters:
        // (see http://www.yiiframework.com/doc-2.0/yii-i18n-formatter.html)
        'description:html',  // description attribute in HTML
        'description:ntext', // description attribute in HTML text 
        [                    // or alternatively
            'attribute' => 'description',
            'label' => 'Main Description',
            'format' => 'html',
            'value' => '<div><b>'.description.'</b></div>',
        ],
 
        intro_image:image,
        [
              'attribute' => 'intro_image',
              'label'     => 'Intro Image',
              'format'    => 'image',
              'value'     => (!empty($model->intro_image) ? Yii::$app->urlManager->createUrl('').'media/'.$model->intro_image : ''),
        ],
        [
              'attribute' => 'intro_image',
              'label'     => 'Intro Image',
 
              // Add image size limit
              'format' => 'raw',
              'value'  => '<img src="'.(!empty($model->intro_image) ? 
                  Yii::$app->urlManager->createUrl('').'media/'.$model->intro_image : 
                  '') . 
                  '" style="width: 700px;"/>'
        ],
 
        // dates
        [                    
            'attribute' => 'publish_date',
            'label' => 'Publish Date',
            'format' => ['date', 'php:Y-m-d'],
            'value' => publish_date,
        ],        
        'publish_date:date',       // requires 'yii\i18n\Formatter' 'defaultTimeZone' to be set in config.php
        'publish_date:datetime',   // requires 'yii\i18n\Formatter' 'defaultTimeZone' to be set in config.php
 
        'ship_cost:currency',      // formatted to default currency
        [   // ship_cost formatted as proforma currency
            'attribute' => 'ship_cost', 
            'format' => ['raw'], 
            'value'     => call_user_func(function($data) {
                // formatted to specific currency
                //return Yii::$app->formatter->asCurrency($data->ship_cost, $data->currency);
 
                // or formatted to USD or EUR currency
                return Yii::$app->formatter->asCurrency($data->ship_cost, 'EUR');
            }, $model),
        ],
 
        // using custom label/values,
        // including values in other related models
        [                    // the owner name of the model
            'label' => 'Owner',
            'value' => $model->owner->name,
        ],
 
        // using boolean logic for values
        [
            'label' => 'Status',
            'value' => ($model->status > 0 ? 'Published' : 'Unpublished')
        ],
        // using Bootstrap html elements
        [
            'label'  => 'Status',
            'format' => 'html',
            'value'  => ($model->status > 0 ? 
                '<span class="label label-success">Active</span>' : 
                '<span class="label label-warning">Inactive</span>')
        ],
        [
                'attribute' => 'is_available ',
                'format' => 'html',
                'value' => ($model->is_available > 0 ? 
                    '<span class="glyphicon glyphicon-ok" aria-hidden="true" style="color:green"></span>' : 
                    '<span class="glyphicon glyphicon-remove" aria-hidden="true" style="color:red"></span>')
        ],
        [   // Call custom function for value  
            'label'     => 'Quantity',
            'attribute' => 'quantity',
            'format'    => ['raw'],
            'value'     => call_user_func(function ($data) {
                      return ($data->quantity >= 0 ? $data->quantity : -1);
             }, $model),
        ],
        [
            'attribute' => 'created_by',
            'label'     => 'Created by',
            'value'     => ($model->createdByUser !== null ? $model->createdByUser->username : 'N/A'),
        ],
        [
            'attribute' => 'updated_by',
            'label'     => 'Updated by',
            'value'     => User::findUsername($model->updated_by),
        ],
        'created_at',
    ],
]);

The created_by field would require this extra code in the model:

use app\models\User;
...
    public function getCreatedByUser()
    {
        // NOTE: Use only one of these:
 
        // 'basic' application
        return User::findIdentity($this->created_by);
 
        // 'advanced' application
        return $this->hasOne(User::className(), ['id' => 'created_by']);
    }
 
    public static function findUsername($id)
    {
        //return isset(self::$users[$id]) ? new static(self::$users[$id]['username']) : null;
        $usr = isset(self::$users[$id]) ? new static(self::$users[$id]) : null;
        if ($usr !== null)  {
            return $usr->username;
        } else {
            return 'N/A';
        }
    }
<?php
    use yii\grid\GridView;
    use yii\helpers\ArrayHelper;
    use yii\helpers\StringHelper;
    use yii\helpers\Url;
    use app\models\ContentCategory;
 
    $total = 0; // to be used in Quantity column total in footer
?>
 
<?= GridView::widget([
    'dataProvider' => $dataProvider,
    'filterModel' => $searchModel,
    'tableOptions' => ['class' => 'table table-striped table-bordered table-hover'],
    'columns' => [
        // record number column
        ['class' => 'yii\grid\SerialColumn'],    
 
        // Record with checkbox column
        // Users may click on the checkboxes to select rows of the grid. 
        // The selected rows may be obtained by calling the following JavaScript code:
        // <startjscode>
        // // keys is an array consisting of the keys associated with the selected rows 
        // var keys = $('#grid').yiiGridView('getSelectedRows');
        // <endjscode>
        ['class' => 'yii\grid\CheckboxColumn'],  
 
        'id',
        [
            'attribute' => 'id',
            'label'     => 'Ref ID',
            'format'    => ['raw'],
            'value'     => function($model) {
                return str_pad($model->id, 8, "0", STR_PAD_LEFT);
            }
        ],
        'first_name',
        //'last_name',
 
        [   // Use 'last_name' as link to view record 
            'attribute' => 'last_name',
            'format' => ['raw'],
            'value' => function($data) {
                // Alternative 1
                //return Html::a(
                //    Html::encode($data['last_name']), 
                //    'index.php?r=customer/view&id='.$data['id']
                //);  
 
                // Alternative 2
                // Must include: use yii\helpers\Url;
                return Html::a(
                    Html::encode($data['last_name']), 
                    Url::toRoute(['view', 'id' => $data['id']], ['data-pjax' => '0' /* disable pjax */])   
                );  
            },
        ],
 
        'company_name',
        [   // field with custom label
            'attribute' => 'company_name',
            'label' => 'Company',
            'value' => 'company_name',
        ],
 
        //'address',
        'city',
        'state_prov',
        'postal_code',
        // 'country',
        // 'email:email',
        // 'phone',
        // 'fax',
        // 'account_number',
        // 'ip_address',
        // 'notes:ntext',
 
        [   // field with truncated content
            'attribute' => 'description',
            'label' => 'Description',
            'format' => 'ntext',
            'value' => function($data) {
                return  yii\helpers\StringHelper::truncateWords($data['description'], 50);
            }
        ],
 
        [   // column with 220px (or 20%) specified width
            'attribute'      => 'connector',
            'value'          => 'connector',
            'headerOptions'  => ['style' => 'width: 20%;'],
            'contentOptions' => ['style' => 'width: 220px;'], 
            //'contentOptions' => ['style' => 'width: 220px; white-space: normal;'], 
            //'contentOptions' => ['style' => 'width: 220px; white-space: nowrap;'], 
        ],
 
        [   // link to related table 'vendor' and get field 'company_name'
            'attribute' => 'Vendor Info',
            'format' => ['raw'],
            'value' => function($model) {
                return $model->vendor->company_name;
            },
        ],
 
        [   // use dropdown listbox for column filter
            'attribute' => 'featured',
            'label' => 'Featured',
            'format' => ['raw'],
            'value' => function($data) {
                return ($data['featured'] > 0 ? 'Yes' : 'No');
            },
            'filter' => [0 => 'No', 1 => 'Yes']
        ],
 
        [   // use dropdown listbox for column filter
            'attribute' => 'status',
            'label' => 'Status',
            'format' => ['raw'],
            'value' => function($data) {
                return ($data['status'] > 0 ? 
                   '<span class="label label-success">Active</span>' : 
                   '<span class="label label-warning">Disabled</span>');
            },
            'filter' => [1 => 'Active', 0 => 'Disabled']
        ],
 
        [   // use dropdown listbox for column filter, using data from array
            'attribute'=>'resolution_type',
            'format' => ['raw'],
            'value' => function($model) {
                return $model->resolution_type;
            },
            'filter' => ['Call Back' => 'Call Back',
                'Pending' => 'Pending',
                'Solved' => 'Solved',
                'Unsolved' => 'Unsolved']
        ],
 
        [   // use dropdown listbox for column filter, using data from lookup table
            'attribute'=>'category_id',
            'format' => ['raw'],
            'value' => function($model) {
                return $model->category->title;
            },
            'filter' => \yii\helpers\ArrayHelper::map(ContentCategory::find()->all(), 'id', 'title'),
            // Or querying same product table:
            //'filter' => \yii\helpers\ArrayHelper::map(app\models\Product::find()->distinct()->all(), 'category_id', 'category_id'),
        ],
        [    // use dropdown listbox for column filter, using data from model
            'attribute'=>'product_code',
            'format' => ['raw'],
            'value' => function($model) {
                return $model->product_code;
            },
            //'filter' => ArrayHelper::map(Product::getProductCodes, 'id', 'product_code'),
            'filter' => Product::getProductCodes(),
        ],
        [  // Column with footer (must enable 'showFooter' and 'footerRowOptions' GridView options)
            'attribute' => 'quantity',
            'format' => 'raw',
            'value' => function($model, $key, $index, $widget) use ($total) {
                $total += $model->quantity;
                return $model->quantity;
            },
            'footer' => getPageTotal($dataProvider->models, 'quantity'),
        ],
        [   // Column with footer showing totals (must enable 'showFooter'), 
            // and formatted as currency, equivalent to 'total_price:currency'
            'attribute' => 'total_price', 
            'label'     => 'Total Price',
            'value'     => function ($modelItem, $key, $index, $widget) {
                return Yii::$app->formatter->asCurrency($modelItem->total_price, 'USD');
            },
            'footer' => Yii::$app->formatter->asCurrency($model->sub_total_cost, 'USD'),
        ],
 
        // Search by Date (on a DateTime field), Alternative 1
        'created_at:date',
        // Add to the search model query app/models/ProductSearch.php
        //public function search($params)
        //{
        //   //...
        //   $query->andFilterWhere(['like', ...);
        //
        //   if (isset($this->created_at) && !empty($this->created_at)){
        //      $query->andFilterWhere(['like', "created_at", "{$this->created_at}"]);
        //   }
        //}
 
        // Search by Date (on a DateTime field), Alternative 2
        //'created_at:datetime',
        [
            'attribute' => 'created_at',
            'filter'    => app\models\Product::getCreatedDateList(),
        ],
        // Add the following to the Product model:
        //public static function getCreatedDateList()
        //{
        //    $dates = (new yii\db\Query())->
        //               select('DISTINCT DATE(`created_at`) as dates')->
        //               from('{{%product}}')->column();
        //    return array_combine($dates, $dates);
        //}
        //
        // Add the following to the ProductSearch model:
        //public function search($params)
        //{
        //   //...
        //
        //   $query->andFilterWhere([
        //      'id' => $this->id,
        //      //'created_at' => $this->created_at,
        //      'DATE(`created_at`)' => $this->created_at,
        //      //...
        //   ]);
        //
        //   //...
        //}
 
        // Search by Year (on a DateTime field)
        //'created_at:datetime',
        [
            'attribute' => 'created_at',
            'filter'    => app\models\Product::getCreatedYearList(),
        ],
        // Add the following to the Product model:
        //public static function getCreatedYearList()
        //{
        //    $years = (new yii\db\Query())->
        //               select('DISTINCT YEAR(`created_at`) as years')->
        //               from('{{%product}}')->column();
        //    return array_combine($years, $years);
        //}
        //
        // Add the following to the ProductSearch model:
        //public function search($params)
        //{
        //   //...
        //
        //   $query->andFilterWhere([
        //      'id' => $this->id,
        //      //'created_at' => $this->created_at,
        //      'YEAR(`created_at`)' => $this->created_at,
        //      //...
        //   ]);
        //
        //   //...
        //}
 
        // 'created_at',
        // 'modified_at',
 
        // 'created_by',
        [   // Custom Created_By
            'attribute' => 'created_by',
            'label'     => 'Created By',
            'value'     => function($model) {
                return User::findUsername($model->created_by);
            },
            'filter'=> User::getUsernames(),
        ],
 
        // 'status',
        // 'user_id',
 
        ['class' => 'yii\grid\ActionColumn'],  // default actions
        [  // custom actions
           'class'    => 'yii\grid\ActionColumn', 
           'template' => '{view} {update} {clone} {download}', /* '{view} {update} {delete}' */
           'buttons' => [
               'view' => function ($url, $model, $key) {
                   return Html::a('<i class="glyphicon glyphicon-eye-open d-icon"></i>', 
                      yii\helpers\Url::toRoute([
                        'carousel-view', 
                        'id' => $model['id']
                      ]));  // view record
               },
               'update'=> function ($url, $model, $key) {
                   return Html::a('<i class="glyphicon glyphicon-pencil d-icon"></i>', 
                     yii\helpers\Url::toRoute([
                       'carousel-update', 
                       'id' => $model['id']
                     ]));  // update record
               },
               'delete'=> function ($url, $model, $key) {
                   return Html::a('<i class="glyphicon glyphicon-trash d-icon"></i>', 
                       ['delete', 'id' => $model['id']], [
                        'data' => [
                           'confirm' => Yii::t('app', 'Are you sure you want to delete this item?'),
                           'method' => 'post',
                      ],
                   ]); // delete record 
               },
               // custom action Clone
               'clone' => function ($url, $model, $key) {
                   return Html::a('<i class="fa fa-clone" aria-hidden="true"></i>',
                       yii\helpers\Url::toRoute(['clone', 'id' => $model['id']])  // clone record
                   );  
               },
               // custom action Download
               'download' => function ($url, $model) {
                   return Html::a('<span class="glyphicon glyphicon-arrow-download"></span>',
                       ['another-controller/another-action', 'id' => $model->id], 
                       [
                           'title' => 'Download',
                           'data-pjax' => '0',
                       ]
                   );
               },
 
           ],
        ],
    ],
    //'summary' => '',   // uncomment this line to remove table summary
    'showFooter' => true,
    'footerRowOptions'=>['style'=>'font-weight:bold'],
]); ?>
 
<?php
function getPageTotal($provider, $fieldName)
{
    $total = 0;
    foreach($provider as $item) {
        $total += $item[$fieldName];
    }
    return $total;
}
?>    
class Product extends \yii\db\ActiveRecord
{
    ...
    public static function getProductCodes()
    {
        return [
            'prod_Big'    => 'Big',
            'prod_Medium' => 'Medium',  
            'prod_Small'  => 'Small',  
        ];
    }
}    
class User extends \yii\base\Object implements \yii\web\IdentityInterface
{
    //...
 
    /**
     * Find username for specified userid.
     *
     * @id integer  $id of user to search.
     * @return string Username if found, 'N/A' if not found.
     */
    public static function findUsername($id)
    {
        $usr = isset(self::$users[$id]) ? new static(self::$users[$id]) : null;
        if (count($usr) > 0)  {
            return $usr->username;
        } else {
            return 'N/A';
        }
    }
 
    /**
     * get list of all usernames, to be used in dropdown.
     * @return array usernames.
     */
    public static function getUsernames()
    {
        return \yii\helpers\ArrayHelper::map(User::$users, 'id', 'username');
    }
}    

Gridview with Searchable Columns

To make a column searchable in the GridView::widget(), you must add the field to the query in app/models/[MyModel]Search.php. Eg: To search vendor by:

  • brand
  • postal_code

Edit app/models/VendorSearch.php:

use app\models\Vendor;        // add model reference to current model
use app\models\Manufacturer;  // add model reference to related model
 
class VendorSearch extends Vendor
{
    // Add the public attributes that will be used to store the custom data to be searched
    // NOTE: Only required for fields not in current table, 
    // Eg: fields in related table such as 'manufacturer.name' using alias 'brand'
    public $brand;
 
    // Add searchable fields to the 'safe' array in rules
    // Eg: 'brand', and 'postal_code'
    public function rules()
    {
        return [
            [['id', 'status', 'created_by', 'updated_by'], 'integer'],
            // Add all searchable field here
            [['name', 'description', 'type', 'address', 'city', 'state_prov', 'postal_code', 'country', 
              'image', 'contact', 'brand', 'created_at', 'updated_at'], 'safe'],
        ];
    }
 
    ...
 
    public function search($params)
    {
        // Perform a JOIN with related table
        $query = Vendor::find()->joinWith('manufacturer'); // make sure to set no order here!!
        ...
        $query->andFilterWhere([
            'id' => $this->id,
            'status' => $this->status,
            'created_by' => $this->created_by,
            'created_at' => $this->created_at,
            'updated_by' => $this->updated_by,
            'updated_at' => $this->updated_at,
        ]);
 
        // Set all searchable fields (make sure they are available at the top (under rules[])
        $query->andFilterWhere(['like', 'name', $this->name])
            ->andFilterWhere(['like', 'description', $this->description])
            ->andFilterWhere(['like', 'type', $this->type])
            ->andFilterWhere(['like', 'address', $this->address])
            ->andFilterWhere(['like', 'city', $this->city])
            // simple lookup using 'postal_code'
            ->andFilterWhere(['like', 'postal_code', $this->postal_code])  
            ->andFilterWhere(['like', 'state_prov', $this->state_prov])
            ->andFilterWhere(['like', 'country', $this->country])
            ->andFilterWhere(['like', 'image', $this->image])
            ->andFilterWhere(['like', 'contact', $this->contact])
            // complex lookup of 'brand' on another table (field 'manufacturer.name')
            ->andFilterWhere(['like', 'LOWER(manufacturer.name)', strtolower($this->brand)]);  
        return $dataProvider;
    }
}    

Gridview with Sortable Columns

To make the column headers sortable, set the sort attributes in the method search(). Edit app/models/ProductSearch.php:

...
class ProductSearch extends Product
{
    // Add the public attributes that will be used to store the custom data to be searched
    // NOTE: Only required for fields not in current table, 
    // Eg: fields in related table such as 'company.name_short' using alias 'brand'
    public $brand;
 
    // Add searchable fields to the 'safe' array in rules
    // Eg: 'brand'
    public function rules()
    {
        return [...
            // Add all searchable field here
            [[... 'brand'], 'safe'],
        ];
    }
 
    ...
 
    public function search($params)
    {
        $query = Product::find()->joinWith('company'); // Make sure to set no order here!! 
                                                       // Use 'defaultOrder' in setSort() instead.
 
        $dataProvider = new ActiveDataProvider([
            'query' => $query,
            'sort'=> ['defaultOrder' => ['code'=>SORT_ASC, 'name'=>SORT_ASC]] 
        ]);
 
        /**
         * Setup your sorting attributes
         * Note: This is setup before the $this->load($params) statement below
         */
        $dataProvider->setSort([
            'attributes' => [
                'id',
                'name',
                'code',
                'model_number',
                'upc',
                'brand' => [
                    'label' => 'Brand',
                    'asc'   => ['company.name_short' => SORT_ASC],
                    'desc'  => ['company.name_short' => SORT_DESC],
                    'default' => SORT_ASC
                ],
                'description',
            ],
            'defaultOrder' => ['code'=>SORT_ASC, 'name'=>SORT_ASC]
        ]);
 
        if (!($this->load($params) && $this->validate())) {
            return $dataProvider;
        }
 
        ...
 
        return $dataProvider;
    }
}

GridView:

<?php
use yii\helpers\ArrayHelper;
use app\models\Product;
use app\models\Company;
?>
...
<?= GridView::widget([
    'dataProvider' => $dataProvider,
    'filterModel' => $searchModel,
    'columns' => [
        ['class' => 'yii\grid\SerialColumn'],
        'id',
        'name',
        'code',
        'model_number',
        //'company_id',
        [   // link to related table 'company' and get field 'name_short'
            'label'     => 'Brand',
            'attribute' => 'brand',
            'format'    => ['raw'],
            'value'     => 'company.name_short',
            'filter'    => ArrayHelper::map(Company::find()->all(), 'name_short', 'name_short'),
        ],
 
        ['class' => 'yii\grid\ActionColumn'],
    ],
]); ?>

Create an action in the controller:

...
class SiteController extends Controller
{
    public function actionPrivacy()
    {
        return $this->render('privacy');
    }
    ...
}    

Create link in view:

<?php 
    // Method 1: Button
    \yii\helpers\Html::button('Privacy Policy', [
        'id'    => 'btnPrivacyPolicy',
        'class' => 'btn btn-default btn-sm',
    ]);
 
    // Method 2: Link styled as button
    echo \yii\helpers\Html::a('Privacy Policy', ['site/privacy']);
 
    // Method 3: Link styled as button
    $url = urldecode(\yii\helpers\Url::toRoute(['site/privacy']));
    echo \yii\helpers\Html::a('Privacy Policy', $url);
 
    // Method 4: Link styled as button
    echo \yii\helpers\Url::to(['site/privacy']); 
?>

Create an image link:

<?php
$logo = Html::img(\Yii::$app>request->BaseUrl . '/images/logo.png',
          ['class' => 'img-responsive',
           'alt'   => 'Logo',
           'title' => 'Company Logo'
          ]);
 
echo Html::a($logo, ['site/index'], ['class' =>'class-name']);
?>

Link with icon and text:

<?php 
use yii\helpers\Url;
...
?>
<?= Html::a('<i class="glyphicon glyphicon-dashboard d-icon"></i>' . ' Admin Dashboard', 
     ['site/admin-dashboard']) 
?>

Link with icon and as a button:

<?php 
use yii\helpers\Url;
...
?>
<?= Html::a('<i class="glyphicon glyphicon-dashboard d-icon"></i>' . ' Admin Dashboard', 
     ['site/admin-dashboard'], ['class'=>'btn btn-default']) 
?>

Link as button with popup for warning:

<?php 
use yii\helpers\Html;
...
?>
<?= Html::a('Print', ['item/print-certificates'], 
    [
        'class'        => 'btn btn-success', 
        'data-confirm' => "It might take a while to generate certificates.  Do you want to proceed?", 
        'data-method'  => "post", 
        'data-pjax'    => '0'
    ]
) ?>

Link as a button with tooltip:

<?php
use yii\helpers\Html;
...
// Initialize button tooltips
$script = <<< JS
$(function () {
  $('[data-toggle="tooltip"]').tooltip()
}) 
JS;
$this->registerJs($script, \yii\web\View::POS_END);
?>
 
<?= Html::a('Cert Settings', ['app-setting/index'], 
    [
        'class'          => 'btn btn-success', 
        'data-toggle'    => "tooltip",
        'data-placement' => "bottom",
        'title'          => "Modify settings for Certificate Title, Body, and Signature"
    ]
) ?>

Link with POST data:

<?= Html::a('Report', ['data-report'], 
    [
        'class' => 'btn btn-default',
        //'data-method'  => 'post', 
        //'data-pjax'    => 0,
        //'data-params'  => "{'serial_range': ".Yii::$app->request->post('serial_range').",'more_data': '123'}",
        'data' => [
            'method' => 'post',
            'pjax'   => 0,
            'params' => [
                'serial_range' => Yii::$app->request->post('serial_range'),
                'more_data' => '123',
            ],
        ]
    ]
) ?>

Button Dropdown

<!-- Keep other buttons inline by surrounding each one with div btn group -->
<div class="btn-group">
  <?= Html::a(Yii::t('app', 'Add'),           
      ['create', 'customer_id'=>$model->customer_id], 
      ['class' => 'btn btn-success']) 
  ?>
</div>
 
<!-- Generate Proforma Options -->
<div class="btn-group">
    <?= Html::a('<span class="glyphicon glyphicon-print" aria-hidden="true"></span> ' . 
      Yii::t('app', 'Generate') . ' <span class="caret">',
        '#', 
        [
          'class'         => 'btn btn-primary dropdown-toggle', 
          'data-toggle'   => "dropdown", 
          'role'          => "button", 
          'aria-haspopup' => "true", 
          'aria-expanded' => "false"
        ]) 
    ?>
    <ul class="dropdown-menu">
        <li><?= Html::a(Yii::t('app', 'Proforma'),      
            ['print', 'id' => $model->id, 'format'=>'proforma'],      
            ['class' => '']) ?>
        </li>
        <li><?= Html::a(Yii::t('app', 'Invoice'),       
            ['print', 'id' => $model->id, 'format'=>'invoice'],       
            ['class' => '']) ?>
        </li>
        <li role="separator" class="divider"></li>      
        <li><?= Html::a(Yii::t('app', 'Delivery Note'), 
            ['print', 'id' => $model->id, 'format'=>'delivery note'], 
            ['class' => '']) ?>
        </li>
    </ul>
</div>

MaskedInput

<?=  $form->field($model, 'publish_date')->widget(\yii\widgets\MaskedInput::className(), [
        'mask' => '99/99/9999',
    ]); ?>

Edit file [app]/views/layouts/main.php to modify default menu:

<?php
use app\models\User;
...
    NavBar::begin([
        'brandLabel' => 'Acme Inc.',
        'brandUrl' => Yii::$app->homeUrl,
        'options' => [
            'class' => 'navbar-inverse navbar-fixed-top',
        ],
        //'submenuOptions' => ['target' => '_blank'],  // to go blank tab for each menu item
    ]);
 
    $menuitems = [
        ['label' => '<span class="glyphicon glyphicon-home"></span> Home', 'url' => ['/site/index']],  // icon requires 'encodeLabels' => false
        ['label' => 'About', 'url' => ['/site/about']],
        ['label' => 'News', 'url' => ['/content/index', 'category' => 'latest']],
        ['label' => 'Contact', 'url' => ['/site/contact']],
        // For external URLs, use raw string, i.e. no [] in url
        ['label' => 'External Site', 'url' => 'http://www.yiiframework.com'], 
        ['label' => 'Workshop', 'url' => ['/site/workshop'], 
            'items' => [
                ['label' => 'Equipment',  'url' => ['/site/workshop', '#'=>'equip']],
                ['label' => 'Techniques', 'url' => ['/site/workshop', '#'=>'tech']],
                '<li role="presentation" class="divider"></li>',     // divider
                '<li class="dropdown-header">Dropdown Header</li>',  // divider with header
                ['label' => 'References', 'url' => ['/site/workshop', '#'=>'ref']],
                ['label' => 'Register (new tab)', 
                    'url' => ['/site/workshop', '#'=>'ref'],
                    'linkOptions' => ['target' => '_blank']],  // open url in new tab
            ]],
    ];
    // Special permissions for Admin user (requires 'use app\models\User')
    if (isset(Yii::$app->user->identity) && (Yii::$app->user->identity->role == User::ROLE_ADMIN)) {
        $menuItems[] = ['label' => 'Admin', 'url' => '',
            'items' => [
                ( YII_ENV_DEV ? ['label' => 'Code Generator (Gii)', 'Gii', 'url' => ['/gii']] : '' ),
                ['label' => 'Web Analytics', 'url' => '/webanalytics'],
                ['label' => 'Database',      'url' => '/support/phpmyadmin'],
                '<li role="presentation" class="divider"></li>',     // divider
                ['label' => 'Users',         'url' => ['user/index']],
        ]];
    }
    if (Yii::$app->user->isGuest) {
        $menuItems[] = ['label' => 'Signup', 'url' => ['/site/signup']];
        $menuItems[] = ['label' => 'Login', 'url' => ['/site/login']];
    } else {
        $menuItems[] = [
            'label' => 'Logout (' . Yii::$app->user->identity->username . ')',
            'url' => ['/site/logout'],
            'linkOptions' => ['data-method' => 'post']
        ];
    }
 
    echo Nav::widget([
        'options' => ['class' => 'navbar-nav navbar-right'],
        'encodeLabels' => false,  // to allow icons in labels
        'items' => $menuitems,
    ]);
 
    NavBar::end();
?>

Process the action parameters. Eg: the content/index action with parameter category:

public function actionIndex($category='N/A')
{
    $categoryAlias = $category;
 
    // to retrieve all *active* customers and order them by their ID:
    $query = Content::find()
        ->joinWith('category')
        ->where(['status' => Content::STATUS_ACTIVE])
        ->where(['content_category.alias' => $categoryAlias])
        //->orderBy('id')
        ->orderBy(['created_at' => SORT_DESC])
        ->all();
 
    return $this->render('index', [
         'models' => $query,
    ]);
}    

Alternatively, process the action parameters as GET request parameters. Eg: the content/index action:

public function actionIndex()
{
    if (Yii::$app->request->isGet) {
        $categoryAlias = Yii::$app->request->get('category');   // GET params
    } else if (Yii::$app->request->isPost) {
        $categoryAlias = Yii::$app->request->post('category');  // POST params
    } else {
        $categoryAlias = 'N/A';
    }
    echo var_dump(Yii::$app->request->method);  // data-method
    echo var_dump(Yii::$app->request->get());   // GET params
    echo var_dump(Yii::$app->request->post());  // POST params
 
    // to retrieve all *active* customers and order them by their ID:
    $query = Content::find()
        ->joinWith('category')
        ->where(['status' => Content::STATUS_ACTIVE])
        ->where(['content_category.alias' => $categoryAlias])
        //->orderBy('id')
        ->orderBy(['created_at' => SORT_DESC])
        ->all();
 
    return $this->render('index', [
         'models' => $query,
    ]);
}    

Tabs (Bootstrap)

Create a view with tabs to divide content. For example, when presenting data in tabs “Content” and “Options”:

use yii\bootstrap\Tabs;
 
<div class="content-form">
 
    <?php $form = ActiveForm::begin(); ?>
    <?= $form->field($model, 'title')->textInput(['maxlength' => 255]); ?>
 
<?php
    //--------------------
    // TAB: Content
    //--------------------
    // Main column
    $tabContent = '<div class="col-md-10"><br />';
    $tabContent .= $form->field($model, 'intro_text')...
    $tabContent .= '</div>';
 
    // Side column
    $tabContent .= '<div class="col-md-2"><br />';
    $tabContent .= $form->field($model, 'category_id')...
    $tabContent .= '</div>';
 
    //--------------------
    // TAB: Publishing
    //--------------------
    $tabOptions = '<div class="col-md-10"><br />';
    $tabOptions .= $form->field($model, 'publish_up')...
    $tabOptions .= '</div>';
?>    
 
<?= Tabs::widget([
    'items' => [
        [
            'label' => 'Content',
            'content' => $tabContent,
            'active' => true
        ],
        [
            'label' => 'Options',
            'content' => $tabOptions,
        ],
    ],
]); ?>   
 
    <div class="form-group col-md-10">
        <?= Html::submitButton($model->isNewRecord ? 
            Yii::t('app', 'Create') : 
            Yii::t('app', 'Update'), ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) 
        ?>
    </div>
 
    <?php ActiveForm::end(); ?>
 
</div>    

TextArea with Tinymce Editor

  • Install:
    • Run: C:\> composer.phar require “letyii/yii2-tinymce” “dev-master”
    • Or: Add to require section in app/componser.json: “letyii/yii2-tinymce”: “dev-master”
  • Add widget to your view.

Basic example:

<?= $form->field($model, 'introtext')->widget(letyii\tinymce\Tinymce::className(), [
    'options' => [
        'id' => 'testid',
    ],
    'configs' => [ // Read more: http://www.tinymce.com/wiki.php/Configuration
        'plugins' => 'code image link media lists advlist table hr textcolor textpattern help',
        'toolbar'=> 'undo redo | styleselect | removeformat bold italic | 
                     alignleft aligncenter alignright alignjustify | 
                     bullist numlist outdent indent | link image code help',
        'height' => 300,
    ],
]); ?>    

Here is one with more options:

<?= $form->field($model, 'introtext')->widget(letyii\tinymce\Tinymce::className(), [
    'options' => [
        'id' => 'testid',
    ],
    'configs' => [ // Read more: http://www.tinymce.com/wiki.php/Configuration
        // full plugin list: http://www.tinymce.com/wiki.php/Plugins
        //'plugins' => 'advlist anchor autolink autoresize autosave bbcode charmap code 
        //             colorpicker compat3x contextmenu directionality emoticons example 
        //             example_dependency fullpage fullscreen hr image insertdatetime layer 
        //             legacyoutput link lists importcss media nonbreaking noneditable 
        //             pagebreak paste preview print save searchreplace spellchecker 
        //             tabfocus table template textcolor textpattern visualblocks visualchars wordcount',
        'plugins' => 'code image link media table template hr spellchecker', 
        'templates' => [ 
            ['title' => 'Template 1', 'description' => 'Basic Template', 'content' => '<b>Basic Template</b>'], 
            ['title' => 'Template 2', 'description' => 'Dev Template', 'url' => 'development.html']
        ],
        'link_list' => [
            [
                'title' => 'My page 1',
                'value' => 'http://www.tinymce.com',
            ],
            [
                'title' => 'My page 2',
                'value' => 'http://www.tinymce.com',
            ],
        ],
        'browser_spellcheck' => true,
        'toolbar'=> 'undo redo | styleselect | removeformat bold italic | 
                     alignleft aligncenter alignright alignjustify | 
                     bullist numlist outdent indent | link image code help',
        //'menubar'=> 'tools table view insert edit format',  // set menu order
        'menu' => [ // this is the complete default configuration
            'file'   => ['title' => 'File',   'items' => 'newdocument'],
            'edit'   => ['title' => 'Edit'  , 'items' => 'undo redo | cut copy paste pastetext | selectall'],
            'insert' => ['title' => 'Insert', 'items' => 'link media | template hr'],
            'view'   => ['title' => 'View'  , 'items' => 'visualaid'],
            'format' => ['title' => 'Format', 'items' => 'bold italic underline 
                            strikethrough superscript subscript | formats | removeformat'],
            'table'  => ['title' => 'Table' , 'items' => 'inserttable tableprops deletetable | cell row column'],
            'tools'  => ['title' => 'Tools' , 'items' => 'spellchecker code'],
        ],
        'height' => 300
    ],
]); ?>

Form Fields

  // Plain text field  
  <?= $form->field($model, 'subject') ?>
 
  // Plain text field with placeholder and hint 
  <?= $form->field($model, 'subject')
       ->textInput(['placeholder' => 'YYYY-MM-DD'])
       ->hint('A simple hint')
  ?>
 
  // TextArea field
  <?= $form->field($model, 'body')->textArea(['rows' => 6]) ?>
 
  // Field as textInput, with restrictions
  <?= $form->field($model, 'first_name')->textInput(['maxlength' => 255]) ?>
 
  // Field as textInput Date
  <?= $form->field($model, 'date')->textInput(['value' => Yii::$app->formatter->asDate('now', 'php:Y-m-d')]) ?>
  <?= $form->field($model, 'resolution_date')->textInput(['type'=>'date', 'placeholder' => 'Date input']) ?>
  <?= $form->field($model, 'due_date')->Input('date') ?>
 
  // Field with Label
  <?= $form->field($model, 'customer_id')->label('Customer') ?>
 
  // Field with Hidden Label
  <?= $form->field($model, 'sample_text')->textArea()->label(false) ?>
 
  // Field with Checkbox
  <?= $form->field($model, 'delivered')->checkBox() ?>
  <?= $form->field($model, 'is_valid')->checkBox(['label' => 'Is Valid?']) ?>
 
  // Field with dropDownList, or listBox
  <?= $form->field($model, 'printed')->dropDownList(['Yes'=>'Yes', 'No'=>'No']) ?>
  <?= $form->field($model, 'delivered')->dropDownList(
                    ['Yes', 'No'], 
                    ['prompt'=>'--Select One--'] // options
                ) ?>
  <?= $form->field($model, 'power_response')->dropDownList(
         yii\helpers\ArrayHelper::merge(
               [$model->power_response],  // current value
               \app\modules\requisition\models\CustomOrder::$power_response_types
         ),
         ['prompt'=>'--Select One--']    // options
  ) ?>
  <?= $form->field($model, 'status')->listBox(
                    ['1'=>'Enabled', '0'=>'Disabled']
                ) ?>
 
  // Field with radio buttons
  <?= $form->field($model, 'isUseful')->label('Is is useful?')->radioList([
                    'Yes',
                    'No'
                ]) ?>
 
  // Field with check list
  <?= $form->field($model, 'productColors')
        ->label('What colors do you like? (choose all that apply)')
        ->checkBoxList([
                    'Red',
                    'Green',
                    'Blue'
                ]) ?> 
 
  // Field with checkboxlist
    <?= $form->field($model, 'help_methods')->label('I can help in the following ways (choose all that apply)')
      ->checkBoxList([
              'Help teach a class for 1-2 hours',
              'Help with a fundraising event',
              'Help cook for the day',
          ], 
      [
          'onclick' => "$(this).val( $('input:checkbox:checked').val()); ", // if you use required as a validation rule, you will need this for the time being until a fix is in place by yii2
          'item'    => function($index, $label, $name, $checked, $value) {
               //return "<label class='ckbox ckbox-primary col-md-4'><input type='checkbox' {$checked} name='{$name}' value='{$value}' tabindex='3'>{$label}</label>";
               //return "<label class='ckbox ckbox-primary'><input type='checkbox' {$checked} name='{$name}' value='{$value}' tabindex='3'>{$label}</label><br>";
               return "<input type='checkbox' {$checked} name='{$name}' value='{$value}' tabindex='3'>&nbsp;{$label}<br>";
          }
      ])
  ?>            
 

See: Yii ActiveField

Hidden Input

    // Conditional hidden field display if this is a new record
    <?= ($model->isNewRecord ? 
            Html::activeHiddenInput($model, 'ip_address', ['value' => Yii::$app->request->userIP]) : 
            $form->field($model, 'ip_address')->textInput(
                ['readonly' => Yii::$app->user->getId() != '100']  // is Admin?
            )  
        ) ?>
 
    // Conditional hidden field selection if this is a new record    
    <?= ($model->isNewRecord ? 
            Html::activeHiddenInput($model, 'created',  
                ['value' => Yii::$app->formatter->asDate('now', 'php:Y-m-d')]) :
            Html::activeHiddenInput($model, 'modified', 
                ['value' => Yii::$app->formatter->asDate('now', 'php:Y-m-d')])
        ) ?>

See more Field options: Yii Widgets: ActiveField

Inline Controls

<div class="form-inline">
    <span class="col-md-2"><b>Style</b></span>
    <div class="form-group">
        <div class="col-md-6">
        <?= $form->field($model, 'style_left')->label('')->dropDownList(
            [
                'IIC'=>'IIC', 'CIC'=>'CIC', 
                'Canal'=>'Canal', 'Mini Canal'=>'Mini Canal', 
                'Half Shell'=>'Half Shell', 'Full Shell'=>'Full Shell', 
            ],
            ['prompt'=>'--Select One--']    // options
        ) ?>
        </div>
        <div class="col-md-6">
        <?= $form->field($model, 'style_right')->label('')->dropDownList(
            [
                'IIC'=>'IIC', 'CIC'=>'CIC', 
                'Canal'=>'Canal', 'Mini Canal'=>'Mini Canal', 
                'Half Shell'=>'Half Shell', 'Full Shell'=>'Full Shell', 
            ],
            ['prompt'=>'--Select One--']    // options
        ) ?>
        </div>
    </div>
</div>
Events

Some events that can be handled in a model are:

afterFind(), beforeValidate(), afterValidate(), beforeSave(), afterSave(), beforeDelete(), afterDelete().

For example:

/**
 * @return success
 */
public function beforeValidate()
{
    if (parent::beforeValidate()) {
        // Do some work before validation.
        // Eg: Populate field with automatic values (set default values).
        $this->LastEdited = new \yii\db\Expression('NOW()');
 
        return true;  // validated
    }
    return false;  // not validated
}
 
/**
 * @return success
 */
public function beforeSave($insert)
{
    if (parent::beforeSave($insert)) {
        // Do some work before saving.
        // Eg: Populate field with automatic values.
        if (empty($this->cost))              { $this->cost               = 0; }
        if (empty($this->warranty_included)) { $this->warranty_included  = 0; }
 
        return true;  // validated
    }
    return false;  // not validated
}

See Also:

Field Validation

Output errors on a controller:

echo "Errors: ".print_r($model->getErrors(), true)."\n";

Output errors on a view:

Method 1
<?= Yii::$app->session->setFlash('error', print_r($model->getErrors(), true) ) ?>
 
Method 2
<?= Html::errorSummary($model, ['class' => 'errors']) ?>
Flash Messages

Set flash message in Controller using:

$count = 25;
Yii::$app->session->setFlash('success', "Saved {$count} items successfully.");
return $this->redirect(['view', 'id' => $model->id]);
...

Display flash message in View using:

<div class="col-lg-12">
    <?php if(Yii::$app->session->hasFlash('success')): ?>
        <div class="alert alert-success" role="alert">
            <?= Yii::$app->session->getFlash('success'); ?>
        </div>
    <?php endif; ?>
    <?php if(Yii::$app->session->hasFlash('error')): ?>
        <div class="alert alert-danger" role="alert">
            <?= Yii::$app->session->getFlash('error'); ?>
        </div>
    <?php endif; ?>
</div>
...

In the view template [app]/views/layouts/main.php, you can simply add Alert::widget() to have flash messages display there:

use frontend\widgets\Alert;  // for advanced app
use app\widgets\Alert;       // for basic app (alternative 1)
use yii\bootstrap\Alert;     // for basic app (alternative 2)
...
<div class="container">
    <?= Breadcrumbs::widget([
         'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],
    ]) ?>
 
    <?= \app\widgets\Alert::widget() ?>
    <?= $content ?>
</div>
Common Behaviors

Some tables require to provide timestamps and users who created or updated a record. With some built-in behaviors we can add the functionality without much code. For each model requiring it, add the following behaviors:

use yii\behaviors\BlameableBehavior;
use yii\behaviors\TimestampBehavior;
use yii\db\Expression;
 
/**
 * @inheritdoc
 */
public function behaviors()
{
    date_default_timezone_set( 
      (!empty(Yii::$app->params['timezone']) ? Yii::$app->params['timezone'] : 'America/New_York') 
    );
 
    return [
        [
            'class' => TimestampBehavior::className(),
            'createdAtAttribute' => 'created_at', // OR 'create_time', to override default field name
            'updatedAtAttribute' => 'updated_at', // OR 'update_time', to override default field name
            'value' => new \yii\db\Expression('NOW()'),  // for PHP/SQL datetime field
            //'value' => time(),                         // for epoch time (unix) field
        ],
        [
            'class' => BlameableBehavior::className(),
            'createdByAttribute' => 'created_by',  // OR 'author_id', to override default field name
            'updatedByAttribute' => 'updated_by',  // OR 'updater_id', to override default field name
        ],
    ];
}

See more:

Authorization

User Access Based on Behavior Rules

Add access settings in the behavior() function in the required controller:

use yii\filters\AccessControl;
//...
 
class MyController extends Controller
{
  //...
  public function behaviors()
  {
    return [
        'access' => [
            'class' => AccessControl::className(),
            'only' => ['index', 'create', 'view', 'update', 'delete'],  // comment this if all pages require auth
            'rules' => [
                //[  // Guest users
                //   'allow' => true,
                //   'actions' => ['index', 'view'],
                //   'roles' => ['?'],  // ? = Guest user
                //],
                [   // Authenticated users
                    'actions' => ['index', 'view'],
                    'allow' => true,
                    'roles' => ['@'],  // @ = Authenticated users
                ],
                [   // Admin users
                    'actions' => ['index', 'create', 'view', 'update', 'delete'],
                    'allow' => true,
                    'matchCallback' => function ($rule, $action) {
                        //return (!Yii::$app->user->isGuest && Yii::$app->user->identity->isAdmin());
                        return (isset(Yii::$app->user->identity) && (Yii::$app->user->identity->role === 'admin'));
                     }
                ],
            ],
        ],
        //...
    ];
  }
}    

See:

User Access Based on Permissions

Allowing access to certain sections based on user roles:

if (Yii::$app->user->isGuest) {
    // show or do something for guest (not authenticated) users only
}

When giving access to only admin:

use app\models\User;
 
// Get user roles (requires authManager to be configured)
if (in_array(User::ROLE_ADMIN, Yii::$app->authManager->getRolesByUser(Yii::$app->user->getId()))) {  
    // Do something
}
// access only to admin user
if (Yii::$app->user->getId() == User::USER_ADMIN) {
    // show or do something for admin user only
}
 
// access only to admin user
if (isset(Yii::$app->user->identity) && ((Yii::$app->user->identity->username == 'admin')) {
    // show or do something for admin user only
}
// access only to admin users (based on role)
if (isset(Yii::$app->user->identity) && (Yii::$app->user->identity->role == User::ROLE_ADMIN)) {  
    // show or do something for users with admin role only
}

Note:

  • Yii::$app→user→identity has all the authenticated user details (id, username, role, etc.), but identity attribute is not available when not logged in.
  • To use getRolesByUser(), you need to have authManager configured first. See Security using RBAC.

Define admin role in app\models\User.php

...
class User extends ActiveRecord implements IdentityInterface
{
    // Roles
    const ROLE_ADMIN      = 1;   // Super user
    const ROLE_MANAGER    = 2;   // Create/update content, edit user profiles
    const ROLE_EDITOR     = 3;   // Create/update content
    const ROLE_AUTHOR     = 4;   // Create content, update own content only
    const ROLE_USER       = 10;  // Basic site use.  Same as ROLE_REGISTERED
    const ROLE_REGISTERED = 10;  // Basic site use.  Same as ROLE_USER
    ...
}

Access Based on Roles (RBAC)

References

System Variables

URL and Path Variables: Root or Base Path

Variable Value Description
<?= Yii::$app→homeUrl ?> http://example.com/myapp/web/
<?= Yii::$app→request→baseUrl ?> http://example.com/myapp/web
str_replace('/frontend/web/', “”, str_replace('/backend/web/', “”, Yii::$app→homeUrl)) /myapp/web Equivalent to Yii::$app→request→baseUrl without host.
<?= Yii::$app→basePath ?> C:\wamp\www\myapp\basic (or C:\wamp\www\myapp\advanced\backend). Eg: Yii::$app→basePath . DS . 'migrations' . DS . 'data' . DS . 'customer-data.rpt';, where define('DS', DIRECTORY_SEPARATOR);
<?= Yii::getAlias('@webroot') ?> C:/wamp/www/myapp/basic (or C:/wamp/www/myapp/advanced/backend). For other predefined aliases, see Yii 2: Predefined Aliases and also Available Path Aliases.
<?= Yii::$app→params['frontendUrl'] ?> Whatever string was stored. Basically, add a global variable $frontendUrl to basic/config/params.php or common/config/params.php, and access it using <?= Yii::$app→params['frontendUrl'] ?>.
<?= Yii::$app→request→userIP ?> 192.168.0.x User IP address.
<?= Yii::$app→user→id ?> 100 User ID.
<?= Yii::$app→user→identity→username ?> admin Username.
<?= Yii::$app→user→isGuest ?> true T if user is guest (not logged in).
$isAdmin = (isset(Yii::$app→user→identity) && (Yii::$app→user→identity→role == User::ROLE_ADMIN)) true T if use is admin. Requires adding role support to User model.
$isAdmin = (!Yii::$app→user→isGuest && (Yii::$app→user→identity→role == self::ROLE_ADMIN)) true T if use is admin. Requires adding role support to User model.
Target Variable Description
controller name Yii::$app→controller→id
controller name Yii::$app→getController()→getId()
controller name $this→id where $this refers to current controller
action name $this→action→id where $this refers to current controller
action name Yii::$app→controller→action→id
module name Yii::$app→controller→module→id
module name $this→module→id
module object Yii::$app→controller→module

You can create backend and frontend urls using this in the [app]/backend/config/main.php file:

return [
    ...
    'components' => [
        'urlManager' => [
            // here is your normal backend url manager config
            //'class' => 'yii\web\UrlManager',
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            'rules' => []
        ],
        'urlManagerFrontend' => [
           // here is your frontend URL manager config
           'class' => 'yii\web\UrlManager',
           'baseUrl' => '../../../frontend/web',
           'enablePrettyUrl' => true,
           'showScriptName' => false,
           'rules' => []
        ],
    ]
];        

In the view, use this:

  echo Yii::$app->urlManager->createUrl('');         // returns ''/backend/web''
  echo Yii::$app->urlManagerFrontend->createUrl(''); // returns ''/frontend/web''
 
  echo Yii::$app->urlManagerFrontend->createUrl(['user/activate', 'id' => $model->id]);            // returns ''/frontend/web/user/activate?id=71''
  echo Yii::$app->urlManagerFrontend->createAbsoluteUrl(['user/activate', 'id' => $model->id]);    // returns ''http://localhost:8080:myapp/frontend/web/user/activate?id=71''

Global Variables

Add global variables to basic/config/params.php or common/config/params.php:

Simple add a key and value pairs:

return [
    'adminEmail' => 'admin@example.com',
    'supportEmail' => 'support@example.com',
    'user.passwordResetTokenExpire' => 3600,
    'frontendUrl' => 'http://www.example.com/frontend/web',
];

You can then use that variable anywhere in the system:

<?= Yii::$app->params['frontendUrl'] ?>

Namespaces

Depending on what application you are using (console, basic, frontend/backend (advanced), etc.), you can end up trying to make reference to a namespace not found in that current application. For example, a migration console script cannot see namespaces defined in the frontend app.

To solve this issue, you can add class maps that register the required namespace. Eg: In console/migrations/m10000_init.php script:

// Add namespace for 'Dealers' frontend module, to access 'Dealer' model
// which resides in [app]/frontend/modules/dealers/models/Dealer.php
Yii::setAlias('@frontend', '@app/../frontend');
Yii::$classMap['app\modules\dealers\models\Dealer'] = '@frontend/modules/dealers/models/Dealer.php';
 
// Make use of namespace. Eg:
$availTables = [
    common\models\Payment::tableName(),
    app\modules\dealers\models\Dealer::tableName(),
    common\models\Distributor::tableName(),
    common\models\AccountAging::tableName(),
    common\models\User::tableName(),
];

To add a custom namespace to the autoloader you need to define an alias for the base directory of the namespace using Yii::setAlias(). For example to load classes in the foo namespace that are located in the path/to/foo directory you will call Yii::setAlias('@foo', 'path/to/foo').

See more: Namespace and Autoloading

Debug Variables / Logging

Use VarDumper which is intended to replace the buggy PHP function var_dump() and print_r().

use yii\helpers\VarDumper;
...
// Use one of these
VarDumper::dump($item);
VarDumper::dumpAsString($item);
VarDumper::export($item);
 
// To be more legible:
echo '<pre>' . yii\helpers\VarDumper::dumpAsString($item) . '</pre>';

Alternatively, you can log directly to the log file:

Yii::trace('Item data: ' . print_r($item, true), __METHOD__);

You can also display error content to a view. For example, if a save() command fails in a controller, we can display the error contents to the view using print_r($model→getErrors()) like this:

public function actionUpdate($id)
{
    $model = $this->findModel($id);
 
    if ($model->load(Yii::$app->request->post())) {
        if($model->save()) {
            return $this->redirect(['view', 'id' => $model->id]);
        } else {
            Yii::$app->session->setFlash('error', 
               "Error during save. Missing field. Error: <pre>" . print_r($model->getErrors(), true) . "</pre>");
            return $this->render('update', [
                'model' => $model,
            ]);
        }
    } else {
        return $this->render('update', [
            'model' => $model,
        ]);
    }
}

YII_DEBUG Variable: Switching Between Development/Production Environments

In development, execute the init command and select dev as environment.

$php /path/to/yii-application/init

Otherwise, in production, execute init in non-interactive mode. NOTE: Make sure all existing config files are backed up. This includes config in Basic App, or frontend/config, backend/config, and common/config directories in Advanced App.

$php /path/to/yii-application/init --env=Production --overwrite=All

The results might look like this. Note all the files that get overwritten:

W:\site>init --env=Production --overwrite=All
Yii Application Initialization Tool v1.0
 
  Start initialization ...
 
  unchanged console/config/params-local.php
  unchanged console/config/main-local.php
      exist common/config/params-local.php
            ...overwrite? [Yes|No|All|Quit]   overwrite common/config/params-local.php
  overwrite common/config/main-local.php
  overwrite backend/web/index.php
  unchanged backend/config/params-local.php
  overwrite backend/config/main-local.php
  overwrite yii
  overwrite frontend/web/index.php
  unchanged frontend/config/params-local.php
  overwrite frontend/config/main-local.php
   generate cookie validation key in backend/config/main-local.php
   generate cookie validation key in frontend/config/main-local.php
      chmod 0777 backend/runtime
      chmod 0777 backend/web/assets
      chmod 0777 frontend/runtime
      chmod 0777 frontend/web/assets
      chmod 0755 yii
 
  ... initialization completed.

To verify, view file [app]/web/index.php:

// The following two lines are active when in dev mode.
// Use '[yiiapp]/init --env=Production --overwrite=All' when deployed to production.
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');

Alternatively, add this:

if ($_SERVER['SERVER_NAME'] == 'localhost' || $_SERVER['SERVER_NAME'] == '127.0.0.1') {
    defined('YII_DEBUG') or define('YII_DEBUG', true); 
    defined('YII_ENV') or define('YII_ENV', 'dev'); 
} else {
    defined('YII_DEBUG') or define('YII_DEBUG', false);
    defined('YII_ENV') or define('YII_ENV', 'prod');
}

See Also:

Controllers

Calling another controller for the current controller:

Yii::$app->runAction('main/goods', ['model_id' => $goods->id]);

Sometimes trying to save data, it fails due to CSRF checks. This is a workaround:

/**
 * ContentController implements the CRUD actions for Content model.
 */
class ContentController extends Controller
{
    public $enableCsrfValidation = true;  // Set to 'false' if having trouble saving data (create/update, etc.)
 
    // ...
 
    /**
     * Updates an existing Content model.
     * If update is successful, the browser will be redirected to the 'view' page.
     * @param integer $id
     * @return mixed
     */
    public function actionUpdate($id)
    {
        // set to 'false' to overcome 'Unable to verify your data submission: Bad request (#400)' error.
        $this->enableCsrfValidation = false;  
 
        $model = $this->findModel($id);
 
        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            return $this->redirect(['view', 'id' => $model->id]);
        } else {
            // set values before rendering
            if (empty($model->updated_by)) $model->updated_by = Yii::$app->user->id;
 
            return $this->render('update', [
                'model' => $model,
            ]);
        }
 
        $this->enableCsrfValidation = true;  // restore CSRF validation
    }
}
Views

Accessing Data in View

Send Data from Controller (push method)

In the controller action, call the render() method with some data.

echo $this->render('report', [
    'foo' => 1,
    'bar' => 2,
]);

Get Data from Controller (pull method)

Sometimes in a view, you need to get data from the controller. Eg:

The controller ID is: <?= $this->context->id ?>
Some controller data: 
<?php
   $controller = $this->context;
   echo 'controller id = ' . $controller->id;
   echo 'controller data = '. $controller->getData(); 
?>

Get Current View Name

Sometimes in a view, you need to get the current view's name. Here is how to do it:

<?php
   $curViewID = Yii::$app->view->context->action->id;  // For view 'site/index', the view ID = 'index'
?>

Security

To avoid cross-site scripting, encode any text to display:

<?php
use yii\helpers\Html;
?>
 
<div class="username">
    <?= Html::encode($user->name) ?>
</div>

To display HTML content, filter it to avoid issues:

<?php
use yii\helpers\HtmlPurifier;
?>
 
<div class="article">
    <?= HtmlPurifier::process($article->text) ?>
</div>

Render a View File from Any View

When wanting to embed and render another view file into the existing view, simply include it in the right place. Eg: For view /site/index, add /site/about:

<?php
use yii\helpers\Html;
use yii\helpers\Url;
 
/* @var $this yii\web\View */
$this->title = 'Index';
$this->params['breadcrumbs'][] = Html::encode($this->title);
?>
 
<!-- Include view Index -->
<div class="site-software">
    <h1><?= $this->title; ?></h1>
    ...
    <!-- some view details here -->
</div>    
<!-- Include view About -->
<?= Yii::$app->view->renderFile('@frontend/views/site/about.php'); ?>

See also:

Custom Layout

Add a custom layout in controller function when needed:

...
class ContentController extends Controller
{
  ...
  public function actionIndex()
  {
     ...
     $this->layout = 'new-layout';
  }
}  

See also:

View Without Model

Create a form with fields not attached to a model:

View (file /app/views/issue/search.php):

use yii\helpers\Html;
use yii\widgets\ActiveForm;
...
<div class="issue-form">
    <?php $form = ActiveForm::begin(); ?>
    <?= Html::label('Account Number') ?> <br/>
    <?= Html::textInput('account_number', '', 
        ['id' => 'account', 'maxlength' => true, 'class' => 'form-control'] 
    ) ?> 
    ...
    <?php ActiveForm::end(); ?>
</div>

View With Model Not Connected to Database

Add the required fields to the model and use them in the view.

Model (file /app/models/Issue.php):

//class Issue extends \yii\db\ActiveRecord  // for database support
class Issue extends \yii\base\Model  // for no database support
{
    // Add field
    public $account_number = '';
    ...
 
    // Add validation rule
    public function rules()
    {
        return [
            [['account_number', ...], 'string', 'max' => 255],
        ];
    }
 
    // Add field label
    public function attributeLabels()
    {
        return [
            ...
            'account_number' => Yii::t('app', 'Account Number'),
        ];
    }
 
    ...
}    

View (file /app/views/issue/search.php):

use yii\helpers\Html;
use yii\widgets\ActiveForm;
...
<div class="issue-form">
    <?php $form = ActiveForm::begin(); ?>
    <?= $form->field($model, 'account_number')->textInput(['maxlength' => true']) ?> 
    ...
    <?php ActiveForm::end(); ?>
</div>
Inline Javascript & AJAX

You can add Javascript functionality anywhere in a view, if necessary:

// NOTE: $position can be:
//   View::POS_READY (the default)
//   View::POS_HEAD 
//   View::POS_BEGIN
//   View::POS_END
$position = \yii\web\View::POS_END;
 
// Inline block of Javascript
$jsBlock = "
  $('#el').on('click', function(e) {
    $.ajax({
       url: '/path/to/action',
       data: {id: '<id>', 'other': '<other>'},
       success: function(data) {
           // process data
       }
    });
  });
";
$this->registerJs($jsBlock, $position);
 
// Alternatively, use Javascript from an external file
$this->registerJsFile('http://example.com/lib/external.js', $position);

Alternatively, write it so:

<?php
$script = <<< JS
$(document).ready(function() {
    // Do something here.  Eg:
    // Refresh button every 3 sec
    setInterval(function(){ $("#refreshButton").click(); }, 3000);
});
JS;
$this->registerJs($script);
?>

Alternatively, to link to a JavaScript file:

<?= Html::jsFile('@web/js/main.js') ?>

See more:

AJAX Example (returning HTML)

Eg: The view _form for the IssueController (file [app]/views/issue/_form.php):

<div class="issue-form">
    <?php $form = ActiveForm::begin(); ?>
 
    <div class="col-sm-12 col-md-6">
        <?= $form->field($model, 'account_number')->textInput([
            'maxlength'  => true, 
            'onchange'   =>'getCustomerRecord()',
            'onmouseover'=>'getCustomerRecord()'
        ]) ?> 
        <p/>
        <?= Html::tag('div', '...', ['id' => 'company_search_results']); ?>
        <p/>
    </div>
    ...
</div>
 
<?php
$jsBlock = "
//$( document ).ready(function() {
//    console.log( 'page loaded.  We are ready!' );
//    getCustomerRecord();
//});
getCustomerRecord();
";
// NOTE: $position can be:
//   View::POS_READY (the default)
//   View::POS_HEAD 
//   View::POS_BEGIN
//   View::POS_END
$position = \yii\web\View::POS_READY;
$this->registerJs($jsBlock, $position);
 
$jsBlock = "
function getCustomerRecord()
{
    $.ajax({
            url: '" . Yii::$app->request->baseUrl. '/issue/get-company' . "',
            data: {
                account_number: $('#issue-account_number').val(),
            },
            type: 'POST',
 
            cache: false,
 
            // Code to run if the request succeeds;
            // the response is passed to the function
            success: function(data) {
              // process data
              console.log('data: [' + data + ']');
              $('#company_search_results').html(data);
            },
 
            // Code to run if the request fails; the raw request and
            // status codes are passed to the function
            error: function( xhr, status, errorThrown ) {
                alert('Sorry, there was a problem! Error: ' + 
                   errorThrown + '. Status: ' + status + ' ' + xhr + ' ' );
                console.log('Error: ' + errorThrown );
                console.log('Status: ' + status );
                console.dir( xhr );
            },
 
            // Code to run regardless of success or failure
            complete: function( xhr, status ) {
                //alert('The request is complete!');
            }
    });
}
";
 
// NOTE: $position can be:
//   View::POS_READY (the default)
//   View::POS_HEAD 
//   View::POS_BEGIN
//   View::POS_END
$position = \yii\web\View::POS_END;
$this->registerJs($jsBlock, $position);
?>

Eg: The action get-company in IssueController (file [app]/controllers/IssueController.php):

use Yii;
use app\models\Customer;
...
class IssueController extends Controller
{
    public function actionGetCompany()
    {
        if (Yii::$app->request->isPost) {
            $account_number = Yii::$app->request->post('account_number');
            Yii::info('POST account_number: ' . $account_number, 'actionGetrec');
        }
        if (empty($account_number)) {
            $modelCust      = new Customer(); 
            $account_number = $modelCust->account_number;
            $company_name   = $modelCust->company_name;
            $arrCustomer    = $modelCust->toArray();
            Yii::info('Empty account_number: ' . 
               $account_number . 'company_name: ' . $company_name, 'actionGetrec');
        } else {
            $modelCust   = Customer::find()->where(['account_number' => $account_number])->one();
            $arrCustomer = Customer::find()->where(['account_number' => $account_number])->asArray()->one();
            if(!empty($modelCust)) {
                $company_name = $modelCust->company_name;
                Yii::info('Found customer: ' . print_r($modelCust, true), 'actionGetrec');
                Yii::info('Found account_number: ' . 
                   $account_number . 'company_name: ' . $company_name, 'actionGetrec');
            } else {
                Yii::info('Customer not found: ' . $account_number, 'actionGetrec');
            }
        }
 
        $model = new Issue();
 
        $data = array();
        //if (!empty($arrCustomer['account_number'])) { $data[] = $arrCustomer['account_number']; }
        if (!empty($arrCustomer['company_name']))   { $data[] = $arrCustomer['company_name']; }
        if (!empty($arrCustomer['address']))        { $data[] = $arrCustomer['address']; }
        if (!empty($arrCustomer['city']))           { $data[] = $arrCustomer['city']; }
        if (!empty($arrCustomer['state_prov']))     { $data[] = $arrCustomer['state_prov']; }
        if (!empty($arrCustomer['country']))        { $data[] = $arrCustomer['country']; }
        if (!empty($arrCustomer['phone']))          { $data[] = $arrCustomer['phone']; }
        if (!empty($arrCustomer['phone']))          { $data[] = $arrCustomer['phone']; }
        if (!empty($arrCustomer['email']))          { $data[] = $arrCustomer['email']; }
 
        if (count($data) > 0) {
            return '<div class="alert alert-success" role="alert">' . implode(', ', $data) . '</div>';
        } else {
            return '<div class="alert alert-danger" role="alert">Customer record not found</div>';
        }
    }
 
    //public function actionGetCustomerRecord()
    //{
    //    if (Yii::$app->request->isAjax) {
    //        $data = Yii::$app->request->post();
    //        $account_number= explode(":", $data['account_number']);
    //        $account_number= $account_number[0];
    //        \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
    //        return [
    //            'account_number' => $account_number,
    //            'code' => 100,
    //        ];
    //    }
    //}
    ...
}    
Assets

Place CSS and JS assets in [app]/web/, then add references in AppAsset.php for main application, or MyComponentAsset.php for a component or site section with specific asset requirements:

// Class name should be AppAsset for the main app assets, 
// or MyComponentAsset for your specific component assets.
class AppAsset extends AssetBundle
{
    public $basePath = '@webroot';
    public $baseUrl = '@web';
    public $css = [
        // Place CSS assets here
        ...
    ];
    public $js = [
        // Place JS assets here
        ...
    ];
    public $depends = [
        'yii\web\YiiAsset',
        'yii\bootstrap\BootstrapAsset',
    ];
}

Using assets in a view:

use app\assets\MyComponentAsset;
MyComponentAsset::register($this);  // $this represents the view object
...

See more: Guide 2.0: Structure Assets

Model Constants

Sometimes we need to use constants when working with models. Eg: Finding all customer with active status (STATUS_ACTIVE):

// Retrieve all *active* customers and order them by their ID:
$customers = Customer::find()
    ->where(['status' => Customer::STATUS_ACTIVE])
    ->orderBy('id')
    ->all();

We defined them in the model like this:

namespace app\models;
use Yii;
 
class Customer extends \yii\db\ActiveRecord
{
    // Model constants
    const STATUS_ACTIVE= 1;
    const STATUS_INACTIVE= 0;
 
    ...
}
General Purpose Data in Views

For example, many forms use Country or State as an input field. We can populate these fields with a predetermined dropdown list.

Create a basic model where to store the data. Eg: app\models\Tool.php:

namespace app\models;
 
use yii\base\Model;         // parent for model classes not associated with database tables
//use yii\db\ActiveRecord;  // parent for model classes that do correspond to database tables
 
/**
 * This is the model class for table "Tools".
 *
 * @property array $countries
 * @property array $countryNames
 * @property array $languages
 * @property array $states
 */
class Tool extends Model
{
    public static $countries = array( 
        'af' => 'Afganistan', 
        'al' => 'Albania', 
        ...
    );
 
    public static $countryNames= array(...);
 
    public static $languages= array(...);    
 
    public static $states = array(
        "N/A"=> "Not Applicable",
        "AL"=> "AL - Alabama",
        ...
        "WI"=> "WI - Wisconsin",
        "WY"=> "WY - Wyoming"
    );
 
    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['countries', 'countryNames', 'languages', 'states'], 'safe'],
        ];
    }
}    

Add fields to a model that needs it. Eg: For model Customer, we add these functions in app\models\Customer.php:

namespace app\models;
 
use Yii;
use app\models\Tool;   // Our model with static data
 
class Customer extends \yii\db\ActiveRecord
{
    ...
 
    public function getStates()
    {
        return Tool::$states;
    }
 
    public function getCountries()
    {
        return Tool::$countryNames;
    }
}    

Make use of that data in a view. Eg: For a create customer view, we add these fields in ap\views\customer\_form.php:

use yii\helpers\Html;
use yii\widgets\ActiveForm;
 
/* @var $this yii\web\View */
/* @var $model app\models\Customer*/
/* @var $form yii\widgets\ActiveForm */
?>
 
<div class="customer-form">
 
    <?php $form = ActiveForm::begin(); ?>
 
    <?= $form->field($model, 'name')->textInput(['maxlength' => 255]) ?>
    ...
    <?= $form->field($model, 'state_prov')->dropDownList(
            $model->states,
            [ 'prompt' => '--Select One--' ]
        ) ?>
    <?= $form->field($model, 'postal_code')->textInput(['maxlength' => 255]) ?>
    <?= $form->field($model, 'country')->dropDownList(
            $model->countries,
            [ 'prompt' => '--Select One--' ]
        ) ?>
    ...
    <div class="form-group">
        <?= Html::submitButton($model->isNewRecord ? 
                  Yii::t('app', 'Create') : 
                  Yii::t('app', 'Update'), 
              ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
    </div>
 
    <?php ActiveForm::end(); ?>
 
</div>
Virtual Attributes (aka Virtual Fields)

A model might need an additional fields not found in the database table. For example, customer has last_name and first_name, but sometimes you need a fullname. Instead of combining last and first names every time, we can use a virtual attributes to create fullname.

In your customer model, add the following:

class Customer extends \yii\db\ActiveRecord
{
    public function rules()
    {
        return [
            [['first_name', 'last_name'], 'string', 'max' => 255],
            ...
            [['fullname'], 'safe']  // virtual attributes
        ];
    }
 
    public function attributeLabels()
    {
        return [
            'id' => Yii::t('app', 'ID'),
            'first_name' => Yii::t('app', 'First Name'),
            'last_name' => Yii::t('app', 'Last Name'),
            ...
            'fullname' => Yii::t('app', 'Name'),
        ];
    }
 
    // Virtual attributes
    public function getFullName()
    {
        return $this->first_name . " " . $this->last_name;
    }
}

This new field can be used in a controller:

  echo $model->fullname;

This can be used in a view like any other field:

    <?= DetailView::widget([
        'model' => $model,
        'attributes' => [
            'id',
            //'first_name',
            //'last_name',
            'fullname',
        ],
    ]) ?>

Alternatively, you can simply add a virtual field to MySQL/MariaDB:

// FULL Account Num: concat(`account_number`, IFNULL(CONCAT('-',`shipto`), '')  )
ALTER TABLE customer 
  ADD full_account_num CHAR(60) 
  GENERATED ALWAYS AS concat(`account_number`, IFNULL(CONCAT('-',`shipto`), '')  VIRTUAL NOT NULL;
 
// FULL Name: concat(`first_name`, IFNULL(`last_name`, '')  )
ALTER TABLE customer 
  ADD full_name CHAR(60) 
  GENERATED ALWAYS AS concat(`first_name`, IFNULL(`last_name`, '')  )  VIRTUAL NOT NULL;

Set relations in model. For example, in model order:

class Order extends \yii\db\ActiveRecord
{
    public function getCustomer()
    {
        // Order has_one Customer via Customer.id -> customer_id
        return $this->hasOne(Customer::className(), ['id' => 'customer_id']);
    }
 
    ...
}    

Alternatively, join tables in a query to access relational data in a view. We do this query in the controller:

//$query = Order::find();   // use this if relations are setup in 'Order' model already 
$query = Order::find()->joinWith('customer');  // use for dataprovider columns for sorting purposes (eg. 'customer.city')

In the view, just make reference to the related model fields. Eg: customer.last_name, customer.company_name:

   <?= GridView::widget([
        'dataProvider' => $dataProvider,
        'filterModel' => $searchModel,
        'columns' => [
            ['class' => 'yii\grid\SerialColumn'],
 
            [
                'attribute' => 'id',
                'format' => ['raw'],
                'value' => function($data) {
                    $ordernum = str_pad($data['id'], 6, "0", STR_PAD_LEFT);  // link to view record
                    return Html::a(Html::encode($ordernum), Url::toRoute(['view', 'id' => $data['id']]));  // link to view record
                },
                'label' => 'Order ID'
            ],
            'date',
            //'customer.first_name',
            //'customer.last_name',
            'customer.fullname',
            'customer.company_name',
            // Searchable fields
            [
                'attribute'=>'city',
                'value'=>'customer.city'
            ],
            [
                'attribute'=>'state_prov',
                'value'=>'customer.state_prov'
            ],
            [
                'attribute'=>'country',
                'value'=>'customer.country'
            ],
 
            ['class' => 'yii\grid\ActionColumn'],
        ],
    ]); ?>

When creating the index view, you would normally have support for search. In the OrderSearch model, this is how it would look (assuming order.customer_id = customer.id):

namespace app\models;
 
use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use app\models\Order;
 
/**
 * OrderSearch represents the model behind the search form about `app\models\Order`.
 */
class OrderSearch extends Order
{
    // Add the public attributes that will be used to store the data to be searched
    public $company_name;
    public $city;
    public $state_prov;
    public $country;
 
    // Add search fields to rules
    public function rules()
    {
        return [
            [['id', 'customer_id', 'user_id'], 'integer'],
            [['date', 'company_name', 'city', 'state_prov', 'country'], 'safe'],
        ];
    }
 
    public function search($params)
    {
        //$query = Order::find();
        $query = Order::find()->joinWith('customer'); 
 
        $dataProvider = new ActiveDataProvider([
            'query' => $query,
        ]);
 
        // Add search fields to sorting attributes
        // Note: This is setup before the $this->load($params) statement below
        $dataProvider->setSort([
            'attributes' => [
                'id',
                'name',
                'date',
                'customer.fullname' => [
                    'asc'   => ['customer.last_name' => SORT_ASC],
                    'desc'  => ['customer.last_name' => SORT_DESC],
                    'label' => 'Customer Name'
                ],
                'company_name' => [
                    'asc'   => ['customer.company_name' => SORT_ASC],
                    'desc'  => ['customer.company_name' => SORT_DESC],
                    'label' => 'Company'
                ],
                'city' => [
                    'asc'   => ['customer.city' => SORT_ASC],
                    'desc'  => ['customer.city' => SORT_DESC],
                    'label' => 'City'
                ],
                'state_prov' => [
                    'asc'   => ['customer.state_prov' => SORT_ASC],
                    'desc'  => ['customer.state_prov' => SORT_DESC],
                    'label' => 'State/Province'
                ]
                'country' => [
                    'asc'   => ['customer.country' => SORT_ASC],
                    'desc'  => ['customer.country' => SORT_DESC],
                    'label' => 'Country'
                ]
            ]
        ]); 
 
        if (!($this->load($params) && $this->validate())) {
            return $dataProvider;
        }
 
        // Add search fields to filter
        $query->andFilterWhere([
            'id'                    => $this->id,
            'date'                  => $this->date,
            'customer_id'           => $this->customer_id,
            //'customer.company_name' => $this->company_name,
            //'customer.city'         => $this->city,
            //'customer.state_prov'   => $this->state_prov,
            //'customer.country'      => $this->country,
            'user_id'               => $this->user_id,
        ])
        // Add searchable attributes
        ->andFilterWhere(['like', 'LOWER(customer.company_name)', strtolower($this->company_name)])
        ->andFilterWhere(['like', 'LOWER(customer.city)',         strtolower($this->city)]);
        ->andFilterWhere(['like', 'LOWER(customer.state_prov)',   strtolower($this->state_prov)]);
        ->andFilterWhere(['like', 'LOWER(customer.country)',      strtolower($this->country)]);
 
        return $dataProvider;
    }
 
    ...
}    
Creating Extensions

Create extension controller in [app]/vendor/acme/myext/myext.php:

namespace acme;
use yii\web\Controller;
 
class MyExtController extends Controller
{
  // Action here
}

Optional Step: Create bootstrap class in component, to attach controller class automatically. Eg. [app]/vendor/acme/myext/MyBootstrap.php:

<?php
namespace acme;
use \yii\base\BootstrapInterface;
 
class MyBootstrap implements BootstrapInterface
{
  /** @param \yii\web\Application $app */
  public function bootstrap($app)
  {
    // Controller addition
    $app->controllerMap['myext'] = '\acme\myext\MyExtController';
  }
}

To register extension manually (when installing and autoloading through composer is not available), write the following code inside the application configuration (or directly on the file [app]/vendor/yiisoft/extensions.php):

$vendorDir = __DIR__ . '/..';
 
'extensions' => array_merge(
   (require __DIR__ . '/../vendor/yiisoft/extensions.php'),
   [
      'acme\myext' => [
         'name' => 'MyExtension',
         'version' => '1.0.0',
         'bootstrap' => '\acme\myext\MyBootstrap',
         'alias' => ['@acme/myext' => $vendorDir . '/acme/myext']  // path to extension 
      ]
   ]
)

Add extension to application configuration. In [app]/config/web.php, add this to components:

...
$config = [
    ...
    'components' => [
        ...
        'myext' => [
           'class' => 'acme\myext\MyExt',
        ],
];
...     

To allow automatic registration using composer, we need to create a composer.json file in the extension directory. Eg: [app]/vendor/acme/myext/composer.json

{
    "name": "acme/yii2-myext",
    "version": "1.0.0",
    "description": "Yii2 MyExtension",
    "keywords": ["yii2", "myext", "Sample Extension"],
    "homepage": "https://github.com/acme/yii2-acme",
    "type": "yii2-extension",
    "license": "BSD 3-Clause",
    "authors": [
        {
            "name": "John Doe",
            "email": "info@example.com",
            "homepage": "http://www.example.com/"
        }
    ],
    "require": {
        "yiisoft/yii2": "*"
    },
    "autoload": {
        "psr-4": {
            "acme\\myext\\": ""
        }
    },
    "extra": {
        "bootstrap": "acme\\Bootstrap"
    }
}

Install with composer. Either run from command line:

  • Linux/Mac:
    $ php composer.phar require "acme/yii2-myext:*"
  • Windows:
    C:\www\Yii2Site\> composer require "acme/yii2-myext:*"

Or:

  • Add this line to the require section of your composer.json file:
    ...
    "require": {
      ...
      "acme/yii2-myext": "dev-master"
    }  
  • Run composer install to install the specified extensions.

See:

Cache Flushing Guide

When you need to invalidate all the stored cache data, you can call yii\caching\Cache::flush().

You can flush the cache from the console by calling yii cache/flush as well.

  • yii cache: lists the available caches in application.
  • yii cache/flush cache1 cache2: flushes the cache components cache1, cache2 (you can pass multiple component names separated with space).
  • yii cache/flush-all: flushes all cache components in the application.

Info: Console application uses a separate configuration file by default. Ensure, that you have the same caching components in your web and console application configs to reach the proper effect.

Source:

DataProvider

You can override certain default values used in the DataProvider.

To filter all records by a customer ID:

$searchModelPrice  = new \app\models\PriceSearch();
$dataProviderPrice = $searchModelPrice->search(yii\helpers\ArrayHelper::merge(
    Yii::$app->request->queryParams,
    ['PriceSearch' => [
        'customer_id' => $id,
    ]]
));

Or override the pagination to be greater than the default 20 rows per page:

$searchModelPrice  = new \app\models\PriceSearch();
$dataProviderPrice = $searchModelPrice->search(Yii::$app->request->queryParams));
$dataProviderPrice->pagination->defaultPageSize = 1000;
$dataProviderPrice->pagination->pageSizeLimit   = false;
Copy to Clipboard using Javascript
  1. Add a reference to the ClipboardJS library either by using Yii2 registerJsFile or adding the file to assets\AppAsset.php
  2. Add javascript to your page that creates an instance of ClipboardJS:
$this->registerJs("var clipboard = new ClipboardJS('.btn-clip');", \yii\web\View::POS_END);
  1. Add a button that follows this logic:
<i class="fa fa-copy btn btn-default btn-clip" data-clipboard-text="value-to-copy"></i>