knuckleswtf / scribe

Generate API documentation for humans from your Laravel codebase.✍
https://scribe.knuckles.wtf/laravel/
MIT License
1.59k stars 281 forks source link

Route model binding issue on Request #296

Closed jayceedaily closed 2 years ago

jayceedaily commented 2 years ago

What happened?

  1. I created the following files
    
    // CONTROLLER
    use App\Http\Requests\TenantUpdateRequest;

class TenantController extends Controller { public function update(TenantUpdateRequest $request, Tenant $tenant) { $tenant->update($request->validated());

    return response([
        'message'=> trans('tenant.updated'),
        'data' => $tenant
    ]);
}

}

// REQUEST class TenantUpdateRequest extends FormRequest { public function authorize() { return true; }

public function rules()
{
    $tenant = request()->route('tenant');

    return [
        'id' => 'required|string|max:32|min:3|unique:tenants,id, ' . $tenant->id
    ];
}

}

// ROUTE Route::put('tenant/{tenant}', [TenantController::class, 'update'])->name('edit');

2. Then I ran `php artisan scribe:generate` ...
3. But got an error on the request file

**Screenshots and stack traces:**
![image](https://user-images.githubusercontent.com/25096029/128514054-458d70b2-488b-4276-9ebc-b3782e2bc2e0.png)

**My environment:**
 - PHP version: 8.0.5
 - Framework: Laravel 
 - Laravel version: 8.53.1
 - Scribe version: 3.8.0 

**My Scribe config (minus the comments):**
```php
<?php

use Knuckles\Scribe\Extracting\Strategies;

return [

    'theme' => 'default',
    'title' => null,
    'description' => '',
    'base_url' => null,
    'routes' => [
        [
            'match' => [
                'prefixes' => ['api/*'],
                'domains' => ['*'],
                'versions' => ['v1'],
            ],
            'include' => [
            ],
            'exclude' => [
            ],
            'apply' => [
                'headers' => [
                    'Content-Type' => 'application/json',
                    'Accept' => 'application/json',
                ],
                'response_calls' => [
                    'methods' => ['GET'],
                    'config' => [
                        'app.env' => 'documentation',
                    ],
                    'queryParams' => [
                    ],
                    'bodyParams' => [
                    ],
                    'fileParams' => [
                    ],
                    'cookies' => [
                    ],
                ],
            ],
        ],
    ],
    'type' => 'laravel',
    'static' => [
        'output_path' => 'public/docs',
    ],
    'laravel' => [
        'add_routes' => true,
        'docs_url' => '/docs',
        'middleware' => [],
    ],

    'try_it_out' => [
        'enabled' => true,
        'base_url' => null,
    ],
    'auth' => [
        'enabled' => true,
        'default' => true,
        'in' => 'bearer',
        'name' => 'key',
        'use_value' => env('SCRIBE_AUTH_KEY'),
        'placeholder' => '{YOUR_AUTH_KEY}',
        'extra_info' => 'You can retrieve your token by visiting your dashboard and clicking <b>Generate API token</b>.',
    ],
    'intro_text' => <<<INTRO
This documentation aims to provide all the information you need to work with our API.

<aside>As you scroll, you'll see code examples for working with the API in different programming languages in the dark area to the right (or as part of the content on mobile).
You can switch the language used with the tabs at the top right (or from the nav menu at the top left on mobile).</aside>
INTRO
    ,
    'example_languages' => [
        'bash',
        'javascript',
    ],
    'postman' => [
        'enabled' => true,
        'overrides' => [
        ],
    ],
    'openapi' => [
        'enabled' => true,
        'overrides' => [
        ],
    ],
    'default_group' => 'Endpoints',
    'logo' => false,
    'faker_seed' => null,
    'strategies' => [
        'metadata' => [
            Strategies\Metadata\GetFromDocBlocks::class,
        ],
        'urlParameters' => [
            Strategies\UrlParameters\GetFromLaravelAPI::class,
            Strategies\UrlParameters\GetFromLumenAPI::class,
            Strategies\UrlParameters\GetFromUrlParamTag::class,
        ],
        'queryParameters' => [
            Strategies\QueryParameters\GetFromFormRequest::class,
            Strategies\QueryParameters\GetFromInlineValidator::class,
            Strategies\QueryParameters\GetFromQueryParamTag::class,
        ],
        'headers' => [
            Strategies\Headers\GetFromRouteRules::class,
            Strategies\Headers\GetFromHeaderTag::class,
        ],
        'bodyParameters' => [
            Strategies\BodyParameters\GetFromFormRequest::class,
            Strategies\BodyParameters\GetFromInlineValidator::class,
            Strategies\BodyParameters\GetFromBodyParamTag::class,
        ],
        'responses' => [
            Strategies\Responses\UseTransformerTags::class,
            Strategies\Responses\UseResponseTag::class,
            Strategies\Responses\UseResponseFileTag::class,
            Strategies\Responses\UseApiResourceTags::class,
            Strategies\Responses\ResponseCalls::class,
        ],
        'responseFields' => [
            Strategies\ResponseFields\GetFromResponseFieldTag::class,
        ],
    ],

    'fractal' => [
        'serializer' => null,
    ],

    'routeMatcher' => \Knuckles\Scribe\Matching\RouteMatcher::class,

    'database_connections_to_transact' => ['scribe']
];

Additional info: dd(request()->route('tenant')) in TenantUpdateRequest returns the model binding via Postman but only returns the id/integer of the model when running scribe:generate

shalvah commented 2 years ago

Hmmm. I'm not sure where to start with this one. Two things I'm thinking:

  1. Possible that the tenant ID being sent does not exist in the database, so Laravel doesn't bind it? Try dumping the value.
  2. (I think this is more likely.) Postman makes an actual request to the endpoint, so Laravel is able to hydrate the model from the URL parameter. However, the GetFromFormRequest strategy (where your problem is happening) doesn't make a request; it just instantiates the form request, so Laravel probably doesn't get to do any binding.

One way to verify (2) is to disable the GetFromFormRequest strategies and enable response calls for that endpoint, which should properly bind the model.

Not sure of the best approach here. A lot of people apparently want a full-fledged request in the FormRequest strategy, but I don't know how viable that is. I think a workaround here would be to add a check for the type of the $tenant value, maybe?

shalvah commented 2 years ago

There's now an instantiateFormRequestUsing() hook that you can use to customise how your requests are created.

johnvoncolln commented 5 months ago

@jayceedaily hey did you ever get this resolved? I've got a similar situation and also can't get instantiateFormRequestUsing() to work correctly... Any help would be appreciated

matiazar commented 3 months ago

same here. I cant make this work