skip to Main Content

What i try: I want to build a collection from a model that has multiple entries. The model has a relation (groups) where I don’t want to output a group (‘Lorem Ipsum’). Since the relation groups has many table columns and I only have id and name I map my result and output the two columns with only().

I try to add a condition to a function. I wonder if there is a function for the Laravel function filter() where you can only get specific fields back. Like for example $someModel->only(['id', 'name']).

Currently I am solving it with a map() after a filter()

$filtered = MainModel::with('relationA')->get()->map(fn (MainModel $mainModel $mainModel) => [
            'id' => $mainModel->getRouteKey(),
            'name' => $mainModel->name,
            'relationAFiltered' => $mainModel->relationA->filter(fn (SomeModel $someModel) => 
    $someModel->name !== 'Lorem Ipsum')->map(fn($g) => $g->only(['id', 'name'])),

2

Answers


  1. I think what you are looking for is to only include certain columns of the relationship, but not to include anything unless it has a certain name?

    To select which columns are included from the related model and apply a condition, the with() method can be used with a closure.

    MainModel::with([
        "relationA" => fn ($q) => $q->select("id", "name")->where("name", "Lorem Ipsum")
    ])
        ->get();
    
    Login or Signup to reply.
  2. Why are you converting your model to the array? Is it for the response?
    If it’s for the response then you should use resources and in this case you can separate your code. For example:

    namespace AppHttpResources;
    
    class MainModelResource extends JsonResource {
        public function toArray($request): array {
            return [
                'id' => $this->resource->id,
                'name' => $this->resource->name,
                'relationA' => $this->whenLoaded(
                    'relationA',
                    fn() => RelationAResource::collection(
                        $this->resource->relationA->filter(
                            fn(RelationA $relationA) => $relationA->name !== 'Lorem Ipsum'
                        )
                    ),
                ),
            ];
        }
    }
    
    class RelationAResource extends JsonResource {
        public function toArray($request): array {
            return [
                'id' => $this->resource->id,
                'name' => $this->resource->name,
            ];
        }
    }
    

    Why resources are better than ordinary arrays:

    • You move the converting to another class (separating the code)
    • You can use the resource in different places (no duplicates)
    • It’s a quite flexible solution (resources themselves)

    If you do it not for the response and you just don’t like to use map + filter several times in your code then you can put this logic to an independent method:

    namespace AppCollections;
    
    class RelationACollection extends IlluminateDatabaseEloquentCollection {
        public function onlyIdAndNameWithoutLorem(): array {
            return $this
                ->filter(
                    fn (RelationA $relationA) => $relationA->name !== 'Lorem Ipsum'
                )
                ->map(
                    fn (RelationA $relationA) => $relationA->only('id', 'name')
                );
        }
    }
    

    Connect your collection with the model

    namespace AppModels;
    
    class RelationA extends Model 
    {
        public function newCollection(array $models = []): RelationACollection   
        {
            return new RelationACollection($models);
        }
    }
    

    Usage:

    $filtered = MainModel::with('relationA')
        ->get()
        ->map(
            fn (MainModel $mainModel) => [
                'id' => $mainModel->getRouteKey(),
                'name' => $mainModel->name,
                'relationAFiltered' => $mainModel->relationA->onlyIdAndNameWithoutLorem()
            ]
        );
    

    Your own collections have advantages and disadvantages:

    • (+) You can move some logic to them
    • (+) You can use your collection like a type everywhere
    • (-) You need to redefine Model::newCollection for each model

    I hope I understood correctly the question

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search