Skip to content

[Bug]: fields specified in with method of Resources are not included in the generated spec #955

Description

@elnurvl

What happened?

Some resources may have defined a with method to include additional fields in responses:
https://laravel.com/docs/12.x/eloquent-resources#top-level-meta-data

The generated spec misses those fields. Is this intended?

How to reproduce the bug

<?php

namespace App\Http\Resources\V1;

use App\Models\Task;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

/**
 * @mixin Task
 */
class TaskResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @return array<string, mixed>
     */
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'user' => new UserResource($this->user),
            'title' => $this->title,
            'status' => $this->status,
            'priority' => $this->priority,
            /** @format date */
            'due_date' => $this->due_date?->format('Y-m-d'),
            'assignee' => new UserResource($this->assignee),
            'tags' => TagResource::collection($this->tags),
        ];
    }

    /**
     * @return array<string, mixed>
     */
    public function with($request): array
    {
        return [
            'data' => [
                'description' => $this->description,
                'created_at' => $this->created_at?->toRfc3339String(),
                'updated_at' => $this->updated_at?->toRfc3339String(),
                'metadata' => $this->metadata,
            ],
        ];
    }
}

Package Version

0.12.34

PHP Version

8.4.12

Laravel Version

12.28.1

Which operating systems does with happen with?

macOS

Notes

No response

Activity

  1. romalytvynenko commented on Sep 14, 2025

    @romalytvynenko
    Member

    @elnurvl please show your controller as well

  2. romalytvynenko commented on Sep 14, 2025

    @romalytvynenko
    Member

    Closing the issue as it clearly works.

    Feel free to reopen, but please make sure that you have the actual bug and you provide a way for me to reproduce. Make sure that the actual API endpoint return the data that Scramble does not document.

  3. elnurvl commented on Sep 14, 2025

    @elnurvl
    Author

    I am reopening the issue as it clearly does not work for me. I would at least like to know what I am doing wrong: if this is the case, please change the label, but I think it would deserve to be addressed as a documentation issue at that point.

    app/Http/Controllers/V1/TaskController.php:

    <?php
    
    namespace App\Http\Controllers\V1;
    
    use App\Http\Controllers\Controller;
    use App\Http\Resources\V1\TaskResource;
    use App\Models\Task;
    use Illuminate\Support\Facades\Gate;
    
    class TaskController extends Controller
    {
        /**
         * Get the task in detail
         */
        public function show(Task $task): TaskResource
        {
            return new TaskResource($task);
        }
    }

    app/Http/Resources/V1/TaskResource:

    <?php
    
    namespace App\Http\Resources\V1;
    
    use App\Models\Task;
    use Illuminate\Http\Request;
    use Illuminate\Http\Resources\Json\JsonResource;
    
    /**
     * @mixin Task
     */
    class TaskResource extends JsonResource
    {
        /**
         * Transform the resource into an array.
         *
         * @return array<string, mixed>
         */
        public function toArray(Request $request): array
        {
            return [
                'id' => $this->id,
                'user' => new UserResource($this->user),
                'title' => $this->title,
                'status' => $this->status,
                'priority' => $this->priority,
                /** @format date */
                'due_date' => $this->whenNotNull($this->due_date?->format('Y-m-d'), null),
                'assignee' => $this->whenNotNull(new UserResource($this->assignee), null),
                /**
                 * Visible to admins only
                 *
                 * @format date-time
                 */
                'deleted_at' => $this->when(auth()->user()->is_admin ?? false, $this->deleted_at?->toRfc3339String()),
            ];
        }
    
        /**
         * @return array<string, mixed>
         */
        public function with($request): array
        {
            return [
                'data' => [
                    'description' => $this->description,
                    /** @format date-time */
                    'created_at' => $this->created_at?->toRfc3339String(),
                    /** @format date-time */
                    'updated_at' => $this->whenNotNull($this->updated_at?->toRfc3339String(), null),
                    'metadata' => $this->metadata,
                ],
            ];
        }
    }

    routes/api.php:

    <?php
    
    use App\Http\Controllers\V1\TaskController;
    use Illuminate\Support\Facades\Route;
    
    Route::prefix('v1')->group(function () {
        Route::get('tasks/{task}', [TaskController::class, 'show']);
    });
    Image Image
  4. elnurvl commented on Sep 14, 2025

    @elnurvl
    Author

    Turns out I do not have a permission to reopen the issue. 🥲

  5. romalytvynenko commented on Sep 14, 2025

    @romalytvynenko
    Member

    Thanks! Apparently the issue here is that you wrap your additional data in 'data' key and this confuses Scramble because that's the wrap key.

    The easiest thing to do is to not wrap it in that key.

    I'm still considering it as a bug, so reopened the issue

  6. romalytvynenko commented on Sep 14, 2025

    @romalytvynenko
    Member

    The resource schema documentation is correct though. Only toArray stuff is the part of the schema. top level additional data is applied only when the resource is the response itself

  7. elnurvl commented on Sep 14, 2025

    @elnurvl
    Author

    @romalytvynenko , additional data is specified with additional method of the resource and intended to be used from within specific controller actions and for that I agree it belongs to the response.

    As for with, in my opinion, it is being defined inside the resource and conditioned by the resource making the fields part of that resource.

    Basically, when you define fields in this method, those fields only appears with singular resource responses(GET@users/{user}) and omitted for list responses(GET@users). This makes it an especially useful feature if you want to define a light-weight variant of a resource for big lists without having to create a separate collection resource. The exclusive fields still become part of the resource, don't they?

  8. romalytvynenko commented on Sep 14, 2025

    @romalytvynenko
    Member

    @elnurvl the use case you have is nice indeed. It really is useful to have light/expanded variant of resources.

    Speaking of with, here is what Laravel documentation says:

    Sometimes you may wish to only include certain meta data with a resource response if the resource is the outermost resource being returned. Typically, this includes meta information about the response as a whole. To define this meta data, add a with method to your resource class.

    Source: https://laravel.com/docs/12.x/eloquent-resources#top-level-meta-data

    Based on this, whatever is in with is treated the same way as whatever you pass to additional. Well, like the framework itself treats it. Also, when you use the resource in some other resource, only the stuff from toArray will be in other resource's data.

    So while it does make sense to include with as a part of schema in your specific case, I don't plan to make it a default behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions