Browse Source

Added init and afterFind events to AR.

Implemented scope support.
tags/2.0.0-beta
Qiang Xue 12 years ago
parent
commit
6dd495b65f
  1. 84
      docs/api/db/ActiveRecord.md
  2. 5
      framework/base/Object.php
  3. 1
      framework/db/ActiveQuery.php
  4. 27
      framework/db/ActiveRecord.php
  5. 16
      framework/db/Schema.php
  6. 2
      framework/db/mysql/Schema.php

84
docs/api/db/ActiveRecord.md

@ -363,4 +363,86 @@ value of `$customer` and then call [[save()]] to save the order into database.
### Data Input and Validation ### Data Input and Validation
// todo TBD
### Life Cycles of an ActiveRecord Object
An ActiveRecord object undergoes different life cycles when it is used in different cases.
Subclasses or ActiveRecord behaviors may "inject" custom code in these life cycles through
method overriding and event handling mechanisms.
When instantiating a new ActiveRecord instance, we will have the following life cycles:
1. constructor
2. [[init()]]: will trigger an [[init]] event
When getting an ActiveRecord instance through the [[find()]] method, we will have the following life cycles:
1. constructor
2. [[init()]]: will trigger an [[init]] event
3. [[afterFind()]]: will trigger an [[afterFind]] event
When calling [[save()]] to insert or update an ActiveRecord, we will have the following life cycles:
1. [[beforeValidate()]]: will trigger an [[beforeValidate]] event
2. [[beforeSave()]]: will trigger an [[beforeSave]] event
3. perform the actual data insertion or updating
4. [[afterSave()]]: will trigger an [[afterSave]] event
5. [[afterValidate()]]: will trigger an [[afterValidate]] event
Finally when calling [[delete()]] to delete an ActiveRecord, we will have the following life cycles:
1. [[beforeDelete()]]: will trigger an [[beforeDelete]] event
2. perform the actual data deletion
3. [[afterDelete()]]: will trigger an [[afterDelete]] event
### Scopes
A scope is a method that customizes a given [[ActiveQuery]] object. Scope methods are defined
in the ActiveRecord classes. They can be invoked through the [[ActiveQuery]] object that is created
via [[find()]] or [[findBySql()]]. The following is an example:
~~~
class Customer extends \yii\db\ActiveRecord
{
// ...
/**
* @param ActiveQuery $query
*/
public function active($query)
{
$query->andWhere('status = 1');
}
}
$customers = Customer::find()->active()->all();
~~~
In the above, the `active()` method is defined in `Customer` while we are calling it
through `ActiveQuery` returned by `Customer::find()`.
Scopes can be parameterized. For example, we can define and use the following `olderThan` scope:
~~~
class Customer extends \yii\db\ActiveRecord
{
// ...
/**
* @param ActiveQuery $query
* @param integer $age
*/
public function olderThan($query, $age = 30)
{
$query->andWhere('age > :age', array(':age' => $age));
}
}
$customers = Customer::find()->olderThan(50)->all();
~~~
The parameters should follow after the `$query` parameter when defining the scope method, and they
can take default values like shown above.

5
framework/base/Object.php

@ -78,7 +78,8 @@ class Object
* will be implicitly called when executing `$object->property = $value;`. * will be implicitly called when executing `$object->property = $value;`.
* @param string $name the property name or the event name * @param string $name the property name or the event name
* @param mixed $value the property value * @param mixed $value the property value
* @throws UnknownPropertyException if the property is not defined or read-only. * @throws UnknownPropertyException if the property is not defined
* @throws InvalidCallException if the property is read-only.
* @see __get * @see __get
*/ */
public function __set($name, $value) public function __set($name, $value)
@ -122,7 +123,7 @@ class Object
* Note that if the property is not defined, this method will do nothing. * Note that if the property is not defined, this method will do nothing.
* If the property is read-only, it will throw an exception. * If the property is read-only, it will throw an exception.
* @param string $name the property name * @param string $name the property name
* @throws UnknownPropertyException if the property is read only. * @throws InvalidCallException if the property is read only.
*/ */
public function __unset($name) public function __unset($name)
{ {

1
framework/db/ActiveQuery.php

@ -14,7 +14,6 @@ use yii\db\Connection;
use yii\db\Command; use yii\db\Command;
use yii\db\QueryBuilder; use yii\db\QueryBuilder;
use yii\db\Expression; use yii\db\Expression;
use yii\db\Exception;
/** /**
* ActiveQuery represents a DB query associated with an Active Record class. * ActiveQuery represents a DB query associated with an Active Record class.

27
framework/db/ActiveRecord.php

@ -32,6 +32,8 @@ use yii\util\StringHelper;
* @property mixed $primaryKey the primary key value. * @property mixed $primaryKey the primary key value.
* @property mixed $oldPrimaryKey the old primary key value. * @property mixed $oldPrimaryKey the old primary key value.
* *
* @event ModelEvent init an event that is triggered when the record is initialized (in [[init()]]).
* @event ModelEvent afterFind an event that is triggered after the record is created and populated with query result.
* @event ModelEvent beforeInsert an event that is triggered before inserting a record. * @event ModelEvent beforeInsert an event that is triggered before inserting a record.
* You may set [[ModelEvent::isValid]] to be false to stop the insertion. * You may set [[ModelEvent::isValid]] to be false to stop the insertion.
* @event Event afterInsert an event that is triggered before inserting a record. * @event Event afterInsert an event that is triggered before inserting a record.
@ -762,6 +764,30 @@ class ActiveRecord extends Model
} }
/** /**
* Initializes the object.
* This method is called at the end of the constructor.
* The default implementation will trigger an [[afterInsert]] event.
* If you override this method, make sure you call the parent implementation at the end
* to ensure triggering of the event.
*/
public function init()
{
parent::init();
$this->trigger('init');
}
/**
* This method is called when the AR object is created and populated with the query result.
* The default implementation will trigger an [[afterFind]] event.
* When overriding this method, make sure you call the parent implementation to ensure the
* event is triggered.
*/
public function afterFind()
{
$this->trigger('afterFind');
}
/**
* Sets the value indicating whether the record is new. * Sets the value indicating whether the record is new.
* @param boolean $value whether the record is new and should be inserted when calling [[save()]]. * @param boolean $value whether the record is new and should be inserted when calling [[save()]].
* @see getIsNewRecord * @see getIsNewRecord
@ -956,6 +982,7 @@ class ActiveRecord extends Model
} }
} }
$record->_oldAttributes = $record->_attributes; $record->_oldAttributes = $record->_attributes;
$record->afterFind();
return $record; return $record;
} }

16
framework/db/Schema.php

@ -329,10 +329,10 @@ abstract class Schema extends \yii\base\Object
/** /**
* Extracts the PHP type from abstract DB type. * Extracts the PHP type from abstract DB type.
* @param string $type abstract DB type * @param ColumnSchema $column the column schema information
* @return string PHP type name * @return string PHP type name
*/ */
protected function getColumnPhpType($type) protected function getColumnPhpType($column)
{ {
static $typeMap = array( // abstract type => php type static $typeMap = array( // abstract type => php type
'smallint' => 'integer', 'smallint' => 'integer',
@ -341,13 +341,13 @@ abstract class Schema extends \yii\base\Object
'boolean' => 'boolean', 'boolean' => 'boolean',
'float' => 'double', 'float' => 'double',
); );
if (isset($typeMap[$type])) { if (isset($typeMap[$column->type])) {
if ($type === 'bigint') { if ($column->type === 'bigint') {
return PHP_INT_SIZE == 8 && !$this->unsigned ? 'integer' : 'string'; return PHP_INT_SIZE == 8 && !$column->unsigned ? 'integer' : 'string';
} elseif ($type === 'integer') { } elseif ($column->type === 'integer') {
return PHP_INT_SIZE == 4 && $this->unsigned ? 'string' : 'integer'; return PHP_INT_SIZE == 4 && $column->unsigned ? 'string' : 'integer';
} else { } else {
return $typeMap[$this->type]; return $typeMap[$column->type];
} }
} else { } else {
return 'string'; return 'string';

2
framework/db/mysql/Schema.php

@ -165,7 +165,7 @@ class Schema extends \yii\db\Schema
} }
} }
$column->phpType = $this->getColumnPhpType($column->type); $column->phpType = $this->getColumnPhpType($column);
if ($column->type !== 'timestamp' || $info['Default'] !== 'CURRENT_TIMESTAMP') { if ($column->type !== 'timestamp' || $info['Default'] !== 'CURRENT_TIMESTAMP') {
$column->defaultValue = $column->typecast($info['Default']); $column->defaultValue = $column->typecast($info['Default']);

Loading…
Cancel
Save