數組助手類(ArrayHelper)
除了 PHP 中豐富的數組函數集, Yii 數組助手類提供了額外的靜態方法,讓你更高效地處理數組。
獲取值(Getting Values)
用原生PHP從一個對象、數組、或者包含這兩者的一個複雜數據結構中獲取數據是非常繁瑣的。 你首先得使用 isset
檢查 key 是否存在, 然後如果存在你就獲取它,如果不存在, 則提供一個默認返回值:
class User
{
public $name = 'Alex';
}
$array = [
'foo' => [
'bar' => new User(),
]
];
$value = isset($array['foo']['bar']->name) ? $array['foo']['bar']->name : null;
Yii 提供了一個非常方便的方法來做這件事:
$value = ArrayHelper::getValue($array, 'foo.bar.name');
方法的第一個參數是我們從哪裏獲取值。第二個參數指定了如何獲取數據, 它可以是下述幾種類型中的一個:
- 數組鍵名或者欲從中取值的對象的屬性名稱;
- 以點號分割的數組鍵名或者對象屬性名稱組成的字符串,上例中使用的參數類型就是該類型;
- 返回一個值的回調函數。
回調函數如下例所示:
$fullName = ArrayHelper::getValue($user, function ($user, $defaultValue) {
return $user->firstName . ' ' . $user->lastName;
});
第三個可選的參數如果沒有給定值,則默認爲 null
,如下例所示:
$username = ArrayHelper::getValue($comment, 'user.username', 'Unknown');
設定值(Setting values)
$array = [
'key' => [
'in' => ['k' => 'value']
]
];
ArrayHelper::setValue($array, 'key.in', ['arr' => 'val']);
// 在 `$array` 中寫入值的路徑可以被指定爲一個數組
ArrayHelper::setValue($array, ['key', 'in'], ['arr' => 'val']);
結果,$array['key']['in']
的初始值將被新值覆蓋
[
'key' => [
'in' => ['arr' => 'val']
]
]
如果路徑包含一個不存在的鍵,它將被創建
// 如果 `$array['key']['in']['arr0']` 不爲空,則該值將被添加到數組中
ArrayHelper::setValue($array, 'key.in.arr0.arr1', 'val');
// 如果你想完全覆蓋值 `$array['key']['in']['arr0']`
ArrayHelper::setValue($array, 'key.in.arr0', ['arr1' => 'val']);
結果將是
[
'key' => [
'in' => [
'k' => 'value',
'arr0' => ['arr1' => 'val']
]
]
]
從數組中獲取值(Take a value from an array)
如果你想獲得一個值,然後立即從數組中刪除它,你可以使用 remove
方法:
$array = ['type' => 'A', 'options' => [1, 2]];
$type = ArrayHelper::remove($array, 'type');
執行代碼後,$array
將包含 ['options' => [1, 2]]
且 $type
將包含 A
。 請注意,與 getValue
方法不同,remove
僅支持簡單的鍵名稱。
檢查鍵名的存在(Checking Existence of Keys)
ArrayHelper::keyExists
工作原理和 array_key_exists 差不多,除了 它還可支持大小寫不敏感的鍵名比較,比如:
$data1 = [
'userName' => 'Alex',
];
$data2 = [
'username' => 'Carsten',
];
if (!ArrayHelper::keyExists('username', $data1, false) || !ArrayHelper::keyExists('username', $data2, false)) {
echo "Please provide username.";
}
檢索列(Retrieving Columns)
通常你要從多行數據或者多個對象構成的數組中獲取某列的值,一個普通的例子是獲取 id 值列表。
$data = [
['id' => '123', 'data' => 'abc'],
['id' => '345', 'data' => 'def'],
];
$ids = ArrayHelper::getColumn($array, 'id');
結果將是 ['123', '345']
。
如果需要額外的轉換或者取值的方法比較複雜, 第二參數可以指定一個匿名函數:
$result = ArrayHelper::getColumn($array, function ($element) {
return $element['id'];
});
重建數組索引(Re-indexing Arrays)
按一個指定的鍵名重新索引一個數組,可以用 index
方法。輸入的數組應該是多維數組或者是一個對象數組。 鍵名(譯者注:第二個參數)可以是子數組的鍵名、對象的屬性名, 也可以是一個返回給定元素數組鍵值的匿名函數。
$groups
屬性是一個鍵數組, 它將根據指定的鍵將輸入數組分組爲一個或多個子數組。
如果 $key
屬性或其特定元素的值爲 null,並且未定義 $groups
, 則數組元素將被丟棄。否則,如果指定了 $groups
, 則數組元素將被添加到沒有任何鍵的結果數組中。
例如:
$array = [
['id' => '123', 'data' => 'abc', 'device' => 'laptop'],
['id' => '345', 'data' => 'def', 'device' => 'tablet'],
['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'],
];
$result = ArrayHelper::index($array, 'id');
結果將是一個關聯數組,其中鍵是 id
屬性的值
[
'123' => ['id' => '123', 'data' => 'abc', 'device' => 'laptop'],
'345' => ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone']
// 原始數組的第二個元素由於相同的 ID 而被最後一個元素覆蓋
]
匿名函數作爲 $key
傳遞,給出了相同的結果。
$result = ArrayHelper::index($array, function ($element) {
return $element['id'];
});
傳遞 id
作爲第三個參數將 id
分配給 $ array
:
$result = ArrayHelper::index($array, null, 'id');
結果將是一個多維數組,它由第一級的 id
分組,並且不在第二級索引:
[
'123' => [
['id' => '123', 'data' => 'abc', 'device' => 'laptop']
],
'345' => [ // all elements with this index are present in the result array
['id' => '345', 'data' => 'def', 'device' => 'tablet'],
['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'],
]
]
匿名函數也可用於分組數組中:
$result = ArrayHelper::index($array, 'data', [function ($element) {
return $element['id'];
}, 'device']);
結果將是一個多維數組,由第一級的 id
分組,第二級的 device
和第三級的 data
索引:
[
'123' => [
'laptop' => [
'abc' => ['id' => '123', 'data' => 'abc', 'device' => 'laptop']
]
],
'345' => [
'tablet' => [
'def' => ['id' => '345', 'data' => 'def', 'device' => 'tablet']
],
'smartphone' => [
'hgi' => ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone']
]
]
]
建立哈希表(Building Maps)
爲了從一個多維數組或者一個對象數組中建立一個映射表(鍵值對),你可以使用 map
方法。$from
和 $to
參數分別指定了欲構建的映射表的鍵名和屬性名。 根據需要,你可以按照一個分組字段 $group
將映射表進行分組,例如,
$array = [
['id' => '123', 'name' => 'aaa', 'class' => 'x'],
['id' => '124', 'name' => 'bbb', 'class' => 'x'],
['id' => '345', 'name' => 'ccc', 'class' => 'y'],
];
$result = ArrayHelper::map($array, 'id', 'name');
// 結果是:
// [
// '123' => 'aaa',
// '124' => 'bbb',
// '345' => 'ccc',
// ]
$result = ArrayHelper::map($array, 'id', 'name', 'class');
// 結果是:
// [
// 'x' => [
// '123' => 'aaa',
// '124' => 'bbb',
// ],
// 'y' => [
// '345' => 'ccc',
// ],
// ]
多維排序(Multidimensional Sorting)
multisort
方法可用來對嵌套數組或者對象數組進行排序,可按一到多個鍵名排序,比如,
$data = [
['age' => 30, 'name' => 'Alexander'],
['age' => 30, 'name' => 'Brian'],
['age' => 19, 'name' => 'Barney'],
];
ArrayHelper::multisort($data, ['age', 'name'], [SORT_ASC, SORT_DESC]);
排序之後我們在 $data
中得到的值如下所示:
[
['age' => 19, 'name' => 'Barney'],
['age' => 30, 'name' => 'Brian'],
['age' => 30, 'name' => 'Alexander'],
];
第二個參數指定排序的鍵名,如果是單鍵名的話可以是字符串,如果是多鍵名則是一個數組, 或者是如下例所示的一個匿名函數:
ArrayHelper::multisort($data, function($item) {
return isset($item['age']) ? ['age', 'name'] : 'name';
});
第三個參數表示增降順序。單鍵排序時,它可以是 SORT_ASC
或者 SORT_DESC
之一。如果是按多個鍵名排序,你可以用一個數組爲 各個鍵指定不同的順序。
最後一個參數(譯者注:第四個參數)是PHP的排序標識(sort flag),可使用的值和調用 PHP sort() 函數時傳遞的值一樣。
檢測數組類型(Detecting Array Types)
想知道一個數組是索引數組還是聯合數組很方便,這有個例子:
// 不指定鍵名的數組
$indexed = ['Qiang', 'Paul'];
echo ArrayHelper::isIndexed($indexed);
// 所有鍵名都是字符串
$associative = ['framework' => 'Yii', 'version' => '2.0'];
echo ArrayHelper::isAssociative($associative);
HTML 編碼和解碼值(HTML Encoding and Decoding Values)
爲了將字符串數組中的特殊字符做 HTML 編解碼,你可以使用下列方法:
$encoded = ArrayHelper::htmlEncode($data);
$decoded = ArrayHelper::htmlDecode($data);
默認情況只會對值做編碼(譯者注:原文中是編碼,應爲編解碼)。通過給第二個參數傳 false
,你也可以對鍵名做編碼。 編碼將默認使用應用程序的字符集,你可以通過第三個參數指定該字符集。
合併數組(Merging Arrays)
您可以使用 ArrayHelper::merge() 將兩個或多個數組合併成一個遞歸的數組。 如果每個數組都有一個具有相同字符串鍵值的元素,則後者將覆蓋前者 (不同於 array_merge_recursive())。 如果兩個數組都有一個數組類型的元素並且具有相同的鍵,則將執行遞歸合併。 對於整數鍵的元素,來自後一個數組的元素將被附加到前一個數組。 您可以使用 yii\helpers\UnsetArrayValue 對象來取消前一個數組的值或 yii\helpers\ReplaceArrayValue 以強制替換先前的值而不是遞歸合併。
例如:
$array1 = [
'name' => 'Yii',
'version' => '1.1',
'ids' => [
1,
],
'validDomains' => [
'example.com',
'www.example.com',
],
'emails' => [
'admin' => '[email protected]',
'dev' => '[email protected]',
],
];
$array2 = [
'version' => '2.0',
'ids' => [
2,
],
'validDomains' => new \yii\helpers\ReplaceArrayValue([
'yiiframework.com',
'www.yiiframework.com',
]),
'emails' => [
'dev' => new \yii\helpers\UnsetArrayValue(),
],
];
$result = ArrayHelper::merge($array1, $array2);
結果將是:
[
'name' => 'Yii',
'version' => '2.0',
'ids' => [
1,
2,
],
'validDomains' => [
'yiiframework.com',
'www.yiiframework.com',
],
'emails' => [
'admin' => '[email protected]',
],
]
對象轉換爲數組(Converting Objects to Arrays)
你經常要將一個對象或者對象的數組轉換成一個數組,常見的情形是,爲了通過 REST API 提供數據數組(或其他使用方式), 將 AR 模型(活動記錄模型)轉換成數組。如下代碼可完成這個工作:
$posts = Post::find()->limit(10)->all();
$data = ArrayHelper::toArray($posts, [
'app\models\Post' => [
'id',
'title',
// the key name in array result => property name
'createTime' => 'created_at',
// the key name in array result => anonymous function
'length' => function ($post) {
return strlen($post->content);
},
],
]);
第一個參數包含我們想要轉換的數據,在本例中,我們要轉換一個叫 Post
的 AR 模型。
第二個參數是每個類的轉換映射表,我們在此設置了一個 Post
模型的映射。 每個映射數組包含一組的映射,每個映射可以是:
- 一個要包含的照原樣的字段名(和類中屬性的名稱一致);
- 一個由你可隨意取名的鍵名和你想從中取值的模型列名組成的鍵值對;
- 一個由你可隨意取名的鍵名和有返回值的回調函數組成的鍵值對;
這上面的轉換結果將會是:
[
'id' => 123,
'title' => 'test',
'createTime' => '2013-01-01 12:00AM',
'length' => 301,
]
也可以在一個特定的類中實現 Arrayable 接口, 從而爲其對象提供默認的轉換成數組的方法。
測試陣列(Testing against Arrays)
通常你需要檢查一個元素是否在數組中,或者一組元素是另一個元素的子集。 雖然PHP提供 in_array()
,這不支持子集或 \Traversable
對象。
爲了支持這些測試,yii\base\ArrayHelper 提供了 yii\base\ArrayHelper::isIn() 和 yii\base\ArrayHelper::isSubset() 與 in_array() 簽名相同。
// true
ArrayHelper::isIn('a', ['a']);
// true
ArrayHelper::isIn('a', new(ArrayObject['a']));
// true
ArrayHelper::isSubset(new(ArrayObject['a', 'c']), new(ArrayObject['a', 'b', 'c'])