Skip to content

Instantly share code, notes, and snippets.

@yesnik
Last active June 17, 2024 05:40
Show Gist options
  • Save yesnik/e79c50a2eaff3b986b2f504f01d609a7 to your computer and use it in GitHub Desktop.
Save yesnik/e79c50a2eaff3b986b2f504f01d609a7 to your computer and use it in GitHub Desktop.
Yii 1 Framework Cookbook

CActiveRecord methods

findAll()

Sort records

// Ascending order
Partner::model()->findAll(array('order' => 'company'));

// Descending order
Partner::model()->findAll(array('order' => 'company DESC'));

// Ascending order
$criteria = new CDbCriteria();
$criteria->order = 't.order ASC';
Partner::model()->findAll($criteria);

Find with condition

$criteria = new CDbCriteria();
$criteria->addCondition('idpartner = :partnerId');
$criteria->params[':partnerId'] = 123;
Partner::model()->findAll($criteria);

Select only one column

// Example 1
$partners = Partner::model()->findAll(array('select' => 'title'));

// Example 2
$criteria = new CDbCriteria();
$criteria->select = 'title';
$partners = Partner::model()->findAll($criteria);

Select distinct values

$criteria = new CDbCriteria();
$criteria->distinct = true;
$criteria->select = '`key`';
$criteria->addColumnCondition([
    'setting_id' => 1260,
    'status' => SettingsParams::STATUS_ACTIVE
]);

$settingParams = SettingsParams::model()->findAll($criteria);

This will produce SQL:

SELECT DISTINCT `key` FROM `settings_params` `t` 
WHERE setting_id = 1260 AND status = 2

findAllBySql()

This method finds ActiveRecord entities by raw SQL:

$sql = "SELECT * FROM products WHERE created_at > :created_at";

$products = Product::model()->findAllBySql($sql, [
  ':created_at' => '2019-01-11 00:00:00',
]);

count()

$criteria = new CDbCriteria();
$criteria->addColumnCondition([
    'user_id' => $user->id,
]);
$count = (int)Posts::model()->count($criteria); // Returns integer, e.g.: 25

Insert list of values into SQL query

We didn't find a way to do this in more beautiful way. This just works:

$statuses = [1, 2];
$statusesString = "'" . implode("', '", $statuses) . "'";

$sql = "SELECT * FROM products WHERE 
    status IN ({$statusesString})
";
echo $sql; // SELECT * FROM products WHERE status IN ('1', '2')
$products = Products::model()->findAllBySql($sql);

exists()

Method returns true if record was found with defined search criteria.

$criteria = new CDbCriteria();
$criteria->addCondition('product_id = '. $this->productOpenId);
$result = ClaimsIndeces::model()->exists($criteria); // true

updateAll()

We can use this method to update several records that meet some condition:

$criteria = new CDbCriteria();
$criteria->addInCondition('id', [1, 5]);

// This query will update `status` field to `sold` for record's ID = 1, 5
Product::model()->updateAll(
    ['status' => 'sold'],
    $criteria
);

Also it's possible to update all rows without condition:

UniversityDegree::model()->updateAll([
    'for_partners' => 0,
    'for_students' => 0,
]);

updateByPk()

Method updates defined fields for one record with defined ID.

$res = Claims::model()->updateByPk(123, ['add_status_info' => 'Pending']);
var_dump($res); // 1

save()

Method allows create or update record.

// Example 1
$product = new Product();
$product->title = 'PHP Secrets';
$product->description = 'Latest secrets about php';
$product->save();

// Example 2
$product = new Product();
$product->attributes = [
  'title' => 'PHP Secrets',
  'description' => 'Latest secrets about php'
];
$product->save();

findByAttributes

$claimsInfo = ClaimsInfo::model()->findAllByAttributes(['mobile' => '79110001155']);
var_dump($claimsInfo); // object(ClaimsInfo)[934] ...

findAllByAttributes

$claimsInfo = ClaimsInfo::model()->findAllByAttributes(['mobile' => '79110001155']);
var_dump($claimsInfo); // array (size=11) ...

deleteAll

We can delete records of some model with limit and condition:

$criteria = new CDbCriteria();
$criteria->limit = 2;
$criteria->addCondition('create_time < :createTime');
$criteria->params[':createTime'] = '2018-04-22 00:00:00';

$apiLogDeleted = ApiLog::model()->deleteAll($criteria);
var_dump($apiLogDeleted); // (int) 2

join

$criteria = new CDbCriteria();
$criteria->addCondition('t.mobile = :mobile');

$criteria->join = 'LEFT JOIN claims c ON c.id = t.claim_id';

$criteria->addColumnCondition(['c.design_id' => 29]);
$criteria->limit = 1;

$criteria->params[':mobile'] = $mobile;

$claimIndex = ClaimsIndeces::model()->find($criteria);

It will execute SQL query:

SELECT `t`.`id`, `t`.`claim_id`, `t`.`product_id`, `t`.`form_id`
FROM `claims_indeces` `t`
LEFT JOIN claims c ON c.id = t.claim_id
WHERE 
	(t.mobile = '79221111111') 
	AND (c.design_id='29')
LIMIT 1

with

$criteria = new CDbCriteria();
$criteria->with = 'product'; // relation's name in `relations()` method of ProductsRkoOptions class

$tariffs = ProductsRkoOptions::model()->findAll($criteria);

$result = [];
foreach ($tariffs as $tariff) {
    $result[] = [
	'id' => $tariff->id,
	'name' => $tariff->product->name,
    ];
}

This code will create single SQL-query that will return records from both tables:

SELECT `t`.`id` AS `t0_c0`, `product`.`name` AS `t1_c3` 
FROM `products_rko_options` `t`
LEFT OUTER JOIN `products` `product` ON (`t`.`product_id`=`product`.`id`)

It will help us to fix n + 1 query problem.

Query Builder

The Yii Query Builder provides an object-oriented way of writing SQL statements. It allows developers to use class methods and properties to specify individual parts of a SQL statement.

See documentation.

delete()

$command = Yii::app()->db->createCommand();
$claim_ids = [545, 546];
$affectedRows = $command->delete('claims_calls', ['in', 'claim_id', $claim_ids] );
var_dump($affectedRows); // (int) 2

This code will execute SQL:

DELETE FROM `claims_calls` WHERE `claim_id` IN (545, 546)

Execute raw SQL query

Without data binding

$sql = "select * from products";
$rows = Yii::app()->db->createCommand($sql)->queryAll();

foreach($rows as $row) {
    echo $row['title'];
    // ...
}

With data binding

One value to bind:

$products = Yii::app()->db->createCommand('SELECT * FROM products WHERE status = :status')
    ->bindValue(':status', $status)
    ->queryAll();

Many values to bind:

$sql = 'SELECT * FROM products WHERE status = :status AND created_at > :created_at';
$products = Yii::app()->db->createCommand($sql)
    ->bindValues([
        ':status' => $status,
        ':created_at' => '2017-01-01'
    ])
    ->queryAll();

Query one row

$sql = 'SELECT * FROM products WHERE id = :id';
$row = Yii::app()->db->createCommand($sql)
    ->bindValue(':id', 4)
    ->queryRow();

var_dump($row['id']); // '4'

Execute raw DELETE query

To get a number of deleted rows we can use execute():

$sql = "DELETE FROM api_log WHERE create_time < :createTime LIMIT 2";
$command = Yii::app()->db->createCommand($sql)
    ->bindValues([
        ':createTime' => '2018-04-22 00:00:00'
    ]);
$affectedRows = $command->execute();
var_dump($affectedRows); // (int) 2

Get text of SQL query

Suppose we want to see SQL generated by this expression:

$criteria = new CDbCriteria();
$criteria->addCondition('t.product_id = :productId');
$criteria->params = [':productId' => 1];
$result = ClaimsIndeces::model()->findAll($criteria);

We can do this via this service method, that can be placed at protected/components/Services.php:

class Services
    // ...
    
    /**
     * Method returns text of SQL-query
     * @param CActiveRecord $model
     * @param CDbCriteria $criteria
     * @return string
     * Example:
     *      $criteria = new CDbCriteria();
     *      $criteria->addCondition('t.product_id = :productId');
     *      $criteria->params = [':productId' => 1];
     *      $result = ClaimsIndeces::model()->findAll($criteria);
     *
     *      echo Services::getSQL(ClaimsIndeces::model(), $criteria);
     *      // Returns: SELECT * FROM `claims_indeces` `t` WHERE t.product_id = 1
     */
    public static function getSQL($model, $criteria)
    {
        if (is_null($criteria->params)) {
            return null;
        }

        $_criteria = $model->getCommandBuilder()->createCriteria($criteria);
        $command = $model->getCommandBuilder()->createFindCommand($model->getTableSchema(), $_criteria);

        $sql = $command->getText();

        krsort($criteria->params);

        foreach ($criteria->params as $key => $value) {
            $sql = str_replace($key, "'$value'", $sql);
        }

        return $sql;
    }
}

Queries with join

Yii doc: query builder

$result = Yii::app()->db->createCommand()
  ->select('pps.*')
  ->from('products_point_sales pps')
  ->join('products_to_point_sales ptps', 'ptps.point_sales_id = pps.sap_id')
  ->join('products p', 'p.crm_id = ptps.product_id')
  ->where('p.alias = :alias', [':alias' => 'open'])
  // ->text  - this will show SQL query
  ->queryAll();

It returns array:

array (size=2)
  0 => 
    array (size=6)
      'id' => string '427' (length=3)
      'sap_id' => string '3' (length=1)
      'name' => string 'Первый' (length=27)
      'mvz' => string '6210' (length=4)
      'open_date' => string '0000-00-00 00:00:00' (length=19)
      'close_date' => string '0000-00-00 00:00:00' (length=19)
  1 => 
    array (size=6)
      'id' => string '1822' (length=4)
      'sap_id' => string '5' (length=1)
      'name' => string 'Второй' (length=33)
      'mvz' => string '6211' (length=4)
      'open_date' => string '0000-00-00 00:00:00' (length=19)
      'close_date' => string '0000-00-00 00:00:00' (length=19)

Query with distinct

$result = Yii::app()->db->createCommand()
    ->selectDistinct('code')
    ->from('gsm_operators')
    ->queryAll();

It returns query:

array (size=79)
  0 => 
    array (size=1)
      'code' => string '900' (length=3)
  1 => 
    array (size=1)
      'code' => string '901' (length=3)

Transaction

Sometimes we need to perform successfully more than one action.

$transaction = Yii::app()->db->beginTransaction();

try {
    // 1. Add product to cart
    // 2. Decrement amount of available products
    
    $transaction->commit();
} catch (Exception $e) {
    $transaction->rollback();
    // Error handling here...
}

Related models

Update attribute of related model

$claim = Claims::model()->findByPk(100);
$claim->info->email = 'hello@gmail.com';
$claim->info->save(); // New email will be saved
// $claim->save(); // Note: this won't save email in related model

Custom ActiveRecord class

We can add new methods to native Yii CActiveRecord class by creating protected/components/ActiveRecord.php:

class ActiveRecord extends CActiveRecord
{
    const SCENARIO_CREATE = 'create';
    const SCENARIO_UPDATE = 'update';
    const SCENARIO_SEARCH = 'search';

    /**
     * This method find record by attributes in database or initialize new one otherwise
     * @param array $attributes
     * @return array|mixed|null
     */
    public function findOrInitialize($attributes = array())
    {
        $object = $this->findByAttributes($attributes);

        if (!is_null($object)) {
            return $object;
        }

        $modelClassName = get_class($this);

        $object = new $modelClassName;
        $object->setAttributes($attributes, false);
        return $object;
    }

    public function findByAttributesOrFail($attributes, $condition = '', $params = array())
    {
        $object = parent::findByAttributes($attributes, $condition, $params);
        if (!$object) {
            throw new Exception('Item not found by attributes: ' . print_r($attributes, true));
        }
        return $object;
    }

    public function saveStrict($runValidation = true, $attributes = null)
    {
        if ($this->save($runValidation, $attributes)) {
            return true;
        } else {
            throw new Exception('Errors on saving: ' . print_r($this->getErrors(), true));
        }
    }
    
    /**
     * Method will return model's errors as string: "attr1: some error, attr2: some error 2"
     */
    public function getErrorsAsString($attribute = null)
    {
        $attributeErrors = array();
        foreach (parent::getErrors($attribute) as $key => $value) {
            $attributeErrors[] = $key . ': ' . $value[0];
        }
        return implode(', ', $attributeErrors);
    }
}

To make new methods available in our models we should extend their classes from this custom ActiveRecord class:

class Car extends ActiveRecord {
    // ...
}

Usage

findByAttributesOrFail()

$car = Car::model()->findByAttributesOrFail(['alias' => 'audi']);

findOrInitialize()

$car = Car::model()->findOrInitialize([
    'model' => 'audi',
    'year' => 2017
]);

saveStrict()

$car = new Car();
$car->alias = 'bmw';
$car->saveStrict();

getErrorsAsString()

$candidateTransferred = new CandidateTransferred();
echo $candidateTransferred->getErrorsAsString();
// "education_level: Education Level cannot be blank., school: School cannot be blank."

Yii 1 Controllers

Get action id / action name

class UniversityDegreeController extends Controller
{
    protected function beforeAction($action)
    {
        $actionId = $this->getAction()->getId();
        echo $actionId; // 'index' | 'update' | 'delete'
        
        return parent::beforeAction($action);
    }
}

Yii Migrations

Yii migration column types

  • pk: an auto-incremental primary key type, will be converted into “int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY”
  • string: will be converted into varchar(255)
  • text: will be converted into text
  • integer: will be converted into int(11)
  • boolean: will be converted into tinyint(1)
  • float: will be converted into float
  • decimal: will be converted into decimal
  • datetime: will be converted into datetime
  • timestamp: will be converted into timestamp
  • time: will be converted into time
  • date: will be converted into date
  • binary: will be converted into blob

Examples of migration commands

Create table

Contents of migration protected\migrations\m200428_054554_create_claims_import_calls.php:

class m200428_054554_create_claims_import_calls extends CDbMigration
{
    const TABLE = 'claims_import_calls';
    
    public function up()
    {
        $this->createTable(self::TABLE,
            [
                'id' => 'INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT',
                'call_center_id' => "INT(11) UNSIGNED NOT NULL COMMENT 'ID in call_centers table'",
                'crm_status' => "ENUM ('', 'E0004', 'E0005', 'E0006') NOT NULL COMMENT 'Status code in CRM'",
                'call_result_id' => "INT(11) UNSIGNED COMMENT 'ID in call_results table'",

                'call_started_at' => "TIMESTAMP NOT NULL COMMENT 'Call start time'",
                'call_finished_at' => "TIMESTAMP NOT NULL COMMENT 'Call finished time'",

                'cost' => "DECIMAL(6,2) COMMENT 'Cost of the call'",

                'create_time' => 'TIMESTAMP NOT NULL DEFAULT NOW()',
                'update_time' => 'TIMESTAMP NOT NULL DEFAULT NOW() ON UPDATE NOW()',
            ]
        );

        $this->execute('set FOREIGN_KEY_CHECKS=0;');

        $this->addForeignKey('fk_claims_import_calls_call_result_id',
            self::TABLE, 'call_result_id',
            'call_results', 'id',
	    'CASCADE', 'CASCADE'
        );

        $this->execute('set FOREIGN_KEY_CHECKS=1;');
    }

    public function down()
    {
        $this->dropForeignKey('fk_claims_import_calls_call_center_id', self::TABLE);
        $this->dropForeignKey('fk_claims_import_calls_call_result_id', self::TABLE);

        $this->dropTable(self::TABLE);
    }
}

Add column

$this->addColumn('claims_info', 'products_point_sales_id', 'INT(11) UNSIGNED AFTER city_name');
$this->addColumn('sms_templates', 'status', 'TINYINT(1) UNSIGNED NOT NULL DEFAULT 0 AFTER text');

Use model in migration

Way 1. Import file manually

File: /protected/migrations/m210622_124327_list_degrees_actualize.php:

Yii::import('application.models.*');

class m210622_124327_list_degrees_actualize extends CDbMigration
{
    public function up()
    {
        UniversityDegree::model()->deleteAllByAttributes(['type_id' => 1]);
    }
    // ...
}

Way 2. Configure console command to autoload classes

Add import section to app/protected/config/console.php. These lines will help to autoload classes for migrate command.

return array(
    'basePath' => dirname(__FILE__) . DIRECTORY_SEPARATOR . '..',
    'name' => 'My Console Application',

    // ...

    // autoloading model and component classes
    'import' => array(
        'application.models.*',
        'application.modules.*',
        'application.modules.questionnaires.models.*',
        'application.components.*',
	// ...
    ),
);

Yii - Run cron command from controller

class SystemController extends CController
{
  public function actionTestCron()
  {
    $cron5min = new PisendCommand('', '');
    // Our command expects an array of params
    $cron5min->run([9]);
  }
}

Yii - Run command from another command

(new SendSmsCommand('', ''))->actionIndex();

Yii - add create_time, update_time fields to model

Method 1

Do you want to track create time and update time for each record? Use Yii behaviors:

class MortgageClaim extends CActiveRecord
{
    public function behaviors()
    {
        return [
            'CTimestampBehavior' => [
                'class' => 'zii.behaviors.CTimestampBehavior',
                'createAttribute' => 'create_time',
                'updateAttribute' => 'update_time',
            ]
        ];
    }
}

Ensure that you have fields create_time, update_time. You can create them via migration:

// ...
$this->createTable(
    'mortgage_claims',
    [
        // ...
        'create_time' => 'timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP',
        'update_time' => 'timestamp NOT NULL'
    ]
);

Important: In some cases you could get an error:

include(CTimestampBehavior.php): failed to open stream: No such file or
directory (/var/www/html/vendor/yiisoft/yii/framework/YiiBase.php:427)
Stack trace:
#0 unknown(0): spl_autoload_call()
#1 /var/www/html/vendor/yiisoft/yii/framework/caching/CCache.php(108):
unserialize()

To fix error add this at config/main.php:

'import' => array(
  // ...
	'zii.behaviors.CTimestampBehavior',		
),

Method 2

Because of error in Method 1 we can implement desired behaviour manually:

class Product {
    protected function beforeSave()
    {
        if (!parent::beforeSave()) {
	    return false;
	}
	
	if ($this->isNewRecord) {
	    $this->create_time = date('Y-m-d H:i:s');
	    $this->create_ip   = Yii::app()->request->userHostAddress;
	}
	$this->update_time = date('Y-m-d H:i:s');
	$this->update_ip   = Yii::app()->request->userHostAddress;
	
	return true;
    }
}

Method 3

You can delegate this task to database. If you have MySQL >= 5.6.5, create fields with migration:

public function up() {
    $this->createTable(
        'claims_processed',
        [
	    'id' => 'pk',
	    // ...
	    'create_time' => 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP',
	    'update_time' => 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP',
        ]
    );
}

Important: Be careful! If you use this method 3 you have to mention names of attributes that you want to update:

$claim = Claim::model()->findByAttributes(['claim_id' => 123]);
$claim->status = 'SUCCESSFUL';
$claim->status_info = 'Processed';

// Won't work! This will NOT update `update_time` field
$claim->save();

// OK. This will update `update_time` field
$claim->save(true, ['status', 'status_info']);

Show SQL query in browser

If you want to see all SQL queries in web browser, add this code to config file protected/config/main.php:

// ...
'components' => [
    'log' => [
        'class'  => 'CLogRouter',
        'routes' => [
	    ['class' => 'CWebLogRoute'],
	    // or
	    // ['class'  => 'CProfileLogRoute'],
        ]
    ]
]

Log SQL queries in a file

Edit config file protected/config/main.php:

'log' => array(
    'class' => 'CLogRouter',
    'routes' => array(
	array(
	    'class' => 'CFileLogRoute',
	    'levels' => 'trace',
	    'categories' => 'system.db.*',
	    'logFile' => 'sql.log',
	),
    ),
),

All SQL will be saved to the file protected/runtime/sql.log.

If you want to log binded params edit the same config file:

'db' => array(
    // ...
    'enableParamLogging' => true,
);

Add Gii generator

'modules' => [
    'gii' => [
        'class' => 'system.gii.GiiModule',
        'password' => '123',
        'ipFilters' => ['*'],
        // 'newFileMode' => 0666,
        // 'newDirMode' => 0777,
    ],
],

Yii 1 useful plugins

Yii 1 datetime picker

See CJuiDateTimePicker plugin page.

Download the code of this plugin and paste it to protected/extensions/CJuiDateTimePicker folder of your project.

Example:

Yii::import('application.extensions.CJuiDateTimePicker.CJuiDateTimePicker');

$this->widget('CJuiDateTimePicker',array(
    'model' => $model, // Model object
    'attribute' => 'publication_time', // Attribute name
    'mode' => 'datetime', // Use "time","date" or "datetime" (default)
    'options' => [
        'minDate' => 0,
        'timeText' => 'Время',
        'hourText' => 'Часы',
        'minuteText' => 'Минуты',
        'showButtonPanel' => false,
        'timeFormat' => 'hh:mm',
        'dateFormat' => "yy-mm-dd",
    ] // jquery plugin options
));

Yii::app methods

Request parameters

Retrieve GET parameter

Let's suppose that we visited page: http://site.com/?name=johny. We can get param name via this method:

echo Yii::app()->request->getParam('name'); // 'johny'

// Returns null if param doesn't exist
echo Yii::app()->request->getParam('name2'); // null

// We can define default param value
echo Yii::app()->request->getParam('name2', 'kenny'); // 'kenny'

Get URL for the page

Yii::app()->request->getUrl(); // '/open?form=new&design=newaccess&ldg=banks'

Yii::app()->request->getRequestUri(); // '/app/index.php?r=universityHome/view&id=2211'

$params = $this->getActionParams();
$params['format'] = 'pdf';
Yii::app()->createUrl($this->route, $params); // '/app/index.php?r=universityHome/view&id=2211&format=pdf'

Get domain name

Yii::app()->getBaseUrl(true); // http://hello.com

Get path of directories

Yii::app()->getRuntimePath(); // /var/www/html/sales/protected/runtime

Yii::app()->getBasePath(); // /var/www/html/sales/protected

Yii::app()->getModulePath(); // /var/www/html/sales/protected/modules

Get absolute URL for the page

We can use this at the Yii view:

Yii::app()->createAbsoluteUrl($this->getRoute(), $this->getActionParams());
// Output: "http://127.0.0.1:8090/app/index.php?r=universityHome/view&id=2211"

Yii Models

Model relations

has_one

class Candidate extends CActiveRecord
{
    public function relations()
    {
        return [
            'candidatePayment' => array(self::HAS_ONE, 'CandidatePayment', 'candidate_id'),
        ];
    }
}

belongs_to

/*
 * The followings are the available columns in table 'candidate_insurances':
 * @property integer $candidate_id
 * @property integer $insurance_type_id
 * @property string $start_date
 * @property string $end_date
 */
class CandidateInsurance extends CActiveRecord
{
    public function relations()
    {
        return array(
            'candidate' => array(self::BELONGS_TO, 'Candidate', 'candidate_id'),
            'insuranceType' => array(self::BELONGS_TO, 'InsuranceType', 'insurance_type_id'),
        );
    }
}

has_many

class Claims extends CActiveRecord
{
    public function relations()
    {
        return [
            'smsCodes' => [self::HAS_MANY, ClaimsSmsCode::class, 'claim_id'],
        ];
    }
}

many_many

class Partner extends CActiveRecord
{
    public function relations()
    {
        return array(
            'universities' => array(self::MANY_MANY, 'University', 'universities_partners(partner_id, university_id)'),
        );
    }
}

Yii 1 Upload file

Model:

class UniversityModel extends CActiveRecord
{
    public function rules()
    {
        return [
            [
                'current_agreement_file',
                'file',
                'allowEmpty' => true,
                'types' => 'pdf,doc,docx',
            ],
        ];
    }
    
    public function afterSave()
    {
        if (!is_null($this->current_agreement_file)) {
            $fileName = 'agreement-' . $this->getPrimaryKey() . '.' . $this->current_agreement_file->getExtensionName();
            $this->logo_file->saveAs(Yii::getPathOfAlias('webroot') . '/uploads/' . $fileName);
        }

        parent::afterSave();
    }
}

Controller:

class UniversityController extends Controller
{
    public function actionUpdate($id)
    {
        $model = new UniversityModel('update');
        
        if (isset($_POST['UniversityModel'])) {
            $attributesToSave = $model->getAttributes();
            
            $model->attributes = $_POST['UniversityModel'];
            
            $model->current_agreement_file = CUploadedFile::getInstance($model,'current_agreement_file');
            
            // If user didn't upload new file we need to prevent clearing of the existing file
            if (empty($model->current_agreement_file)) {
                unset($attributesToSave['current_agreement_file']);
            }
            
            if ($model->save(true, array_keys($attributesToSave))) {
                Yii::app()->user->setFlash('success', 'The information has been successfully saved!');
                
                $this->redirect(CHtml::normalizeUrl(['university/update', 'id' => $model->getPrimaryKey()]));
            }
        }
        
        $this->render('update', ['model' => $model]);
    }
}

Yii 1 validators

Yii has many built in system validators.

Yii Length validator

public function rules()
{
    return [
        ['code', 'length', 'is' => 13],
        ['last_name', 'length', 'min' => 2, 'tooShort' => 'Last name should be more than 2 chars'],
        ['office_id', 'length', 'max' => 100, 'tooLong' => 'Office Id cannot be more than 100'],
    ];
}

Yii 1 date validator

See date validation docs.

Example of usage:

public function rules()
{
    return [
        ['published_on', 'date', 'dateFormat' => 'dd.MM.yyyy'],
    ];
}

How to create Yii custom dateValidator

Important: Don't reinvent the weel, because Yii 1 has built-in date validator.

If you want to write your own custom date validator you can create file at protected/components/validators/ folder. This validator uses DateTime::createFromFormat function, that returns false on failure.

class dateValidator extends CValidator
{
    public $dateFormat = 'd.m.Y';
    
    public $message = 'Invalid date format';

    protected function validateAttribute($object, $attribute)
    {
        // Let another validator to check empty value
        if (empty($object->$attribute)) {
            return;
        }

        $dateString = $object->$attribute;

        $date = DateTime::createFromFormat($this->dateFormat, $dateString);

        if ($date === false) {
            $this->addError($object, $attribute, $this->message);
        }
    }
}

To use this validator, just add this line to your rules() method:

    public function rules()
    {
        return [
            // By default 'dateFormat' param has value 'd.m.Y'
            ['published_on', 'dateValidator', 'dateFormat' => 'Y-m-d'],
        ];
    }

Yii Views

Form fields

dropDownListRow

This method allows to display <select> tag:

<?php
$products = CHtml::listData(Product::model()->findAll(), 'id', 'title');
echo $form->dropDownListRow($model, 'product_id', $products, [
    'empty' => 'Select product',
]);
?>

dropDownList

<?php 
$season = 'Fall (August)';

echo CHtml::dropDownList('season', $season, [
    'Spring (January)' => 'Spring 2023',
    'Fall (August)' => 'Fall 2023',
]); ?>

This code will generate HTML:

<select name="season" id="season">
    <option value="Spring (January)">Spring 2023</option>
    <option value="Fall (August)" selected="selected">Fall 2023</option>
</select>

Add CSS file, add javascript file

You can publish js, css files from your folder to assets directory:

$assetsPath = Yii::app()->getAssetManager()->publish(__DIR__ . '/assets/', false, -1, YII_DEBUG);
Yii::app()->clientScript->registerCssFile($assetsPath . '/css/anti-mite-widget.css');
Yii::app()->clientScript->registerScriptFile($assetsPath . '/js/scripts.js');

Link for page

<?php echo CHtml::link('Update', ['/admin/reports/update', 'id' => $model->id]) ?>
// HTML: <a href="/admin/reports/update?id=1">Update</a>

Render Partial

We can place the fragment of HTML in a file (they call it partial) and then render this file on different pages.

File: protected/views/partnerHomeUniversity/view.php:

// Render partial 'protected/views/partnerHomeUniversity/_insurance_options.php'
$this->renderPartial('_insurance_options', ['name' => 'Kenny']);

Encode output

Don't forget to encode output in Yii views for safety:

<?php echo CHtml::encode($model->title); ?>
// In the DB: '<script>alert(1)</script>'
// On the web page: &lt;script&gt;alert(1)&lt;/script&gt;

zii.widgets.CDetailView

// some/view.php
$this->widget('zii.widgets.CDetailView', [
    'data' => $model,
    'attributes' => [
        'id',
        [
            'label' => 'Claims per day, max',
            'name' => 'daily_limit',
        ],
        [
            'name' => 'Claims per day, fact',
            'value' => function ($data) {
                return SomeService::getValue($data->id);
            }
        ],
        [
            'name' => 'password',
            'value' => function ($data) {
                return ($data->password_hash) ? 'Has saved password' : 'No password';
            }
        ],
        [
            'name' => 'status',
            'value' => CallCenters::$STATUSES[$model->status],
        ],
        'created_at',
    ],
]);

Grid View

View:

<?php $this->widget(
    'zii.widgets.grid.CGridView',
    array(
        'dataProvider' => $dataProvider,
        'columns' => array(
            array(
                'header' => 'University',
                'name' => 'university.company',
            ),
            array(
                'name' => 'urgent',
                'header' => 'Urgent',
                'value' => function ($data) {
                    return $data->urgent ? '<div class="badge badge-danger">Urgent</div>' : 'No';
                },
                'htmlOptions' => array('class' => 'column-urgent'),
                'type' => 'raw'
            ),
            'created_at',
        ),
        'itemsCssClass' => 'table-class',
        'htmlOptions' => array(
            'class' => 'example',
        ),
    )
); ?>

This will create HTML:

<div class="example">
    <table class="table-class">
        
    </table>
</div>`

Controller:

```php
class UniversityChangesController extends Controller
{
    public function actionIndex()
    {
        // ...
        $criteria = new CDbCriteria();
        $criteria->addInCondition('t.university_id', $universitiesIds);
        $criteria->order = 't.id DESC';

        $models = UniversityChange::model()->findAll($criteria);

        $dataProvider = new CArrayDataProvider(
            $models, array(
                'pagination' => array(
                    'pageSize' => 50,
                )
            )
        );

        $this->render('index', array('dataProvider' => $dataProvider));
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment