Robert Fridzema
Robert Fridzema

Reputation: 523

Laravel paginate append attributes

I am rebuilding my search function from php to vue.

In php i used to add attributes my model class for example a html label, thumbnail etc. I could just grab that with $model->HtmlThumb and the attributes will be loaded. Now since i get a json response back the label are not present there. I know i could simply use the $appends property on the model class.

The problem is that the attribute always is appended, what i definitely not want.

I know i can manually hide these attributes with hideAttributes or something like that, but it is not convenient to do this in an existing app.

This way it should work but now it returns an array of items instead of the paginator collection.

$results = $db_query->paginate($num_of_results)->appends($result)->each(function($project){
    $result->setAppends([
        'ProjectDescription',
        'FileCountLabel',
    ]);
});

I am looking for a way to do this properly.

Upvotes: 4

Views: 5377

Answers (2)

rh16
rh16

Reputation: 1073

The accepted answer by the OP is correct and works, but I want to present an alternative answer that has two advantages:

  1. It saves having to create a new ResourceCollection for every model type and every combination of appended attributes.

  2. The responses from the paginator will be formatted in the same way as from a response from $db_query->paginate($num_of_results) (ResourceCollection by default moves the pagination metadata into two subobjects in the response, links and meta, meaning the code that consumes the API would need to be refactored).

The idea is to create a single generic ResourceCollection which allows us to control the appended attributes dynamically:

app/Resources/AppendablePaginator.php

<?php

namespace App\Http\Resources;

use Illuminate\Support\Arr;
use Illuminate\Http\Resources\Json\PaginatedResourceResponse;
use Illuminate\Http\Resources\Json\ResourceCollection;
use Illuminate\Pagination\AbstractPaginator;

class AppendablePaginator extends ResourceCollection
{
    private $appends = [];

    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'data' => $this->collection->each->append($this->appends)
        ];
    }

    /**
     * Create an HTTP response that represents the object.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function toResponse($request)
    {
        return $this->resource instanceof AbstractPaginator
                    ? (new FlattenedPaginatedResourceResponse($this))->toResponse($request)
                    : parent::toResponse($request);
    }

    /**
     * Append to the underlying models
     * @param  array|string|null  $key
     */
    public function modelAppend($key) {
        if (is_null($key)) {
            return $this;
        }

        if (is_array($key)) {
            $this->appends = array_merge($this->appends, $key);
        } else {
            $this->appends[] = $key;
        }
        
        return $this;
    }
}

class FlattenedPaginatedResourceResponse extends PaginatedResourceResponse {
    /**
     * Add the pagination information to the response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    protected function paginationInformation($request)
    {
        $paginated = $this->resource->resource->toArray();
        return Arr::except($paginated, ['data']);
    }
}

And now we can append dynamically:

$results = (new AppendablePaginator($db_query->paginate($num_of_results))->modelAppend([
    'ProjectDescription',
    'FileCountLabel'
]);

Meaning we can easily change the appended attributes in place, and since this code will for for any model type, we don't have to worry about generating new collections.

Upvotes: 4

Robert Fridzema
Robert Fridzema

Reputation: 523

I just fixed it with a ResourceCollection like this:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\ResourceCollection;

class ProjectCollection extends ResourceCollection
{
    /**
     * Transform the resource collection into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        // return parent::toArray($request);

        return [
          'data' => $this->collection->each(function($model){
            $model->setAppends([
                'ProjectDescription',
                'FileCountLabel',
            ]);
          }),
        ];
    }
}
$results = new ProjectCollection($db_query->paginate($num_of_results)->appends($appends));

Upvotes: 1

Related Questions