Подтвердить что ты не робот

Получите только определенные атрибуты из коллекции Laravel

Я просматривал документацию и API для коллекций Laravel, но, похоже, не нашел то, что искал:

Я хотел бы получить массив с данными модели из коллекции, но получить только указанные атрибуты.

т.е. что-то вроде Users::toArray('id','name','email'), где коллекция фактически содержит все атрибуты для пользователей, потому что они используются в другом месте, но в этом конкретном месте мне нужен массив с userdata и только указанные атрибуты.

В Laravel, похоже, нет помощника для этого? - Как я могу сделать это самым простым способом?

4b9b3361

Ответ 1

Вы можете сделать это, используя комбинацию существующих методов 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 обратно в простой массив.

Сложите все это вместе, и вы получите "Для каждого пользователя в коллекции пользователей верните массив только атрибутов id, name и email".


Обновление Laravel 5.5

В Laravel 5.5 добавлен 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, это может быть еще более упрощено:

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

Ответ 2

используйте User::get(['id', 'name', 'email']), он вернет вам коллекцию с указанными столбцами, и если вы хотите сделать ее массивом, просто используйте toArray() после метода get(), например:

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

В большинстве случаев вам не нужно преобразовывать коллекцию в массив, потому что коллекции на самом деле являются массивами на стероидах, и у вас есть простые в использовании методы для управления коллекцией.

Ответ 3

Ответ 4

Теперь у меня есть собственное решение:

1. Создал общую функцию для извлечения определенных атрибутов из массивов

В приведенной ниже функции извлекаются только определенные атрибуты из ассоциативного массива или массив ассоциативных массивов (последнее - это то, что вы получаете при выполнении $collection- > toArray() в Laravel).

Его можно использовать следующим образом:

$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. Реализовать описанное выше с помощью пользовательской коллекции я 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
{
...

Теперь функции от no. 1, которые были использованы в коллекции, чтобы сделать доступным метод $collection->extract().

Ответ 5

похоже, что он работает, но не уверен, что он оптимизирован для производительности или нет.

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

выход:

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

Ответ 6

Я предполагаю, что вы настроили соединение с БД и строят свой запрос в своем контроллере со всем по порядку.

Вы можете просто сделать это...:)

$user_details = User::get(['id','username','email']);

dd($user_details);

Ответ 7

У меня была похожая проблема, когда мне нужно было выбрать значения из большого массива, но я хотел, чтобы результирующая коллекция содержала только значения одного значения.

pluck() этой целью можно использовать pluck() (если требуется только 1 ключевой элемент)

Вы также можете использовать reduce(). Как то так с уменьшением:

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

Ответ 8

это продолжение patricus [ответ] [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();

Ответ 9

$users = Users::get();

$subset = $users->map(function ($user) {
    return array_only(user, ['id', 'name', 'email']);
});