He estado revisando la documentación y la API de Laravel Collections, pero parece que no encuentro lo que estoy buscando:
Me gustaría recuperar una matriz con datos de modelo de una colección, pero solo obtener atributos especificados.
Es decir, algo así como Users::toArray('id','name','email')
, donde la colección de hecho contiene todos los atributos para los usuarios, porque se usan en otros lugares, pero en este lugar específico necesito una matriz con datos de usuario y solo los atributos especificados.
¿No parece haber un ayudante para esto en Laravel? - ¿Cómo puedo hacer esto de la manera más fácil?
Collection::implode()
. Puede tomar una propiedad y extraerla de todos los objetos de la colección. Esto no responde exactamente a esta pregunta, pero podría ser útil para otros que han venido aquí desde Google, como lo hice yo. laravel.com/docs/5.7/collections#method-implodeRespuestas:
Puede hacer esto usando una combinación de
Collection
métodos existentes . Puede ser un poco difícil de seguir al principio, pero debería ser bastante fácil de romper.// 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(); });
Explicación
Primero, el
map()
método básicamente solo itera a través delCollection
, y pasa cada elemento delCollection
a la devolución de llamada pasada. El valor devuelto por cada llamada de la devolución de llamada crea el nuevoCollection
generado por elmap()
método.collect($user->toArray())
es simplemente construir un nuevo, temporal aCollection
partir de losUsers
atributos.->only(['id', 'name', 'email'])
reduce el temporalCollection
a solo los atributos especificados.->all()
convierte laCollection
copia temporal en una matriz simple.Póngalo todo junto y obtendrá "Para cada usuario de la colección de usuarios, devuelva una matriz de solo los atributos de identificación, nombre y correo electrónico".
Actualización de Laravel 5.5
Laravel 5.5 agregó un
only
método en el Modelo, que básicamente hace lo mismo que elcollect($user->toArray())->only([...])->all()
, por lo que esto se puede simplificar ligeramente en 5.5+ para:// 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']); });
Si combina esto con la "mensajería de orden superior" para las colecciones introducidas en Laravel 5.4, se puede simplificar aún más:
// 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']);
fuente
::get([ 'fields', 'array' ])
comportamiento de QueryBuilder . Básicamente, sería útil para "vertederos".$newArray = Arr::only($initialArray, [ 'id', 'name' /* allowed keys list */ ]);
$users->map->only(['column', ...])
use
User::get(['id', 'name', 'email'])
, le devolverá una colección con las columnas especificadas y si desea convertirlo en una matriz, solo usetoArray()
después delget()
método así:User::get(['id', 'name', 'email'])->toArray()
La mayoría de las veces, no necesitará convertir la colección en una matriz porque las colecciones son en realidad matrices con esteroides y tiene métodos fáciles de usar para manipular la colección.
fuente
->get(['id', 'email', 'name'])
que no volverá a consultar la base de datos, pero devolverá una nueva colección con solo los valores seleccionados. Métodos como pluck / list, get, first, etc.tienen los mismos nombres tanto en la clase del generador de consultas como en la clase de colección.El método siguiente también funciona.
$users = User::all()->map(function ($user) { return collect($user)->only(['id', 'name', 'email']); });
fuente
Ahora he encontrado una solución propia para esto:
1. Creó una función general para extraer atributos específicos de matrices
La función a continuación extrae solo atributos específicos de una matriz asociativa o una matriz de matrices asociativas (la última es lo que obtiene al hacer $ collection-> toArray () en Laravel).
Se puede usar así:
$data = array_extract( $collection->toArray(), ['id','url'] );
Estoy usando las siguientes funciones:
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; }
Esta solución no se centra en las implicaciones de rendimiento al recorrer las colecciones en grandes conjuntos de datos.
2. Implementar lo anterior a través de una colección personalizada en Laravel
Como me gustaría poder hacer simplemente
$collection->extract('id','url');
en cualquier objeto de colección, he implementado una clase de colección personalizada.Primero creé un modelo general, que extiende el modelo Eloquent, pero usa una clase de colección diferente. Todos sus modelos necesitan ampliar este modelo personalizado, y no el modelo 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 ); } } ?>
En segundo lugar, creé la siguiente clase de colección personalizada:
<?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 ); } } ?>
Por último, todos los modelos deberían extender su modelo personalizado en su lugar, como tal:
<?php namespace App\Models; class Article extends Model { ...
Ahora las funciones del no. 1 de arriba son utilizados cuidadosamente por la colección para que el
$collection->extract()
método esté disponible.fuente
Necesita definir
$hidden
y$visible
atributos. Se establecerán de forma global (eso significa que siempre devolverán todos los atributos de la$visible
matriz).Usando el método
makeVisible($attribute)
ymakeHidden($attribute)
puede cambiar dinámicamente los atributos ocultos y visibles. Más: Eloquent: Serialización -> Modificar temporalmente la visibilidad de la propiedadfuente
Tuve un problema similar en el que necesitaba seleccionar valores de una matriz grande, pero quería que la colección resultante solo contuviera valores de un solo valor.
pluck()
podría usarse para este propósito (si solo se requiere 1 elemento clave)también podrías usar
reduce()
. Algo como esto con reducir:$result = $items->reduce(function($carry, $item) { return $carry->push($item->getCode()); }, collect());
fuente
este es un seguimiento del patricus [respuesta] [1] anterior, pero para matrices anidadas :
$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();
fuente
DB::table('roles')->pluck('title', 'name');
Referencias: https://laravel.com/docs/5.8/queries#retrieving-results (buscar pluck)
fuente
esto parece funcionar, pero no estoy seguro de si está optimizado para el rendimiento o no.
$request->user()->get(['id'])->groupBy('id')->keys()->all();
salida:
array:2 [ 0 => 4 1 => 1 ]
fuente
$users = Users::get(); $subset = $users->map(function ($user) { return array_only(user, ['id', 'name', 'email']); });
fuente