Obtenga solo atributos específicos de Laravel Collection

8 minutos de lectura

He estado revisando la documentación y la API de Laravel Collections, pero parece que no encuentro lo que busco:

Me gustaría recuperar una matriz con datos de modelo de una colección, pero solo obtener atributos específicos.

es decir algo 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?

  • Otros espectadores podrían estar interesados ​​en 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/colecciones#método-implosión

    – musicin3d

    20 de octubre de 2018 a las 1:24


  • Si está buscando hacer lo contrario y obtener todas las propiedades excepto ciertas propiedades, use excepto Users::get()->except(['password', 'secret_answer']);

    –Cameron Wilby

    18/03/2021 a las 20:36


avatar de usuario
patricio

Puede hacer esto usando una combinación de Collection métodos. Puede ser un poco difícil de seguir al principio, pero debería ser lo suficientemente fácil de descomponer.

// 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() básicamente solo itera a través del Collectiony pasa cada elemento en el Collection al pasado en devolución de llamada. El valor devuelto de cada llamada de la devolución de llamada crea el nuevo Collection generado por el map() método.

collect($user->toArray()) es solo construir un nuevo, temporal Collection fuera de Users atributos

->only(['id', 'name', 'email']) reduce el temporal Collection hasta sólo los atributos especificados.

->all() convierte lo temporal Collection de nuevo en una matriz simple.

Póngalo todo junto y obtendrá “Para cada usuario en 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 en el modelo, que básicamente hace lo mismo que el collect($user->toArray())->only([...])->all()por lo que esto se puede simplificar ligeramente en 5.5+ 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(function ($user) {
    return $user->only(['id', 'name', 'email']);
});

Si combinas esto con el “mensajes de orden superior” para colecciones introducido 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']);

  • ¡Gracias! He marcado esto como la respuesta aceptada, porque funciona de maravilla con la funcionalidad existente de Laravel. – Ahora, esto podría estar disponible para la colección a través de una clase de colección personalizada, como describí en mi solución alternativa.

    – Presa

    18 de julio de 2016 a las 7:48

  • Eso es todo. Pero desearía que hubiera una forma menos detallada de emular la de QueryBuilder ::get([ 'fields', 'array' ]) comportamiento. Básicamente, sería útil para “volcados”.

    – pilat

    18 de mayo de 2018 a las 6:20


  • para arreglos que puedes hacer $newArray = Arr::only($initialArray, [ 'id', 'name' /* allowed keys list */ ]);

    – Brinca brinca

    22 de julio de 2018 a las 9:43


  • En versiones recientes de Laravel esto se puede escribir aún más conciso: $users->map->only(['column', ...])

    – okdewit

    12/10/2018 a las 21:34

  • @ Fx32 Gracias por el comentario. He agregado esta información a la respuesta.

    – patricio

    30 de abril de 2019 a las 21:49

usar User::get(['id', 'name', 'email'])le devolverá una colección con las columnas especificadas y si desea convertirlo en una matriz, simplemente use toArray() después de la get() 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.

  • La colección ya está creada por una consulta anterior, donde se necesitan más atributos. Por lo tanto, necesito generar una matriz de datos donde solo tenga atributos específicos.

    – Presa

    17 de julio de 2016 a las 13:14

  • Puede hacer que su consulta sea dinámica pasando una matriz de columnas para devolver o puede hacer otra ->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 arrancar / listas, obtener, primero, etc. tienen los mismos nombres tanto en la clase de creación de consultas como en la clase de colección.

    – Davor Minchorov

    17 de julio de 2016 a las 15:28

  • Ruffles, eso no funciona en Laravel 5.3. – El método “obtener” del generador de consultas, de hecho, toma columnas, pero también realiza otra consulta. El método “obtener” de la colección puede extraer un modelo específico por clave de la colección, que no es lo que busco aquí.

    – Presa

    17 de julio de 2016 a las 16:21


  • get() siempre devuelve una colección, no importa si es DB::table()->get() o Model::get(), así que si guarda la consulta en una variable y ejecuta el método get en eso $consulta variable, no debería obtener otra consulta a la base de datos.

    – Davor Minchorov

    17/07/2016 a las 16:30

  • Cuando almaceno la consulta en una variable como $query = Model::where(…), ejecuta una consulta a la base de datos cada vez que hago $query->get(). – Además, su sugerencia en mi humilde opinión no es una solución deseada para el problema. Lo resolví con algunas nuevas funciones de matriz que implementé a través de una colección personalizada (ver mi propia respuesta).

    – Presa

    17 de julio de 2016 a las 19:26

El método a continuación también funciona.

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

  1. necesitas definir $hidden y $visible atributos Se establecerán de forma global (eso significa que siempre devuelven todos los atributos de $visible formación).

  2. Usando el método makeVisible($attribute) y makeHidden($attribute) puede cambiar dinámicamente los atributos ocultos y visibles. Más: Elocuente: Serialización -> Modificación temporal de la visibilidad de la propiedad

avatar de usuario
Presa

Ahora he encontrado una solución propia para esto:

1. Creó una función general para extraer atributos específicos de matrices

La siguiente función extrae solo atributos específicos de una matriz asociativa, o una matriz de matrices asociativas (la última es lo que obtienes cuando haces $colección->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 enfoca en las implicaciones de rendimiento al recorrer las colecciones en grandes conjuntos de datos.

2. Implemente lo anterior a través de una colección personalizada i Laravel

Ya que me gustaría poder simplemente hacer $collection->extract('id','url'); en cualquier objeto de colección, he implementado una clase de colección personalizada.

Primero creé un Modelo general, que amplía el modelo Eloquent, pero usa una clase de colección diferente. Todos sus modelos necesitan extender este modelo personalizado, y no el Eloquent Model entonces.

<?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, así:

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

Ahora las funciones de no. 1 arriba son cuidadosamente utilizados por la colección para hacer el $collection->extract() método disponible.

avatar de usuario
cableado00

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

este es un seguimiento del patricus [answer][1] arriba pero para anidado arreglos:

$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();

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad