Laravel and output buffering - ajax

I have a problem with output buffering in laravel 4.
This code works good in other frameworks but in laravel i get fatal error for ob_get_flush()
class Ajax
{
public function __construct()
{
if ( Request::ajax() )
{
ob_clean();
ob_start(function(){});
register_shutdown_function(array(&$this,'setOutput'));
}
}
public function setOutput()
{
$html = ob_get_flush();
}
}
error :
ob_get_flush(): failed to delete and flush buffer. No buffer to delete or flush

Looks like you are trying to flush before you ob_start()ed.

It's "just" a Notice, not an Error. Your buffer content is empty, so PHP (through laravel) sends you a notice because it has nothing to do with ob_get_flush. Maybe notices are disabled in other frameworks you tried. According to this answer, you can do something like this, if you want to make it work even if your buffer is empty:
public function setOutput()
{
if (ob_get_level() > 1) $html = ob_get_flush();
}
Hope it will help !

Is it possible that the class is being called before Request::ajax() can return true?
If so then your constructor conditional would return false and ob_start would not be called. Perhaps you can move that part out of the conditional or try rewriting it to where you write the data to the object and then call renderOutput() and this method would have your ob calls in the proper order.

Related

Laravel: why every route is executed twice?

In my laravel app, I noticed that every route is executed twice, and can't figure out why
for example:
Route::get('called_twice', function () {
dump('---');
});
return string '---' twice
Edit:
trying to backtrace the source of the issue, I put a dump in file
src/Illuminate/Foundation/Http/Kernel.php
protected function sendRequestThroughRouter($request)
{
$this->app->instance('request', $request);
Facade::clearResolvedInstance('request');
$this->bootstrap();
dump('kernel');
return (new Pipeline($this->app))
->send($request)
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
->then($this->dispatchToRouter());
}
and another dump in the constructor of the file
src/Illuminate/Pipeline/Pipeline.php
public function __construct(Container $container = null)
{
dump('pipeline');
$this->container = $container;
}
and I get this:
the Pipeline class is called many time
Laravel 6.8.0
I think $next($request) is probably called twice in a middleware. The reason is that the response is passed throught each middleware (pipes) before it is returned back.
So if $next($request) is called twice in one middleware it is normal that all pipes will be called again.
Found mine in the master. there was a script that was causing the page to reload in the background.
I built a little counter with admin access only and smoked it out. Came down to one script.

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.

redirect is not working in codeigniter _construct function

I am facing a problem using redirect in _construct function,
In timesheet controller I wrote the following code and I am getting an error in the browser "
This problem can sometimes be caused by disabling or refusing to
accept
cookies.
Here is my code
class Timesheet extends MY_Controller {
public function __construct()
{
parent::__construct();
$this->load->model('timesheet_model');
//$this->load->library('auth');
$username=$this->session->userdata('logged_in');
//$this->load->model('login_model');
if($username['fullname']!=""){
redirect('timesheet');
}
else{
redirect('login');
}
}
Please help me to find a way to get rid of this problem.
Thanks.
It looks to me like you're going around in a loop.
You check to see if the fullname element in your $username array is empty and, if it is, you redirect back to the same controller. I'm willing to bet it goes around in a circle like that for a while before the webserver throws up the error you mention.
If I'm reading what you're trying to do correctly, wouldn't you call another function within your Timesheet constructor if the fullname element is present to show whatever information it is that you're trying to display?
I'd suggest changing your logic to do the following:
if($username['fullname'] == ""){
redirect('login');
}
else{
//go to another controller method here
}
OK. I got it. The error is just because of I am redirecting to same controller where I have written the code. Everytime when it redirects to timesheet it enters into the construct function and it redirects again to timesheet. And the same thing is working like an endless loop.
So the fault was redirecting to the same controller.
I
use redirect like this : redirect('timesheet', 'refresh');

CodeIgniter page caching with a exception

I like to use CodeIgniters page caching. But I've got a views counter in the controller, something like:
$this->db->query("UPDATE tb_product SET popularity=popularity+1 WHERE product_id=".$this->db->escape($this->uri->segment(2))."");
Is it possible to use page caching but make a exception to run this query only?
Yes. You could try to implement a hook. This would have to be the 'cache_override' hook. You could do the DB call from the hook.
http://ellislab.com/codeigniter/user-guide/general/hooks.html
Your hook method will have to call the original caching itself after doing the DB call.
function your_hook( )
{
// your DB code here
// Use some CI globals for this, see /system/core/Codeigniter.php
if ($OUT->_display_cache($CFG, $URI) == TRUE)
{
exit;
}
}
You could write a custom Output.php class and override (decorate) the original _display_cache method. Place your MY_Ouput.php in the /application/core directory and CI will use it automatically.
http://ellislab.com/codeigniter/user-guide/general/core_classes.html
Put something like this in it:
class MY_Output extends CI_Output
{
function _display_cache( &$CFG, &$URI )
{
// your DB call
// The original call
return parent::_display_cache( $CFG, $URI );
}
}
I didn't try this myself, but it should help you on your way. One of these will probably work. Good luck!
Thanks for your answer, but I've striped the counter out and wrote a cronjob which will get statistics from Google Analytics trough GAPI.

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