PHP MVC - adding more Views - php

I recently implemented the following MVC code using this tiny mvc boilerplate.
I did not want to use Zend or Symfony as I only require a small structure but i really need to expend this one slightly.
I am new to PHP so wondered if anyone has used this or knows how i go about adding another View. I have got the link version working which i use to load my layout but would like to add a Content section within this layout which is able to call other pages.
Any help with this would be great!

Gods below .. that video is horrible.
In that existing example, if you want to add another "view" (which is no really what view is), you will need another method in the controller:
class Controller
{
// -- snip --
// you need to change the constructor too
public function __construct()
{
$this->load = new Load;
$this->model = new Model;
}
// -- snip --
public function gallery()
{
$list = $this->model->get_urls();
if ( count($list) > 0 )
{
$this->load->view('gallery.php' , $list);
}
else
{
$this->load->view('error.php', array(
'source' => 'gallery',
'reason' => 'empty'
));
}
}
// -- snip --
}
And you also would need to change the tinyMvc.php file:
$c = new Controller;
$action = 'home';
if ( isset( $_GET['page']))
{
$action = $_GET['page'];
}
if ( method_exists( $c, $action) )
{
$c->{$action}();
}
else
{
echo 'no such action !';
}
Anyway. Whole that "tutorial" uses the terms of MVC, that isn't really whats made there. His "view" is actually just a simple template. Which is not completely a thing to learn how to do for a beginner, but his implementation sucked too .. If you want to learn how to make simple native php templates, you might find this article quite useful.

Related

Change Zend Framework 2 default template resolver behavior

I've just started looking into Zend framework 2 .One thing that I can’t seem to figure out is how to change the behavior of the framework when its deciding what view template to use when i’m not passing it in the viewmodel.
When looking for the answer myself I found the following, which states that Zend resolves view templates using the pathing below:
{normalized-module-name}/{normalized-controller-name}/{normalized-action-name}
(Source: http://zend-framework-community.634137.n4.nabble.com/Question-regarding-template-path-stack-tp4660952p4660959.html)
Now I’m looking to edit or remove the normalized-module-name segment. All the view files stay in my module/views folder. The reason I want to change this is because I’m using sub namespaces as my module name, resulting in the first segment of the namespace as the normalized module name (which is not specific enough for me).
To give you an example, the module Foo\Bar will result in an example view being rendered from:
/modules/Foo/Bar/view/foo/test/index.phtml.
I would like to change that default behavior to:
/modules/Foo/Bar/view/bar/test/index.phtml
Starting with zf 2.3 you can use extra config parameter view_manager['controller_map'] to enable different template name resolving.
Look at this PR for more info: https://github.com/zendframework/zf2/pull/5670
'view_manager' => array(
'controller_map' => array(
'Foo\Bar' => true,
),
);
Will result in controller FQCN starting with 'Foo\Bar' to be resolved following those rules:
strip \Controller\ namespace
strip trailing Controller in classname
inflect CamelCase to dash
replace namespace separator with slash
Eg: Foo\Bar\Controller\Baz\TestController -> foo/bar/baz/test/actionname
Update:
Starting with zend-mvc v3.0 this is default behavior
I had a similar problem and here's my solution.
Default template injector is attached to an event manager of the current controller with priority -90, and it resolves a template name only if a view model is not provided with one.
Knowing this, you can create your own template injector with a required logic and attach it to the event manager with the higher priority.
Please see the code below:
public function onBootstrap(EventInterface $event)
{
$eventManager = $event->getApplication()->getEventManager();
$eventManager->getSharedManager()
->attach(
'Zend\Stdlib\DispatchableInterface',
MvcEvent::EVENT_DISPATCH,
new TemplateInjector(),
-80 // you can put here any negative number higher -90
);
}
Your template injector which resolves template paths instead of the default one.
class TemplateInjector
{
public function __invoke(MvcEvent $event)
{
$model = $event->getResult();
if (!$model instanceof ViewModel)
{
return;
}
$controller = $event->getTarget();
if ($model->getTemplate())
{
return ;
}
if (!is_object($controller))
{
return;
}
$namespace = explode('\\', ltrim(get_class($controller), '\\'));
$controllerClass = array_pop($namespace);
array_pop($namespace); //taking out the folder with controllers
array_shift($namespace); //taking out the company namespace
$moduleName = implode('/', $namespace);
$controller = substr($controllerClass, 0, strlen($controllerClass) - strlen('Controller'));
$action = $event->getRouteMatch()->getParam('action');
$model->setTemplate(strtolower($moduleName.'/'.$controller.'/'.$action));
}
}
Here's the link from my blog where I wrote about it in more details: http://blog.igorvorobiov.com/2014/10/18/creating-a-custom-template-injector-to-deal-with-sub-namespaces-in-zend-framework-2/
Right template to ViewModel is injected in MVC event 'dispatch' (defined in ViewManager) by Zend\Mvc\View\Http\InjectTemplateListener with priority -90
You'll have to create custom InjectTemplateListener and register it with higher priority to same event.
But I'd recommend to set template in every action by hand, because of performance - see http://samminds.com/2012/11/zf2-performance-quicktipp-1-viewmodels/
template name resolving is a heavy process(on system resources), and all the articles about ZF2 performance says that you should provide the template name manually to increase performance.
so don't waste time finding a way to do something that you will end up doing manually :D
In order to improve Next Developer answer, I write the following code in TemplateInjector.php:
class TemplateInjector
{
public function __invoke(MvcEvent $event)
{
$model = $event->getResult();
if (!$model instanceof ViewModel) {
return;
}
if ($model->getTemplate()) {
return;
}
$controller = $event->getTarget();
if (!is_object($controller)) {
return;
}
$splitNamespace = preg_split('/[\\\]+/', ltrim(get_class($controller), '\\'));
$moduleName = $splitNamespace[1];
$controller = $splitNamespace[0];
$action = $event->getRouteMatch()->getParam('action');
$model->setTemplate(strtolower($moduleName . '/' . $controller . '/' . $action));
}
}
I've changed the way to build the Template path. Using regexp is faster than using array methods.

When should the Controller get instantiated?

I am building an AJAX web app, using PHP for my back end. I am trying to design a routing system that will let me easily drop new pages in, and let me focus on the Javascript. The actual pages that PHP will be serving up are simple, just views that are essentially containers for Javascript charts (built with d3.js). Thus, my controller won't even have to interact with my model until I start making AJAX calls.
I am new to OOP, especially in back end. I've been doing a bit with Javascript, but I am brand new to incorporating OOP with MVC & solving the issue of routing. I know there are modules/plugins out there that have Routing classes written, but as the back end part of this project is very straight-forward - essentially, how best to serve up an 'About' page on a blog - I'd like to take this opportunity to learn it thoroughly myself.
I have one controller:
<?php
//controller.php
include 'views/view.php';
class Controller
{
public function homeAction() {
$view = new View();
$view->setTemplate('views/home.php');
$view->render();
}
public function categoryAction($category) {
$view = new View();
$view->setTemplate("views/Monitor/{$category}/{$category}.php");
$view->setCategory($category);
$view->render();
}
public function monitorAction($category, $monitor) {
$view = new View();
$view->setTemplate("views/Monitor/{$category}/{$monitor}.php");
$view->setCategory($category);
$view->setMonitor($monitor);
$view->render();
}
}
?>
Right now, I instantiate my controller at the beginning of index.php:
<?php
// Load libraries
require_once 'model.php';
require_once 'controller.php';
$controller = new Controller();
$uri = str_replace('?'.$_SERVER['QUERY_STRING'], '', $_SERVER['REQUEST_URI']);
// home action
if ($uri == '/') {
$controller->homeAction();
// /{category}/{monitor}
} elseif (preg_match("#/(.+)/(.+)#", $uri, $matches) ) {
$category = $matches[1];
$monitor = $matches[2];
$controller->monitorAction($category, $monitor);
// /{category}
} elseif (preg_match("#/([^/.]+)#", $uri, $matches) ) {
$category = $matches[1];
$controller->categoryAction($category);
// 404
} else {
header('Status: 404 Not Found');
echo '<html><body><h1>Page Not Found</h1></body></html>';
}
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && (!empty($_GET)) && $_GET['action'] == 'get_data') {
$function = $_GET['chart'] . "_data";
$dataJSON = call_user_func($function);
header('Content-type: application/json');
echo $dataJSON;
}
?>
I have read a bit about PHP's autoloader, but I'd like to get it down manually first, because I want to make sure and understand the fundamentals.
Is this the appropriate place to instantiate my Controller object?
First, your architecture is facing some major problems. You need a router to take care of your requested URIs by the users and next you need an initialization state for your system. The usual way to create Controllers is to extend a parent class, then in your parent class __construct method you can initialize your children controllers, however, your system isn't in a good shape.
This is a gold link that I never delete:
http://johnsquibb.com/tutorials/mvc-framework-in-1-hour-part-one

Managing different output formats or device-types

I have to display different views for mobile devices and I want to provide a simple JSON-API.
I wrote a little module for the Kohana Framework which loads different views depending on some circumstances, which should help me in this case: https://github.com/ClaudioAlbertin/Kohana-View-Factory
However, I'm not very happy with this solution because I can't set different assets for different device-types. Also, when I'd output JSON with a JSON-view, it's still wrapped in all the HTML-templates.
Now, I'm looking for a better solution. How do you handle different output formats or device-types in your MVC-applications?
I had an idea: just split the controller into two controllers: a data-controller and an output-controller.
The data-controller gets and sets data with help of the models, does
all the validating etc. It gets the data from the models and write it to a data-object
which is later passed to the view.
The output-controller loads the views and give them the data-object from the data-controller. There is an output-controller for each format or device-type: an output-controller for mobile-devices could load the mobile-views and add all the mobile-versions of stylesheets and scripts. A JSON-output-controller could load a view without all the html-template stuff and convert the data into JSON.
A little example:
<?php
class Controller_Data_User extends Controller_Data // Controller_Data defines a data-object $this->data
{
public function action_index()
{
$this->request->redirect('user/list');
}
public function action_list()
{
$this->data->users = ORM::factory('user')->find_all();
}
public function action_show($id)
{
$user = new Model_User((int) $id);
if (!$user->loaded()) {
throw new HTTP_Exception_404('User not found.');
}
$this->data->user = $user;
}
}
class Controller_Output_Desktop extends Controller_Output_HTML // Controller_Output_HTML loads a HTML-template
{
public function action_list($data)
{
$view = new View('user/list.desktop');
$view->set($data->as_array());
$this->template->body = $view;
}
public function action_show($data)
{
$view = new View('user/show.desktop');
$view->set($data->as_array());
$this->template->body = $view;
}
}
class Controller_Output_JSON extends Controller_Output // Controller_Output doesn't load a template
{
public function action_list($data)
{
$view = new View('user/list.json');
$view->users = json_encode($data->users->as_array());
$this->template = $view;
}
public function action_show($data)
{
$view = new View('user/show.json');
$view->user = json_encode($data->user);
$this->template = $view;
}
}
What do you think?
Hmm... From the 1st view it loooks strange, and somehow like fractal -- we are breaking on MVC one of our MVC -- C.
But why is this app returns so different results, based on point-of-entry (or device)?
The task of the controller is only to get the data and choose the view -- why do we need standalone logic for choosing something based on point-of-entry (device)?
I think these questions should be answered first. Somewhere could be some problem.
Also the cotroller should select only one view ideally, and dont' do "encode" or else with data, based on current output. I think all this should be in some kind of "layouts" or else. As data always the same and even different views should be the same -- only some aspects changes.

Dynamic PHP website with MVC - What navigation pattern is this?

All,
I am studying some sample code given in my web dev class as an example of MVC (again, for the web). In this code, there's a system to navigate from the index.php page to the various controllers (which then call the Model and View modules), and then back into index.php.
I understand how the MVC works.
What I'm grappling with is the navigation mechanism. I am having difficulties understanding how all the pieces work together.
Could anyone take a look at the code below and tell me if this matches a well known method / pattern to deal with dynamic website navigation? (Maybe the Front Controller?) If it does, then my hope is that I can more easily do some more research on it.
Many thanks!
JDelage
Index.php
<?php
require_once("User.php");
session_start();
if (isset($_GET['action']))
$action= $_GET['action'];
else
$action="";
switch ($action) {
case 'login':
require_once('Login.php');
$command= new LoginControler();
break;
case 'logoff':
require_once('Logoff.php');
$command= new LogoffControler();
break;
// Several other cases
default:
require_once('Unknown.php');
$command= new UnknownControle();
}
$command->execute();
require_once('EntryMenu.php'); // Those are objects that represent both the
// menu label and the links.
$menu= array(
new EntryMenu("Login", "index.php", array("action" => "logon")),
new EntryMenu("Logoff", "index.php", array("action" => "logoff")),
new EntryMenu("Write", "index.php", array("action" => "write")),
new EntryMenu("Read", "index.php", array("action" => "read"))
);
if ($command->redirect) {
header('Location: ' . $command->redirect);
} else if ($command->page) {
include("ui/header.php");
include("ui/menu.php");
echo "<div class='content'>";
include("ui/". $command->page);
echo "</div>";
include("ui/footer.php");
}
?>
Controler.php
<?php
class Controler {
public $page= "problem.php";
function execute() {}
}
?>
LogoffControler.php
<?php
require_once('Controler.php');
class LogoffControler extends Controler {
function execute() {
$this->redirect= "index.php";
unset($_SESSION['user']);
}
}
?>
LoginControler.php
<?php
require_once('LoginModel.php'); // This manages the exchanges with the user db
require_once('Controler.php');
class ConnexionControle extends Controler {
public $page= "LoginForm.php";
function execute() {
// More code to deal with incorrectly filled login forms
$login = new LoginModel();
$login->loginUser($_POST['login'], $_POST['password']);
if ($login->userLogedIn()) {
$_SESSION['user']= $login->user;
$this->redirect= "index.php";
}
// More code to deal with invalid logins
}
}
?>
I am assuming you understand the controller part, and is asking about the switch..case statements. I haven't come across an official name for that yet,but most MVC frameworks for PHP (Kohana, CakePHP, CodeIgniter, Fat Free and etc.) calls that 'routing'. It's mapping of a URL to a controller.
Using a switch..case sets of statement is one of the easier ways. More sophisticated solutions use RegEx to match pre-defined URL patterns to resolve what controller to invoke, and what are its parameters (usually bundled as a 'request')
Other methods include using URL rewriting to come up with pretty urls, such as /articles/month/nov/article-id/3
which in 'ugly url form' is :
action=articles&month=nov&article-id=3
If you would like an easy-to-dissect verion of a MVC system you could try the 1kb PHP MVC which handles everything you are attempting in a much cleaner fashion. Though you might have to break up the code if you really want to read it as it is in compressed form.
With this system you simply place a controller in /classes/controller/ named somthing.php and you can then access it from the URL like http://site.com/something.
Loading Models is also easy and doesn't require any include or require calls.
class Controller_Something
{
public function index()
{
$model = new Model_User();
}
}

Zend - refactoring to include a form globally on site

I currently have a search form in the search controller, so the only way I can get to it is through /search/. I have to refactor my code so that this search form appears not only in the Search Controller but also globally throughout the site.
( The code isnt exact as I had to retype some of it )
My class that extends Zend_Form is located in application/forms/forms/SearchForm.php:
class Form_SearchForm extends Zend_Form {
public function init() {};
}
My search controller is something like..
class SearchController extends Zend_Controller_Action
{
public function search() {
$searchForm = new Form_SearchForm();
$this->view->form = $searchForm;
}
}
In my Bootstrap.php I have an autoloader for models:
protected function _initAutoload() {
$autoLoader = Zend_Loader_Autoloader::getInstance();
$resourceLoader = new Zend_Loader_Autoloader_Resource(
array(
'basePath' => APPLICATION_PATH,
'namespace' => '',
'resourceTypes' => array(
'form' => array(
'path' => 'forms',
'namespace' => 'Form_',
),
'model' => array(
'path' => 'models/',
'namespace' => 'Model_',
),
),
)
);
return $autoLoader;
}
I'm wondering where I can store my code so that globally the search form is generated in the view.
My global layout file is located in application/layouts/scripts/layout.phtml and currently spits out a dynamic content area:
<div id="main">
<?php echo $this->layout()->content;?>
</div>
Should I just add the form to this layout.phtml or is there some generic controller I should use?
Edit: Sorry for not specifying this too, but what if for example I wanted to not include it for 1-2 special pages ( maybe an admin section ).. if I hardcoded it into layout.phtml it would still appear.. or should I serve a different layout file to say, an admin area?
Creating a searchAction() is not good for performance because it requires a brand new dispatch cycle. If, and only if, you have very complex logic that justifies a separate action, you could create a Controller Plugin and add searchAction() to the ActionStack. If you are only instantiating/assigning the form or if you don't need the search form for every request, it's not an optimal solution.
Another possibility would be to instantiate and assign the form in the bootstrap. This kind-of breaks separation of concerns, but provides better performance.
protected function _initSearchForm()
{
$this->bootstrap('view');
$view = $this->getResource('view');
$searchForm = new Form_SearchForm();
$view->searchForm = $searchForm;
return $searchForm;
}
Finally, my preferred solution would be a custom view helper:
<?php
class My_View_Helper_SearchForm extends Zend_View_Helper_Abstract
{
public function searchForm()
{
$searchForm = new Form_SearchForm();
return $searchForm;
}
}
For either of these solutions, you'd ideally output the form in your layout file to minimise duplication.
layout.phtml:
<?php echo $this->searchForm() ?>
And create an alternate layout admin.phtml for admin area pages. This gives you the flexibility to change the admin pages significantly when new requirements pop up.
You can create your Form in a Controller Plugin and add it to view vars somehow (by Zend_Controller_Front?), which are accessible in layout, too. But it's too complicated in current ZF version (or I'm too dumb)
You can make Form_SearchForm a singleton
class Form_SearchForm ... {
static function getInstance() {
static $instance;
if (!$instance)
$instance = new Form_SearchForm();
return $instance;
}
}
Now instead of creating new Form_SearchForm() just get it as
$form = Form_SearchForm::getInstance();
You can put an instance of Form_SearchForm to the registry
I probably have missed a very cool a simple way :)
I would split it into a partial and a place holder.
in layout.phtml:
<?php if($searchForm = $this->placeHolder('searchForm'): ?>
<?php echo $searchForm; ?>
<?php endif; ?>
then in your views you can call:
<?php $this->placeHolder('searchForm')->set($this->partial('search-from.phtml', 'search')); ?>
IF you wanted you could even make a search view helper that basically does the place holder call.
The Controller plugin would be better if you have more pages that dont need it than d though. I would still probably use placeholder though to accomplish it. That way you can easily override or append to it later on a view-by-view basis without calling anything on the front controller.

Categories