MVC in PHP - How does the controller know the view? - php

I'm new to MVC in PHP and I was just wondering what the best way to do this is?
At the moment I've got a simple setup like this:
Model
User.php
Controller
controller.php
View
login.php
register.php
my_account.php
The model has all the database functionality for logging in and registering, and the view files have the relevant working forms.
My main question is, what is the best way to have the controller call pages? Currently, it looks something like:
public function show_page()
{
if ($_GET['p'] == "login")
{
include('View/login.php');
if (isset($_POST['username']))
{
$this->user->login($_POST['username'], $_['pass']
}
}
if ($_GET['p'] == "register") { include('View/register.php'); }
if ($_GET['p'] == "my_account") { include('View/my_account.php'); }
}
This doesn't seem logical, am I doing it wrong?

I think that the best way is to use some kind of routing system so you have a map somewhere with the possible url pattern / page to show combinations and after deciding which controller to call you can load the appropriate view in your controller.
What you presented here seems somewhat blurred to me. I think that you should check out implementations out there like Pure mvc or symfony so you can get a grip on the concept quickly. I believe that you (or anyone else for that matter) shouldn't reinvent the wheel but study, understand and improve what you can get.
If you are going to create your own MVC framework then you should check out the basic MVC concepts and plan your software before trying to write it.

Most PHP based MVC frameworks that I've used utilize a front controller. The .htaccess file directs all traffic to a single (usually index.php) file (usually) in the root of the project.
This file is responsible for determining which application controller to load. That controller is then responsible for any and all application level logic.
in a framework I wrote, in my front controller I do this
$page = tgsf_parse_url();
require resolve_controller( $page );
The tgsf_parse_url function in the above code parses $_SERVER['REDIRECT_URL'] to determine what variables are being passed.
the resolve_controller in the above code handles plugin hooks and 404 conditions, but the bottom line is that it always returns a path to send to include/require so that variable scoping doesn't become an issue (including in a function limits variable scope)
Any variables that are set in the controller are automatically available in a view when you include a view like this:
// this is an application level controller file
$name = 'Mr. Example';
include view( 'example' );
Then in the view file:
<h2><? echo $name; ?></h2>

What you have is not dynamic at all.
This is how I do it in my MVC:
private function loadView(){
//Bring the variables to the global scope
extract($this->getAll()); //load all variables into local scope
if(Config::get('view')){
$template_file = 'view/' . Config::get('view') . '/' . Config::get('method') . '.stp';
if(is_file($template_file)){
include $template_file;
}
else {
include 'view/missingview.stp'; //no such view error
}
}
else {
Config::set('template', 'blank');
include 'view/missingfunction.stp'; //no such function error
}
}
Have something set the view somewhere in the code.
This is how I set the view in the controller:
public function __construct(SessionManager $SESSION) {
$this->pageOn = Config::get('page');
$this->session = $SESSION;
$model_name = $this->name;
if(class_exists($model_name) && is_subclass_of($model_name, 'AppModel')){
/**
* #var AppModel $model_name
*/
$this->$model_name = new $model_name();
}
else {
//default model (no database table chosen)
$this->$model_name = new AppModel();
}
/* Get all posts */
$this->posts = $this->$model_name->getPosts();
Config::set('view', strtolower($model_name)); //<<RIGHT HERE!
if(!$this->session->get(strtolower($model_name))){
$this->session->set(strtolower($model_name), array());
}
}

Related

Using common functions (controllers) in code igniter

I am using Code Igniter as the framework for a project I am working on. I am a little new to the MVC world but learning as I go.
In my project, I have a front end controller and a back end admin controller.
On the front end, I have a registration form that has about 5 drop down's that are populated by a database call. In the admin side of things, I show the user submitted record along with pre-selecting the option in the dropdown that was chosen.
In order to do this, I had to also populate the dropdowns in the admin controller.
This is where my question comes into play. In a scenario like that, I essentially have 3 identical functions in the front end controller and the admin controller.
How can I go about creating a common or controller that both of these controllers have access to?
Here is an example of a shared function:
public function fetchSites($org = null)
{
if (!$org) {
if (null !== $this->input->post('org')) {
$sites = $this->support_model->getSites($this->input->post('org'));
header('Content-Type: application/json');
echo json_encode($sites);
}
} else {
$sites = $this->support_model->getSites($org);
return $sites;
}
}
Both the front end and backend share the same model, just different controllers.
I looked into creating a controller in the application/core but I couldn't figure out how to access the data from that controller.
You can easily create a helper and call it from both controllers, that way you wont be repeating code.
Something along with this in your helper file:
function sharedFunctionality($org = null)
{
$CI = & get_instance();
$CI->load->model('path/to/support_model');
if (!$org){
$sites = $CI->support_model->getSites($CI->input->post('org'));
header('Content-Type: application/json');
echo json_encode($sites);
} else {
$sites = $CI->support_model->getSites($org);
return $sites;
}
}
In both your controllers you will only need to call the helper and then the function:
$this->load->helper('nameOfHelper');
$sites = sharedFunctionality($this->input->post('field'));
More information to create a helper file:
https://ellislab.com/codeigniter/user-guide/general/helpers.html

How do I make this code reusable on different views? Proper method

I am somewhat confused on the proper way to perform the following. Is this a class a function or an object? (Not sure) So here goes (please be kind) I'm learning codeigniter/php simultaneously.
$is_live = $this->session->userdata('email');
$is_live = ucwords($is_live);
if (!$is_live == null)
echo 'Hello, '.$is_live.
' <b>(Not you? '.'<a href="' .base_url().
'main/logout">Log Out</a>) </b>';
I wrote this code, which checks if a session is set and if true echo the details. I currently have this code on my view/main However I want to have this code displayed on all pages.
What is the proper way to do this?
Do i create a view/header which is auto loaded on each page and insert this code there?
Do I create a class/public function in a helper file and load it where needed?
Do I create a class/public function in a library file and load it where needed?
Do I create a public function in a controller?
-EXTRA-
My assumption is option 1 but I'm curious what if I want to display this information in another location on a particular page? I don't want to have to copy/paste the code block because that is sloppy.
How would I set it up so that I can just call it where i need it?
e.g. $this->load->helper('check_for_sess');
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class isLive
{
public function is_live() {
$is_live = $this->session->userdata('email');
$is_live = ucwords($is_live);
if (!$is_live == null)
echo 'Hello, '.$is_live.
' <b>(Not you? '.'Log Out) </b>';
}
}
On the view I tried this:
<?php
$isLive = new isLive;
$isLive->is_live();
?>
This however doesn't work and causes a ' function userdata() on a non-object error '
Can someone please explain the proper way to achieve my desired solution and the correct syntax. Thanks in advance!
-UPDATE-- Tried to create a library - still getting error.
#Created a library
class CheckSess
{
function IsLive()
{
$is_live = $this->session->userdata('email');
$is_live = ucwords($is_live);
if (!$is_live == null)
{
$check_msg = 'Hello, '.$is_live.
' <b>(Not you? '.'Log Out) </b>';
}
return $check_msg;
}
}
#In the View
<?php echo $this->CheckSess->IsLive(); ?>
#in the controller
$this->load->library('CheckSess');
--UPDATE--
class CheckSess {
function IsLive()
{
$CI =& get_instance();
$is_live = $CI->session->userdata('email');
$is_live = ucwords($is_live);
if (!$is_live == null)
{
return 'Hello, '.$is_live.
' <b>(Not you? '.'Log Out) </b>';
}
}
}
Set this^ is the libraries folder
the view shows <?php echo $this->CheckSess->IsLive(); ?>
and the controller shows $this->load->library('CheckSess');
please advise. Thanks again!
ERROR---
( ! ) SCREAM: Error suppression ignored for
( ! ) Fatal error: Call to a member function IsLive() on a non-object in C:\wampserver\www\test-app\application\views\homepage.php on line 56
Call Stack
Time Memory Function Location
1 0.0005 151432 {main}( ) ..\index.php:0
2 0.0014 187792 require_once( 'C:\wampserver\www\test-app\system\core\CodeIgniter.php' ) ..\index.php:202
3 0.0277 1537000 call_user_func_array ( ) ..\CodeIgniter.php:359
4 0.0277 1537048 Main->index( ) ..\CodeIgniter.php:359
5 0.0283 1540200 CI_Loader->view( ) ..\main.php:8
6 0.0283 1540640 CI_Loader->_ci_load( ) ..\Loader.php:419
7 0.0287 1566584 include( 'C:\wampserver\www\test-app\application\views\homepage.php' ) ..\Loader.php:833
The best ways to write such reusable in CodeIgniter are Models and Libraries.
With libraries being better because the code and logic you use isn't actually what models in MVC are, but with CodeIgniter and especially for a beginner it's pretty much ok to write this in models if you are not planning to distribute or share this code in any way.
I would suggest reading more on what goes where in CodeIgniter, you can find plenty information here, on SO or on official CI forums, but as a quick reference, I will write a list of what goes where.
Views
Static, dumb data output, formatting, iteration(for displaying tables, lists, etc.).
The only things you want to use here are some of the basic PHP functions, CodeIgniter helpers, for, foreach, while loops and conditionals (if, case)
Controllers
Form data capture and validation, catching data from models, sending data to views, redirects.
Models
Querying, reading, writing and updating data from databases, remote sources (APIs), files.
Helpers
Often used functions that are expected to be used in views - formatting HTML, parsing and displaying trees as lists <ul><li></li></ul>, shorthand functions for other functions like
function mydate() {
return date("d.m.Y");
}
Stuff that does not use any of the CodeIgniter libraries/models (Technically it can use, but should be avoided in most cases).
Libraries
Custom classes and objects, data processing, API interfaces, complex of the aforementioned
If you want to display some view file with data, I would put it in a library. You can put it in to a model. In my projects when I use some parts, I have a library/model (difference in our case is minimal), lets call it pmodel.
class Pmodel extends CI_Model() {
function ShowHeader() {
$data = array(
'user' => $this->usermodel->GetCurrentUser()
}
$this->load->view('header', $data);
}
function ShowMyPart() {
$is_live = $this->session->userdata('email');
$is_live = ucwords($is_live);
if (!$is_live == null)
echo 'Hello, '.$is_live.
' <b>(Not you? '.'<a href="' .base_url().
'main/logout">Log Out</a>) </b>';
}
}
Now anywhere in the view files you can call $this->pmodel->ShowMyPart() if you have loaded it before (models of this type should be autoloaded anyway).
There is one little change I would do as it's considered as good practice - never echo anything from anywhere but the view files, return values instead.
Change the echo keyword to return and in your view file simply write echo $this->pmodel->ShowMyPart().
Why should you do that? Because, if you would like to call any method that echoes something from the controller right between loading header and footer(eg.), CodeIgniter will echo this before the header as CodeIgniter does not echo your loaded views right away, it saves them to output storage and sends the finalized output right before destructing himself. More reading can be found here.
But you can do even better - use a view file to output any data you want, just create a helper and load it from your model/library as you do in your controllers.
Hope that helps!
EDIT
For your edited question, the reason why you are getting errors with your library is that your library doesn't know that an instance of CodeIgniter even exists as you are not extending your library from it, as you do with your models and controllers. You don't use $this to refer to CodeIgniter classes in your libraries, as $this is a reference to your library itself, which is a separate class. To use CodeIgniter classes you should get a CodeIgniter instance:
$CI =& get_instance();
And then you can use $CI instead of $this to call CodeIgniter classes by changing the line:
$is_live = $this->session->userdata('email');
to
$is_live = $CI->session->userdata('email');
You could make a basic html helper and autoload it in the config file.
Then you can make a function for this...
function displayLoginStatus($em)
{
$html = '';
if($em)
{
$html = 'Hello, '.ucwords($em).' <b>(Not you? '.'Log Out) </b>';
}
return $html;
}
then you call it like a normal function:
<?php echo displayLoginStatus($this->session->userdata('email')); ?>
but ideally you would place something like this in a template or header file that wraps your standard page views. There's no need to make a function out of something like this since typically you don't have the login / logout in multiple places on the page.
It could be condensed simply to:
<?php if($this->session->userdata('email')): ?>
Hello, <?php echo ucwords($this->session->userdata('email')); ?> <b>(Not you? Log Out) </b>
<?php endif; ?>
I prefer to make a site template and wrap the views with it. Makes common elements much easier to handle.
Some people template it this way:
http://codeigniter.com/forums/viewthread/88335/
There are many ways you could handle this, but the general idea is to separate your most common elements into one template view. I don't know of a succinct way of instructing you on how to do this so I will only update this post if you feel the need for much more explanation on templating.
helper docs:
http://codeigniter.com/user_guide/general/helpers.html

Updated: Best practices for managing static content in Zend Framework?

I have some questions concerning the Zend Framework. I am attempting to route all static pages through the default controller using the now default displayAction() method. The intention is to have the displayAction() process the request by looking at the page param, determine if the script page exists, if it does render the view otherwise throw a 404 page not found error. Additionally, a test is done to see if a method with the same name as the param exists, if so, call on that action.
Listed here is the routing configuration from the application.ini
resources.router.routes.static-pages.route = /:page
resources.router.routes.static-pages.defaults.module = default
resources.router.routes.static-pages.defaults.controller = index
resources.router.routes.static-pages.defaults.action = display
Here is the controller actions:
public function someAction() {
// do something
}
public function displayAction() {
// extract page param, e.g. 'some'
$page = $this->getRequest()->getParam('page');
// create zend styled action method, e.g. 'someAction'
$page_action = $page.'Action';
// if a method within this controller exists, call on it
if (method_exists($this, $page_action)) {
$this->$page_action();
}
// if nothing was passed in page param, set to 'home'
if (empty($page)) {
$page = 'home';
}
// if script exists, render, otherwise, throw exception.
if (file_exists($this->view->getScriptPath(null)."/".$this->getRequest()->getControllerName()."/$page.".$this->viewSuffix)) {
$this->render($page);
} else {
throw new Zend_Controller_Action_Exception('Page not found', 404);
}
}
Now, here are my questions: Is there a better way of doing this? I'm relatively new to this framework, so are there best practices which apply? Is there a better way of calling on an action from within a controller? I have done A LOT of looking around through the documentation, however, quite a bit of it seems to contradict itself.
Update 1:
After having a think and a read, I've managed to simplify the solution and include a few things which were mentioned. NOTE: I use PagesController as my default static-content controller.
Listed here is the routing configuration from the application.ini. For calls to the home page i.e. "/", I pass "home" as the action param, for all other requests, the user defined / url link param is sent in action.
resources.router.routes.home.route = "/"
resources.router.routes.home.defaults.module = "default"
resources.router.routes.home.defaults.controller = "pages"
resources.router.routes.home.defaults.action = "home"
resources.router.routes.pages.route = "/:action"
resources.router.routes.pages.defaults.module = "default"
resources.router.routes.pages.defaults.controller = "pages"
Here is the controller actions. If user define parameter exists as an action, it will be called, else it falls to the php magic function __call.
public function someAction()
{
// Do something
}
public function __call($method, $args)
{
// extract action param, e.g. "home"
$page = $title = $this->getRequest()->getParam('action');
// test if script exists
if (file_exists($this->view->getScriptPath(null) . "/"
. $this->getRequest()->getControllerName() . "/$page . " . $this->viewSuffix))
{
// pass title to layout
$this->view->assign(compact('title'));
// render script
$this->render($page);
} else {
throw new Zend_Controller_Action_Exception('Page not found', 404);
}
}
It works. So, here are my questions: Would you consider standardising on using this method to manage static content? If not, why not? How would you improve it? Also, considering this is a GET request, would it be a wise move to use Zend_Filter_input to cleanse input or is that just overkill?
Your approach seems reasonable to me. However, perhaps you should take advantage of the __call method instead, which would allow you to more easily route your actions...
Setup your route like this:
resources.router.routes.static-pages.route = /:action
resources.router.routes.static-pages.defaults.module = default
resources.router.routes.static-pages.defaults.controller = index
And your controller like so:
public function someAction() {
//going to URL /some will now go into this method
}
public function __call($name, $args) {
//all URLs which don't have a matching action method will go to this one
}
I think your on the right track however here are some other ideas.
Break up your routing per sections in your INI:
ie a blog router, a static page router a forum router etc.. (I think you are already doing this)
Use the various router classes to handle routing per section rather than sending it to a controller.
Static:
http://framework.zend.com/manual/en/zend.controller.router.html#zend.controller.router.routes.static
All:
http://framework.zend.com/manual/en/zend.controller.router.html
Some links that may help:
codeutopia.net/blog/2007/11/16/routing-and-complex-urls-in-zend-framework/
www.vayanis.com/2009/03/20/intro-to-zend-framework-routing/

Codeigniter dynamic routing (or loading one controller using different urls from db)

I have the following problem, I get urls like domain.com/en/contact or domain.com/de/kontakt.
I would like for both to point to the controller contact.php.
At the moment I am using the follwoing code:
$route['(\w{2})/(\w{2})/contact/?(.)?'] = 'contact';
$route['(\w{2})/(\w{2})/kontakt/?(.)?'] = 'contact';
I would rather have it like this.
fn get_type()
{
gets pages and the type (indicating contoller e.g. contact)
}
$type = get_type($param = ...);
load_controller($type); // loads controller from within base controller
So I know this is not correct codeigniter syntax, I would rather not use libraries and clutter my library directory with libraries like thoses.
Does anyone have a good idea for this?
Thanks.
You could look into remapping the method being passed instead of using routes:
function index(){
// my default method
}
function _remap($method){
if ($method == 'en'){
$this->english();
} else if($method == 'de') {
$this->german();
} else {
$this->index();
}
}
function english(){
// my english method
}
function german(){
// my german method
}
To get dynamic routing to work I now do the following.
The CMS writes a file into the codeigniter config dir, lets call it dynamic_route.php
The written file is analog to the normale route.php config file.
An writes stuff like
$route['(\w{2})/(\w{2})/URL_PART_FROM_CMS/?(.)?'] = 'CONTROLLER_CHOSEN_IN_CMS';
This file is included into the routs.php
$route['default_controller'] = "content";
$route['404_override'] = '';
// include file
include('dynamic_routes.php');
Every time the cms changes sth. the dynamic file is rewritten. This makes it very dynamic without any performance impact.

What is an example of MVC in PHP?

I'm trying to understand the MVC pattern. Here's what I think MV is:
Model:
<?php
if($a == 2){
$variable = 'two';
}
else{
$variable = 'not two';
}
$this->output->addContent($variable);
$this->output->displayContent();
?>
View:
<?php
class output{
private $content;
public function addContent($var){
$this->content = 'The variable is '.$var;
}
public function displayContent(){
include 'header.php';
echo $content;
include 'footer.php';
}
}
?>
Is this right? If so, what is the controller?
The controller is your logic, the model is your data, and the view is your output.
So, this is the controller:
$model = new UserDB();
$user = $model->getUser("Chacha102");
$view = new UserPage($user->getName(), $user->getEmail());
echo $view->getHTML();
The model is the UserDB class which will give me my data. The view is the UserPage that I give the data from the model to, and it will then output that page.
As you can see, the controller doesn't do much in this example, because you are simply getting user data and displaying it. That is the beauty of MVC. The controller doesn't have to deal with the User SQL or HTML stuff, it just grabs the data and passes it to the view.
Also, the view doesn't know anything about the model, and the model doesn't know anything about the view. Therefore, you can chance the implementation of either, and it won't affect the other.
Relating more to your example, you have the view correct, but you have mixed your controller and model.
You could relieve this by:
Controller:
$model = new NumberToWord();
$word = $model->getWord($a);
$this->output->addContent($word);
$this->output->displayContent();
Model:
class NumberToWord{
public function getWord($number)
{
if($number == 2){
return 'two';
}
else{
return 'not two';
}
}
}
And keep your same output
Controllers receive user requests - usually there is some kind of router that takes a URL and routes the request to the appropriate controller method.
Models are used by a controller to query data to/from a database (or other data source).
Views are called from a controller to render the actual HTML output.
If all you want to do is create a simple template system, you might aswell go with:
$content = 'blaba';
$tpl = file_get_contents('tpl.html');
echo str_replace('{content}',$content,$tpl);
With a template file like:
<html>
<head><title>Whatever</title></head>
<body>{content}</body>
</html>
In your example, it's more like you've split a Controller into a Model and a View.
Model: Business logic / rules and typically some sort of database to object relational mapping
Controller: Responds to url requests by pulling together the appropriate Model(s) and View(s) to build an output.
View: The visual structure the output will take. Typically a "dumb" component.
It can be confusing when you first encounter MVC architecture for a web app, mainly because most web frameworks are not MVC at all, but bear a much closer resemblance to PAC. In other words, the Model and View don't talk, but are two elements pulled together by the context the Controller understands from the given request. Check out Larry Garfield's excellent commentary on the subject for more information:
http://www.garfieldtech.com/blog/mvc-vs-pac
Also, if you are interested in the MVC pattern of development, I suggest you download one of the many frameworks and run through a tutorial or two. Kohana, CodeIgnitor, CakePHP, and Zend should be enough to kick-start a Google-a-thon!
Zend Framework: Surviving The Deep End has some good sections explaining MVC. Check out the MCV Intro and especially this seciton on the model.
There are numerous interpretations of the Model but for many programmers the Model is equated with data access, a misconception most frameworks inadvertently promote by not obviously acknowledging that they do not provide full Models. In our buzzword inundated community, many frameworks leave the definition of the Model unclear and obscured in their documentation.
To answer "where's the controller":
Controllers must define application behaviour only in the sense that they map input from the UI onto calls in Models and handle client interaction, but beyond that role it should be clear all other application logic is contained within the Model. Controllers are lowly creatures with minimal code who just set the stage and let things work in an organised fashion for the environment the application operates in.
I think you'll fine it (and his references of other articles and books) a good read.
Here is a very simple example of MVC using PHP. One thing missing is THE router. It selects one of the controller to do the job. We have only one controller, the customer.
If we compare it with 3 tiers
Model: The database
View: Client
Server:Controller
Router:It selects a controller
When you select something from an application on web browser, the request goes to router, from router it goes to controller. Controller asks from model and make a view. View is rendered to you.
Only model can talk to controller back and forth.
1- Model.php
<?php
class Model
{
private $con=null;
private $r=null;
function connect()
{
$host="localhost";
$db="mis";
$user="root";
$pass="";
$this->con=mysqli_connect($host,$user,$pass) or die(mysqli_error());
if(!$this->con){
echo die(mysqli_error());
}
else mysqli_select_db($this->con,$db);
}
function select_all()
{
$this->connect();
$sql="select * from customers";
$this->r=mysqli_query($this->con,$sql) or die(mysqli_error());
return $this->r;
}
function display_all()
{
$i=0;
echo "aaaaaaaaaa";
$this->r=$this->select_all();
while($q=mysqli_fetch_array($this->r))
{
$i++;
echo $i."-Id:".$q['id']."</br>";
echo $i."-Name:".$q['name']."</br>";
echo $i."-Phone:".$q['phone']."</br></br>";
}
}
}
?>
2. Controller: There may may be many controllers.
<?php
class Customers extends Model
{
function select_all1()
{
//echo "aaaaaaa";
$this->display_all();
}
}
?>
3. View: There may be many views
<?php
include("model.php");
include("customers.php");
?>
<html>
<head><title>Customers</title></head>
<body>
<?php
$c=new Customers();
echo $c->select_all1();
?>
</body>
</html>

Categories