// 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);
$criteria = new CDbCriteria();
$criteria->addCondition('idpartner = :partnerId');
$criteria->params[':partnerId'] = 123;
Partner::model()->findAll($criteria);
// Example 1
$partners = Partner::model()->findAll(array('select' => 'title'));
// Example 2
$criteria = new CDbCriteria();
$criteria->select = 'title';
$partners = Partner::model()->findAll($criteria);
$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
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',
]);
$criteria = new CDbCriteria();
$criteria->addColumnCondition([
'user_id' => $user->id,
]);
$count = (int)Posts::model()->count($criteria); // Returns integer, e.g.: 25
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);
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
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,
]);
Method updates defined fields for one record with defined ID.
$res = Claims::model()->updateByPk(123, ['add_status_info' => 'Pending']);
var_dump($res); // 1
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();
$claimsInfo = ClaimsInfo::model()->findAllByAttributes(['mobile' => '79110001155']);
var_dump($claimsInfo); // object(ClaimsInfo)[934] ...
$claimsInfo = ClaimsInfo::model()->findAllByAttributes(['mobile' => '79110001155']);
var_dump($claimsInfo); // array (size=11) ...
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
$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
$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.
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.
$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)
$sql = "select * from products";
$rows = Yii::app()->db->createCommand($sql)->queryAll();
foreach($rows as $row) {
echo $row['title'];
// ...
}
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();
$sql = 'SELECT * FROM products WHERE id = :id';
$row = Yii::app()->db->createCommand($sql)
->bindValue(':id', 4)
->queryRow();
var_dump($row['id']); // '4'
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
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;
}
}
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)
$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)
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...
}
$claim = Claims::model()->findByPk(100);
$claim->info->email = '[email protected]';
$claim->info->save(); // New email will be saved
// $claim->save(); // Note: this won't save email in related model
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 {
// ...
}
$car = Car::model()->findByAttributesOrFail(['alias' => 'audi']);
$car = Car::model()->findOrInitialize([
'model' => 'audi',
'year' => 2017
]);
$car = new Car();
$car->alias = 'bmw';
$car->saveStrict();
$candidateTransferred = new CandidateTransferred();
echo $candidateTransferred->getErrorsAsString();
// "education_level: Education Level cannot be blank., school: School cannot be blank."