2016-07-16 137 views
15

Sto rivedendo la documentazione e le API per laravel collezioni, ma non mi sembra di trovare ciò che sto cercando:Ottenere attributi specifici con solo dal laravel Collection

Vorrei recuperare una matrice con il modello dati da una raccolta, ma ottengono solo attributi specificati.

I.e. qualcosa come Users::toArray('id','name','email'), in cui la raccolta contiene tutti gli attributi per gli utenti, perché sono usati altrove, ma in questo specifico luogo ho bisogno di un array con userdata e solo gli attributi specificati.

Non sembra esserci un aiuto per questo a Laravel? - Come posso farlo nel modo più semplice?

risposta

40

È possibile farlo utilizzando una combinazione di metodi esistenti Collection. All'inizio potrebbe essere un po 'difficile da seguire, ma dovrebbe essere abbastanza facile da abbattere.

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

Spiegazione

In primo luogo, il metodo map() fondamentalmente solo scorre la Collection, e passa ogni elemento della Collection per il passato in callback. Il valore restituito da ogni chiamata del callback crea il nuovo Collection generato dal metodo map().

collect($user->toArray()) sta creando un nuovo, temporaneo Collection degli attributi Users.

->only(['id', 'name', 'email']) riduce temporaneamente lo Collection solo agli attributi specificati.

->all() trasforma temporaneamente il Collection in un array semplice.

Metti tutto insieme e ottieni "Per ogni utente della collezione di utenti, restituisci un array con solo gli attributi id, name e email".

+0

Grazie! Ho contrassegnato questo come la risposta accettata, perché funziona come un fascino con funzionalità Laravel esistenti. - Ora, questo potrebbe essere reso disponibile alla collezione tramite una classe di raccolta personalizzata, come ho descritto nella mia soluzione alternativa. – preyz

1
  1. è necessario definire $hidden e $visible attributi. Saranno impostati globali (che significa sempre restituire tutti gli attributi dall'array $visible).

  2. Utilizzando il metodo makeVisible($attribute) e makeHidden($attribute) è possibile modificare dinamicamente gli attributi nascosti e visibili. Più: Eloquent: Serialization -> Temporarily Modifying Property Visibility

+0

Non voglio che questo sia globale, ma selezionare gli attributi per estrarre dalla raccolta in modo dinamico. – preyz

5

uso User::get(['id', 'name', 'email']), lo restituirà una raccolta con le colonne specificate e se si vuole fare un array, basta usare toArray() dopo il metodo get() in questo modo:

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

La maggior parte delle volte, non è necessario convertire la raccolta in un array perché le raccolte sono in realtà array su steroidi e si dispone di metodi facili da usare per manipolare la raccolta.

+0

La raccolta è già stata creata da una query precedente, in cui sono necessari più attributi. Pertanto ho bisogno di generare un array di dati in cui ho solo attributi specifici. – preyz

+0

Puoi rendere dinamica la tua query passando una serie di colonne da restituire o puoi fare un altro '-> get (['id', 'email', 'name'])' che non rasterizza il database ma restituirà una nuova raccolta con solo i valori selezionati. Metodi come pluck/lists, get, first, etc. hanno lo stesso nome in entrambi, la classe del builder di query e la classe di raccolta. – Ruffles

+0

Ruffles, che non funziona in Laravel 5.3. - Il metodo "get" del generatore di query prende infatti le colonne, ma esegue anche un'altra query. Il metodo "get" della collezione può estrarre un modello specifico per chiave dalla raccolta, che non è quello che sto cercando qui. – preyz

2

Sono giunto con una propria soluzione a questo:

1. Creazione di una funzione generale per estrarre gli attributi specifici da matrici

La funzione qui sotto estratto unici attributi specifici da un array associativo, o una serie di array associativi (l'ultimo è ciò che ottieni quando fai $ collection-> toArray() in Laravel).

Esso può essere utilizzato in questo modo:

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

Sto usando le seguenti funzioni:

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

Questa soluzione non si concentra sulle implicazioni di prestazioni in loop attraverso le collezioni di grandi set di dati.

2. la realizzazione degli scenari tramite una raccolta personalizzata i laravel

Dal momento mi piacerebbe essere in grado di fare semplicemente $collection->extract('id','url'); su qualsiasi oggetto di raccolta, ho implementato una classe insieme personalizzato.

Prima ho creato un modello generale, che estende il modello Eloquent, ma utilizza una classe di raccolta diversa. Tutti i modelli devono estendere questo modello personalizzato e non il modello 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); 
    }  
} 
?> 

In secondo luogo ho creato la seguente classe di insieme personalizzata:

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

Infine, tutti i modelli dovrebbero quindi estendere il proprio modello personalizzato invece, come ad esempio:

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

Ora le funzioni da non . 1 qui sopra sono ordinatamente utilizzati dalla collezione per rendere disponibile il metodo $collection->extract().

+0

Consulta la documentazione di Laravel sull'estensione delle raccolte: https://laravel.com/docs/5.5/collections#extending-collections –

0

sembra funzionare, ma non è sicuro se sia ottimizzato per le prestazioni o meno.

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

uscita:

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

Sto assumendo aver installato l'connessione DB e sta costruendo la query nel controller con tutto in ordine.

si può solo fare questo ... :)

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

dd($user_details); 
0

ho avuto un problema simile in cui avevo bisogno di selezionare i valori da una vasta gamma, ma ho voluto la collezione risultante contenga solo valori di un singolo valore.

pluck() potrebbero essere utilizzati per questo scopo (anche se solo 1 articolo chiave è necessaria)

si potrebbe anche usare reduce(). Qualcosa come questo con riduzione:

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