3 个关注者

ArrayHelper

除了 丰富的 PHP 数组函数集,Yii 数组助手还提供额外的静态方法,使您可以更有效地处理数组。

获取值

从数组、对象或由两者组成的复杂结构中检索值,使用标准 PHP 很重复。您必须首先使用 isset 检查键是否存在,如果存在,您就获取它,否则提供默认值

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']);
// the path to write the value in `$array` can be specified as an array
ArrayHelper::setValue($array, ['key', 'in'], ['arr' => 'val']);

结果,$array['key']['in'] 的初始值将被新值覆盖

[
    'key' => [
        'in' => ['arr' => 'val']
    ]
]

如果路径包含一个不存在的键,它将被创建

// if `$array['key']['in']['arr0']` is not empty, the value will be added to the array
ArrayHelper::setValue($array, 'key.in.arr0.arr1', 'val');

// if you want to completely override the value `$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 列表。

$array = [
    ['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 方法。输入应为多维数组或对象数组。$key 可以是子数组的键名、对象的属性名或匿名函数,该函数必须返回用作键的值。

$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']
    // The second element of an original array is overwritten by the last element because of the same 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');
// the result is:
// [
//     '123' => 'aaa',
//     '124' => 'bbb',
//     '345' => 'ccc',
// ]

$result = ArrayHelper::map($array, 'id', 'name', 'class');
// the result is:
// [
//     '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) {
    // sort by age if it exists or by name otherwise
    return isset($item['age']) ? $item['age'] : $item['name'];
});

第三个参数是方向。在按单个键排序的情况下,可以是 SORT_ASCSORT_DESC。如果按多个值排序,可以提供一个排序方向数组来以不同的方式对每个值进行排序。

最后一个参数是 PHP 排序标志,它可以接受与传递给 PHP sort() 相同的值。

检测数组类型

了解数组是索引还是关联数组非常方便。以下是一个示例

// no keys specified
$indexed = ['Qiang', 'Paul'];
echo ArrayHelper::isIndexed($indexed);

// all keys are strings
$associative = ['framework' => 'Yii', 'version' => '2.0'];
echo ArrayHelper::isAssociative($associative);

HTML 编码和解码值

为了将字符串数组中的特殊字符编码或解码为 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' => '[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]',
    ],
]

将对象转换为数组

通常您需要将对象或对象数组转换为数组。最常见的情况是转换活动记录模型,以便通过 REST API 提供数据数组或以其他方式使用它。以下代码可用于执行此操作

$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\helpers\ArrayHelper 提供了 isIn()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']));

发现错别字或您认为此页面需要改进?
在 github 上编辑 !