Laravel Controller use callAction to meke ApiController
Published: 2022-07-29 14:00:00

In most projects, the controller will be used to return the response to specify JSON to determine the content.

I found that in laravel's routing, ControllerDispatcher uses callback function to call the corresponding Method

`Illuminate\Routing\ControllerDispatcher`

```php
    public function dispatch(Route $route, $controller, $method)
    {
        $parameters = $this->resolveClassMethodDependencies(
            $route->parametersWithoutNulls(), $controller, $method
        );

        if (method_exists($controller, 'callAction')) {
            return $controller->callAction($method, $parameters);
        }

        return $controller->{$method}(...array_values($parameters));
    }
```

You can see that the Controller will check whether your Controller has a function called `callAtion` before executing it. If not, it will return and call the Controller method specified by the route.

This led me to an idea that when Controller implements API or WEB routing, it must return a response to display content

Among them, most people will unify json for API return, but the implementation methods are quite diverse.

I have seen the [Response::macro](https://medium.com/@JoubranJad/laravel-response-macros-for-apis-260198b3114f) method, or the [ApiResponseTrait](https://dev.to/bawa_geek/laravel-api-the-best-way-to-return-the-uniform-response-for-all-type-of-requests-2ao1) method, but this method will make me feel difficult to maintain later,
 
You will see that your Controller will have a lot of judgments to return 200 or 400 or 200 + content of http status code.

### API Controller

In my own project, I built an ApiController, let all the content under the API folder in the route inherit ApiController, intercept all results, and convert them into API Response 

`ApiController`

```php
    public function callAction($method, $parameters)
    {
        $result =  $this->{$method}(...array_values($parameters));
        $httpStatus = ($method  === 'store') ? 201 : 200;
        if ($result instanceof ResponseSchema) {
            $response = &$result;
        } else {
            $response = new ResponseSchema($result);
        }
        $response->setStatus($httpStatus);
        return response()->json($response, $httpStatus);
    }
```

I created a ResponseSchema Class, specifically defined the JSON Schema format, and then returned it uniformly. The content returned by the Controller will be the result of successful execution. The only small difference is that the newly added method is unified and I call it store, which will put http status code returns 201.

Since only successful information is returned here, the Controller does not need to write a bunch of try catches, and can focus on returning the necessary information.

### Error Handler 
How to handle error handling? According to [the-exception-handler](https://laravel.com/docs/8.x/errors#the-exception-handler) mentioned on the official website, there is a special error handling Handler for errors, so only `App\Exceptions\Handler` , This program focuses on handling errors. Loading ResponseSchema in the render function and spit out the corresponding method can unify the JSON Response Schema.

```php
    public function render($request, Throwable $exception)
    {
        $responseSchema = new ResponseSchema();
        $statusCode = 500;
        $responseSchema->setException($exception);
        $responseSchema->setMessage('Server Error');
        $responseSchema->setStatus($statusCode);
        if ($request->is(['api/*', 'oauth/*'])) {
            $request->headers->set('Accept', 'application/json');
            return response($responseSchema->toArray(), $statusCode);
        }
        return parent::render($request, $exception);
    }

```

At present, this method is my current method of implementing API Response, so that I can focus on Controller writing and achieve the purpose of hierarchical division of labor