除了 PHP 中丰富的数组函数集, Yii 数组助手类提供了额外的静态方法,让你更高效地处理数组。
用原生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');
$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']
]
]
]
如果你想获得一个值,然后立即从数组中删除它,你可以使用 remove
方法:
$array = ['type' => 'A', 'options' => [1, 2]];
$type = ArrayHelper::remove($array, 'type');
执行代码后,$array
将包含 ['options' => [1, 2]]
且 $type
将包含 A
。
请注意,与 getValue
方法不同,remove
仅支持简单的键名称。
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.";
}
通常你要从多行数据或者多个对象构成的数组中获取某列的值,一个普通的例子是获取 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'];
});
按一个指定的键名重新索引一个数组,可以用 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']
]
]
]
为了从一个多维数组或者一个对象数组中建立一个映射表(键值对),你可以使用
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',
// ],
// ]
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() 函数时传递的值一样。
想知道一个数组是索引数组还是联合数组很方便,这有个例子:
// 不指定键名的数组
$indexed = ['Qiang', 'Paul'];
echo ArrayHelper::isIndexed($indexed);
// 所有键名都是字符串
$associative = ['framework' => 'Yii', 'version' => '2.0'];
echo ArrayHelper::isAssociative($associative);
为了将字符串数组中的特殊字符做 HTML 编解码,你可以使用下列方法:
$encoded = ArrayHelper::htmlEncode($data);
$decoded = ArrayHelper::htmlDecode($data);
默认情况只会对值做编码(译者注:原文中是编码,应为编解码)。通过给第二个参数传 false
,你也可以对键名做编码。
编码将默认使用应用程序的字符集,你可以通过第三个参数指定该字符集。
您可以使用 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' => 'admin@example.com',
'dev' => 'dev@example.com',
],
];
$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' => 'admin@example.com',
],
]
你经常要将一个对象或者对象的数组转换成一个数组,常见的情形是,为了通过 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 接口, 从而为其对象提供默认的转换成数组的方法。
通常你需要检查一个元素是否在数组中,或者一组元素是另一个元素的子集。
虽然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'])
Found a typo or you think this page needs improvement?
Edit it on github !
Signup or Login in order to comment.