Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to send a response from a method that is not the controller method?

I've got a Controller.php whose show($id) method is hit by a route.

public function show($id)
{
    // fetch a couple attributes from the request ...

    $this->checkEverythingIsOk($attributes);

    // ... return the requested resource.
    return $response;
}

Now, in checkEverythingIsOk(), I perform some validation and authorization stuff. These checks are common to several routes within the same controller, so I'd like to extract these checks and call the method everytime I need to perform the same operations.

The problem is, I'm unable to send some responses from this method:

private function checkEverythingIsOk($attributes)
{
    if (checkSomething()) {
        return response()->json('Something went wrong'); // this does not work - it will return, but the response won't be sent.
    }

    // more checks...
    return response()->callAResponseMacro('Something else went wrong'); // does not work either.

    dd($attributes); // this works.
    abort(422); // this works too.
}

Note: Yes, I know in general one can use middleware or validation services to perform the checks before the request hits the controller, but I don't want to. I need to do it this way.

like image 720
dazedviper Avatar asked Jul 24 '16 17:07

dazedviper


3 Answers

As of Laravel 5.6 you can now use for example response()->json([1])->send();.

There is no need for it to be the return value of a controller method.

Note that calling send() will not terminate the output. You may want to call exit; manually after send().

like image 93
Boris D. Teoharov Avatar answered Oct 19 '22 16:10

Boris D. Teoharov


You are probably looking for this:

function checkEverythingIsOk() {
    if (checkSomething()) {
        return Response::json('Something went wrong');
    }
    if(checkSomethingElse()) {
        return Response::someMacro('Something else is wrong')
    }
    return null; // all is fine
}

And in the controller method:

$response = $this->checkEverythingIsOk();
if($response !== null) { // $response instanceof Response
    return $response;
}
like image 25
Voyowsky Avatar answered Oct 19 '22 18:10

Voyowsky


It's probably overkill, but I will throw it in anyway. You might want to look into internal requests. Also this is just pseudoish code, I have not actually done this, so take this bit of information with caution.

// build a new request
$returnEarly = Request::create('/returnearly');

// dispatch the new request
app()->handle($newRequest);

// have a route set up to catch those
Route::get('/returnearly', ...);

Now you can have a Controller sitting at the end of that route and interpret the parameters, or you use multiple routes answered by multiple Controllers/Methods ... up to you, but the approach stays the same.

UPDATE

Ok I just tried that myself, creating a new request and dispatching that, it works this way. Problem is, the execution does not stop after the child-request has exited. It goes on in the parent request. Which makes this whole approach kind of useless.

But I was thinking about another way, why not throw an Exception and catch it in an appropriate place to return a specified response?

Turns out, thats already built into Laravel:

// create intended Response
$response = Response::create(''); // or use the response() helper

// throw it, it is a Illuminate\Http\Exception\HttpResponseException
$response->throwResponse();  

Now usually an Exception would be logged and you if you are in Debug mode, you would see it on screen etc. etc. But if you take a look into \Illuminate\Foundation\Exceptions\Handler within the render method you can see that it inspects the thrown Exception if it is an instance of HttpResponseException. If it is then the Response will be returned immediately.

like image 6
mwallisch Avatar answered Oct 19 '22 17:10

mwallisch