Following this tutorial http://johnsquibb.com/tutorials/mvc-framework-in-1-hour-part-one Im trying to write my first MVC Blog.
I understood how it works, router.php Calls the suitable page controller. This controller calls the model, Then calls the View page with the returned value.
My question is, What if I want to add to the same news.php page a header\footer. Normally I would write "include("header.php"). But now when using MVC, how could I implement that?
These are my files:
router.php:
<?php
/**
* This controller routes all incoming requests to the appropriate controller
*/
//Automatically includes files containing classes that are called
//fetch the passed request
$pageURL = $_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"];
$path_parts = pathinfo($pageURL);
$page_name = $path_parts['filename'];
$parsed = explode('?' , $page_name);
//the page is the first element
$page = array_shift($parsed);
// If there is any variables, GET them.
if(!empty($parsed))
{
$parsed = explode('&' , $parsed[0]);
$getVars = array();
foreach ($parsed as $argument)
{
//explode GET vars along '=' symbol to separate variable, values
list($variable , $value) = explode('=' , $argument);
$getVars[$variable] = urldecode($value);
}
}
//compute the path to the suitable file
$target ='controllers/' . $page . '_controller.php';
//get target controller
if (file_exists($target))
{
include_once($target);
//modify page to fit naming convention
$class = ucfirst($page) . '_Controller';
//instantiate the appropriate class
if (class_exists($class))
{
$controller = new $class;
}
else
{
//did we name our class correctly?
die('class does not exist!');
}
}
else
{
//can't find the file in 'controllers'!
die('page does not exist!');
}
//once we have the controller instantiated, execute the default function
//pass any GET varaibles to the main method
$controller->main($getVars);
// AutoLoad
function __autoload($className)
{
// Parse out filename where class should be located
// This supports names like 'Example_Model' as well as 'Example_Two_Model'
list($suffix, $filename) = preg_split('/_/', strrev($className), 2);
$filename = strrev($filename);
$suffix = strrev($suffix);
//select the folder where class should be located based on suffix
switch (strtolower($suffix))
{
case 'model':
$folder = '/models/';
$filename = ($className);
break;
case 'library':
$folder = '/libraries/';
break;
case 'driver':
$folder = '/libraries/drivers/';
break;
}
//compose file name
$file = SERVER_ROOT . $folder . strtolower($filename) . '.php';
//fetch file
if (file_exists($file))
{
//get file
include_once($file);
}
else
{
//file does not exist!
die("File '$filename' containing class '$className' not found in
'$folder'.");
}
}
?>
post_controller.php
<?php
/**
* This file handles the retrieval and serving of posts posts
*/
class Posts_Controller
{
/**
* This template variable will hold the 'view' portion of our MVC for this
* controller
*/
public $template = 'posts';
/**
* This is the default function that will be called by router.php
*
* #param array $getVars the GET variables posted to index.php
*/
public function main(array $getVars)
{
//$b_controller =new Bottom_Bar_Controller;
//$b_controller->main($getVars);
$postsModel = new Posts_Model;
//get an post
$post = $postsModel->get_post($getVars['id']);
//create a new view and pass it our template
$header = new View_Model('header_template');
$bottom_bar = new View_Model('bottom_bar');
$view = new View_Model($this->template);
//assign post data to view
$view->assign('header', $header->render(FALSE));
$view->assign('bottom', $bottom_bar->render(FALSE));
$view->assign('title' , $post['title']);
$view->assign('content' , $post['content']);
$view->assign('date' , $post['date']);
$view->assign('by' , $post['added_by']);
$view->render();
}
}
posts_model.php
<?php
/**
* The Posts Model does the back-end heavy lifting for the Posts Controller
*/
class Posts_Model
{
/**
* Holds instance of database connection
*/
private $db;
public function __construct()
{
$this->db = new MysqlImproved_Driver;
}
/**
* Fetches article based on supplied name
*
* #param string $author
*
* #return array $article
*/
public function get_post($id)
{
//connect to database
$this->db->connect();
//sanitize data
$author = $this->db->escape($id);
//prepare query
$this->db->prepare
(
"
SELECT * FROM `posts`
WHERE
`id` = '$id'
LIMIT 1
;
"
);
//execute query
$this->db->query();
$article = $this->db->fetch('array');
return $article;
}
}
?>
view_model.php
<?php
/**
* Handles the view functionality of our MVC framework
*/
class View_Model
{
/**
* Holds variables assigned to template
*/
private $data = array();
/**
* Holds render status of view.
*/
private $render = FALSE;
/**
* Accept a template to load
*/
public function __construct($template)
{
//compose file name
$file = SERVER_ROOT . '/views/' . strtolower($template) . '.php';
if (file_exists($file))
{
/**
* trigger render to include file when this model is destroyed
* if we render it now, we wouldn't be able to assign variables
* to the view!
*/
$this->render = $file;
}
}
/**
* Receives assignments from controller and stores in local data array
*
* #param $variable
* #param $value
*/
public function assign($variable , $value)
{
$this->data[$variable] = $value;
}
/**
* Render the output directly to the page, or optionally, return the
* generated output to caller.
*
* #param $direct_output Set to any non-TRUE value to have the
* output returned rather than displayed directly.
*/
public function render($direct_output = TRUE)
{
// Turn output buffering on, capturing all output
if ($direct_output !== TRUE)
{
ob_start();
}
// Parse data variables into local variables
$data = $this->data;
// Get template
include($this->render);
// Get the contents of the buffer and return it
if ($direct_output !== TRUE)
{
return ob_get_clean();
}
}
public function __destruct()
{
}
}
posts.php
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link href="../style/style.css" rel="stylesheet" type="text/css" media="screen" />
<title> Posts (View)</title>
</head>
<body>
<div id="main">
<div class="container">
<?=$data['header'];?>
<div id="content">
<div class="content-background">
<h2> <?=$data['title'];?></h2>
<h4> <?=$data['date'];?> </h4>
<p><?=$data['content'];?></p>
</div>
</div>
</div>
</div>
</body>
</html>
Part of the problem is, that your tutorial has a pretty primitive interpretation of MVC-inspired design pattern (you actually cannot implement classical MVC in PHP, but there are patterns, that are based on it).
View is not just a template. Views are supposed to be class instances, which contain all presentation logic and deal with multiple templates. What you actually have there is a layout template which contains posts template.
// class \Application\View\Posts
public function render()
{
$layout = new Template( $this->defaultTemplateDirectory . 'layout.html');
$content = new Template( $this->defaultTemplateDirectory . 'posts.html' );
$layout->assign( 'content' , $content->render() );
return $layout->render();
}
Also, one of the things, that a view instance should do, is requesting information from the model layer.
Few materials that you might find useful:
Model-View-Confusion part 1: Why the model is accessed by the view in MVC
Simple PHP Template Engine
How should a model be structured in MVC?
And if you want to expand you knowledge in OOP, this post contains a list of recommended lectures and books.
There is no good solution in the MVC structure for that. You can find a solution in every framework. In CakePHP for example you can use an AppController, a controller which is always called for every requests. Sort of base controller.
But no, not very nice to do.
Most simple one is to ask the data directly from the view. That sounds weird but it is accepted in the MVC structure. So in your view you call $News->getLatestItems(10); and work with them.
I don't like it but it works.
If you have lots of widgets and blocks MVC alone might just not be the right structure. Then you could take a look at things like: http://techportal.inviqa.com/2010/02/22/scaling-web-applications-with-hmvc/ which is a derivate of MVC.
Another solution which you see more and more: Request it via AJAX calls. So just load them after the page has loaded. That way you also solve the issue since one MVC request then becomes multiple MVC requests.
Related
Is it possible to create custom HTML attributes on CHtml::checkboxList?
For example, I want to generate an input like this, adding the custom attribute "data-input-x":
<input class="customClass" id="Model_inputX_0" value="1" name="Model[relationX][]" type="checkbox" data-input-x="3">
I already tried using the code bellow, but it not worked:
echo $form->checkboxList($model, 'relationX', $dataList, array('class'=>'checkboxFase refeicaoFaseComum', 'data-input-x'=>3));
If you run your code and inspect element it you will see values the values created by Yii, the difference. Echos under a foreach loop will work nicely..
You can extend CHtml like that:
In folder "components" you create a new file named MyCHtml. In there create the class MyCHtml and copy the core code of framework for checkBoxList (https://github.com/yiisoft/yii/blob/1.1.16/framework/web/helpers/CHtml.php#L1123).
class MyCHtml extends CHtml {
//Final method is provided below
}
Then you add the parameter $extraAttributes=array() after $htmlOptions=array().
The trick is to add those attributes and their values at $htmlOptions array of each input.
If all your configurations are correct and you have access to your componenets as normal, you can call the new checkBoxList function like this:
<?php
//Values can be created dynamically or statically depending on situation
//Each value corresponds to each checkbox value that you want to contain the extra attribute
$extraAttributes = array(
'data-input-x'=>array(
6=>'k',
11=>'a',
7=>'b'),
'data-input-y'=>array(
6=>'c',
2=>'d'),
);
echo MyCHtml::checkboxList(($name, $select, $data, $htmlOptions, $extraAttributes);
?>
The whole class is the following:
<?php
class MyCHtml extends CHtml
{
/**
* Generates a list box.
* ...
* #param array $extraAttributes extra HTML attributes corresponding on each checkbox
* ...
*/
public static function checkBoxList($name,$select,$data,$htmlOptions=array(), $extraAttributes=array())
{
$template=isset($htmlOptions['template'])?$htmlOptions['template']:'{input} {label}';
$separator=isset($htmlOptions['separator'])?$htmlOptions['separator']:self::tag('br');
$container=isset($htmlOptions['container'])?$htmlOptions['container']:'span';
unset($htmlOptions['template'],$htmlOptions['separator'],$htmlOptions['container']);
if(substr($name,-2)!=='[]')
$name.='[]';
if(isset($htmlOptions['checkAll']))
{
$checkAllLabel=$htmlOptions['checkAll'];
$checkAllLast=isset($htmlOptions['checkAllLast']) && $htmlOptions['checkAllLast'];
}
unset($htmlOptions['checkAll'],$htmlOptions['checkAllLast']);
$labelOptions=isset($htmlOptions['labelOptions'])?$htmlOptions['labelOptions']:array();
unset($htmlOptions['labelOptions']);
$items=array();
$baseID=isset($htmlOptions['baseID']) ? $htmlOptions['baseID'] : self::getIdByName($name);
unset($htmlOptions['baseID']);
$id=0;
$checkAll=true;
foreach($data as $value=>$labelTitle)
{
$checked=!is_array($select) && !strcmp($value,$select) || is_array($select) && in_array($value,$select);
$checkAll=$checkAll && $checked;
$htmlOptions['value']=$value;
$htmlOptions['id']=$baseID.'_'.$id++;
//********This does the trick
foreach($extraAttributes as $attributesKey => $attributesValue) {
$found = false;
foreach($attributesValue as $subAttributesKey => $subAttributesValue) {
if ($value === $subAttributesKey) {
$htmlOptions[$attributesKey] = $subAttributesValue;
$found = true;
break;
}
}
if (!$found) {
$htmlOptions[$attributesKey] = '';
}
}
//********All the rest is the same with core method
$option=self::checkBox($name,$checked,$htmlOptions);
$beginLabel=self::openTag('label',$labelOptions);
$label=self::label($labelTitle,$htmlOptions['id'],$labelOptions);
$endLabel=self::closeTag('label');
$items[]=strtr($template,array(
'{input}'=>$option,
'{beginLabel}'=>$beginLabel,
'{label}'=>$label,
'{labelTitle}'=>$labelTitle,
'{endLabel}'=>$endLabel,
));
}
if(isset($checkAllLabel))
{
$htmlOptions['value']=1;
$htmlOptions['id']=$id=$baseID.'_all';
$option=self::checkBox($id,$checkAll,$htmlOptions);
$beginLabel=self::openTag('label',$labelOptions);
$label=self::label($checkAllLabel,$id,$labelOptions);
$endLabel=self::closeTag('label');
$item=strtr($template,array(
'{input}'=>$option,
'{beginLabel}'=>$beginLabel,
'{label}'=>$label,
'{labelTitle}'=>$checkAllLabel,
'{endLabel}'=>$endLabel,
));
if($checkAllLast)
$items[]=$item;
else
array_unshift($items,$item);
$name=strtr($name,array('['=>'\\[',']'=>'\\]'));
$js=<<<EOD
jQuery('#$id').click(function() {
jQuery("input[name='$name']").prop('checked', this.checked);
});
jQuery("input[name='$name']").click(function() {
jQuery('#$id').prop('checked', !jQuery("input[name='$name']:not(:checked)").length);
});
jQuery('#$id').prop('checked', !jQuery("input[name='$name']:not(:checked)").length);
EOD;
$cs=Yii::app()->getClientScript();
$cs->registerCoreScript('jquery');
$cs->registerScript($id,$js);
}
if(empty($container))
return implode($separator,$items);
else
return self::tag($container,array('id'=>$baseID),implode($separator,$items));
}
public static function activeCheckBoxList($model,$attribute,$data,$htmlOptions=array())
{
self::resolveNameID($model,$attribute,$htmlOptions);
$selection=self::resolveValue($model,$attribute);
if($model->hasErrors($attribute))
self::addErrorCss($htmlOptions);
$name=$htmlOptions['name'];
unset($htmlOptions['name']);
if(array_key_exists('uncheckValue',$htmlOptions))
{
$uncheck=$htmlOptions['uncheckValue'];
unset($htmlOptions['uncheckValue']);
}
else
$uncheck='';
$hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false);
$hidden=$uncheck!==null ? self::hiddenField($name,$uncheck,$hiddenOptions) : '';
return $hidden . self::checkBoxList($name,$selection,$data,$htmlOptions);
}
/**
* Generates a push Html button that can submit the current form in POST method.
* #param string $label the button label
* #param mixed $url the URL for the AJAX request. If empty, it is assumed to be the current URL. See {#link normalizeUrl} for more details.
* #param array $ajaxOptions AJAX options (see {#link ajax})
* #param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
* attributes are also recognized (see {#link clientChange} and {#link tag} for more details.)
* #return string the generated button
*/
public static function ajaxSubmitHtmlButton($label,$url,$ajaxOptions=array(),$htmlOptions=array())
{
$ajaxOptions['type']='POST';
$htmlOptions['type']='submit';
return self::ajaxHtmlButton($label,$url,$ajaxOptions,$htmlOptions);
}
}
I'm developing the base of an MVC project in PHP. I'm advancing with the View part, but something is now working as expected in the rendering of the HTML template.
Here are some contents of the files I'm coding to make this work:
my_project_root/views/templates/humans_list.php
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Humans List</title>
</head>
<body>
<h1>Humans List</h1>
<ul>
<?php foreach($humans as $human) { ?>
<li><?php echo $human ?></li>
<?php } ?>
</ul>
</body>
</html>
my_project_root/views/HumanView.php
<?php
/**
* A class representing a View
*/
class HumanView
{
/** #var array contains a list of names */
var $humans;
/**
* Renders the view in HTML
* #return void
*/
public function render()
{
// Proccess some names to follow some constraints
$this->process($this->humans);
// Here I want to use the template in my view, but
// I need access to the "$humans" variable
require 'templates/humans_list.php';
}
/**
* Proccess humans names to cut surnames for too long names
* #param array &$names
* #return void
*/
private function process(&$names)
{
foreach ($names as $key => $name)
{
if (strlen($name) > 15)
{
$name_parts = explode(' ', $name);
if (isset($name_parts[1])) {
$name_parts[1] = strtoupper(substr($name_parts[1], 0, 1)) . '.';
$names[$key] = implode(' ', $name_parts);
}
else {
$names[$key] = substr($name, 0, 15);
}
}
}
}
// the rest of code
// ...
}
// In my controller I'll use the view and set the names
// retrieved from my model, but to make testing easier I just
// set the object here and set a mock array to test the
// render method, but It doesn't recognize my $humans variable
$view = new HumanView();
$view->humans = [
'John First Smith',
'Peter Second Johnson',
'Marcus Third Willians',
'Lucas Fourth Brown'
];
$view->render();
I expected that the render method printed the HTML from the template with the name in the $human variable as an unordered list (in the ul tag). But as I said before, the variable is not recognized, tough I already set it.
An html more or less like this:
Humans List
John F. Smith
Peter S. Johnson
Marcus T. Willians
Lucas F. Brown
But I just get this:
Human List
Not a single name being printed.
What could be the problem in the code that doesn't let my template to render the variable I just set?
Suppose your template file should also reference the object, since as far as I can see the $humans variable won't automagically jump from the class variable into your method like that. Try this:
<?php foreach($this->humans as $human) { ?>
<li><?php echo $human ?></li>
<?php } ?>
So, this is my first encounter with SuiteCRM or any other CRM for that matter. I need to query the db on a table that is not used by the CRM for our quote system. So, I have created the module using module builder and modified the module file so that it uses the correct table. The problem is that when the query runs, SuiteCRM still adds its default where clauses and adds the deleted = 0 condition to the query.
So, I tried using the method that is described on this SO page. That doesn't work as I get the an error that I am using an undefined variable (db) and that I am calling to a member function fetchByAssoc() on a non-object. Now, I am placing the code in the moduleName.php file. Maybe that is my issue. I don't know as I have never worked on any other CRM project. If anyone can point me in the right direction as to what I will need to do to be able to query a different table other than the default CRM table and then show the results from that query inside of a dashlet, your help will be greatly appreciated.
I got the errors fixed. They were my fault as I had not referenced the object.
So, as requested, here is some of my code. This is my php file.
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
require_once('include/Dashlets/Dashlet.php');
class FrtwQuotesDashlet extends Dashlet {
var $height = '200'; // height of the dashlet
var $quoteData = "";
/**
* Constructor
*
* #global string current language
* #param guid $id id for the current dashlet (assigned from Home module)
* #param array $def options saved for this dashlet
*/
function FrtwQuotesDashlet($id, $def) {
$this->loadLanguage('FrtwQuotesDashlet');
if(!empty($def['height'])) // set a default height if none is set
$this->height = $def['height'];
parent::Dashlet($id); // call parent constructor
$this->isConfigurable = true; // dashlet is configurable
$this->hasScript = false; // dashlet has javascript attached to it
// if no custom title, use default
if(empty($def['title'])) $this->title = $this->dashletStrings['LBL_TITLE'];
else $this->title = $def['title'];
}
/**
* Displays the dashlet
*
* #return string html to display dashlet
*/
function display() {
$sql = "SELECT QuoteNbr, crmname, ShipToCity FROM quotes.quotehdr LIMIT 10";
$result = $GLOBALS["db"]->query($sql);
$quoteData = "<table>";
while($quotes = $GLOBALS["db"]->fetchByAssoc($result)){
foreach ($quotes as $quote) {
$quoteData .="<tr><td>".$quote[0]."</td><td>".$quote[1]."</td><td>".$quote[2]."</td></tr>";
}
}
$ss = new Sugar_Smarty();
//assign variables
//$ss->assign('greeting', $this->dashletStrings['LBL_GREETING']);
$ss->assign('quoteData', $this->quoteData);
$ss->assign('height', $this->height);
$str = $ss->fetch('custom/modules/Home/FrtwQuotesDashlet/FrtwQuotesDashlet.tpl');
return parent::display().$str;
}
}
?>
The issue was with the foreach loop. I removed it and now it works fine. In analyzing the code, the while loop actually does the iterations needed. So, by adding the foreach, what was happening was that the code was iterating over each row returned from the db and then doing some weird stuff -- as in, it would only return a partial string of what each value should be. Since I am querying on 3 fields, it would also loop over each row 3 times, thereby creating 3 different rows from each row. So, for anyone with similar issue, this is how working code looks.
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
require_once('include/Dashlets/Dashlet.php');
class FrtwQuotesDashlet extends Dashlet {
var $height = '200'; // height of the dashlet
var $quoteData = "";
/**
* Constructor
*
* #global string current language
* #param guid $id id for the current dashlet (assigned from Home module)
* #param array $def options saved for this dashlet
*/
function FrtwQuotesDashlet($id, $def) {
$this->loadLanguage('FrtwQuotesDashlet');
if(!empty($def['height']))
$this->height = $def['height'];
parent::Dashlet($id);
$this->isConfigurable = true;
$this->hasScript = false;
// if no custom title, use default
if(empty($def['title'])) $this->title = $this->dashletStrings['LBL_TITLE'];
else $this->title = $def['title'];
}
/**
* Displays the dashlet
*
* #return string html to display dashlet
*/
function display() {
$sql = "SELECT QuoteNbr, revnbr, crmname, ShipToCity FROM quotes.quotehdr LIMIT 10";
$result = $GLOBALS["db"]->query($sql);
$this->quoteData = "Need headers here when we determine exact fields....";
while($quotes = $GLOBALS["db"]->fetchByAssoc($result)){
$this->quoteData .="<tr><td width = \"30%\">".$quotes["QuoteNbr"].' '.$quotes['revnbr']."</td><td width = \"30%\">".$quotes["crmname"]."</td><td width = \"30%\">".$quotes["ShipToCity"]."</td></tr>";
}
$ss = new Sugar_Smarty();
//assign variables
// $ss->assign('greeting', $this->dashletStrings['LBL_GREETING']);
$ss->assign('greeting', "This is the Greeting....");
$ss->assign('quoteData', $this->quoteData);
$ss->assign('height', $this->height);
$str = $ss->fetch('modules/Home/Dashlets/FrtwQuotesDashlet/FrtwQuotesDashlet.tpl');
return parent::display().$str; // return parent::display for title and such
}
}
?>
I need 3 different templates for my Codeigniter application. I had read about Themes' library. But still I didn't get any idea about how to add a template to Codeignier ..
I got about how to involke template in Controller .
Please help
I'm using this template library, is really simple and works well for me.
application/libraries/Template.php
<?php
class Template {
var $template_data = array();
var $use_template = '';
/**
* Set variable for using in the template
*/
function set($name, $value)
{
$this->template_data[$name] = $value;
}
/**
* Set template name
*/
function set_template($name)
{
$this->use_template = $name;
}
/**
* Load view
*/
function load($view = '' , $view_data = array(), $template = '', $return = FALSE)
{
$this->CI =& get_instance();
if (empty($template)) {
$template = $this->CI->config->item('template_master');
}
if (!empty($this->use_template)) {
$template = $this->use_template;
}
$this->set($this->CI->config->item('data_container'), $this->CI->load->view($view, array_merge($view_data, array ('template' => $this->template_data)), true));
return $this->CI->load->view($this->CI->config->item('template_folder') . '/' . $template, $this->template_data, $return);
}
}
application/config/template.php
<?php
$config['template_master'] = 'main';
$config['template_folder'] = 'templates';
$config['data_container'] = 'content';
application/views/templates/main.php
Header<br />
<?php echo $content; ?></br>
Footer
application/controllers/welcome.php
<?php
class Welcome extends CI_Controller
{
public function index()
{
$this->load->config('template');
$this->load->library('template');
$this->template->load('welcome', array('view' => 'data'));
}
}
I usually put the config/library files on autoload, and you can use anytime $this->template->set_template('other_template'); to use another one :)
Hope it helps.
I've used the following setup in a CodeIgniter project:
The different templates along with stylesheets and images are in the following folder:
/templates/1/header.php
/templates/1/footer.php
/templates/1/images/*
/templates/1/style/*
/templates/2/header.php
/templates/2/footer.php
/templates/2/images/*
/templates/2/style/*
In your Controllers determine which template you want to load and pass the path to that template as a variable ( templatepath in this case ) to your View files. Inside the view files you do the following:
<?php include($templatepath.'/header.php'); ?>
at the top and
<?php include($templatepath.'/footer.php'); ?>
at the bottom.
I have been reading a lot about how and why to use an MVC approach in an application. I have seen and understand examples of a Model, I have seen and understand examples of the View.... but I am STILL kind of fuzzy on the controller. I would really love to see a thorough enough example of a controller(s). (in PHP if possible, but any language will help)
Thank you.
PS: It would also be great if I could see an example of an index.php page, which decides which controller to use and how.
EDIT: I know what the job of the controller is, I just don't really understand how to accomplish this in OOP.
Request example
Put something like this in your index.php:
<?php
// Holds data like $baseUrl etc.
include 'config.php';
$requestUrl = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
$requestString = substr($requestUrl, strlen($baseUrl));
$urlParams = explode('/', $requestString);
// TODO: Consider security (see comments)
$controllerName = ucfirst(array_shift($urlParams)).'Controller';
$actionName = strtolower(array_shift($urlParams)).'Action';
// Here you should probably gather the rest as params
// Call the action
$controller = new $controllerName;
$controller->$actionName();
Really basic, but you get the idea... (I also didn't take care of loading the controller class, but I guess that can be done either via autoloading or you know how to do it.)
Simple controller example (controllers/login.php):
<?php
class LoginController
{
function loginAction()
{
$username = $this->request->get('username');
$password = $this->request->get('password');
$this->loadModel('users');
if ($this->users->validate($username, $password))
{
$userData = $this->users->fetch($username);
AuthStorage::save($username, $userData);
$this->redirect('secret_area');
}
else
{
$this->view->message = 'Invalid login';
$this->view->render('error');
}
}
function logoutAction()
{
if (AuthStorage::logged())
{
AuthStorage::remove();
$this->redirect('index');
}
else
{
$this->view->message = 'You are not logged in.';
$this->view->render('error');
}
}
}
As you see, the controller takes care of the "flow" of the application - the so-called application logic. It does not take care about data storage and presentation. It rather gathers all the necessary data (depending on the current request) and assigns it to the view...
Note that this would not work with any framework I know, but I'm sure you know what the functions are supposed to do.
Imagine three screens in a UI, a screen where a user enters some search criteria, a screen where a list of summaries of matching records is displayed and a screen where, once a record is selected it is displayed for editing. There will be some logic relating to the initial search on the lines of
if search criteria are matched by no records
redisplay criteria screen, with message saying "none found"
else if search criteria are matched by exactly one record
display edit screen with chosen record
else (we have lots of records)
display list screen with matching records
Where should that logic go? Not in the view or model surely? Hence this is the job of the controller. The controller would also be responsible for taking the criteria and invoking the Model method for the search.
<?php
class App {
protected static $router;
public static function getRouter() {
return self::$router;
}
public static function run($uri) {
self::$router = new Router($uri);
//get controller class
$controller_class = ucfirst(self::$router->getController()) . 'Controller';
//get method
$controller_method = strtolower((self::$router->getMethodPrefix() != "" ? self::$router->getMethodPrefix() . '_' : '') . self::$router->getAction());
if(method_exists($controller_class, $controller_method)){
$controller_obj = new $controller_class();
$view_path = $controller_obj->$controller_method();
$view_obj = new View($controller_obj->getData(), $view_path);
$content = $view_obj->render();
}else{
throw new Exception("Called method does not exists!");
}
//layout
$route_path = self::getRouter()->getRoute();
$layout = ROOT . '/views/layout/' . $route_path . '.phtml';
$layout_view_obj = new View(compact('content'), $layout);
echo $layout_view_obj->render();
}
public static function redirect($uri){
print("<script>window.location.href='{$uri}'</script>");
exit();
}
}
<?php
class Router {
protected $uri;
protected $controller;
protected $action;
protected $params;
protected $route;
protected $method_prefix;
/**
*
* #return mixed
*/
function getUri() {
return $this->uri;
}
/**
*
* #return mixed
*/
function getController() {
return $this->controller;
}
/**
*
* #return mixed
*/
function getAction() {
return $this->action;
}
/**
*
* #return mixed
*/
function getParams() {
return $this->params;
}
function getRoute() {
return $this->route;
}
function getMethodPrefix() {
return $this->method_prefix;
}
public function __construct($uri) {
$this->uri = urldecode(trim($uri, "/"));
//defaults
$routes = Config::get("routes");
$this->route = Config::get("default_route");
$this->controller = Config::get("default_controller");
$this->action = Config::get("default_action");
$this->method_prefix= isset($routes[$this->route]) ? $routes[$this->route] : '';
//get uri params
$uri_parts = explode("?", $this->uri);
$path = $uri_parts[0];
$path_parts = explode("/", $path);
if(count($path_parts)){
//get route
if(in_array(strtolower(current($path_parts)), array_keys($routes))){
$this->route = strtolower(current($path_parts));
$this->method_prefix = isset($routes[$this->route]) ? $routes[$this->route] : '';
array_shift($path_parts);
}
//get controller
if(current($path_parts)){
$this->controller = strtolower(current($path_parts));
array_shift($path_parts);
}
//get action
if(current($path_parts)){
$this->action = strtolower(current($path_parts));
array_shift($path_parts);
}
//reset is for parameters
//$this->params = $path_parts;
//processing params from url to array
$aParams = array();
if(current($path_parts)){
for($i=0; $i<count($path_parts); $i++){
$aParams[$path_parts[$i]] = isset($path_parts[$i+1]) ? $path_parts[$i+1] : null;
$i++;
}
}
$this->params = (object)$aParams;
}
}
}
Create folder structure
Setup .htaccess & virtual hosts
Create config class to build config array
Controller
Create router class with protected non static, with getters
Create init.php with config include & autoload and include paths (lib, controlelrs,models)
Create config file with routes, default values (route, controllers, action)
Set values in router - defaults
Set uri paths, explode the uri and set route, controller, action, params ,process params.
Create app class to run the application by passing uri - (protected router obj, run func)
Create controller parent class to inherit all other controllers (protected data, model, params - non static)
set data, params in constructor.
Create controller and extend with above parent class and add default method.
Call the controller class and method in run function. method has to be with prefix.
Call the method if exisist
Views
Create a parent view class to generate views. (data, path) with default path, set controller, , render funcs to
return the full tempalte path (non static)
Create render function with ob_start(), ob_get_clean to return and send the content to browser.
Change app class to parse the data to view class. if path is returned, pass to view class too.
Layouts..layout is depend on router. re parse the layout html to view and render
Please check this:
<?php
global $conn;
require_once("../config/database.php");
require_once("../config/model.php");
$conn= new Db;
$event = isset($_GET['event']) ? $_GET['event'] : '';
if ($event == 'save') {
if($conn->insert("employee", $_POST)){
$data = array(
'success' => true,
'message' => 'Saving Successful!',
);
}
echo json_encode($data);
}
if ($event == 'update') {
if($conn->update("employee", $_POST, "id=" . $_POST['id'])){
$data = array(
'success' => true,
'message' => 'Update Successful!',
);
}
echo json_encode($data);
}
if ($event == 'delete') {
if($conn->delete("employee", "id=" . $_POST['id'])){
$data = array(
'success' => true,
'message' => 'Delete Successful!',
);
}
echo json_encode($data);
}
if ($event == 'edit') {
$data = $conn->get("select * from employee where id={$_POST['id']};")[0];
echo json_encode($data);
}
?>