CakePHP: Action runs twice, for no good reason - php

I have a strange problem with my cake (cake_1.2.0.7296-rc2).
My start()-action runs twice, under certain circumstances, even though only one request is made.
The triggers seem to be :
- loading an object like: $this->Questionnaire->read(null, $questionnaire_id);
- accessing $this-data
If I disable the call to loadAvertisement() from the start()-action, this does not happen.
If I disable the two calls inside loadAdvertisement():
$questionnaire = $this->Questionnaire->read(null, $questionnaire_id);
$question = $this->Questionnaire->Question->read(null, $question_id);
... then it doesn't happen either.
Why?
See my code below, the Controller is "questionnaires_controller".
function checkValidQuestionnaire($id)
{
$this->layout = 'questionnaire_frontend_layout';
if (!$id)
{
$id = $this->Session->read('Questionnaire.id');
}
if ($id)
{
$this->data = $this->Questionnaire->read(null, $id);
//echo "from ".$questionnaire['Questionnaire']['validFrom']." ".date("y.m.d");
//echo " - to ".$questionnaire['Questionnaire']['validTo']." ".date("y.m.d");
if ($this->data['Questionnaire']['isPublished'] != 1
//|| $this->data['Questionnaire']['validTo'] < date("y.m.d")
//|| $this->data['Questionnaire']['validTo'] < date("y.m.d")
)
{
$id = 0;
$this->flash(__('Ungültiges Quiz. Weiter zum Archiv...', true), array('action'=>'archive'));
}
}
else
{
$this->flash(__('Invalid Questionnaire', true), array('action'=>'intro'));
}
return $id;
}
function start($id = null) {
$this->log("start");
$id = $this->checkValidQuestionnaire($id);
//$questionnaire = $this->Questionnaire->read(null, $id);
$this->set('questionnaire', $this->data);
// reset flow-controlling session vars
$this->Session->write('Questionnaire',array('id' => $id));
$this->Session->write('Questionnaire'.$id.'currQuestion', null);
$this->Session->write('Questionnaire'.$id.'lastAnsweredQuestion', null);
$this->Session->write('Questionnaire'.$id.'correctAnswersNum', null);
$this->loadAdvertisement($id, 0);
$this->Session->write('Questionnaire'.$id.'previewMode', $this->params['named']['preview_mode']);
if (!$this->Session->read('Questionnaire'.$id.'previewMode'))
{
$questionnaire['Questionnaire']['participiantStartCount']++;
$this->Questionnaire->save($questionnaire);
}
}
function loadAdvertisement($questionnaire_id, $question_id)
{
//$questionnaire = array();
$questionnaire = $this->Questionnaire->read(null, $questionnaire_id);
//$question = array();
$question = $this->Questionnaire->Question->read(null, $question_id);
if (isset($question['Question']['advertisement_id']) && $question['Question']['advertisement_id'] > 0)
{
$this->set('advertisement', $this->Questionnaire->Question->Advertisement->read(null, $question['Question']['advertisement_id']));
}
else if (isset($questionnaire['Questionnaire']['advertisement_id']) && $questionnaire['Questionnaire']['advertisement_id'] > 0)
{
$this->set('advertisement', $this->Questionnaire->Question->Advertisement->read(null, $questionnaire['Questionnaire']['advertisement_id']));
}
}
I really don't understand this... it don't think it's meant to be this way.
Any help would be greatly appreciated! :)
Regards,
Stu

Check your layout for non-existent links, for example a misconfigured link to favicon.ico will cause the controller action to be triggered for a second time. Make sure favicon.ico points towards the webroot rather than the local directory, or else requests will be generated for /controller/action/favicon.ico rather than /favicon.ico - and thus trigger your action.
This can also happen with images, stylesheets and javascript includes.
To counter check the $id is an int, then check to ensure $id exists as a primary key in the database before progressing on to any functionality.

For me it was a JS issue.
Take care of wrap function with jQuery that re-execute JS in wrapped content!

You might want to try and find out where it comes from using the debug_print_backtrace() function. (http://nl.php.net/manual/en/function.debug-print-backtrace.php

Had the same problem, with a certain action randomly running 2-3 times. I tracked down two causes:
Firefox add-on Yslow was set to load automatically from it's Preferences, causing pages to reload when using F5 (not when loading the page from the browser's address bar and pressing Enter).
I had a faulty css style declaration within the options of a $html->link(); in some cases it would end up as background-image: url('');, which caused a rerun also. Setting the style for the link to background-image: none; when no image was available fixed things for me.
Hope this helps. I know this is quite an old post, but as it comes up pretty high in Google when searching for this problem, I thought it might help others by still posting.
Good luck
Jeroen den Haan

I had a problem like this last week.
Two possible reasons
Faulty routes (DO check your routes configuration)
Faulty AppController. I add loads of stuff into AppController, especially to beforeFilter() and beforeRender() so you might want to check those out also.
One more thing, are where are you setting the Questioneer.id in your Session? Perhaps that's the problem?

Yes, it occurs when there is a broken link in the web page. Each browser deals with it variously (Firefox calls it 2x). I tested it, there is no difference in CakePHP v1.3 and v2.2.1. To find out who the culprit is, add this line to the code, and then open the second generated file in you www folder:
file_put_contents("log-" . date("Hms") . ".txt", $this->params['pass'] ); // CakePHP v1.3
file_put_contents("log-" . date("Hms") . ".txt", $this->request['pass'] ); //CakePHP v2.2.1
PS: First I blame jQuery for it. But in the end it was forgotten image for AJAX loading in 3rd part script.

I had the same problem in chrome, I disabled my 'HTML Validator' add on. Which was loading the page twice

I was having a similar issue, the problem seemed to be isolated to case-insensitivity on the endpoint.
ie:
http://server/Questionnaires/loadAvertisement -vs-
http://server/questionnaires/loadavertisement
When calling the proper-cased endpoint, the method ran once -whereas the lower-cased ran twice. The problem was occurring sporadically -happening on one controller, but not on another (essentially the same logic, no additional components etc.). I couldn't confirm, but believe the fault to be of the browser -not the CakePHP itself.
My workaround was assuring that every endpoint link was proper-cased. To go even further, I added common case-variants to the Route's configuration:
app/config/routes.php
<?php
// other routes..
$instructions = ['controller'=>'Questionnaires','action'=>'loadAvertisement'];
Router::connect('/questionnaires/loadavertisement', $instructions);
Router::connect('/QUESTIONNARIES/LOADADVERTISEMENT', $instructions);
// ..etc

If you miss <something>, for example a View, Cake will trigger a missing <something> error and it will try to render its Error View. Therefore, AppController will be called twice. If you resolve the missing issue, AppController is called once.

Related

PHP: variable is suddenly null

I have the problem that my data variable is suddenly null.
It's happening directly after an IF-Statement when nothing was written to this variable. Does anyone knows what it is happening here?
public function render()
{
ob_start();
if($this->ajax)
$ext = '.ajax';
else if(file_exists($this->scriptPath.$this->template.'.mst'))
$ext = '.mst';
else
$ext = '.phtml';
#var_dump($this->data); // <-- is filled with many data
if($ext === '.mst'){
var_dump($this->data); // <-- is null
$mustache = new \Mustache_Engine(
array(
'escape' => function($value){return $value;},
'partials_loader' => new \Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/../../../frontendTarget/classes/lib/de/preis/frontend/viewFragments/partials',array('extension' => 'mst'))
)
);
$content = file_get_contents($this->scriptPath.$this->template.$ext);
$content = $mustache->render(($content),$this->data);
echo $content;
} else {
include $this->scriptPath.$this->template.$ext;
}
return ob_get_clean();
}
I've here two var_dumps(). One before the if, where the var is filled with data and one after the if, where the data is suddenly completely gone.
Could anyone assist me on this one?
Thanks in advance
More of a learned lesson than an answer!
I had a problem like this once that seemed like a real mystery, it took me while to figure out because it didn't make any sense at the time. My answer was that I was running my code with netbeans debugger and I had previously set a watch to clear (unset()) a variable to allow me to debug a code segment - I'd forgotten to remove the watch so it was executing during my debug session and nulling my variable
Thanks to #Wee Zel for the hints.
I double checked my code and figured out that the class method was called twice. In the first case, the data were given, in the 2nd it was not.
In my particular case, I had a MVC model where the layout was rendered first (with data) and then the view (without data). I needed the data in the view and not the layout. So I just had to pass the data to the correct layer.

Symfony2 not saving sessions properly

I'm having a problem with Symfony creating a new session on each page load, rather than carrying data across requests. The auto_start in the session section in the config.yml is set to false, and regular php sessions work fine. It's only when running in symfony that I get the problem.
For example, I created the test action:
public function sessionTestAction()
{
$s_Response = '<html><head></head><body><p>Foo</p></body></html>'; //Initialize response and headers
$a_Headers = array();
$i_StatusCode = 200;
$oSession = $this->get('session');
var_dump($oSession->all());
if(!$oSession->has('Test'))
{
$oSession->set('Test', 'Bar');
}
$oSession->save();
return new Response($s_Response, $i_StatusCode, $a_Headers);
}
The expected action is, that on the first page load, the var_dump will yield nothing, and that on any subsequent executions, it will contain Test=>Bar. However, it never gets that data across requests.
In addition, it creates a new session id for each request.
I am using Symfony v2.0.15, and PHP v5.4
Anyone have any ideas?
Edit:
I made some progress, I think. I made the following changes to the test action:
public function sessionTestAction()
{
//Initialize response and headers
$oRequest = $this->get('request');
$a_Headers = array();
if (isset($oRequest->headers->all()['cookie']))
{
$a_Headers['Set-Cookie'] = $oRequest->headers->all()['cookie'];
}
$i_StatusCode = 200;
$oSession = $oRequest->getSession();
$oSession->start();
$s_Response = print_r($oSession->all(), true);
if(!$oSession->has('Test'))
{
$oSession->set('Test', 'Bar');
}
$oSession->save();
$oResponse = new Response($s_Response, $i_StatusCode, $a_Headers);
return $this->render('Bundle:Default:index.html.twig', array('response' => $s_Response), $oResponse);
}
Where that twig file has just {{response|raw}}. It now holds the session for 2 out of 3 of the requests. However, on the third request, it's cleared.
Turned out the problem was, someone added a line to set a session cookie whenever the app.php was run, not knowing that symfony handled sessions itself, I guess. Problem solved.
I got this problem a couple times, its very annoying. So, let me describe possible solution.
Open dev environment - yourdomain.com/app_dev.php/ Try to refresh page a couple times. If you see session ID changed each time - it means that sessions are broken.
If you are using chrome (if not - you should, its the best for developers ;) ) - you can open developers tools (click F12).
Next, check Network tab, refresh page and locate your main request.
Check headers for your request - if should see "Cookie:PHPSESSID".
If you dont see - something wrong with cookies. In my case it was
framework:
session:
cookie_domain: mydomain.com

Clean URLS in MVC structure using PHP

I am creating a website using the MVC structure. Below is a code I have used to use clean URLS and load the appropriate files. However it only works for the first level.
Say I wanted to visit mywebsite.com/admin it would work, however mywebsite.com/admin/dashboard would not. The problem is in the arrays, how could I get the array to load content after the 2nd level along with the second level.
Would it be best to create an array like this?
Array
- controller
- view
- dashboard
Any help here would be great. Also as a side question. What would be the best way to set up "custom" urls. So if I were to put in mywebsite.com/announcement it would check to see if its got controllers, failing that, check to see if it's got custom content (maybe a file of the same name in "customs" folder, and then if there's nothing execute the 404 page not found stuff) This isn't a priority question though, but loosely associated in how the code works so I thought it best to add.
function hook() {
$params = parse_params();
$url = $_SERVER['REQUEST_URI'];
$url = str_replace('?'.$_SERVER['QUERY_STRING'], '', $url);
$urlArray = array();
$urlArray = explode("/",$url);
var_dump($urlArray);
if (isset($urlArray[2]) & !empty($urlArray[2])) {
$route['controller'] = $urlArray[2];
} else {
$route['controller'] = 'front'; // Default Action
}
if (isset($urlArray[3]) & !empty($urlArray[3])) {
$route['view'] = $urlArray[3];
} else {
$route['view'] = 'index'; // Default Action
}
include(CONTROLLER_PATH.$route['controller'].'.php');
include(VIEW_PATH.$route['controller'].DS.$route['view'].'.php');
var_dump($route['controller']);
var_dump($route['view']);
var_dump($urlArray);
var_dump($params);
// reseting messages
$_SESSION['flash']['notice'] = '';
$_SESSION['flash']['warning'] = '';
}
// Return form array
function parse_params() {
$params = array();
if(!empty($_POST)) {
$params = array_merge($params, $_POST);
}
if(!empty($_GET)) {
$params = array_merge($params, $_GET);
}
return $params;
}
Can you clarify this: "The problem is in the arrays, how could I get the array to load content after the 2nd level along with the second level."
I don't understand how you want this thing to work. I checked your code and it works. Maybe you just need to put $urlArray[1] instead of $urlArray[2] and 2 instead of 3? First element in the array is at index 0.
Usually it's done like this:
Url format:
/controller/action/param1/param2/...
-controller- should be a class. That class has a method/function called -action-.
ex. /shoes/show/121/ --> this will load controller shoes
and execute the method/function show(121)
that will show the shoes that have the id 121 in the
database.
ex. /shoes/list/sport --> this will load controller shoes
and execute function list('sport') that will list all
shoes in the sport category.
As you can see, you only load one controller and from that controller you run only one function and that function will get the rest of the path and use it as parameters.
If you want to have multiple controllers for one URL, then the rest of the controllers will have to be loaded from the main controller. Most MVCs (like CodeIgniter) load only one controller per URL.
Second question:
Best way for pretty urls would be to save them in the db. This means you can have URLs like this:
/I-can-write-anything-here-No-need-to-add-ids-or-controller-names
Then you take this URL and search it in db and get the -controller- and -action- that you need for this URL.
But I have yet to see a popular MVC framework do this. I guess the reason is that the db will get a lot of queries for text matches and that will slow things down.
Popular MVC frameworks use:
/controller/action/param1/param2
This has the benefit that you can directly find the controller/action from the url.
The downside is that you will get urls like:
/shoes/list/sport
//when what you really want is
/shoes/sport
//or just
/sport //if the website only sells shoes
This can be fixed by redirecting /shoes/sport to /shoes/list/sport
If you make your own MVC then you should use OOP because if not, thing will get ugly quick: all actions/functions are in the same namespace.
Personally I would recommend that you use one of the many PHP frameworks that exist as that will take care of the routing for you and let you concentrate on writing your application. CakePHP is one that I've used for a while and it makes my life so much easier.
What I do:
I create a .htaccess file that redirects an url like www.example.com/url/path/or/something to www.example.com/index.php?url=url/path/or/something, so it will be pretty easy to do an explode on your $_GET['url']
Second, it's better because everything a user input, will be redirected to your index.php, so you have FULL control over EVERYTHING.
If you want I can PM you the url to my mvc (bitbucket) so you can have a look on how I do this ;)
(Sorry for the others, but I don't like to put url's to my site in public)
edit:
To be more precise to your particular question; It will solve your problem, because everything goes to index.php and you have full control over the requested url.

Prevent Page/Controller/Method Caching on Clean Codeigniter 2.1.1

I have a activate button in my CMS that allows the user to quickly toggle a active/inactive flag for news articles. When clicked the toggle makes a POST request to the url /news/toggle_active/$id, after which the news_model updates the boolean variable in the database for the specific articles id, and the controller redirects back to the index.
Controller
function toggle_active()
{
$url = $this->uri->uri_string();
$last_segment = count($this->uri->segment_array());
$id = $this->uri->segment($last_segment);
$row = $this->news_model->set_active($id);
redirect('news/index', 'refresh');
}
Model
function set_active($id)
{
$data = array(
'display_flg' => 'NOT display_flg'
);
$this->db->where('id', $id);
$this->db->update($this->tbl_news, $data);
}
The first time the activate button is clicked it works, but subsequent clicks do nothing. If I close the browser window and re-open the page, the button works again, but only for one click.
I haven't specifically enabled any caching, so I'm not sure whats going on.
If I remove the redirect from the end of the toggle_active() method, and output the current value of the display_flg variable, and POST to /news/toggle_active/$id it appears to do nothing on the first request. However on each subsequent refresh the variable changes values. Why is it not doing anything the first time the link is hit?
I found some people with what seems to be a similar issue over at the codeigniter forums, although there was no solid solution.
I don't think this was a caching problem in the end. I made two major changes to fix the problem.
Firstly, I not check to see whether or not the database request was successful before redirecting.
I also re-wrote my the models set_active() method. Passing the SET clause as an array to the update functions second parameter wasn't working. I needed to use the set functions third parameter to stop the query from escaping the NOT part of the query.
I adjusted my functionality as follows:
Controller
function show_toggle($id)
{
if($this->news_model->set_active($id))
{
redirect('news/index', 'refresh');
}
}
Model
function set_active($id)
{
$this->db->where('id', $id);
$this->db->set('display_flg', 'NOT display_flg', FALSE);
$query = $this->db->update($this->tbl_news);
return $query;
}

Loading 'modules' Data Into Views on a Per User Basis Dynamically From One Controller Using CodeIgniter

I am trying to load views for a set of 'modules' for a user who has selected any number of available 'modules'. I can get the name of the modules, or any column from the database.
load->view($name . '_view');
I can't seem to figure a way to load the data for the view based on the 'module' name though.
//Loads the rows (selected modules) I need for this user into an array
$modules['modulearr'] = $this->module_model->getModuleUser();
for($i = 0; $i < count($modules['modulearr']); $i++){
//Get the variable from the array
$name = $modules['modulearr'][$i]->mod_name;
//The below works.
$this->load->view($name.'_view');
//The below would not work. (this is the crux of my problem)
$data = $this->$name.'_model'->get();
$this->load->view($name.'_view', $data);
}
There is also an issue with loading the models in the controller based on the fact I can't change $this->load->THIS_PART dynamically.
I am new to everything, so there may be a basic concept I am not grasping. If you could point me in the right direction; that would be awesome. I could do a whole bunch of if statements, but that seems messy. Surely there is a better way. Thanks in advance!
maybe you wanted
$data = $this->$name.'_model'->get();
you forgot to concatenate the strings
but can't seem to figure a way to load
the data for the view based on the
'module' name.
The module name seems to be defined from the line
$name = $modules['modulearr'][$i]->mod_name;
if this works...
$this->load->view($name.'_view');
maybe you want to do this?
$data = $name.'_model'->get();
instead of $this->name ?
If that doesn't work (i don't really know what you have going on) try echoing $this->name and making sure the output makes sense attached to '_model'
It was a capitalization issue: The fields from the database (in array form)
$name = $modules['modulearr'][$i]->mod_name;
were sometimes in capitals. I fixed it by using
$name = strtolower($name);
$nameMod = $name.'_model';
then
//it doesn't seem to like combinations of things where nameMod is below.
$data[$name.'_result'] = $this->$nameMod->get()
$this->load->view($name.'_view', $data);
I don't know why it originally worked in the view loading part and not the loading data by calling model function part, but it does now. I am using it for a main landing (after sign up or login) page function that selects and displays the modules added by each user to their profile, I am probably going about this in a completely gammy way, but I am learning heaps by making mistakes, Thanks for the help answerers you definitely put me on the right track

Categories