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

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.

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().

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;
}

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.

To me the most simple and elegant way is:
response()->json($messages_array, $status_code)->throwResponse();
(you don`t need return)
It can be called from a private function or another class...
I use this in a helper class to check for permissions, and if the user doesn`t have it I throw with the above code.

Related

Redirect using get and then redirect to the response

Lets start off with the code
if($request->berbix == true || $request->berbix == 'true'){
THIS IS WHERE I NEED THE LOGIC TO GO
}
else {
session()->flash('flash.banner', 'Profile updated!');
session()->flash('flash.bannerStyle', 'success');
return Redirect::route('dashboard');
}
In the if statement this is what needs to happen.
user needs to be send to /verification/create with their user model
When that is completed, it will provide a URL
user then needs to be redirected to said URL
I know this should be simple but I am having a HECK of a time wrapping my head around the simple logic. The main thing I am struggling with (and I know why, but don't know how to solve it), is when I do for example Http::get() it isnt sending it as the user so the request doesn't know the user info
Any help either a solution or a nudge on what I need to do would be very nice right now!
While I would still consider redirecting to a different route/controller to handle this, since you said you want it all in one place, it is possible to call controller methods directly and retrieve a response without leaving the current method or creating another HTTP request.
You could do something like this.
$url = app()->call('App\Http\Controllers\VerificationController#create', [
'user' => auth()->user()
]);
if ($url) {
return redirect($url);
}

refresh page after updated model - Laravel

I want to refresh current page after any user update
I'm trying to do something like that in User Model :
public static function boot()
{
self::updated(function ($model) {
return back(); //or redirect(Request::url())
});
}
but it wasn't working.
How can I refresh the page if any user updated
In general, the model event functions creating/created/updating/updating/saved/saving should only ever return false or nothing (void). Returning false in for instance updating (that is: before the model is persisted in the database) cancels the model update (this works for all propagating events, see https://laravel.com/docs/9.x/events#stopping-the-propagation-of-an-event).
To "refresh" the current page after a user update, you have to be more specific about what you require. The back() function that you use (or the aliases redirect()->back() or even app('redirect')->back()) is (to my knowledge) to be used in controllers and it uses the Referer header or a property in the user's session to determine to which page to send the client to. This is most often used with validation errors, so that you can send the user back to the page they came from along with any possible validation error messages.
Using back() (or any other request completions like return response()->json('mydata')) inside events is wrong and it doesn't even work since the result of such events is not being used to complete the request. The only valid thing you "could" do is to try validation inside an event, which in turn could throw ValidationExceptions and is therefore automatically handled.
What you should do is use the controller method that actually handles the request that updates the user:
// UserController.php
/** (PUT) Update a user */
public function update(Request $request, User $user)
{
if($user->update($this->validate($request, [/* validations */])) {
return redirect()->back();
// or even be explicit and use `return redirect()->route('users.show', $user);`
}
// `update()` returned false, meaning one of the model events returned `false`
// (there is no exception thrown here).
session()->flash('alert-info', 'Something went wrong');
return redirect()->back();
}

Laravel: Capture a difference between a Controller __construct()'s stage and other methods run stage

I am extending Laravel's controller for a package.
So I know Laravel controllers run their constructor and method at different stages of the app.
public function __construct()
{
//Middlewares have not run yet
//auth()->check() or auth()->user() do not work yet
}
And in any other method in your Controller
public function anyOtherMethod()
{
//All good, everything has booted.
}
I am looking a way of distinguishing between the two stages. For example, is there a method that says?
app()->middlewaresHaveBeenHandled(); //returns true or false
//or
app()->authIsBootedYouMayUseIt(); //returns true or false
The session from the request is null if the latter wan't yet handled by the app. So a good check would be
if(request()->route() && !request()->hasSession()){
//request has not been handled
}else{
//request has been handled
}
The check for the request route is necessary to make sure it is an HTTP request and it won't interfere with Tests or Console actions

Returning a variable from a filter. Laravel 4

I am new to Laravel and I am trying to set up some basic routing logic. I have a route that will process a certain URL pattern. These URLs will usually be an ajax request (returning data for a popup window). However, search engines and users with javascript disabled will follow a normal link, so I want the data to be returned on a separate page. To do this, I need to determine if the request is ajax. I understand I can do this using:
if(Request::ajax()){
//
}
My plan was to do this as part of a 'before' filter attached to the route. If my thinking is correct, I would need to return a boolean ajax=true/false back from the filter. Maybe this is a very simple question, but I can't find anywhere that explains how you actually return a value like this from a filter? Everything I can find seems to assume that the default outcome of any filter logic must be a redirect.
Thanks
EDIT: I've come to the conclusion that I am simply not using the filtering method in the way it was intended, and simply placing the handler in my controller method. But I would still like to know if it is possible to return data from a filter.
In a controller:
if(Request::ajax()){
return Response::json(['message' => 'Hi. Your request was ajax!', 'status' => 1]);
}
The simple solution I found for that is to use Session::flash which populates data for the next request only and you can easily get the result anywhere. So for example :
Route::filter('something', function() {
if(false) {
View:make('error); // or whenever you want
} else {
return Session:flash('result_var', $my_result_var);
}
});

How can I debug $Model after validation?

I want to see the content of validationErrors => array(???) of the $Model after a failed validation, but there is no "afterValidation()" method.
Does anyone know how can I see that or at least how would it look exactely?
Thank's!
On Controller, you can validate data before you trying save:
$this->ModelName->set($this->request->data);
if ($this->ModelName->validates()) {
// success
} else {
// failed
$errors = $this->ModelName->validationErrors;
}
Reference:
Validating Data from the Controller
Use $this->ModelName->invalidFields() after you have made the save/whatever you're doing:
For example:
debug($this->ModelName->invalidFields());
If you have a redirect at some point after that call, you might not see the data in your view. In this case, you can always do die(); either right after or wrapped around your call like so:
die(debug($this->ModelName->invalidFields());

Resources