I'm new to zend so I'm not sure what's the best way to organize what I'm trying to do. I direct the user to a series of quizzes.
mysite.com/quiz1
mysite.com/quiz2
mysite.com/quiz3
mysite.com/quiz4
When the user answers the first quiz, he is forwarded to a page that tells him if his answer is correct, and on the same page he can choose to answer another quiz. If he answers it, he again will be taken to a page where he is told if his answer was correct and presented with the third quiz.
From the architecture side of things, are each of these quiz1, quiz2, etc. pages considered a controller of their own? Their path says they may be, but doesn't make sense to me if they are. Is there a way to have them at those same paths but bundle them in the same controller. As I said I'm new to Zend so would appreciate some feedback about the right way to do this.
I would do a Quiz controller that will have actions like showQuiz(), validateQuiz() that will read the quiz parameter. This way you will reuse most of the code. The quizes will be entries in the db and there you can also build paths or connections with them.
class QuizController extends Zend_Controller_Action
{
public function showAction()
{
// you can play this in routes but it could be basically something like this
// localhost/quiz/id/1
$quiz_id = $this->_request->getParam('id');
$this->view->quiz = $this->getQuizTable()->find($quiz_id);
}
public function validateAction()
{
$quiz_id = $this->_request->getParam('id');
$quiz = $this->getQuizTable()->find($quiz_id);
$quiz->validate(); // build your own validator function
}
}
Related
I am trying to develope good code organization habits and work exclusively with OOP in php but I can't seem to wrap my head around something.
Here is a simplified description of what I am working with:
I have all my class files in a folder '/resources/Classes'
I have all my html and javascript in '/public_html' & '/public_html/script respectively'
My question is concerning files that are the actions of forms or AJAX requests. For example 'formAction.php' and 'ajaxURL.php'. These files are not Classes and also do not contain any html or other such GUI.
I have been putting them in a folder 'resources/actions' but my gut tells me something about this is not fully OOP.
Is my usage of these files incorrect if I am trying for complete OOP? if so how can I approach this differently.
Here is an actual file from my project as a concrete example:
//file: getBalance.php
<?php
/**
* This script gets the balance of an account from the server
*/
if (!isset($Database)) {
$Database = require_once "../clear_finance_pkg.php";
}
/** #var User $User */
$User = $Database->getUserByID("1");//TODO: user should be set dynamically
$User->setAccounts($Database->getAccountsByUser($User));
if (isset($arg1)) {
$accountID = $arg1;
foreach ($User->getAccounts() as $Account) {
if ($Account->getId() == $accountID) {
$RbcChequing = RbcAccount::accountToRbcAccount($Account, "Chequing");
echo '$' . Money::toDollars($RbcChequing->getBalance());
break;
}
}
} else throw new Exception('Account ID is not set. Could not get balance');
It's difficult to say if your code is complete OOP, but i think it isn't. It looks like you are on the right track, because you are using classes, objects and methods. That's the basic of OOP. No longer large if/else statements and a lot of variables that doesn't make sense, but objects and methods without code-duplication.
I think your question in more related to the seperation of logic and view. First of all. In general it's ok to make a file for a function, but in a large application you will loose the overview. What you are doing know is combine view-related and logic-related things in one file, but actually that's not what you want. The ideal situation is full seperation of logic and view so you can create multiple seperate views based on the same logic.
My advice is to take a look at the MVC-pattern. Take a look at this link. It will help you to get a basic understanding of the MVC-pattern. Notice that you won't longer need to have a file for each function. And you have a seperation of your logic and view elements because you can use the same model in multiple views (although this is maybe not the best example).
In a MVC pattern, what's the best way to handle when a single view could have multiple actions of the same type (e.g POST)?
Say for instance in a TODO list application. You might allow a user to create multiple lists. Each list could have multiple items. So a user navigates to site.com/list/1 which shows them all the items on the 1st list (1 is a GET parameter). There are then 2 forms (POST) on this page to allow a user to:
Create a new item
Delete an existing item
Should the bootstrap create a "listcontroller", inspect the POST variables and then call the appropriate method similar to :
$lc = new ListController();
if(strtolower($request->verb) === 'post'):
if(isset($_POST['title'])) :
$data = $lc->newItem($_POST);
$load->view('newitem.php', $data);
else if(isset($_POST['delete']) && isset($_POST['id'])):
$data = $lc->deleteItem($_POST);
$load-view('deleteitem.php', $data);
endif;// End if post title
else:
//GET request here so show view for single list
endif; //
Or is it better to just do something like
$lc = new ListController();
if(isset($_POST)):
//controller handles logic about what function to call
$data = $lc->PostAction($_POST);
// $data could also potentially hold correct view name based on post
$load->view();
else:
//again just show single list
endif;
I'm just struggling how best to have a controller potentially handle multiple different actions, as there's potentially quite a few nested if/else or case statements to handle different scenarios. I know these would have to sit somewhere, but where is cleanest?
I know that there are many frameworks out there, but I'm going through the whole "want to understand best practice" behind it phase. Or is this totally the wrong way to do it? Should the controllers actually be structured differently?
To begin with, I actually really like, how you are dealing with implementation of MVC. None of that rails-like parody, where view is managed inside the controller.
Here is what I think is the root of your problem: you are still using a "dumb view" approach.
View is not supposed to be a synonym for "template". Instead it should be a full object, which has knowledge-of and ability-to deal with multiple templates. Also, in most of MVC-inspired design patterns, the view instances are able to request information from model layer.
In your code the issue can be traced back to view's factory ( the $load->view() method ), which only gets what controller sends it. Instead controller should only change the name of the view, and maybe send something that would change the state of view.
The best solution for you would be to create full-blown view implementation. Such that view itself could request data from model layer and , based on data it received, decide which template(s) to use and whether to require additional information from model layer.
I think you're somewhat on the right track with the latter approach. However, you should not hard code the calling of actions in your bootstrap. The bootstrap should interpret the URL and call the action methods dynamically through the use of a function like call_user_func_array.
Also, I would suggest that you leave the rendering of views up to the action code so the action logic is self sufficient and flexible. That would allow the action to analyse the input for correctness and render errors or views appropriately. Also, you've got the method 'deleteItem' on your controller, but that should really be the work of a model. Perhaps you should read up some more on MVC and try to work with an existing framework to understand the concepts better before you try to implement your own framework (I would suggest the Yii framework for that).
Here's an example of how I think your logic should be implemented in a good MVC framework.
class ListController extends BaseController
{
public function CreateAction($title){
if(ctype_alnum($title))
{
$list = new List();
$list->Title = $title;
if($list->insert())
{
$this->render_view('list/create_successful');
}
else
{
$this->render_view('list/create_failed');
}
}
else
{
$this->render_view('list/invalid_title');
}
}
public function DeleteAction($id){
$list = List::model()->getById($id);
if($list == null)
{
$this->render_view('list/errors/list_not_found');
}
elseif($list->delete())
{
$this->render_view('list/delete_successful');
}
else
{
$this->render_view('list/delete_failed');
}
}
}
here is a great tutorial on how to write your own MVC framework
I've started development on a CakePHP project since a few weeks now. Since the beginning I was struggling with the amount of code inside the controllers. The controllers have, in most cases more lines of code than the models. By knowing the expression "Skinny controller, fat model" I'm searching for some days now for a way to put more code in the models.
The question arises at this point is, "where to draw the line". What should the controller do and what should the model do. There are already some questions/answers on this only I'm searching for a more practical explanation. For example I've put a function below which is now inside the controller. I think a part of this code must and can be moved to the model. So my question is: what part can I move to the model and what can remain in the controller.
/**
* Save the newly added contacts and family members.
*/
public function complete_contacts()
{
if ($this->request->is('post')) {
if (isset($this->data['FamilyMembers'])) {
$selected_user = $this->Session->read('selected_user');
$family_members = $this->data['FamilyMembers'];
$this->ContactsConnection->create();
foreach ($family_members as $family_member) {
// connection from current user to new user
$family_member['ContactsConnection']['contact_family_member_id'] = $selected_user['id'];
$family_member['ContactsConnection']['nickname'] = $selected_user['first_name'];
$this->ContactsConnection->saveAll($family_member);
// inverted connection from new user to current user
$inverted_connection['ContactsConnection']['family_member_id'] = $selected_user['id'];
$inverted_connection['ContactsConnection']['contact_family_member_id'] = $this->FamilyMember->inserted_id;
$inverted_connection['ContactsConnection']['nickname'] = $family_member['FamilyMember']['nickname'];
$this->ContactsConnection->saveAll($inverted_connection);
}
}
}
}
Should I create a function in the FamilyMember model called: "save_new_family_member($family_member, $selected_user)"?
As far as the purposes of the M and the C
The model manages the behavior and data of the application domain,
responds to requests for information about its state (usually from the
view), and responds to instructions to change state (usually from the
controller).
The controller receives user input and initiates a response by making
calls on model objects. A controller accepts input from the user and
instructs the model and a view port to perform actions based on that
input.
I would suggest you can pass
$selected_user = $this->Session->read('selected_user');
To your Model and perform your for each inside of your Model. You may want to change rules as to how the data is stored or perform some transformations on it and the Controller should be blind to this. Basically use the Controller to get your information [from the View often] to the Model. Don't directly manipulate the Model from the Controller. In short YES create the function that you suggested :)
That being said sometimes I find myself in a position where my Controller has to do more than I'd like, in which case at least break the task down into helper methods that way your controller is more manageable and you can reuse code where needed.
You are doing it right.
You can of course create some methods in model and make it fat with:
function updateContactFamilyMemberId($id){}
function updateNickname($nickname){}
...
In my opinion it still will be correct, but unnecessary.
is there a way in Zend to call one controller from another?
I have seen the action stack but that does not seem to work for me and i have read that alot of people think it to be EVIL!
What i am trying to acchive is as follows:
Reports Controller scans through all the modules in the system, it then checks to see if a route has been registered for that module called MODULENAME-reports-run
The controller then reuns that registered route to generate all the reports from all the modules.
The idea is that i can create modules for my application that clients can simple drag and drop into place and the system picks up on the reports.
Your controller should not do any of these things. Your controller should only accept any input from the User Interface and then decide to delegate this to the appropriate classes in your Model.
If you have a ReportController, have it accept any input and forward it to a ReportsService or something else in the Model responsible for generating Reports. It's not the controllers responsibility to generate them.
It should look something like this:
public function generateReportAction()
{
try {
$service = new Model_ReportService;
$service->setReportToGenerate($this->getRequest()->getParam('reportId'));
$this->view->report = $service->generateReport();
} catch (ReportException $e) {
// do something with $e
}
}
If your ReportService needs to generate multiple reports, change the ReportService so it knows how to do that. You could do something like
$service = new Model_ReportService;
$service->setModulesDirectory('something');
$this->view->reports = $service->generateReportsForModules();
Personally I dont think a ReportService should need to know about a Module Directory, so you will want to give the public interface of that Service some more thought. But in general, this is the way to go.
Whatever you do, dont do it in the controller. Controllers ought to be slim.
I'm about to write a admin panel for my CMS written in CodeIgniter. There will be some user information visible at all time - both in the layout's header section and the sidebar. I'm used to do it in a way that I personally hope and think could be done a lot easier, since I'm tired of sending the same parametres to the view over and over again, when it's dynamic data that needs to be displayed on every page anyways (such as unread messages, username, name, status, etc).
I'll need controllers and models, I know that, but do I have to pass, just for an example, the user's username, unread messages etc. every time I need to load a view? Should I do some kind of library for this?
Now my question is: How would I do it when it comes to best practice and for making it easy to maintain in the future?
I hope my question is understandable :)
Personally, I would extend the Controller library (create a MY_Controller by following the guidance at the bottom of Creating Libraries at codeigniter.com).
You would use your model etc as normal. Then you would create a private function in your MY_Controller class to get the relevant "global" data and call
$this->load->vars('everywhere_data', $data_from_relevant_models);
which would make the data available to all views called from that point on as $everywhere_data. Then add a reference to that function in the constructor of MY_Controller, perhaps with a conditional checking for the user to be actually logged in.
If it's complex to collect and get all that data, you might write a library to handle it for you, but the 'controller' part would still be done by MY_Controller: i.e. to get the data and then use load->vars() to publish it to the view.
As a quick and untested example, MY_Controller would start something like as follows:
<?php
class MY_Controller extends Controller
{
private $logged_in_user;
function MY_Controller()
{
parent::Controller();
if( $this->_logged_in_userid() > 0 )
{
$this->logged_in_user = $this->_get_user( $this->logged_in_userid() );
$this->load->vars('logged_in_username', $this->logged_in_user->username );
} else {
$this->logged_in_user = false;
}
}
...
}
Note that things like _logged_in_userid() would access the session for you (e.g. return $this->session->userdata('logged_in_userid');), and _get_user() would access the relevant models.
Finally, you would have a view that accesses $logged_in_username (or everywhere_data in my first example) which you would call into your headers etc. This leaves your normal controllers uncluttered so that they can focus on delivering their specific functionality, stops you rewriting your code several times AND maintains the MVC ideals.
You could create a View just to hold the information and get it from a $_SESSION variable in the View itself if you want to keep it all in one place.