Render from another controller method - laravel-5

I'm using Laravel 5.5 version and I have a question.
Let's say that my controller has 2 methods like this:
public function errorsCheck(){
return view('errors_check');
}
public function index(){
$this->errorsCheck();
// some other important code
}
And when I open my webpage it goes to index() method first. Then it calls errorsCheck(). And errorsCheck() has to render view file and stop script executing.
How can I do it? I can't make something like:
return $this->errorsCheck();
because I need code above to execute if errorsCheck method doesnt found any errors. And this is not validation errors, btw.
Any ideas? Thanks.
SOLUTION:
Well, solution is:
throw new HttpResponseException(
Response::view('account.denied.not_active_account')
);
It will throw exception, but Laravel catch it and will render view.

Well, solution is:
throw new HttpResponseException(
Response::view('account.denied.not_active_account')
);
It will throw exception, but Laravel catch it and will render view.

Related

Method render() is not being called when custom Laravel exception is thrown from view composer

Edited:
I have a custom exception with render method which is being called when I throw it e.g. from controller, but not being called when I throw it in View composer.
So when I do something like that
public function compose(View $view)
{
throw new CustomException();
}
and put dd() to exception render method
public function render()
{
dd('render is called');
}
I get no result.
If I log my exception directly, finds out that first the CustomException being thrown, then as the result I see ErrorException.
I found a place where it being thrown.
\Illuminate\View\Engines\CompilerEngine::handleViewException
protected function handleViewException(Exception $e, $obLevel)
{
$e = new ErrorException($this->getMessage($e), 0, 1, $e->getFile(), $e->getLine(), $e);
parent::handleViewException($e, $obLevel);
}
I didn't found any mentions in Laravel docs about that case.
I found a tread on github with the same issue: https://github.com/laravel/framework/issues/24658
So the question is, is this expected? Is there any adequate way to avoid this behaviour?
Edit
So, as you know, any exception during view compilation is intercepted and rethrown as ErrorException or as FatalThrowableError.
What you can do is intercept ErrorException and check if ($e->getPrevious() instanceof \CustomException) if so, you do your code, else, let the handler continue.
So I've found working solution for myself.
I've extended CompilerEngine and added additional processing in order to not throw ErrorException when I don't want to.
The important thing is - your resulting Exception must be inherited from ErrorException. Otherwise you will face multiple calls to \App\View\Engines\CompilerEngine::handleViewException which can break your logic and write multiple log entities to your log file.

How to load a CodeIgniter view in $promise->then() in a controller?

In my CodeIgniter 2 controller I call a model method which returns a ReactPHP promise, and I want to load a CodeIgniter view in the function called by that promise's ->then() method. How can I do this? What happens instead is the controller method returns nothing, so I get a blank page in the browser.
Here is a simplified example illustrating what I'm trying to do:
class My_class extends My_Controller {
function my_method() {
$this->my_model->returns_a_promise()->then(function ($data) {
// How can I pass the promise's resolved value to the template here?
// It seems this never gets called, because my_method() returns
// before we get here. :(
$this->load->view('my_view', $data);
});
}
}
Is there any way to tell the controller method not to send output to the browser until after the promise has resolved?
I'm not sure what are you trying to do but if you want to stop view from outputting and return it as a string then output it with echo yourself you can do this:
$view = this->load->view('my_view', $data, TRUE);
Now you have the view as a var string you can use it to do what you are trying to do.
It turns out the code in my original question does work. So the question is the answer. But the reason it wasn't working for me was that returns_a_promise() was not returning a resolved promise, so ->then() was not called and the view was not rendered. In order to make it return a resolved promise, I had to call $deferred->resolve(); in the code that returned the promise.
The upshot of this is that this code example demonstrates it is possible to run asynchronous PHP (via ReactPHP in this case) in CodeIgniter controller methods. My particular use case is to run many database queries concurrently in the CodeIgniter model.
try this:
function my_method() {
$data = array();
$data['promise'] =$this->my_model->returns_a_promise();
$data['view'] = 'my_view';
$this->load->view('my_view', $data);
}

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

Laravel 4 route-model binding exceptions doesn't work despite docs and examples

I read a lot about Laravel4 Route-model binding (L4 docs, tutorials, etc.) but still exceptions (i.e. the model is not found) don't work for me
These are my basic files
routes.php:
Route::model('game', 'Game', function(){
// override default 404 behavior if model not found, see Laravel docs
return Redirect::to('/games');
});
...
Route::get('/games/edit/{game}', 'GamesController#edit');
GamesController.php
class GamesController extends BaseController {
...
public function edit(Game $game){
return View::make('/games/edit', compact('game'));
}
}
Pretty straight, but I get this error: Argument 1 passed to GamesController::edit() must be an instance of Game, instance of Illuminate\Http\RedirectResponse given
If I type http://mysite.dev/games/edit/1 all is fine (model with ID = 1 exists)
If I type http://mysite.dev/games/edit/12345 (no model with that ID) the ugly error above is triggered instead of the redirect I specified
I also looked at this (the bottom part where a Redirect closure is suggested: that is just what I am doing!) but no way to make it work: laravel 4 handle not found in Route::model
What's wrong with it? Please any help?
Thanks in advance
In Route::model you declare which variable will be a model instance, you shouldn't use it to do a redirection that way. Instead of that, specify that $game is of type Game and then work with your routes:
Route::model('game', 'Game');
...
Route::get('/games/edit/{game}', 'GamesController#edit');
Then if you access to /games/edit/3 GamesController::edit will receive an instance of Game class whose id=3
I ended up by setting a general "Not Found" error catcher, like this:
// routes.php
App::error(function(Symfony\Component\HttpKernel\Exception\NotFoundHttpException $e) {
return Response::make('Not Found', 404);
});
...
Route::model('game', 'Game');
...
Route::get('/games/edit/{game}', 'GamesController#edit');
What I understand is that if I want a custom redirect and not a general 404 page (i.e. take the user to games' list if model not found), I CAN'T use the route-model-binding
In other words, I have to use Route::get('/games/edit/{id}', 'GamesController#edit'); and then do my application logic inside the 'edit' method:
public function edit($id){
$game = Game::findOrFail($id);
// if fails then redirect to custom page, else go on saving
}
I'm very new to Laravel, but as far as I can see this has nothing to do with the closure, but with the use of "Redirect::to" inside that closure. Using "App::abort( 404 );" works.

Laravel 4: redirect if item doesn't exists, ModelNotFoundException doesn't work anyway I try it

I'm following Dayle Rees' book "Code Bright" tutorial on building a basic app with Laravel (Playstation Game Collection).
So far so good, the app is working but, following his advices at the end of the chapter, I'm doing my homeworks trying to improve it
So, this snippet is working fine for existing models but throws an error if the item doesn't exists:
public function edit(Game $game){
return View::make('/games/edit', compact('game'));
}
In other words, http://laravel/games/edit/1 shows the item with ID = 1, but http://laravel/games/edit/21456 throws an error since there's no item with that ID
Let's improve this behaviour, adapting some scripts found also here on StackOverflow (Laravel 4: using controller to redirect page if post does not exist - tried but failed so far):
use Illuminate\Database\Eloquent\ModelNotFoundException; // top of the page
...
public function edit(Game $game){
try {
$current = Game::findOrFail($game->id);
return View::make('/games/edit', compact('game'));
} catch(ModelNotFoundException $e) {
return Redirect::action('GamesController#index');
}
}
Well... nothing happens! I still have the error with no redirect to the action 'GamesController#index'... and please notice that I have no namespaces in my Controller
I tried almost anything:
Replace catch(ModelNotFoundException $e) with catch(Illuminate\Database\Eloquent\ModelNotFoundException $e): no way
put use Illuminate\Database\Eloquent\ModelNotFoundException; in Model instead of Controller
Return a simple return 'fail'; instead of return Redirect::action('GamesController#index'); to see if the problem lies there
Put almost everywhere this snippet suggested in Laravel documentation
App::error(function(ModelNotFoundException $e)
{
return Response::make('Not Found', 404);
});
Well, simply nothing happened: my error is still there
Wanna see it? Here are the first two items in the errors stack:
http://www.iwstudio.it/laravelerrors/01.png
http://www.iwstudio.it/laravelerrors/02.png
Please, can someone tell me what am I missing? This is driving me mad...
Thanks in advance!
Here are few of my solutions:
First Solution
The most straightforward fix to your problem will be to use ->find() instead of ->findOrFail().
public function edit(Game $game){
// Using find will return NULL if not found instead of throwing exception
$current = Game::find($game->id);
// If NOT NULL, show view, ELSE Redirect away
return $current ? View::make('/games/edit', compact('game')) : Redirect::action('GamesController#index');
}
Second solution
As I notice you may have been using model binding to your route, according to Laravel Route model binding:
Note: If a matching model instance is not found in the database, a 404 error will be thrown.
So somewhere where you define the model binding, you can add your closure to handle the error:
Route::model('game', 'Game', function()
{
return Redirect::action('GamesController#index');
});
Third Solution
In your screenshot, your App::error seems to work as the error says HttpNotFound Exception which is Laravel's way of saying 404 error. So the last solution is to write your redirect there, though this apply globally (so highly discouraged).

Resources