orchestral / tenanti

[Package] Multi-tenant Database Schema Manager for Laravel
MIT License
587 stars 53 forks source link

Route Model Binding type error with Route::resource() #43

Closed kamihouse closed 7 years ago

kamihouse commented 7 years ago

Hello. I'm developing an API using Laravel's Resource Controllers features and using Orchestral Multi-tenant Database Schema Manager as a Single Database.

In my control methods I am using the Route Model Binding to injecting a model ID to a route or controller action, often query to retrieve the model that corresponds to that ID.

Some relevant routes from my API:

| POST      | api/v1/{tenant}/fornecedores                     | store  | App\Http\Controllers\Api\Fornecedor\FornecedorController@store  | api,tenant,jwt.auth |
| GET|HEAD  | api/v1/{tenant}/fornecedores                     | index  | App\Http\Controllers\Api\Fornecedor\FornecedorController@index  | api,tenant,jwt.auth |
| GET|HEAD  | api/v1/{tenant}/fornecedores/count               |        | App\Http\Controllers\Api\Fornecedor\FornecedorController@count  | api,tenant,jwt.auth |
| PUT|PATCH | api/v1/{tenant}/fornecedores/{fornecedor}        | update | App\Http\Controllers\Api\Fornecedor\FornecedorController@update | api,tenant,jwt.auth |
| GET|HEAD  | api/v1/{tenant}/fornecedores/{fornecedor}        | show   | App\Http\Controllers\Api\Fornecedor\FornecedorController@show   | api,tenant,jwt.auth |
| GET|HEAD  | api/v1/{tenant}/fornecedores/{fornecedor}/audits |        | App\Http\Controllers\Api\Fornecedor\FornecedorController@audits | api,tenant,jwt.auth |
...

An example of my API route file:

// API V1
Route::group(['prefix' => 'v1/{tenant}', 'middleware' => 'tenant'], function () {

    // Fornecedores
    Route::group(['prefix' => 'fornecedores'], function () {
        Route::get('count', 'Api\Fornecedor\FornecedorController@count');
        Route::get('{fornecedor}/audits', 'Api\Fornecedor\FornecedorController@audits');

        Route::resource('/', 'Api\Fornecedor\FornecedorController', [
            'parameters' => ['' => 'fornecedor'],
            'except' => ['create', 'edit', 'destroy']
        ]);
    });
});

These are some methods of my FornecedorController.php:

public function index()
{
    return $this->fornecedorService->getFornecedoresPaginate();
}

// This is line 48 (throw below):
public function show(Fornecedor $fornecedor)
{
    return $fornecedor;
}

However when trying to access the URL that uses the Route Model Binding I get the following error:

URL: /api/v1/1/fornecedores/1

FatalThrowableError in FornecedorController.php line 48:
Type error: Argument 1 passed to App\Http\Controllers\Api\Fornecedor\FornecedorController::show() must be an instance of App\Models\Fornecedor, string given

And this is the all trace:

in FornecedorController.php line 48
at FornecedorController->show('1', object(Fornecedor))
at call_user_func_array(array(object(FornecedorController), 'show'), array('tenant' => '1', 'fornecedor' => object(Fornecedor))) in Controller.php line 55
at Controller->callAction('show', array('tenant' => '1', 'fornecedor' => object(Fornecedor))) in ControllerDispatcher.php line 44
at ControllerDispatcher->dispatch(object(Route), object(FornecedorController), 'show') in Route.php line 203
at Route->runController() in Route.php line 160
at Route->run() in Router.php line 559
at Router->Illuminate\Routing\{closure}(object(Request)) in Pipeline.php line 30
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in GetUserFromToken.php line 46
at GetUserFromToken->handle(object(Request), object(Closure)) in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in VerifyTenant.php line 25
at VerifyTenant->handle(object(Request), object(Closure)) in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in SubstituteBindings.php line 41
at SubstituteBindings->handle(object(Request), object(Closure)) in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in ThrottleRequests.php line 49
at ThrottleRequests->handle(object(Request), object(Closure), '60', '1') in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in Pipeline.php line 102
at Pipeline->then(object(Closure)) in Router.php line 561
at Router->runRouteWithinStack(object(Route), object(Request)) in Router.php line 520
at Router->dispatchToRoute(object(Request)) in Router.php line 498
at Router->dispatch(object(Request)) in Kernel.php line 174
at Kernel->Illuminate\Foundation\Http\{closure}(object(Request)) in Pipeline.php line 30
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in HandleCors.php line 36
at HandleCors->handle(object(Request), object(Closure)) in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in TransformsRequest.php line 30
at TransformsRequest->handle(object(Request), object(Closure)) in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in TransformsRequest.php line 30
at TransformsRequest->handle(object(Request), object(Closure)) in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in ValidatePostSize.php line 27
at ValidatePostSize->handle(object(Request), object(Closure)) in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in CheckForMaintenanceMode.php line 46
at CheckForMaintenanceMode->handle(object(Request), object(Closure)) in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in Pipeline.php line 102
at Pipeline->then(object(Closure)) in Kernel.php line 149
at Kernel->sendRequestThroughRouter(object(Request)) in Kernel.php line 116
at Kernel->handle(object(Request)) in index.php line 53

I could verify that there is apparently a confusion when retrieving the parameters. When inspecting route parameters, this is the result:

public function show($fornecedor)
{
    dd(\Route::current()->parameters());
}

URL: /api/v1/1/fornecedores/18

Result:

array:2 [
  "tenant" => "1"
  "fornecedor" => "18"
]

And when trying to retrieve the provider's route:

public function show($fornecedor)
{
    dd($fornecedor);
}

URL: /api/v1/1/fornecedores/18

Result:

"1"

Even using bind the parameter I get is not correct.
The other routes that do not use a second parameter are ok.

Thanks!

kamihouse commented 7 years ago

If I use the below example, its ok:

public function show($tenant, Fornecedor $fornecedor)
{
    return $fornecedor;
}

Can it be a problem with the grouping routes?

crynobone commented 7 years ago

This is strictly related to Laravel routing system, and therefore has nothing to do with Tenanti.