```php <?php /** * @link http://www.yiiframework.com/ * @copyright Copyright (c) 2008 Yii Software LLC * @license http://www.yiiframework.com/license/ */ namespace yii\base; use Yii; use yii\helpers\ArrayHelper; use yii\web\Link; use yii\web\Linkable; /** * ArrayableTrait provides a common implementation of the [[Arrayable]] interface. * ArrableTrait是[[Arrayable]]的一個實作 * * ArrayableTrait implements [[toArray()]] by respecting the field definitions as declared * in [[fields()]] and [[extraFields()]]. * ArrayableTrait通過在[[fields()]和[[extraFields()]]中宣告的欄位來實作[[toArray()], * * @author Qiang Xue <[email protected]> * @since 2.0 */ trait ArrayableTrait { /** * Returns the list of fields that should be returned by default by [[toArray()]] when no specific fields are specified. * 當在[[toArray()]]中沒有指定特明的欄位的時候, 默認回傳的欄位串列 * * A field is a named element in the returned array by [[toArray()]]. * 欄位名作為[[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 ($model, $field) { * // return field value * // 回傳欄位的值 * } * ``` * * For example, the following code declares four fields: * 舉個例子, 下面的代碼定義了4個欄位: * * - `email`: the field name is the same as the property name `email`; * - `email`: 欄位名和和物件的屬性名相同 * * - `firstName` and `lastName`: the field names are `firstName` and `lastName`, and their * values are obtained from the `first_name` and `last_name` properties; * - `firstName` and `lastName`: 陣列的鍵名是`firstName`和`lastName`, 他們的鍵值分別從當前物件的屬性`first_name`和`last_name`中獲取 * * - `fullName`: the field name is `fullName`. Its value is obtained by concatenating `first_name` * and `last_name`. * - `fullName`: 陣列的鍵名是`fullName`, 他的值從`first_name`以及`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 the privilege of the current application user, * you may return different sets of visible fields or filter out some fields. * 在這個方法中, 你也可以基于當前的背景關系回傳不同的欄位串列. * 舉個例子, 你可以依據當前應用的用戶權限, 回傳不同的可見欄位或者過濾掉某些欄位 * * The default implementation of this method returns the public object member variables indexed by themselves. * 這個方法的默認實作, 是回傳自己公共成員變數 * * @return array the list of field names or field definitions. * @return array 一個欄位集合或者是欄位的定義 * * @see toArray() */ public function fields() { // 這里是方法的默認實作 // 1. 通過Yii::getObjectVars($this)獲取當前物件的所有公共成員 // 2. 取出這些成員陣列的所有的屬性名 $fields = array_keys(Yii::getObjectVars($this)); // 將上面取出的陣列的鍵名和鍵值, 組成一個新陣列, 鍵名和鍵值一致. // 這里其實直接回傳`$fields`, 結果是一致的 // 但是不這么操作, 個人理解是為了后面其它物件復寫該方法(畢竟這里只是一個默認實作) // 復寫的時候, 如果想去掉一個默認屬性, 這個方法就比較方便了, 直接unset($fields['xxx'])就可以了, 而不用再次遍歷去除 // 復寫的時候, 添加一個同名屬性的話, 可以直接覆寫掉, 而不會包含兩個元素(一個是具名的, 一個是自己復寫指定的), 那么之后回圈的時候, 可以少執行. return array_combine($fields, $fields); } /** * Returns the list of fields that can be expanded further and returned by [[toArray()]]. * 回傳可進一步擴展并由[[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. * 這個方法與[[fields()]]相似, 不同之處在于回傳的欄位串列[[toArray()]]默認情況下是不回傳的. * 只有在呼叫[[toArray()]]的時候顯示指定這些欄位的時候才會回傳. * * The default implementation returns an empty array. * 這個方法的默認實作是回傳一個空陣列 * * You may override this method to return a list of expandable fields based on some context information * (e.g. the current application user). * 您可以重寫此方法,以基于某些背景關系資訊回傳可擴展欄位串列.(比如當前用戶) * * @return array the list of expandable field names or field definitions. Please refer * to [[fields()]] on the format of the return value. * @return array 一個可以擴展的欄位或者欄位的定義, 具體的回傳格式可以參照[[fields()]] * * @see toArray() * @see fields() */ public function extraFields() { return []; } /** * Converts the model into an array. * 將物件轉換為一個陣列 * * This method will first identify which fields to be included in the resulting array by calling [[resolveFields()]]. * 該方法首先通過呼叫[[resolveFields()]]來標識結果陣列中包含哪些欄位, * * It will then turn the model into an array with these fields. If `$recursive` is true, * any embedded objects will also be converted into arrays. * 然后將模型轉換為具有這些欄位的陣列. 如果`$recursive`為true的話, 所有嵌入的物件也將轉換為陣列. * * When embeded objects are [[Arrayable]], their respective nested fields will be extracted and passed to [[toArray()]]. * 當嵌入的物件實作了[[Arrayable]]的時候, 他們嵌套的欄位將被提取并傳遞給[[toArray()]]. * * If the model implements the [[Linkable]] interface, the resulting array will also have a `_link` element * which refers to a list of links as specified by the interface. * 如果物件實作了[[Linkable]]介面, 那么結果將會自動加上一個`_link`元素, 關聯由介面指定的鏈接串列 . * * @param array $fields the fields being requested. * @param array $fields 需要轉換為陣列的欄位 * * If empty or if it contains '*', all fields as specified by [[fields()]] will be returned. * 如果為慷訓者包含了'*', 那么將回傳所有在在[[fields()]]中定義的欄位 * * Fields can be nested, separated with dots (.). e.g.: item.field.sub-field * 欄位可以嵌套, 使用(.)隔開, 如: item.field.sub-field * * `$recursive` must be true for nested fields to be extracted. If `$recursive` is false, only the root fields will be extracted. * 如果要提取嵌套的欄位的話, `$recursive`必須設定為true, 如果`$recursive`設定為false, 那么只會匯出根欄位 * * @param array $expand the additional fields being requested for exporting. Only fields declared in [[extraFields()]] * will be considered. * @param array $expand 想要匯出的附加欄位, 只有在[[extraFields()]]中定義的欄位才會被匯出 * * Expand can also be nested, separated with dots (.). e.g.: item.expand1.expand2 * 附加欄位也可以嵌套, 使用(.)分隔, 如: item.expand1.expand2 * * `$recursive` must be true for nested expands to be extracted. If `$recursive` is false, only the root expands will be extracted. * 如果要匯出嵌套的附加欄位, 那么`$recursive`必須被設為true, 否則只有根欄位才會被匯出 * * @param bool $recursive whether to recursively return array representation of embedded objects. * @param bool $recursive 是否遞回匯出物件 * * @return array the array representation of the object * @return array 物件的陣列 */ public function toArray(array $fields = [], array $expand = [], $recursive = true) { $data = []; // $this->resolveFields() 用來清理不在[[fields()]]以及[[extraFields()]]中定義的元素 // 回傳一個kv結構的陣列, k是需要匯出的陣列名稱, v是該元素的定義(可能是字串, 表示是物件的屬性名, 也可能是一個回呼, 用來回傳該元素的值) foreach ($this->resolveFields($fields, $expand) as $field => $definition) { // 如果定義是一個字串, 那么將當前物件的該屬性值賦值給$attribute // 如果不是一個字串, 那就作為一個回呼, 將回呼的執行結果賦值給$attribute $attribute = is_string($definition) ? $this->$definition : $definition($this, $field); // 如果遞回處理? if ($recursive) { // 將`$fields`中的以`$field`作為父節點的嵌套欄位取出來, 作為一個陣列 // 其實取出的就是`$field.xxx` `$field.yyy`的欄位, 回傳["xxx","yyy"] $nestedFields = $this->extractFieldsFor($fields, $field); // 將擴展欄位`$expand`也按照如上操作 $nestedExpand = $this->extractFieldsFor($expand, $field); if ($attribute instanceof \JsonSerializable) { // 如果`$attribute`實作了`\JsonSerializable`介面? 直接呼叫`jsonSerialize()`方法回傳 $attribute = $attribute->jsonSerialize(); } elseif ($attribute instanceof Arrayable) { // 如果`$attribute`實作了`Arrayable`介面? 繼續執行`toArray()`, $attribute = $attribute->toArray($nestedFields, $nestedExpand); } elseif (is_array($attribute)) { // 如果`$attribute`是一個陣列? // 使用`array_map`遞回處理 $attribute = array_map( function ($item) use ($nestedFields, $nestedExpand) { if ($item instanceof \JsonSerializable) { return $item->jsonSerialize(); } elseif ($item instanceof Arrayable) { return $item->toArray($nestedFields, $nestedExpand); } return $item; }, $attribute ); } } // 將最后處理出來的`$attribute`賦值給陣列 $data[$field] = $attribute; } // 處理處理結束了, `$data`里面其實已經有很多資料了. // 檢測當前物件是否實作了`Linkable` // 如果實作了, 則添加一個['link'] if ($this instanceof Linkable) { $data['_links'] = Link::serialize($this->getLinks()); } // 呼叫ArrayHelper::toArray($data) // 為什么這里要呼叫ArrayHelper::toArray()呢 // 因為上面的代碼實際只處理了集中型別的資料 // 1. 實作了`Arrayable`的物件 // 2. 實作了`JsonSerializable`的資料 // 3. $attribute處理完畢的非物件的資料 // 所以其它未實作這倆介面的資料的物件, 統一交由ArrayHelper::toArray()處理(如果是遞回處理的話). // 如果不遞回處理的話, 當前級別已經處理完畢了, $attrite里面的其它物件就不用管了 return $recursive ? ArrayHelper::toArray($data) : $data; } /** * Extracts the root field names from nested fields. * 從嵌套欄位中提取根欄位名 * * Nested fields are separated with dots (.). e.g: "item.id" * 嵌套欄位以(.)分隔, 如: "item.id" * * The previous example would extract "item". * 上面的示例將提取"item" * * @param array $fields The fields requested for extraction * @param array $fields 需要被提取的欄位 * * @return array root fields extracted from the given nested fields * @return array 從嵌套欄位中提取出來的根欄位 * * @since 2.0.14 */ protected function extractRootFields(array $fields) { $result = []; foreach ($fields as $field) { $result[] = current(explode('.', $field, 2)); } if (in_array('*', $result, true)) { $result = []; } return array_unique($result); } /** * Extract nested fields from a fields collection for a given root field * 從指定的一個欄位集合中提取給定的根欄位的欄位名 * * Nested fields are separated with dots (.). e.g: "item.id" * 嵌套的欄位都是以(.)分隔, 如: "item.id" * * The previous example would extract "id". * 上面的示例將提取出"id" * * * * @param array $fields The fields requested for extraction * @param array $fields 想要被提取的欄位 * * @param string $rootField The root field for which we want to extract the nested fields * @param string $rootField 想要提取的嵌套欄位的根欄位 * * @return array nested fields extracted for the given field * @return array 指定欄位從嵌套欄位中提取出的下級欄位 * @since 2.0.14 */ protected function extractFieldsFor(array $fields, $rootField) { $result = []; foreach ($fields as $field) { if (0 === strpos($field, "{$rootField}.")) { $result[] = preg_replace('/^' . preg_quote($rootField, '/') . '\./i', '', $field); } } return array_unique($result); } /** * Determines which fields can be returned by [[toArray()]]. * 確定哪些欄位可以被[[toArray()]]回傳 * * This method will first extract the root fields from the given fields. * 這個方法首先從給定的欄位中提取出根欄位 * * Then it will check the requested root fields against those declared in [[fields()]] and [[extraFields()]] * to determine which fields can be returned. * 然后會再次檢查[[fields()]]以及[[extraFields()]]中定義的欄位, 來確定哪些欄位可以被回傳 * * @param array $fields the fields being requested for exporting * @param array $fields 即將被匯出的欄位 * * @param array $expand the additional fields being requested for exporting * @param array $expand 即將被匯出的附加欄位 * * @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. * @return array 將要被匯出的欄位集合. 陣列的鍵名就是欄位名, 陣列的鍵值就是物件對應的屬性名或者一個值的回呼 */ protected function resolveFields(array $fields, array $expand) { $fields = $this->extractRootFields($fields); $expand = $this->extractRootFields($expand); $result = []; foreach ($this->fields() as $field => $definition) { if (is_int($field)) { $field = $definition; } if (empty($fields) || in_array($field, $fields, true)) { $result[$field] = $definition; } } if (empty($expand)) { return $result; } foreach ($this->extraFields() as $field => $definition) { if (is_int($field)) { $field = $definition; } if (in_array($field, $expand, true)) { $result[$field] = $definition; } } return $result; } } ```
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/4500.html
標籤:PHP
