Chỉ nhận các thuộc tính cụ thể từ Bộ sưu tập Laravel


82

Tôi đã xem xét tài liệu và API cho Bộ sưu tập Laravel, nhưng dường như không tìm thấy thứ tôi đang tìm kiếm:

Tôi muốn truy xuất một mảng có dữ liệu mô hình từ một bộ sưu tập, nhưng chỉ lấy các thuộc tính được chỉ định.

Có nghĩa là Users::toArray('id','name','email'), nơi bộ sưu tập trên thực tế chứa tất cả các thuộc tính cho người dùng, vì chúng được sử dụng ở nơi khác, nhưng ở nơi cụ thể này, tôi cần một mảng với dữ liệu người dùng và chỉ các thuộc tính được chỉ định.

Dường như không có người trợ giúp cho việc này trong Laravel? - Làm thế nào tôi có thể làm điều này một cách dễ dàng nhất?


Những người xem khác có thể quan tâm Collection::implode(). Nó có thể lấy một thuộc tính và trích xuất nó từ tất cả các đối tượng trong bộ sưu tập. Điều này không trả lời chính xác câu hỏi này, nhưng nó có thể hữu ích cho những người khác đã đến đây từ Google, như tôi đã làm. laravel.com/docs/5.7/collections#method-implode
musicin3d

Câu trả lời:


173

Bạn có thể làm điều này bằng cách sử dụng kết hợp các Collectionphương pháp hiện có . Có thể hơi khó để làm theo lúc đầu, nhưng nó sẽ đủ dễ dàng để chia nhỏ.

// 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();
});

Giải trình

Đầu tiên, map()về cơ bản phương thức này chỉ lặp qua Collectionvà chuyển từng mục trong lệnh Collectiongọi lại được truyền vào. Giá trị được trả về từ mỗi lần gọi của lệnh gọi lại xây dựng giá trị mới Collectionđược tạo bởi map()phương thức.

collect($user->toArray())chỉ đang xây dựng một mới, tạm thời Collectionra khỏi Usersthuộc tính.

->only(['id', 'name', 'email'])giảm tạm thời Collectionxuống chỉ những thuộc tính được chỉ định.

->all()biến tạm thời Collectiontrở lại thành một mảng đơn giản.

Đặt tất cả lại với nhau và bạn nhận được "Đối với mỗi người dùng trong bộ sưu tập người dùng, trả về một mảng chỉ gồm các thuộc tính id, tên và email."


Cập nhật Laravel 5.5

Laravel 5.5 đã thêm một onlyphương thức trên Model, về cơ bản nó thực hiện điều tương tự như phương thức collect($user->toArray())->only([...])->all(), vì vậy điều này có thể được đơn giản hóa một chút trong 5.5+ thành:

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

Nếu bạn kết hợp điều này với "thông báo thứ tự cao hơn" cho các bộ sưu tập được giới thiệu trong Laravel 5.4, nó có thể được đơn giản hóa hơn nữa:

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

Cảm ơn! Tôi đã đánh dấu đây là câu trả lời được chấp nhận, bởi vì nó hoạt động giống như một sự quyến rũ với chức năng Laravel hiện có. - Bây giờ, điều này có thể được cung cấp cho bộ sưu tập thông qua một lớp bộ sưu tập tùy chỉnh, giống như tôi đã mô tả trong giải pháp thay thế của mình.
baitz

Đó là nó. Nhưng tôi ước có một cách ít dài dòng hơn để mô phỏng ::get([ 'fields', 'array' ])hành vi của QueryBuilder . Về cơ bản, nó sẽ tiện dụng cho những "bãi chứa".
pilat

cho mảng bạn có thể làm$newArray = Arr::only($initialArray, [ 'id', 'name' /* allowed keys list */ ]);
Hop hop

này là rất tốt, nhưng tôi tự hỏi làm thế nào nó có thể làm việc nếu tôi có một mảng bên trong lồng nhau
abbood

1
Trong các phiên bản gần đây của Laravel này có thể được viết ngắn gọn hơn:$users->map->only(['column', ...])
okdewit

17

sử dụng User::get(['id', 'name', 'email']), nó sẽ trả về cho bạn một tập hợp với các cột được chỉ định và nếu bạn muốn biến nó thành một mảng, chỉ cần sử dụng toArray()sau get()phương thức như sau:

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

Hầu hết các trường hợp, bạn sẽ không cần phải chuyển đổi bộ sưu tập thành một mảng vì bộ sưu tập thực sự là các mảng trên steroid và bạn có các phương pháp dễ sử dụng để thao tác bộ sưu tập.


1
Bộ sưu tập đã được tạo bởi một truy vấn trước đó, nơi cần thêm các thuộc tính. Do đó, tôi cần tạo một mảng dữ liệu mà tôi chỉ có các thuộc tính cụ thể.
baitz

Bạn có thể làm cho truy vấn của mình động bằng cách chuyển một mảng cột để trả về hoặc bạn có thể thực hiện một thao tác khác ->get(['id', 'email', 'name'])sẽ không yêu cầu cơ sở dữ liệu nhưng nó sẽ trả về một tập hợp mới chỉ với các giá trị đã chọn. Các phương thức như pluck / list, get, first, v.v. có cùng tên trong cả hai, lớp trình tạo truy vấn và lớp thu thập.
Ruffles

Ruffles, điều đó không hoạt động trong Laravel 5.3. - Phương thức "get" của trình tạo truy vấn trên thực tế lấy các cột, nhưng nó cũng thực hiện một truy vấn khác. Phương thức "lấy" của bộ sưu tập có thể trích xuất một mô hình cụ thể theo từng khóa từ bộ sưu tập, đây không phải là điều tôi đang tìm kiếm ở đây.
baitz

get () luôn trả về một tập hợp, không quan trọng đó là DB :: table () -> get () hay Model :: get (), vì vậy nếu bạn lưu truy vấn trong một biến và chạy phương thức get trên đó biến truy vấn $, bạn không nên nhận một truy vấn khác vào cơ sở dữ liệu.
Ruffles

1
Khi tôi lưu trữ truy vấn trong một biến như $ query = Model :: where (...), thì nó sẽ chạy một truy vấn đến cơ sở dữ liệu bất cứ khi nào tôi thực hiện một $ query-> get (). - Ngoài ra, đề xuất của bạn imho không phải là một giải pháp mong muốn cho vấn đề. Tôi đã giải quyết nó với các hàm mảng mới mà tôi đã triển khai thông qua một bộ sưu tập tùy chỉnh (đây là câu trả lời của riêng tôi).
baitz

3

Phương pháp dưới đây cũng hoạt động.

$users = User::all()->map(function ($user) {
  return collect($user)->only(['id', 'name', 'email']);
});

1

Bây giờ tôi đã đưa ra một giải pháp riêng cho điều này:

1. Đã tạo một hàm chung để trích xuất các thuộc tính cụ thể từ các mảng

Hàm bên dưới chỉ trích xuất các thuộc tính cụ thể từ một mảng kết hợp hoặc một mảng mảng kết hợp (cuối cùng là những gì bạn nhận được khi thực hiện $ collection-> toArray () trong Laravel).

Nó có thể được sử dụng như thế này:

$data = array_extract( $collection->toArray(), ['id','url'] );

Tôi đang sử dụng các chức năng sau:

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;   
}

Giải pháp này không tập trung vào các hàm ý về hiệu suất khi lặp qua các bộ sưu tập trong bộ dữ liệu lớn.

2. Thực hiện những điều trên thông qua một bộ sưu tập tùy chỉnh i Laravel

Vì tôi muốn có thể thực hiện đơn giản $collection->extract('id','url');trên bất kỳ đối tượng bộ sưu tập nào, tôi đã triển khai một lớp bộ sưu tập tùy chỉnh.

Đầu tiên, tôi tạo một Mô hình chung, mở rộng mô hình Eloquent, nhưng sử dụng một lớp tập hợp khác. Tất cả các mô hình bạn cần để mở rộng mô hình tùy chỉnh này, chứ không phải Mô hình 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 );
    }    
}
?>

Thứ hai, tôi đã tạo lớp bộ sưu tập tùy chỉnh sau:

<?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 );
    }
}   
?>

Cuối cùng, tất cả các mô hình sau đó sẽ mở rộng mô hình tùy chỉnh của bạn thay thế, như vậy:

<?php
namespace App\Models;
class Article extends Model
{
...

Bây giờ các chức năng từ không. 1 ở trên được bộ sưu tập sử dụng gọn gàng để làm cho $collection->extract()phương pháp khả dụng.


Xem tài liệu của Laravel về cách mở rộng bộ sưu tập: laravel.com/docs/5.5/collections#exfining-collections
Andreas Hacker

0
  1. Bạn cần xác định $hidden$visiblecác thuộc tính. Chúng sẽ được đặt toàn cục (có nghĩa là luôn trả về tất cả các thuộc tính từ $visiblemảng).

  2. Sử dụng phương pháp makeVisible($attribute)makeHidden($attribute)bạn có thể thay đổi động các thuộc tính ẩn và hiện. Thêm: Eloquent: Serialization -> Tạm thời sửa đổi chế độ hiển thị thuộc tính


Tôi không muốn điều này là toàn cầu, nhưng hãy chọn các thuộc tính để trích xuất động từ bộ sưu tập.
baitz

0

Tôi gặp sự cố tương tự khi tôi cần chọn các giá trị từ một mảng lớn, nhưng tôi muốn tập hợp kết quả chỉ chứa các giá trị của một giá trị duy nhất.

pluck() có thể được sử dụng cho mục đích này (nếu chỉ cần 1 mục chính)

bạn cũng có thể sử dụng reduce(). Một cái gì đó như thế này với giảm:

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

0

đây là phần tiếp theo về patricus [answer] [1] ở trên nhưng đối với các mảng lồng nhau :

$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

điều này dường như hoạt động, nhưng không chắc liệu nó có được tối ưu hóa cho hiệu suất hay không.

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

đầu ra:

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

-3
$users = Users::get();

$subset = $users->map(function ($user) {
    return array_only(user, ['id', 'name', 'email']);
});
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.