Erhalten Sie nur bestimmte Attribute mit aus der Laravel-Sammlung

82

Ich habe die Dokumentation und API für Laravel-Sammlungen überprüft, finde aber anscheinend nicht, wonach ich suche:

Ich möchte ein Array mit Modelldaten aus einer Sammlung abrufen, aber nur bestimmte Attribute erhalten.

Dh so etwas wie Users::toArray('id','name','email'), wo die Sammlung tatsächlich alle Attribute für die Benutzer enthält, weil sie an anderer Stelle verwendet werden, aber an dieser bestimmten Stelle benötige ich ein Array mit Benutzerdaten und nur die angegebenen Attribute.

In Laravel scheint es dafür keinen Helfer zu geben? - Wie kann ich das am einfachsten machen?

preyz
quelle
Andere Zuschauer könnten interessiert sein Collection::implode(). Es kann eine Eigenschaft nehmen und aus allen Objekten in der Sammlung extrahieren. Dies beantwortet diese Frage nicht genau, kann aber für andere nützlich sein, die wie ich von Google hierher gekommen sind. laravel.com/docs/5.7/collections#method-implode
musicin3d

Antworten:

173

Sie können dies mit einer Kombination vorhandener CollectionMethoden tun . Es mag zunächst etwas schwierig sein, dem zu folgen, aber es sollte leicht genug sein, um zusammenzubrechen.

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

Erläuterung

Erstens durchläuft die map()Methode im Grunde nur das Collectionund übergibt jedes Element im Collectionan den übergebenen Rückruf. Der von jedem Aufruf des Rückrufs zurückgegebene Wert Collectionerstellt den von der map()Methode generierten neuen Wert .

collect($user->toArray())baut nur ein neues, temporäres Collectionaus den UsersAttributen.

->only(['id', 'name', 'email'])reduziert die temporäre Collectionauf nur die angegebenen Attribute.

->all()verwandelt das temporäre Collectionzurück in ein einfaches Array.

Wenn Sie alles zusammenfügen, erhalten Sie "Geben Sie für jeden Benutzer in der Benutzersammlung ein Array mit nur den Attributen ID, Name und E-Mail zurück."


Laravel 5.5 Update

Laravel 5.5 hat onlydem Modell eine Methode hinzugefügt , die im Grunde das Gleiche tut wie die collect($user->toArray())->only([...])->all(), sodass dies in 5.5+ leicht vereinfacht werden kann, um:

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

Wenn Sie dies mit dem in Laravel 5.4 eingeführten "Messaging höherer Ordnung" für Sammlungen kombinieren , kann dies noch weiter vereinfacht werden:

// 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']);
patricus
quelle
Vielen Dank! Ich habe dies als akzeptierte Antwort markiert, da es mit der vorhandenen Laravel-Funktionalität wie ein Zauber wirkt. - Dies kann nun über eine benutzerdefinierte Sammlungsklasse für die Sammlung verfügbar gemacht werden, wie ich es in meiner alternativen Lösung beschrieben habe.
Preyz
Das ist es. Ich wünschte jedoch, es gäbe eine weniger ausführliche Möglichkeit, das ::get([ 'fields', 'array' ])Verhalten von QueryBuilder zu emulieren . Grundsätzlich wäre es praktisch für "Dumps".
Pilat
für Arrays können Sie tun$newArray = Arr::only($initialArray, [ 'id', 'name' /* allowed keys list */ ]);
Hop Hop
Das ist großartig, aber ich frage mich, wie es funktionieren kann, wenn ich ein verschachteltes Array im Inneren habe
Abbood
1
In neueren Versionen von Laravel kann dies noch prägnanter geschrieben werden:$users->map->only(['column', ...])
okdewit
17

Mit use User::get(['id', 'name', 'email'])wird eine Sammlung mit den angegebenen Spalten zurückgegeben. Wenn Sie daraus ein Array machen möchten, verwenden Sie einfach toArray()die folgende get()Methode:

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

In den meisten Fällen müssen Sie die Sammlung nicht in ein Array konvertieren, da Sammlungen tatsächlich Arrays auf Steroiden sind und Sie über benutzerfreundliche Methoden zum Bearbeiten der Sammlung verfügen.

Rüschen
quelle
1
Die Sammlung wurde bereits von einer vorherigen Abfrage erstellt, bei der weitere Attribute benötigt werden. Daher muss ich ein Datenarray generieren, in dem ich nur bestimmte Attribute habe.
Preyz
Sie können Ihre Abfrage entweder dynamisch gestalten, indem Sie ein Array von Spalten übergeben, die zurückgegeben werden sollen, oder Sie können eine andere ->get(['id', 'email', 'name'])Abfrage ausführen, die die Datenbank nicht anfordert, aber eine neue Sammlung mit nur den ausgewählten Werten zurückgibt. Methoden wie Zupfen / Listen, Erhalten, Erste usw. haben sowohl in der Abfrage-Generatorklasse als auch in der Auflistungsklasse dieselben Namen.
Rüschen
Rüschen, das funktioniert in Laravel 5.3 nicht. - Die Methode "get" des Abfrage-Generators akzeptiert zwar Spalten, führt jedoch auch eine andere Abfrage aus. Die "get" -Methode der Sammlung kann ein bestimmtes Modell per Schlüssel aus der Sammlung extrahieren, was ich hier nicht suche.
Preyz
get () gibt immer eine Auflistung zurück. Es spielt keine Rolle, ob es sich um DB :: table () -> get () oder Model :: get () handelt. Wenn Sie also die Abfrage in einer Variablen speichern und die get-Methode dafür ausführen $ query variable, Sie sollten keine weitere Abfrage an die Datenbank senden.
Rüschen
1
Wenn ich die Abfrage in einer Variablen wie $ query = Model :: where (...) speichere, wird bei jeder $ query-> get () eine Abfrage an die Datenbank ausgeführt. - Auch Ihr Vorschlag imho ist keine gewünschte Lösung für das Problem. Ich habe es mit einigen neuen Array-Funktionen gelöst, die ich über eine benutzerdefinierte Sammlung implementiert habe (siehe meine eigene Antwort).
Beute
3

Die folgende Methode funktioniert auch.

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

Ich habe jetzt eine eigene Lösung dafür gefunden:

1. Erstellt eine allgemeine Funktion zum Extrahieren bestimmter Attribute aus Arrays

Die folgende Funktion extrahiert nur bestimmte Attribute aus einem assoziativen Array oder einem Array von assoziativen Arrays (das letzte erhalten Sie, wenn Sie $ collection-> toArray () in Laravel ausführen).

Es kann folgendermaßen verwendet werden:

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

Ich benutze folgende Funktionen:

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

Diese Lösung konzentriert sich nicht auf die Auswirkungen auf die Leistung beim Durchlaufen der Sammlungen in großen Datenmengen.

2. Implementieren Sie das Obige über eine benutzerdefinierte Sammlung in Laravel

Da ich einfach $collection->extract('id','url');auf jedes Sammlungsobjekt zugreifen möchte , habe ich eine benutzerdefinierte Sammlungsklasse implementiert.

Zuerst habe ich ein allgemeines Modell erstellt, das das Eloquent-Modell erweitert, aber eine andere Auflistungsklasse verwendet. Alle Ihre Modelle müssen dieses benutzerdefinierte Modell erweitern und nicht das eloquente Modell.

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

Zweitens habe ich die folgende benutzerdefinierte Sammlungsklasse erstellt:

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

Schließlich sollten alle Modelle stattdessen Ihr benutzerdefiniertes Modell wie folgt erweitern:

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

Nun sind die Funktionen von Nr. 1 oben werden von der Sammlung ordentlich verwendet, um die $collection->extract()Methode verfügbar zu machen .

preyz
quelle
Lesen
Andreas Hacker
0
  1. Sie müssen definieren $hiddenund $visibleAttribute. Sie werden global festgelegt (dh, es werden immer alle Attribute vom $visibleArray zurückgegeben).

  2. Unter Verwendung der Methode makeVisible($attribute)und makeHidden($attribute)Sie können dynamisch versteckt ändern und sichtbare Attribute. Mehr: Eloquent: Serialisierung -> Vorübergehende Änderung der Sichtbarkeit von Eigenschaften

Grzegorz Gajda
quelle
Ich möchte nicht, dass dies global ist, sondern wähle die Attribute aus, die dynamisch aus der Sammlung extrahiert werden sollen.
Preyz
0

Ich hatte ein ähnliches Problem, bei dem ich Werte aus einem großen Array auswählen musste, aber ich wollte, dass die resultierende Sammlung nur Werte eines einzelnen Werts enthält.

pluck() könnte für diesen Zweck verwendet werden (wenn nur 1 Schlüsselelement erforderlich ist)

Sie könnten auch verwenden reduce(). So etwas mit reduzieren:

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

Dies ist eine Fortsetzung des obigen patricus [Antwort] [1], jedoch für verschachtelte Arrays:

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

Dies scheint zu funktionieren, ist sich aber nicht sicher, ob es für die Leistung optimiert ist oder nicht.

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

Ausgabe:

array:2 [
  0 => 4
  1 => 1
]
Ken
quelle
-3
$users = Users::get();

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