Получите только определенные атрибуты из коллекции Laravel
Я просматривал документацию и API для коллекций Laravel, но, похоже, не нашел то, что искал:
Я хотел бы получить массив с данными модели из коллекции, но получить только указанные атрибуты.
т.е. что-то вроде Users::toArray('id','name','email')
, где коллекция фактически содержит все атрибуты для пользователей, потому что они используются в другом месте, но в этом конкретном месте мне нужен массив с userdata и только указанные атрибуты.
В Laravel, похоже, нет помощника для этого? - Как я могу сделать это самым простым способом?
Ответы
Ответ 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
-
Вам нужно определить атрибуты $hidden
и $visible
. Они будут установлены глобально (это означает, что всегда возвращать все атрибуты из массива $visible
).
-
Используя метод makeVisible($attribute)
и makeHidden($attribute)
, вы можете динамически изменять скрытые и видимые атрибуты. Подробнее: Красноречивый: Сериализация → Временное изменение видимости свойств
Ответ 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']);
});