phplaravelresourcesextend

Laravel, Reusable API Resource method


I've created an API Resource and in the toArray() method, I've filtered which attributes get returned via query strings. I want to be able to do this for most of my API Resources so it makes sense to move this into a reusable method. What would be the best way of doing this?

I was thinking about extending the base Resource but I'm not sure how I would go about doing that. Or should it be moved into a service class or repository?

My method is below;

public function toArray($request)
{
    if($request->fields) {
        $fields         = [];
        $selectedFields = explode(',', $request->fields);
        foreach($selectedFields as $field) {
            if(isset($this->{$field})) {
                $fields[$field] = $this->{$field};
            }
        }
        return $fields;
    } else {
        return parent::toArray($request);
    }
}

Ideally, I would like to do something like...

public function toArray($request)
{
    if($request->fields) {
        return parent::filterFields($request); // Not sure whether it should be parent::, $this or something else?
    } else {
        return parent::toArray($request); // Not sure whether it should be parent::, $this or something else?
    }

    // or event just
    return parent::filterFields($request); // Not sure whether it should be parent::, $this or something else?
}

Full Classes:

ProjectResource.php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\Resource;

class ProjectResource extends Resource
{

    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request
     * @return array
     */
    public function toArray($request)
    {
        dd(parent::filterFields());
        // or
        dd($this->filterFields());

        if($request->fields) {
            $fields         = [];
            $selectedFields = explode(',', $request->fields);
            foreach($selectedFields as $field) {
                if(isset($this->{$field})) {
                    $fields[$field] = $this->{$field};
                }
            }
            return $fields;
        } else {
            return parent::toArray($request);
        }
    }
}

Test.php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\Resource;

class Test extends Resource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request
     * @return array
     */
    public function filterFields($request)
    {
        return 'test';
    }
}

Solution

  • If you're extending the class, you can use either parent:: or $this->. The difference being parent will always refer to the parent class, $this will only refer to the parent if the method does not exist on the current class. Therefore, using $this allows extension of methods and properties.

    Example:

    <?php
    class A {
        public function test() {
            echo 'A';
        }
    }
    
    class B extends A {
        public function test() {
            echo 'B';
        }
    
        public function callParent(){
            parent::test();
        }
    
        public function callThis() {
            $this->test();
        }
    }
    
    
    $b = new B();
    $b->callParent(); // Echoes A
    $b->callThis(); // Echos B
    

    In your updated class representation, you have both classes extending Resource but ProjectResource will have no idea about the methods in Test. You would need ProjectResource to extend Test, which will also inherit methods from Resource since Test extends Resource (multiple inheritence).


    Beyond that, how you implement is going to be opinion based and you have lots of options. You can extend an abstract class, you can use a dedicated filtering class, or you can use a trait.