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).
Related
In my website I want to allow users to view some of the features by using a guest account. The guest account would see much of the same information as a regular user but some things will be limited.
What would be a good approach, programming wise, to this problem?
I copy paste the exact pages that a user would normally have to different .php files, example: guestProfile.php. Then I manually remove the buttons or information a guest would see.
I add a bunch of checks on my views such as:
if(userIsGuest)
// then hide info.
Another solution.
What makes the most logical sense in terms of software engineering? Please try to quantify your answer instead of using opinion.
Abstraction is your friend. Rather than stuffing your views with conditionals, you should pass your views any sub-views you wish to be included in the output. Your view doesn't need to know the inner workings of these sub-views, and you can swap them out depending on whether the user is a normal user or a guest user.
Here's a simplistic example:
class View {
/** Does some view stuff... */
public function addSubview($name, Subview $subview) {
//store subviews to be used in the template
}
}
/**
* Somewhere in a controller far far away...
*/
class GuestController extends Controller {
/** Does some controllery stuff... */
public function exec() {
//..
//prepare the view
$this->view->addSubview('toolbar', $guestToolbarSubview);
}
}
In that case it is better to make the checks (Solution 2). This stays true to the DRY (Don't Repeat Yourself) principle. It keeps your code less redundant and you are less likely to forget to copy changes to your second version of the code.
Try to keep common code in separate php file and include that file wherever required.
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'm in the creation of my own CMS. I'm doing this with the help of an MVC framework. I think a CMS should be build with a set of standard elements. Like, buttons, textfields and div containers where all the form elements reside in etc.
For instance, when i want to create a basic page then i want to do something like this in a view:
$cmsForm->beginTab('tab name');
$cmsForm->textfield('name', 'default value');
$cmsForm->textfield('email', '');
$cmsForm->textarea('info', '');
$cmsForm->submit('Save');
$cmsForm->endTab();
The problem is, is that i'm not sure where to put these methods. I obviously can't put these methods inside a controller. But perhaps a model is a good place? On the other hand, the model layer is used for storing/retrieving data from a persisten layer, not for returning HTML...
What would be a good place in an MVC architexture for this kind of thing?
This type of code belongs in your view layer. Remember, there's no rule that says your view layer can only be templates.
There's no rule that says what you have to do.
If I was you, I'd write my own class and implement it in framework, because it would be easier to re-factor later in case you need it. Also you won't change the logic of framework accidentally (everything may happen).
On the other hand, the model layer is used for storing/retrieving data from a persisten layer, not for returning HTML
Actually model layer is for business logic.
This clearly is code related to presentation logic, and thus should be in the View. Thought i think, that instead of such strange generator script, you would be better off by writing a template, which view then handles.
Also, your API is horrible. Haven't you even heard about SOLID principles.
If would be much better (but still misguided, IMHO )to have something like:
$foo = new HTML\Form();
$name = new HTML\Input('name');
$name->setValue('default value');
$name->setRules(array('required'));
$foo->addField( $name );
$email = new HTML\Input('email');
$email->setRules(array('required', 'email'));
$foo->addField( $email )
// other fields added
$template = new Template('/path/to/template.something');
$template->bind('form', $foo);
$output = $template->render();
It's been around 5 months since I picked up a PHP book and started coding in PHP. At first, I created all my sites without any organizational plan or MVC. I soon found out that was a pain..
Then I started to read on stackoverflow on how to separate php and html and that's what I have been doing ever since.
Ex:
profile.php <--this file is HTML,css. I just echo the functions here.
profile_functions.php <--this file is mostly PHP. has the functions.
This is how I have been separating all my coding so far and now I feel I should move on and start MVC. But the problem is, I never used classes before and suck with them. And since MVC (such as cakephp and codeigniter) is all classes, that can't be good.
My question: Is there any good books/sites/articles that teaches you how to code in MVC? I am looking for beginner beginner books :)
I just started reading the codeigniter manuel and I think I am going to use that.
EDIT: Is it possible to have a MVC organization structure to your coding without using cake, codeigniter, etc? Basically just separate say profile.php into 3 different files(the view, controller, model)
to answer your question
Is it possible to have a MVC
organization structure to your coding
without using cake, codeigniter, etc?
Basically just separate say
profile.php into 3 different files(the
view, controller, model)
absolutely...
first file profile.php ( the view, what gets hit by the browser )
<?php
include( 'controllers/UsersController.php' );
$controller = new UsersController();
$controller->profile();
$pageData = $controller->data;
?>
the controller
<?php
include 'models/UsersModel.php';
class UsersController{
public $data;
public $model;
public function __construct(){
$this->model = new UserModel();
}
public function profile(){
$this->data = $this->model->findUser();
}
}
the model
<?php
class UsersModel{
public function __constuct(){
// connect to your db or whatever you need to do
}
public function findUser(){
return mysql_query( "SELECT * FROM users WHERE users.id = 2 LIMIT 1" );
}
}
MVC is just a design pattern. It's not really something you can "code in".
If you like to code in PHP, here is an article regarding MVC in PHP. It has an overview explaining the design pattern, and then an example follows.
How I learned was by going through this tutorial:
http://www.symfony-project.org/jobeet/1_4/Doctrine/en/
The primary focus is to learn the Symfony Framework, but by default, you will be exposed to and learn good MVC principles.
It's not PHP, but see if you can get a copy of Tate's Bitter Java. It will discuss the organizational side of things (how and why the organizational code improves stuff).
I'm a bit hesitant to recommend one of the great Java books for PHP programming, but this book is one of the few that starts with code written without an organizational plan and improves it into a MVC like structure without the use of 3rd party libraries. This way it teaches you what the organization is about from a practical point of view. Hopefully once you understand the pattern, it won't be too difficult to translate the ideas into PHP.
Another alternative is to grab one of the dozens of PHP frameworks and recode to the framework. Doing so will get your results much faster, but the drawback is that you'll likely understand those results in greater detail, and there is a small chance your code will not behave the same after you rewrite it from scratch. We all like to think the new stuff will do everything the old stuff did, but often we forget something (or it behaves differently).
MVC is a "generic" design pattern that is not particular to any language. More of a coding philosophy. On the most basic level it's just separating data from business logic from presentation. Below is a simple example of a "templating" system using MVC. You would be able to swap out any of the parts without breaking anything, and the data is not tied to the formatting/display. This is sample code, not efficient.
Model, get the data:
function getName($id) {
$name = array('_first_'=>'Joe', '_last_'=>'Smith', '_mi_'=>'C');
return $name
}
Controller, processes it:
$name = getName(1);
$name['_fullname_'] = $name['_first_'].' '.$name['_mi_'].'. '.$name['_last_'];
outputView($name);
View, output the content:
// Example html file: <b>Hello _fullname_, how are you</b>
function outputView($view, $data) {
switch ($view) {
case 'xml':
$out = file_get_contents('view.xml');
case 'html':
$out = file_get_contents('view.html');
case 'json':
$out = file_get_contents('view.json');
}
$search_for = array_keys($data);
$replace_with = $data;
echo str_replace($search_for, $replace_with, $out);
}
I'm using CodeIgniter, and will likely use their template library as I want to keep things extremely simple to use. The content for the template variables will come from the database, but I want the business admins to know what content areas are available. Basically the names of the parameters when they choose a specific template. For instance, Joomla uses an extra XML file that defines each area, whereas Wordpress uses comments within a page template to inform the system that the PHP file is a template. I like the Joomla approach because you don't have to parse the PHP file to find the areas, but I like the Wordpress approach because you don't have an extra XML file associated with every template. Are there other approaches that I'm missing?
I think the nicest way would be to add a small hack to the template parser class. The code looks quite readable and clean in system/libraries/Parser.php. You could insert a hook in that class that can be used to keep track of the variables. I don't know, if it works, but here's a snippet:
class CI_Parser {
var $varCallback;
function setVarCallback($callbackFunction) {
$this->varCallback = $callbackFunction;
}
...
function _parse_single(...) {
$callback = $this->varCallback;
$callback($key);
}
...
//Somewhere in your code
function storeVarName($variableName) {
// Persist the variable name wherever you want here
}
$this->parser->setVarCallback('storeVarName');
You could do this directly in the controller:
// in the controller
print_r($data);
$this->load->view("main", $data);
Or a little more rudimentary, but you could pass to the template a PHP array of variables (or an object):
// in the controller
$data = array();
$data["namespace"] = array(
"title" => "My website",
"posts" => array("hi", "something else")
);
$this->load->view("main", $data);
And then in the view, have a flag to print_r the namespace to show all the vars available, so that business admins know exactly what to use.
// in the view
if(isset($namespace["showAllVars"])) print_r($namespace);
One option would be to call token_get_all on the PHP file (only when your business admins are loading it up), and parse the output of that.
The best approach, in my opinion, is to keep the variable definitions in another place (such as a database table, or a separate file). This will help with testing (i.e., a programmer can't just remove a tag and it's gone) and making sure things are still working as you move on with the application development in time.
Another advantage is that your application logic will be independent from the templating engine.
On a side note, if you expect a lot of traffic, you may consider using smarty instead. We have done extensive testing with most of the templating engines around and smarty is the fastest.