Access a variable from an non associated Controller in Cakephp - php

I am developing a quiz using a 'form' in cakephp. I have declared a variable within my CourseModules controller ($passMark) where the HR developing the quiz can set the pass percentage the user needs to successfully complete the quiz. I have declared the variable like so:
case "Quiz":
$quiz = $this->CourseModules->FormTemplates->find('list')->where(['active'=>true,'type'=>'Quiz']);
$passMark = [100=>'100%',90=>'90%',80=>'80%',70=>'70%',60=>'60%',
50=>'50%',40=>'40%',30=>'30%',20=>'20%',10=>'10%',0=>'0%'];
$this->set('passMark',$passMark);
$this->set('quiz',$quiz);
break;
I then need to access the variable $passMark within my FormsController so that I can check it against another variable ($percCorrect). $percCorrect is declared as so in my Forms Controller:
$percCorrect = $numberCorrect / $numberOfQuizQuestions * 100;
$this->set('percCorrect', $percCorrect);
I want to do an if statement so check in if $percCorrect is < $passMark but I'm unsure how to access $passMark because CourseModules isn't associated with Forms Controller.
I do have another controller called CoursesEnrolledModules that is related so I'm wondering if I can somehow access it through there?
I have the following code in my FormsController to load the CoursesEnrolledModules:
//Check if courses_enrolled_module_id is set
$courses_enrolled_module_id = $this->request->getQuery('courses_enrolled_module_id');
//If so make sure it is valid
if($courses_enrolled_module_id){
$this->loadModel('CoursesEnrolledModules');
$coursesEnrolledModule = $this->CoursesEnrolledModules->get($courses_enrolled_module_id,
['contain'=>[],
]);
//Pass variable to view so we can show correct back button
$this->set('coursesEnrolledModule', $coursesEnrolledModule);
//Also after save we will redirect.
}

Any time you ask yourself "how do I access one controller from another controller", just stop yourself right there and think about a different option. In this case, you should put your array in a central location accessible by everything that might need it. Include it in the config in your app.php, perhaps, and then reference it with Configure::read(...) anywhere you need it.

Related

Laravel : form does not change the URL after submit, causing me to be unable to do another POST in web.php

I have a bit of a complicated issue. I could use some help.
I have a form that is being handled by the following function:
$module = request('module');
$classe = request('classe');
$horaire = request('horaire');
$date = request('date');
$students = DB::select('SELECT * FROM `etudiants` WHERE etudiants.id_classe = '.$classe);
return view('g_absence.absence',['module'=> $module, 'classe'=>$classe,'horaire'=>$horaire,'date'=>$date,'students'=>$students]);
I take the values $module, $class, $horaire, $date and $students and need to use them inside a different view: g_absence.absence. This works fine and when the view is returned I have access to said variables.
The issue is, inside the g_absence.absence view, I have another form that also needs to be handled, and because the url remains the same even tho a different view is returned, I cant make two posts for the same path.
web.php:
Route::get('/testboy', [App\Http\Controllers\g_absence::class,'index'])->name('marquer');
Route::post('/testboy',[App\Http\Controllers\g_absence::class, 'marquer']);
Route::post('/testboy',[App\Http\Controllers\g_absence::class, 'ajoutabsence']);
The first line is the one that send to the form page just a simple
return view
The second one handle the form in that view
The third one, I want it to handle the form inside the
g_absence.absence view, but they share the same path.
Excuse me if I'm being unclear, I'm a bit of a beginner in Laravel
your problem is using the same route for different methods
basically the first route gets executed every time you use the '/testboy' action that is why your second function never get's called.
you can solve this issue by changing your urls for example:
Route::post('/testboy-marquer',[App\Http\Controllers\g_absence::class, 'marquer']);
Route::post('/testboy-ajoutabsence',[App\Http\Controllers\g_absence::class, 'ajoutabsence']);
Or you can use one function that's handle both with one url by pathing additional parameter to your url depending on your function call :
Route::post('/testboy?type=marquer',[App\Http\Controllers\g_absence::class, 'ajoutabsence']);
in your function check the type :
if(request('type') == 'marquer') {
execute marquer logic here...
} else {
execute absence logic here...
}
Using method and path with two functionalities is wrong, but if you want to somehow use both routes my same method and path which I don't recommend you must let the request to pass through like a middleware in your first block of code Instead of return a view.
Recommended way is to have 2 routes with different paths or at least one route with a parameter by which you can determine what code block must be executed.

PHP MVC Controller Created Over and Over Again

I am new to PHP. I am using a MVC project as an example, and I noticed that each time a submit button is pressed my controller is called. The issue with this is that it creates a new model every time the button is pressed.
To fix this, I used a hidden field to check if the button has already been pressed. If it has, then I do not instantiate a new Model, otherwise I do. Code from controller is below:
//code listed below is in the controller which is called each time button
//has been pressed...
$myModel;//used to access model and its functions from controller
if(isset($_POST['has_started']))
{
//stores some logic that uses $myModel variable
playingGame();
}
else
{
echo "just starting...";
$myModel=new HangManModel();
startGame($myModel);
}
This seems to work, BUT then when playingGame() is called it tells me I cannot use $myModel and its functions because it was not declared, or it is a non-object. How can I fix this? Thanks for the help!
First, there is no mvc pattern I see here.
Secondly, php executes and generates the output in html and javascript and sent to the browser.
At the next postback, php does not store any state for this.
As per my understanding, for storing states you can use one of the following.
1) Option1-->Using session.
//code listed below is in the controller which is called each time button
//has been pressed...
$myModel;//used to access model and its functions from controller
if(!isset($_SESSION)){session_start();}
if(isset($_POST['someuniqueID']))
{
//stores some logic that uses $myModel variable
$someuniqueID = $_POST['someuniqueID'];
$myModel = $_SESSION[$someuniqueID]; //retrieving the session object.
playingGame($myModel); //passing your model to the main function.
}
else
{
echo "just starting...";
$myModel=new HangManModel();
//start session
$_SESSION['someuniqueID'] = $myModel; // do not store very complex model object to avoid server memory problem.
startGame($myModel);
}
2) Option2--> you can serialise that object and send it in hidden field then it will be posted back but this will have bandwidth consumption problem. Each time, the page is posted, it will send your model back and forth.
3) Option3--> If your model is big, Use database and create a temp table where you can store unique id and serialised $myModel object. You will need to clean the unwanted states from database. because unlike session and hidden field it is permanent storage.

CakePHP - Render different view and set variables

I am attempting to render and set variables to a view that does not belong to any controller. Here is an example of my folder structure:
app/
-->View/
-->ExternalReportViews/ (There is no ExternalReportViews Controller)
-->example_view.ctp
I am trying to pass a variable called $orders to the view, I have verified that this variable exists and contains data by debugging from the controller.
I have succesfully rendered the view using any of the following methods:
// Method 1
$this->render('/ExternalReportViews/example_view');
$this->set('orders', $orders);
// Method 2
$view = new View($this, false);
$view->viewPath = 'ExternalReportViews';
$view->render('example_view');
$view->set('orders', $orders);
// Method 3
$this->viewPath = 'ExternalReportViews';
$this->render('example_view');
$this->set('orders', $orders);
It seems that regardless of whether the set method is placed before or after the render in any of the above methods, the $orders variable is not passed to the rendered view.
When attempting to debug($orders) in the view, I see that the variable is undefined.
I could obviously bypass this problem by temporarily storing $orders in $this->Session, but that seems a bit messy.
The reason for all of this is that I will have a number of views that will be created on a per-report basis, and I would rather have them in a separate (sub)folder(s) for cleaner file management.
As it would happen, I just needed to change the variable name. I'm either hitting on a reserved keyword, or I've previously defined the variable in my beforeFilter method. Not sure which yet, but credit goes to scrowler for pointing that one out.

CakePHP - Controller or No Controller?

I am currently building a web app which has two models, Donor and Donation Models respectively. It has multiple user roles. When the staff user first registers a donor, I want him to be redirected to another form which allows him to fill in the Donation details(the donor is registered once the first donation is successful).
Firs of all, should I create a donation controller, from which I would redirect the user using:
return $this->redirect(array('controller'=>'donations','action'=>'add'));
For the above to work, it requires me to save the newly registered donor's id in a session like so :
$this->Session->write('id', $this->Donor->id);
So the user is redirected to 'donations/add' in the url, and this works fine.. However I think this has some flaws. I was wandering whether I should create another action inside the Donor controller called 'add_donation', which will have its respective 'View'. The idea is to be able to form a url of the sort : 'donors/add_donation/4' (4 being the donor_id ! )
This URL follows this construct: 'controller/action/id'
If anyone could shed some light on best practices, or describe any caveats to my solution(the former, using session etc.) , please do help a brother out! Ill be deeply indebted to you! Thanks in advance!
After you saved the data you can do this in the DonorsController:
$this->redirect(array(
'controller' => 'donations',
'action' => 'add',
$this->Donor->getLastInsertId()
));
There is no need to return a redirect, it's useless because you get redirected. Notice that we pass the last inserted record id as get param in the redirect. The redirect method of the controller calls by default _stop() which calls exit().
CakePHP3: There is a discussion about changing that default behavior in 3.0. Looks like in CakePHP 3.0 the redirect() won't exit() by default any more.
DonationsController:
public function add($donorId = null) {
// Get the donor to display it if you like to
if ($this->request->is('post')) {
$this->request->data['Donation']['donor_id'] = $donorId;
// Save code here
}
}
I would not use the session here, specially not by saving it to a totally meaningless and generic value named "id". If at all I would use always meaningful names and namespaces, for example Donor.lastInsertId as session key.
It's not always clear where to put things if they're related but the rule of thumb goes that things should go into the domain they belong to, which is pretty clear in this case IMHO.
Edit:
Leaving this edit here just if someone else needs it - it does not comply with the usage scenario of the asker.
If you have the user logged in at this stage, modify the add function to check if the userId passed is the same as the one logged in:
DonationsController:
public function add($donorId = null) {
// Get the donor to display it if you like to
if ($this->request->is('post')) {
if ($this->Auth->user('id') != $donorId) {
throw new InvalidArgumentException();
}
$this->request->data['Donation']['donor_id'] = $donorId;
// Save code here
}
}
You can use also the same controller using more models with uses.
Or you can also to ask to another controller with Ajax and morover to get response with Json.

symfony 1.4 passing variables between templates and actions

symfony 1.4 passing variables between templates and actions
I've got an index page which includes a call to a series of partials through a switch statement; and it works. I now need to restrict access to the partial dependent upon the user's type; furthermore, I believe my switch statement should be in the actions class according to MVC, but I can't get that to work either. This might be better explained through example:
Here's my file structure for the dashboard module:
..dashboard
..actions
..config
..templates
_admins.php
_employers.php
_employees.php
_guest.php
indexSuccess.php
Here is my current indexSuccess template (which currently works... but without restricting access if the logged user's type doesn't match the page type):
$type = sfContext::getInstance()->getUser()->getGuardUser()->getProfile()->getType()->getName();
switch($type)
{
case ('Employer'):
include_partial('dashboard/employers');
$page_user_type = "employer"; //this example line currently does not exist, it's for example purpose below
$break;
case ('Employee'):
include_partial('dashboard/employees');
break;
case ('Administrator'):
include_partial('dashboard/admins');
break;
default: include_partial('dashboard/guest');
break;
}
Here's my actions class (currently empty):
public function executeIndex(sfWebRequest $request)
{
}
Basically, what I need is the switch statement moved to the action (I think), and a forward404Unless() method added that does the following:
$logged_user = sfContext::getInstance()->getUser()->getGuardUser()->getId();
$this->forward404Unless($logged_user == $page_user_type); //where the $page_user_type variable is retrieved by the switch statement in the example line above.
I've tried using the getAttribute() and setAttribute() with no success... and I'd rather not share attempts due to embarrassment. Just a beginner here...
Any help would be appreciated. Thanks in advance.
UPDATE:
Here's more information about the switch and the different partials:
The switch renders a different partial based upon the user's type. What it doesn't do is keep other logged-in users of a different type from accessing all the other partials... which in my design, is very bad. For example: logged-in users of type "employer" may not view the partial of type "employee". Currently they can (by explicitly typing in the other url), even though they are being redirected to the appropriate page during the the index action.
The 404 page should be called when a user of the wrong type tries to access the other partial by explicitly typing in the url. That's why I was attempting to add a variable to the switch statment when the appropriate partial is called and then passing that variable to the index action which would then evaluate it and either permit the partial to be rendered, or if the user_type and partial_type did not match -> forward to a 404 page. Make sense? I hope I explained that thouroughly enough. I'm sure there is an easier way... I'm just not schooled enough to know what that might be.
I sure do appreciate your response and attempt to resolve my issue.
You should play with the credential system to block not authorized user to access a ressource.
The 'type' of your user can become the name of a credential. Then you just have to create the security.yml to handle that.
I'm having a little trouble understanding when the 404 should happen. Does this handle it?
Action:
public function executeIndex(sfWebRequest $request)
{
$this->profileType = $this->getUser()->getGuardUser()->getProfile()->getType()->getName();
$this->forward404Unless(in_array($this->profileType, array('type1', 'type2')), 'Invalid profile type');
}
It's perfectly acceptable to have a switch statement in a veiw, though if that is the entirety of indexSuccess.php you may wish to call sfAction::setTemplate, instead.
Okay, I figured this one out on my own. Here's what I did to get the desired result:
Changed the route so that it cannot be explicitly typed and accessed. Problem solved.

Categories