仅从Laravel Collection获取特定属性

问题描述 投票:45回答:8

我一直在审查Laravel Collections的文档和API,但似乎找不到我要找的东西:

我想从集合中检索包含模型数据的数组,但只获取指定的属性。

即像Users::toArray('id','name','email')这样的东西,其中集合实际上拥有用户的所有属性,因为它们在别处使用,但在这个特定的地方我需要一个带有userdata的数组,只有指定的属性。

在Laravel中似乎没有帮助吗? - 我怎样才能以最简单的方式做到这一点?

php laravel
8个回答
97
投票

您可以使用现有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更新

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']);

11
投票

使用User::get(['id', 'name', 'email']),它将返回一个具有指定列的集合,如果你想使它成为一个数组,只需在toArray()方法之后使用get(),如下所示:

User::get(['id', 'name', 'email'])->toArray()

大多数情况下,您不需要将集合转换为数组,因为集合实际上是类固醇上的数组,并且您可以使用易于使用的方法来操作集合。


1
投票

我现在想出了一个自己的解决方案:

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()方法。


0
投票
  1. 您需要定义$hidden$visible属性。它们将被设置为全局(这意味着始终返回来自$visible数组的所有属性)。
  2. 使用方法makeVisible($attribute)makeHidden($attribute),您可以动态更改隐藏和可见属性。更多:Eloquent: Serialization -> Temporarily Modifying Property Visibility

0
投票

我有一个类似的问题,我需要从一个大数组中选择值,但我希望结果集合只包含单个值的值。

pluck()可用于此目的(如果只需要一个关键项)

你也可以使用reduce()。像这样的东西减少:

$result = $items->reduce(function($carry, $item) {
    return $carry->push($item->getCode());
}, collect());

0
投票

这是对上面的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();

-1
投票

这似乎有效,但不确定它是否针对性能进行了优化。

$request->user()->get(['id'])->groupBy('id')->keys()->all();

输出:

array:2 [
  0 => 4
  1 => 1
]

-1
投票
$users = Users::get();

$subset = $users->map(function ($user) {
    return array_only(user, ['id', 'name', 'email']);
});
© www.soinside.com 2019 - 2024. All rights reserved.