From 9beba31fc00da9d22e4dedcf60cdca3310f5d3a9 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 28 Feb 2014 08:34:51 -0500 Subject: [PATCH] rest WIP --- framework/base/Model.php | 122 +++++++++++++++++++++++++- framework/db/BaseActiveRecord.php | 24 ++++- framework/helpers/BaseArrayHelper.php | 51 ++++++----- 3 files changed, 171 insertions(+), 26 deletions(-) diff --git a/framework/base/Model.php b/framework/base/Model.php index 8ec6e400a7..5cb35b86ba 100644 --- a/framework/base/Model.php +++ b/framework/base/Model.php @@ -13,6 +13,7 @@ use ArrayObject; use ArrayIterator; use ReflectionClass; use IteratorAggregate; +use yii\helpers\ArrayHelper; use yii\helpers\Inflector; use yii\validators\RequiredValidator; use yii\validators\Validator; @@ -788,14 +789,131 @@ class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayab return $valid; } + /** + * Returns the list of fields that should be returned by default by [[toArray()]] when no specific fields are specified. + * + * A field is a named element in the returned array by [[toArray()]]. + * + * This method should return an array of field names or field definitions. + * If the former, the field name will be treated as an object property name whose value will be used + * as the field value. If the latter, the array key should be the field name while the array value should be + * the corresponding field definition which can be either an object property name or a PHP callable + * returning the corresponding field value. The signature of the callable should be: + * + * ```php + * function ($field, $model) { + * // return field value + * } + * ``` + * + * For example, the following code declares four fields: + * + * - `email`: the field name is the same as the property name `email`; + * - `firstName` and `lastName`: the field names are `firstName` and `lastName`, and their + * values are obtained from the `first_name` and `last_name` properties; + * - `fullName`: the field name is `fullName`. Its value is obtained by concatenating `first_name` + * and `last_name`. + * + * ```php + * return [ + * 'email', + * 'firstName' => 'first_name', + * 'lastName' => 'last_name', + * 'fullName' => function () { + * return $this->first_name . ' ' . $this->last_name; + * }, + * ]; + * ``` + * + * In this method, you may also want to return different lists of fields based on some context + * information. For example, depending on privilege of the current application user, you may return different + * sets of visible fields. + * + * The default implementation of this method returns [[attributes()]] indexed by the same attribute names. + * + * @return array the list of field names or field definitions. + * @see toArray() + */ + public function fields() + { + $fields = $this->attributes(); + return array_combine($fields, $fields); + } + + /** + * Returns the list of fields that can be expanded further and returned by [[toArray()]]. + * + * This method is similar to [[fields()]] except that the list of fields returned + * by this method are not returned by default by [[toArray()]]. Only when field names + * to be expanded are explicitly specified when calling [[toArray()]], will their values + * be exported. + * + * The default implementation returns an empty array. + * + * @return array the list of expandable field names or field definitions. Please refer + * to [[fields()]] on the format of the return value. + * @see toArray() + * @see fields() + */ + public function expandableFields() + { + return []; + } + /** * Converts the object into an array. * The default implementation will return [[attributes]]. + * @param array $fields the fields being requested. If empty, all fields as specified by [[fields()]] will be returned. + * @param array $expand the additional fields being requested for exporting. Only fields declared in [[expandableFields()]] + * will be considered. + * @param boolean $recursive whether to recursively return array representation of embedded objects. * @return array the array representation of the object */ - public function toArray() + public function toArray(array $fields = [], array $expand = [], $recursive = true) { - return $this->getAttributes(); + $data = []; + foreach ($this->resolveFields($fields, $expand) as $field => $definition) { + $data[$field] = is_string($definition) ? $this->$definition : call_user_func($definition, $field, $this); + } + return $recursive ? ArrayHelper::toArray($data) : $data; + } + + /** + * Determines which fields can be returned by [[toArray()]]. + * This method will check the requested fields against those declared in [[fields()]] and [[expandableFields()]] + * to determine which fields can be returned. + * @param array $fields the fields being requested for exporting + * @param array $expand the additional fields being requested for exporting + * @return array the list of fields to be exported. The array keys are the field names, and the array values + * are the corresponding object property names or PHP callables returning the field values. + */ + protected function resolveFields(array $fields, array $expand) + { + $result = []; + + foreach ($this->fields() as $field => $definition) { + if (is_integer($field)) { + $field = $definition; + } + if (empty($fields) || in_array($field, $fields, true)) { + $result[$field] = $definition; + } + } + + if (empty($expand)) { + return $result; + } + + foreach ($this->expandableFields() as $field => $definition) { + if (is_integer($field)) { + $field = $definition; + } + if (in_array($field, $expand, true)) { + $result[$field] = $definition; + } + } + + return $result; } /** diff --git a/framework/db/BaseActiveRecord.php b/framework/db/BaseActiveRecord.php index 4e49a8ff1f..0fcf3cdcd9 100644 --- a/framework/db/BaseActiveRecord.php +++ b/framework/db/BaseActiveRecord.php @@ -19,8 +19,6 @@ use yii\base\InvalidCallException; /** * ActiveRecord is the base class for classes representing relational data in terms of objects. * - * @include @yii/db/ActiveRecord.md - * * @property array $dirtyAttributes The changed attribute values (name-value pairs). This property is * read-only. * @property boolean $isNewRecord Whether the record is new and should be inserted when calling [[save()]]. @@ -1346,4 +1344,26 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface return $this->generateAttributeLabel($attribute); } + + /** + * @inheritdoc + * + * The default implementation returns the names of the columns whose values have been populated into this record. + */ + public function fields() + { + $fields = array_keys($this->_attributes); + return array_combine($fields, $fields); + } + + /** + * @inheritdoc + * + * The default implementation returns the names of the relations that have been populated into this record. + */ + public function expandableFields() + { + $fields = array_keys($this->getRelatedRecords()); + return array_combine($fields, $fields); + } } diff --git a/framework/helpers/BaseArrayHelper.php b/framework/helpers/BaseArrayHelper.php index a77ddc6bc2..278eaf096c 100644 --- a/framework/helpers/BaseArrayHelper.php +++ b/framework/helpers/BaseArrayHelper.php @@ -58,35 +58,42 @@ class BaseArrayHelper */ public static function toArray($object, $properties = [], $recursive = true) { - if (!empty($properties) && is_object($object)) { - $className = get_class($object); - if (!empty($properties[$className])) { - $result = []; - foreach ($properties[$className] as $key => $name) { - if (is_int($key)) { - $result[$name] = $object->$name; - } else { - $result[$key] = static::getValue($object, $name); + if (is_array($object)) { + if ($recursive) { + foreach ($object as $key => $value) { + if (is_array($value) || is_object($value)) { + $object[$key] = static::toArray($value, true); } } - return $result; } - } - if ($object instanceof Arrayable) { - $object = $object->toArray(); - if (!$recursive) { - return $object; + return $object; + } elseif (is_object($object)) { + if (!empty($properties)) { + $className = get_class($object); + if (!empty($properties[$className])) { + $result = []; + foreach ($properties[$className] as $key => $name) { + if (is_int($key)) { + $result[$name] = $object->$name; + } else { + $result[$key] = static::getValue($object, $name); + } + } + return $recursive ? static::toArray($result) : $result; + } } - } - $result = []; - foreach ($object as $key => $value) { - if ($recursive && (is_array($value) || is_object($value))) { - $result[$key] = static::toArray($value, true); + if ($object instanceof Arrayable) { + $result = $object->toArray(); } else { - $result[$key] = $value; + $result = []; + foreach ($object as $key => $value) { + $result[$key] = $value; + } } + return $recursive ? static::toArray($result) : $result; + } else { + return [$object]; } - return $result; } /**