Skip to content

Instantly share code, notes, and snippets.

@smohadjer smohadjer/yii2.md

Last active Jun 11, 2020
Embed
What would you like to do?
Saeid's Yii2 Cookbook

To display validation errors in a model:

$model->getErrors()

Custom validators

Custom validators can be written either inside a model or as a class in an external file so they can be used by different models.

Custom validator inside a model:

  public function attributeLabels()
  {
    return [
      'user_fname' => Yii::t('app', 'First Name'),
      'user_lname' => Yii::t('app', 'Last Name'),
      'user_email_mess' => Yii::t('app', 'Email'),
      'secnd_resp' => Yii::t('app', 'First Backup')
    ];
  }
  
  public function rules()
  {
    return [
      ['active', 'validateStatus']
    ];
  }
  
  public function validateStatus($attribute, $params, $validator)
  {
    if ($this->$attribute == 'F' && $this->secnd_resp == 'true') {
      $this->addError($attribute, Yii::t('app/error', '{attribute} can not have an inactive status.', [
     	 'attribute' => $this->attributeLabels()[$attribute]
      ]));
    }
  }

Custom validator in external file:

// components/validators/DivisionValidator.php
namespace app\components\validators;
use Yii;
use yii\validators\Validator;
class DivisionValidator extends Validator
{
  private $reservedDivisionNames = ['all', 'alle'];
  public function validateAttribute($model, $attribute)
  {
    if (in_array(strtolower($model->$attribute), $this->reservedDivisionNames)) {
      $error = Yii::t('app/error', '"{division}" is a reserved division name.', [
        'division' => $model->$attribute
      ]);
      $this->addError($model, $attribute, $error);
    }
  }
}

// messages/de-DE/error.php
return [
  '{attribute} can not be empty.' => '{attribute} darf nicht leer sein.',
  '"{division}" is a reserved division name.' => '"{division}" ist ein reserviertes Wort.'
];

// models/CompanyDivisions.php
namespace app\models;
use yii\db\ActiveRecord;
use app\components\validators\DivisionValidator;
class CompanyDivisions extends ActiveRecord
{
  public function rules()
  {
    return [
      ['company_division', DivisionValidator::className()]
    ];
  }
}

Using external libraries in a Yii2 app

Last updated on 13.04.2020

Related links:

  1. If the library you wish to use is a NPM package, you can install it by adding it to "require" property of composer.json using a syntax such as: "npm-asset/tablesorter": "2.31.3". The library will be saved in vendor/npm-asset directory when you run composer update. If the library is not released on NPM and is an online repository such as a library on github, it must have a valid composer.json in the root. Then you add the repository's path to your project's composer.json's repositories field like this:
    "repositories": [
    	{
    		"type": "git",
    		"url": "https://github.com/smohadjer/filterList.git"
    	}
    ]
    
    and then add the library path to "require" property in composer.json. Here you can refer to a branch, but you have to prefix branch name with dev- as in this example:
    "require": {
    	"smohadjer/filterlist": "dev-master"
    }
    
    or if the library has releases tagged you can refer to a specific release like this:
    "require": {
    	"smohadjer/filterlist": "3.2.1"
    }
    
    Then running composer update will install the library in vendor folder.
  2. Since vendor folder is not accessible from browser, the package should be published under web folder. To do this we need to create an AssetBundle. To do this you can duplicate assets/AppAsset.php and rename it to something like TablesorterAsset.php. In this bundle unlike AppAsset bundle we don't use $basePath and $baseUrl and use instead $sourcePath like this: public $sourcePath = '@npm/tablesorter/dist';. The @npm is an alias to vendor/npm-asset defined in aliases array of config/web.php. If library is installed in another subfolder of vendor, you can use @app alias to refer to root of your app and then specify the full path like this: public $sourcePath = '@app/vendor/smohadjer/filterlist/dist'; Here is content of TablesorterAsset.php as an example:
    <?php
    namespace app\assets;
    use yii\web\AssetBundle;
    
    class TablesorterAsset extends AssetBundle
    {
      public $sourcePath = '@npm/tablesorter/dist';
      public $css = [
        'css/theme.default.min.css'
    	];
      public $js = [
        'js/jquery.tablesorter.min.js',
      ];
      public $depends = [
          'yii\web\YiiAsset'
      ];
      public $publishOptions = [
          'only' => [
    	  'js/jquery.tablesorter.min.js',
    	  'css/theme.default.min.css'
          ]
      ];
    }
    
  3. Now that we have listed resources we need in a bundle, we should add the bundle to our view like this:
    use app\assets\TablesorterAsset;
    TablesorterAsset::register($this);
    
    Now if you open the view in browser, Yii will create a folder under 'web/assets/' and copy bundle resources there and add script and css files to the page. The order by which css and js are listed in markup is based on dependency of bundles and how bundles and other resources are added to the view.
  4. If you need to initialize plugin from another script or customize its styling in another CSS file, you should put these scripts/styles in your web directory and refer to them in your view like this:
    $this->registerCssFile(
      '@web/css/myCustomStyles.css',
      ['depends' => [TablesorterAsset::className()]]
    );
    
    $this->registerJsFile(
      '@web/js/myCusotmScript.js',
      ['depends' => [
        \yii\web\JqueryAsset::className(),
        AppAsset::className()
      ]]
    );	
    

Customizing error messages

Custom error:

['email', 'required', 'message' => 'No good, {attribute} can not be empty!']

Custom error with i18n support:

// model.php
// This function allows you to customize attribute label used in error
public function attributeLabels()
{
	return [
		'user_fname' => Yii::t('app', 'First Name')
	];
}
	
public function rules()
{
	return [
		['user_fname', 'required', 'message' => Yii::t('app/error', '{attribute} can not be empty.')]
	]
}

// messages/de-DE/error.php
return [
    '{attribute} can not be empty.' => '{attribute} darf nicht leer sein.'
];

UniqueValidator validates that the attribute value is unique in the specified database table. https://www.yiiframework.com/doc/api/2.0/yii-validators-uniquevalidator


To access controlloer object from a view use:

$this->context

For example if you have a property in controller like public $title = 'Recall HTML Admin'; you can read it in your layout using $this->context->title. https://www.yiiframework.com/doc/guide/2.0/en/structure-views#accessing-data-in-views


To hide Yii debugger comment out following code in config/web.php:

	$config['bootstrap'][] = 'debug';
	$config['modules']['debug'] = [
		'class' => 'yii\debug\Module',
		// uncomment the following to add your IP if you are not connecting from localhost.
		//'allowedIPs' => ['127.0.0.1', '::1'],
	];

Cloning a database row as a new row:

$model = Events::findOne($id);
$model->isNewRecord = true;
$model->event_id = null;
$model->save();

ActiveForm input field with custom HTML:

$form->field($model, 'event_name', [
	'template' => '{label}{input}{hint}{error}',
	'options' => [
		'tag' => false
	],
	'errorOptions' => [
		'class' => ['myerror'],
		'tag' => 'span'
	]
])
->input('text', [
	'form' => 'myformid',
	'placeholder' => 'placeholder',
	'class' => ['myinput']
])
->label('Label', [
	'class' => ['mylabel']
])
->hint('Add hint here...', [
	'tag' => 'span',
	'class' => ['myhint']
]);

Radio buttons example:

$model->sex = 'm'; //setting default state for radio buttons
$form->field($model, 'sex', [
		'template' => '<div class="col1">{label}</div><div class="col2">{input}{hint}{error}</div>',
	])
	->radioList(['m'=>'Male', 'f' => 'Female'], [
		'itemOptions' => [
			'form' => 'starterWizard__form'
		]
	])
	->label('Sex:');

Checkbox:

$form->field($model, 'includeAttachments')->checkbox([
	'label'=>'Include all attachments’,
	'labelOptions' => [
		'title' => 'blah blah',
		'class' => 'my-custom-label'
	],
	'form' => ‘idOfFormIfCheckboxIsOutsideOfForm',
	'checked' => false, // Set to true if you want checkbox to be checked by default
	'value' => 'true', // Use if you want to send a different value than default value of 1
	'uncheck' => 'false' // Use if you want to send a different value than default value of 0. If you don't want yii to send any value set it to null.
]);

By default value of checkbox is either 1 or 0 depending on whether it's checked or not. If you want a different value you can add it to options array using this syntax: 'value' => $valueOfCheckbox


CheckboxList example:

$formFile->field($modelEventAttachments,'deleteFiles[]’)
->label('Select files to delete:')
->checkboxList($checkBoxItems, [
	'itemOptions' => [
		'form' => 'starterWizard__fileManager'
	]
]);

Multiple checkboxes can also be added without using checkboxList() as shown below.

// model.php
public $deleteDivisions;

// view.php
foreach($myArray as $value) {
	echo $form->field($model, 'deleteDivisions[]')->checkbox([
		'label'=> $value,
		'value' => $value
	]);
}

Dropdown


echo $form->field($model, 'user_company_division', [
	'inputOptions' => []
])
->label(Yii::t('app', 'Division') . ':')
->dropDownList($items, [
	'prompt'=> Yii::t('app', 'Select'),
	'title' => 'My divisions selector'
]);


Read files from directory:

$dir = Yii::$app->basePath . '/web/uploads;
if (is_dir($dir)) {
	// get rid of the dots that scandir() picks up in Linux
	$scanned_dir = array_diff(scandir($dir), array('..', '.'));
	
	if (!empty($scanned_dir)) {
		foreach ($scanned_dir as $file) {
			var_dump($file);
		}
	}
}

Adding a rich text editor (Quill)

//add following dependencies to composer.json and run composter update:
"require": {
	"bizley/quill": "2.5.0"
}

//add validation to model:
[['reason_or_descriptionhtml'], 'required', 'on' => ['step2_reason']],
[['reason_or_descriptionhtml'], 'string', 'length' => [1, 2000], 'on' => ['step2_reason']],

//add Quill to view:
<?php
$toolbarOptions = [
		['bold', 'italic', 'underline', 'strike'],
		[array(
			'header' => [1, 2, 3, 4, 5, 6, false]
		)],
		[array(
			'list' => 'bullet'
		)],
		[array(
			'color' => [],
			'background' => []
		)],
		[array(
			'font' => []
		)],
		[array(
			'align' => []
		)],
		['clean']
	];

	echo $form->field($model, 'reason_or_descriptionhtml')
		->label('Recall reason: *')
		->widget(\bizley\quill\Quill::class, [
			'toolbarOptions' => $toolbarOptions
			'hiddenOptions' => [
				'form' => 'starterWizard__form'
			]
	]);
?>

Implementing a DateTime Picker using Kartik plugins:

//add following dependencies to composer.json and run composter update:
"require": {
    "kartik-v/yii2-datecontrol": "@dev",
    "kartik-v/yii2-widget-datetimepicker": "1.4.9"
}

//add validation rule to the model, e.g.:
['event_start_time', 'datetime', 'format' => 'php:Y-m-d H:i:s', 'on' => ['step1', 'validateAll']]

//add datetime picker to view:
use kartik\datecontrol\DateControl;
use \kartik\widgets\DateTimePicker;
$form->field($model, 'event_start_time', [
	'template' => '<div class="col1">{label}</div><div class="col2">{input}{hint}{error}</div>'
])
->widget(DateControl::classname(), [
	'displayFormat' => 'php:Y-m-d H:i:s',
	'type'=> DateControl::FORMAT_DATETIME,
	'saveOptions' => [
		'form' => 'starterWizard__form'
	],
	'widgetOptions' => [
		'pluginOptions' => [
			'autoclose' => true,
			'todayHighlight' => true,
			'todayBtn' => true,
			'startDate'=> date('Y-m-d H:i:s',time()),
		],
		'options' => [
			'placeholder' => 'Select date and time ...'
		]
	]
])
->label('Recall Start Date and Time: *');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.