我一直在审查Laravel Collections的文档和API,但似乎找不到我要找的东西:
我想从集合中检索包含模型数据的数组,但只获取指定的属性。
即像Users::toArray('id','name','email')
这样的东西,其中集合实际上拥有用户的所有属性,因为它们在别处使用,但在这个特定的地方我需要一个带有userdata的数组,只有指定的属性。
在Laravel中似乎没有帮助吗? - 我怎样才能以最简单的方式做到这一点?
您可以使用现有Collection
方法的组合来完成此操作。一开始可能有点难以理解,但它应该很容易打破。
// get your main collection with all the attributes...
$users = Users::get();
// build your second collection with a subset of attributes. this new
// collection will be a collection of plain arrays, not Users models.
$subset = $users->map(function ($user) {
return collect($user->toArray())
->only(['id', 'name', 'email'])
->all();
});
说明
首先,map()
方法基本上只是遍历Collection
,并将Collection
中的每个项目传递给传入的回调。从每次回调调用返回的值构建由Collection
方法生成的新map()
。
collect($user->toArray())
正在建立一个新的,临时的Collection
Users
属性。
->only(['id', 'name', 'email'])
将临时Collection
减少到仅指定的属性。
->all()
将临时的Collection
变成了一个普通的阵列。
把它们放在一起就可以得到“对于users集合中的每个用户,返回一个只包含id,name和email属性的数组。”
Laravel 5.5在Model上添加了一个only
方法,它基本上与collect($user->toArray())->only([...])->all()
做同样的事情,所以这可以在5.5+中略微简化为:
// get your main collection with all the attributes...
$users = Users::get();
// build your second collection with a subset of attributes. this new
// collection will be a collection of plain arrays, not Users models.
$subset = $users->map(function ($user) {
return $user->only(['id', 'name', 'email']);
});
如果将它与Laravel 5.4中引入的"higher order messaging" for collections结合使用,可以进一步简化:
// get your main collection with all the attributes...
$users = Users::get();
// build your second collection with a subset of attributes. this new
// collection will be a collection of plain arrays, not Users models.
$subset = $users->map->only(['id', 'name', 'email']);
使用User::get(['id', 'name', 'email'])
,它将返回一个具有指定列的集合,如果你想使它成为一个数组,只需在toArray()
方法之后使用get()
,如下所示:
User::get(['id', 'name', 'email'])->toArray()
大多数情况下,您不需要将集合转换为数组,因为集合实际上是类固醇上的数组,并且您可以使用易于使用的方法来操作集合。
我现在想出了一个自己的解决方案:
1.创建了一个从数组中提取特定属性的通用函数
下面的函数只从关联数组或关联数组数组中提取特定属性(最后一个是在Laravel中执行$ collection-> toArray()时得到的)。
它可以像这样使用:
$data = array_extract( $collection->toArray(), ['id','url'] );
我使用以下功能:
function array_is_assoc( $array )
{
return is_array( $array ) && array_diff_key( $array, array_keys(array_keys($array)) );
}
function array_extract( $array, $attributes )
{
$data = [];
if ( array_is_assoc( $array ) )
{
foreach ( $attributes as $attribute )
{
$data[ $attribute ] = $array[ $attribute ];
}
}
else
{
foreach ( $array as $key => $values )
{
$data[ $key ] = [];
foreach ( $attributes as $attribute )
{
$data[ $key ][ $attribute ] = $values[ $attribute ];
}
}
}
return $data;
}
此解决方案不关注在大型数据集中循环遍历集合的性能影响。
2.通过自定义集合i Laravel实现上述功能
由于我希望能够在任何集合对象上简单地执行$collection->extract('id','url');
,因此我实现了一个自定义集合类。
首先,我创建了一个通用模型,它扩展了Eloquent模型,但使用了不同的集合类。所有模型都需要扩展此自定义模型,而不是Eloquent模型。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model as EloquentModel;
use Lib\Collection;
class Model extends EloquentModel
{
public function newCollection(array $models = [])
{
return new Collection( $models );
}
}
?>
其次,我创建了以下自定义集合类:
<?php
namespace Lib;
use Illuminate\Support\Collection as EloquentCollection;
class Collection extends EloquentCollection
{
public function extract()
{
$attributes = func_get_args();
return array_extract( $this->toArray(), $attributes );
}
}
?>
最后,所有模型都应该扩展您的自定义模型,如下所示:
<?php
namespace App\Models;
class Article extends Model
{
...
现在的功能从没有。该集合巧妙地使用上面的图1来制作$collection->extract()
方法。
$hidden
和$visible
属性。它们将被设置为全局(这意味着始终返回来自$visible
数组的所有属性)。makeVisible($attribute)
和makeHidden($attribute)
,您可以动态更改隐藏和可见属性。更多:Eloquent: Serialization -> Temporarily Modifying Property Visibility我有一个类似的问题,我需要从一个大数组中选择值,但我希望结果集合只包含单个值的值。
pluck()
可用于此目的(如果只需要一个关键项)
你也可以使用reduce()
。像这样的东西减少:
$result = $items->reduce(function($carry, $item) {
return $carry->push($item->getCode());
}, collect());
这是对上面的patricus [answer] [1]的跟进,但是对于嵌套数组:
$topLevelFields = ['id','status'];
$userFields = ['first_name','last_name','email','phone_number','op_city_id'];
return $onlineShoppers->map(function ($user) {
return collect($user)->only($topLevelFields)
->merge(collect($user['user'])->only($userFields))->all();
})->all();
这似乎有效,但不确定它是否针对性能进行了优化。
$request->user()->get(['id'])->groupBy('id')->keys()->all();
输出:
array:2 [
0 => 4
1 => 1
]
$users = Users::get();
$subset = $users->map(function ($user) {
return array_only(user, ['id', 'name', 'email']);
});