diff --git a/docs/internals/ar.md b/docs/internals/ar.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/internals/database.md b/docs/internals/database.md new file mode 100644 index 0000000..e69de29 diff --git a/framework/base/Model.php b/framework/base/Model.php index 1571ae4..8756b5c 100644 --- a/framework/base/Model.php +++ b/framework/base/Model.php @@ -119,7 +119,7 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess } /** - * Returns a list of scenarios and the corresponding relevant attributes. + * Returns a list of scenarios and the corresponding active attributes. * The returned array should be in the following format: * * ~~~ @@ -130,27 +130,14 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess * ) * ~~~ * - * Attributes relevant to the current scenario are considered safe and can be - * massively assigned. When [[validate()]] is invoked, these attributes will - * be validated using the rules declared in [[rules()]]. - * * If an attribute should NOT be massively assigned (thus considered unsafe), * please prefix the attribute with an exclamation character (e.g. '!attribute'). * - * WARNING: The default implementation returns the 'default' scenario and the result of - * [[attributes()]]. This means if the model is in 'default' scenario, all - * public member variables can be massively assigned and will be validated when - * calling [[validate()]]. Make sure you override this method if you do not want - * this behavior (e.g. you only want some of the attributes to be massively assigned - * and validated.) - * * @return array a list of scenarios and the corresponding relevant attributes. */ public function scenarios() { - return array( - 'default' => $this->attributes(), - ); + return array(); } /** @@ -354,8 +341,7 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess */ public function isAttributeSafe($attribute) { - $scenarios = $this->scenarios(); - return in_array($attribute, $scenarios[$this->getScenario()], true); + return in_array($attribute, $this->safeAttributes(), true); } /** @@ -481,22 +467,20 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess * @param array $names list of attributes whose value needs to be returned. * Defaults to null, meaning all attributes listed in [[attributes()]] will be returned. * If it is an array, only the attributes in the array will be returned. + * @param array $except list of attributes whose value should NOT be returned. * @return array attribute values (name=>value). */ - public function getAttributes($names = null) + public function getAttributes($names = null, $except = array()) { $values = array(); - - if (is_array($names)) { - foreach ($this->attributes() as $name) { - if (in_array($name, $names, true)) { - $values[$name] = $this->$name; - } - } - } else { - foreach ($this->attributes() as $name) { - $values[$name] = $this->$name; - } + if ($names === null) { + $names = $this->attributes(); + } + foreach ($names as $name) { + $values[$name] = $this->$name; + } + foreach ($except as $name) { + unset($values[$name]); } return $values; @@ -567,14 +551,19 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess */ public function safeAttributes() { + $scenario = $this->getScenario(); $scenarios = $this->scenarios(); - $attributes = array(); - foreach ($scenarios[$this->getScenario()] as $attribute) { - if ($attribute[0] !== '!') { - $attributes[] = $attribute; + if (isset($scenarios[$scenario])) { + $attributes = array(); + foreach ($scenarios[$scenario] as $attribute) { + if ($attribute[0] !== '!') { + $attributes[] = $attribute; + } } + return $attributes; + } else { + return $this->activeAttributes(); } - return $attributes; } /** @@ -583,11 +572,23 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess */ public function activeAttributes() { + $scenario = $this->getScenario(); $scenarios = $this->scenarios(); - $attributes = $scenarios[$this->getScenario()]; - foreach ($attributes as $i => $attribute) { - if ($attribute[0] === '!') { - $attributes[$i] = substr($attribute, 1); + if (isset($scenarios[$scenario])) { + // scenario declared in scenarios() + $attributes = $scenarios[$this->getScenario()]; + foreach ($attributes as $i => $attribute) { + if ($attribute[0] === '!') { + $attributes[$i] = substr($attribute, 1); + } + } + } else { + // use validators to determine active attributes + $attributes = array(); + foreach ($this->attributes() as $attribute) { + if ($this->getActiveValidators($attribue) !== array()) { + $attributes[] = $attribute; + } } } return $attributes; diff --git a/framework/db/ar/ActiveRecord.php b/framework/db/ar/ActiveRecord.php index 3691571..b3b69e4 100644 --- a/framework/db/ar/ActiveRecord.php +++ b/framework/db/ar/ActiveRecord.php @@ -44,6 +44,10 @@ use yii\util\StringHelper; abstract class ActiveRecord extends Model { /** + * @var ActiveRecord[] global model instances indexed by model class names + */ + private static $_models = array(); + /** * @var array attribute values indexed by attribute names */ private $_attributes = array(); @@ -56,6 +60,21 @@ abstract class ActiveRecord extends Model */ private $_related; + + /** + * Returns a model instance to support accessing non-static methods such as [[table()]], [[primaryKey()]]. + * @return ActiveRecord + */ + public static function model() + { + $className = get_called_class(); + if (isset(self::$_models[$className])) { + return self::$_models[$className]; + } else { + return self::$_models[$className] = new static; + } + } + /** * Returns the metadata for this AR class. * @param boolean $refresh whether to rebuild the metadata. @@ -194,7 +213,7 @@ abstract class ActiveRecord extends Model public static function updateAll($attributes, $condition = '', $params = array()) { $query = new Query; - $query->update(static::tableName(), $attributes, $condition, $params); + $query->update(static::model()->tableName(), $attributes, $condition, $params); return $query->createCommand(static::getDbConnection())->execute(); } @@ -215,7 +234,7 @@ abstract class ActiveRecord extends Model $counters[$name] = new Expression($value >= 0 ? "$quotedName+$value" : "$quotedName$value"); } $query = new Query; - $query->update(static::tableName(), $counters, $condition, $params); + $query->update(static::model()->tableName(), $counters, $condition, $params); return $query->createCommand($db)->execute(); } @@ -229,7 +248,7 @@ abstract class ActiveRecord extends Model public static function deleteAll($condition = '', $params = array()) { $query = new Query; - $query->delete(static::tableName(), $condition, $params); + $query->delete(static::model()->tableName(), $condition, $params); return $query->createCommand(static::getDbConnection())->execute(); } @@ -250,9 +269,9 @@ abstract class ActiveRecord extends Model * You may override this method if the table is not named after this convention. * @return string the table name */ - public static function tableName() + public function tableName() { - return StringHelper::camel2id(basename(get_called_class()), '_'); + return StringHelper::camel2id(basename(get_class($this)), '_'); } /** @@ -266,7 +285,7 @@ abstract class ActiveRecord extends Model * If the key is a composite one consisting of several columns, it should * return the array of the key column names. */ - public static function primaryKey() + public function primaryKey() { } @@ -633,27 +652,6 @@ abstract class ActiveRecord extends Model $this->_attributes[$name] = $value; } - /** - * Returns all column attribute values. - * Note, related objects are not returned. - * @param null|array $names names of attributes whose value needs to be returned. - * If this is true (default), then all attribute values will be returned, including - * those that are not loaded from DB (null will be returned for those attributes). - * If this is null, all attributes except those that are not loaded from DB will be returned. - * @return array attribute values indexed by attribute names. - */ - public function getAttributes($names = null) - { - if ($names === null) { - $names = $this->attributes(); - } - $values = array(); - foreach ($names as $name) { - $values[$name] = isset($this->_attributes[$name]) ? $this->_attributes[$name] : null; - } - return $values; - } - public function getChangedAttributes($names = null) { if ($names === null) { diff --git a/framework/db/dao/Query.php b/framework/db/dao/Query.php index 862154c..0769f92 100644 --- a/framework/db/dao/Query.php +++ b/framework/db/dao/Query.php @@ -75,6 +75,7 @@ class Query extends BaseQuery $qb->query = $this; return call_user_func_array(array($qb, $method), $params); } else { + /** @var $qb QueryBuilder */ return $qb->build($this); } }