Zend Framework Example Shop Application

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

Zend Framework Example Shop Application

Postby Christopher » Wed Apr 04, 2007 11:08 pm

This is split from this thread with the goal to talk though the design concepts and tradeoffs necessary to build an application using the Zend Framework. I believe that it was dreamscape that suggested that implementing the Java Pet Store might be a good idea. Here is a link to a Pet Store installation:

http://www.sygel.com/petstore/jsp/shop/ ... anguage=en

I proposed starting simpler without departments/categories-- and growing out. Maybe adding the shopping cart/checkout second. They implement a Pager for their product lists. And they probably have a simple CRUD system for the backend.

Here is a list of basic concepts from the previous thread:
- Standard config and index.php files
- A couple of Routing schemes that just work
- Basic Front Controller settings
- Basic Action Controller settings
- How to deal with exceptions
- A couple of templating schemes that just work
- Organizing the Response and Views

Subversion repository:

http://www.w3style.co.uk/devnet-projects/pet-store/
Last edited by Christopher on Wed Apr 25, 2007 7:54 pm, edited 1 time in total.
(#10850)
User avatar
Christopher
Site Administrator
 
Posts: 12686
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Postby Christopher » Wed Apr 04, 2007 11:13 pm

Here's some stuff to get started.

Example index.php:
Syntax: [ Download ] [ Hide ]
<?php

// setup error_reporting(E_ALL|E_STRICT);

date_default_timezone_set('America/Los_Angeles');

set_include_path('.' . PATH_SEPARATOR . '/path/to/application/'. PATH_SEPARATOR . get_include_path());



// load configuration

include "Zend/Config/Ini.php";

$Config = new Zend_Config_Ini('/path/to/application/config.ini', 'general');



// setup registry

include "Zend/Registry.php";

$Registry = Zend_Registry::getInstance();

$Registry->set('Config', $Config);



// setup and run front controller

include "Zend/Controller/Front.php";

$Front = Zend_Controller_Front::getInstance();

$Front->throwExceptions(true);

$Front->setBaseUrl('/demos/petshop');

$Front->setControllerDirectory('/path/to/application/controllers');

$Front->setParam('Registry', $Registry);

$Front->returnResponse(true);

$Response = $Front->dispatch();



// echo the response

$Response->sendResponse();


Example filesystem layout:
Syntax: [ Download ] [ Hide ]
/application
   /controllers
   /models
   /views
      /filters
      /helpers
      /scripts
/document_root
   /images
   /scripts
   /styles
   .htaccess
   index.php
/php
   /Zend


Example .htaccess:
Syntax: [ Download ] [ Hide ]
RewriteEngine on
RewriteRule !\.* index.php
Last edited by Christopher on Thu Apr 05, 2007 1:54 pm, edited 1 time in total.
(#10850)
User avatar
Christopher
Site Administrator
 
Posts: 12686
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Postby Maugrim_The_Reaper » Thu Apr 05, 2007 5:35 am

Should we setup Subversion somewhere to track this code? Eventually it's be easier to just throw anyone reading a link to grab what's added (after discussion here of course).

Just to note to everyone else reading the AppRequest and AppResponse don't exist yet - don't throw yourself off the deep end if you run this ;).

Syntax: [ Download ] [ Hide ]
<?php



// Development settings only!!!

ini_set('display_errors', 1);

error_reporting(E_ALL|E_STRICT);





/**

 * Set the /application path. This can either be the suggested

 * option of a path below the webroot, or a relative path from

 * the webroot (if distributing online).

 */


// $applicationPath = './application'; // if "/application" is in webroot

$applicationPath = '/path/to/application/directory';





// Basic setup (adjust timezone as needed)

date_default_timezone_set('America/Los_Angeles');

set_include_path(

    '.' . PATH_SEPARATOR

    . $applicationPath . '/user/models' . PATH_SEPARATOR

    . $applicationPath . '/library/Zend/Incubator' . PATH_SEPARATOR

    . $applicationPath . '/library/Zend/Core' . PATH_SEPARATOR

    . get_include_path()

);





// Load configuration

require 'Zend/Config/Ini.php';

$Config = new Zend_Config_Ini($applicationPath . '/config/config.ini', 'general');





// Setup the Registry

require 'Zend/Registry.php';

$Registry = Zend_Registry::getInstance();

$Registry->set('Config', $Config);





// Detect Base URL from sanitised PHP_SELF Server variable

$_SERVER['PHP_SELF'] = substr($_SERVER['PHP_SELF'], 0, strlen($_SERVER['PHP_SELF']) - @strlen($_SERVER['PATH_INFO']));

$baseUrl = substr($_SERVER['PHP_SELF'], 0, -9); // strips the ending "index.php"





// Get a Front Controller instance

require 'Zend/Controller/Front.php';

$Front = Zend_Controller_Front::getInstance();





// Setup the Front Controller with any application specific settings

$Front->throwExceptions(true)

    ->setBaseUrl($baseUrl)

    ->setControllerDirectory($applicationPath . '/controllers')

    ->setParam('Registry', $Registry)

    ->returnResponse(true);





// Dispatch the Request and get the Response!

$Response = $Front->dispatch(new AppRequest(), new AppResponse());





// Echo the Response

echo $Response;


And we're off...;). I made a few minor changes to the index.php and .htaccess. This also adds Base URL detection from $_SERVER['PHP_SELF'] (quickly sanitised since it's technically user input), and uses the fluid interface for the front controller. I tend to heavily comment the index.php since it's where a lot of complex things can end up happening until subclassing/plugins get added. Added some variables to reduce the places where paths are defined. Also defined separate paths to Zend/Incubator and Zend/Core so Incubator classes are selected in priority to Core classes. I'm assuming here that there is no Zend.php base file for those (it's deprecated but still in Subversion as of 0.91).

Here's the .htaccess:

Syntax: [ Download ] [ Hide ]
RewriteEngine on
RewriteCond %{REQUEST_URI} !/public.*
RewriteRule .* index.php


Slightly different to yours. Maps everything onto index.php as path information, but excludes a specific directory instead for files which may be directly referenced. I find this a bit better than the suggested Zend Framework Manual .htaccess which specifies file extensions one by one.

My own suggested filesystem layout is a bit different. Still retains the important distinction between files below, and above the webroot. Otherwise it's the suggested Zend Framework layout from the Developer Wiki only with all public files sourced from a single "public" directory (to limit Rewrite Conditions in .htaccess). "webroot" is simply the "/demo/petshop" path in the document root of the webserver. Removed the View subdirs until needed. Haven't defined /library/Zend subdirectories on the assumption the framework download/updating will be managed via subversion externals (will post a quickie on that later).

Syntax: [ Download ] [ Hide ]
/application
   /config
   /library
      /Zend
   /user
      /controllers
      /models
      /views
/webroot
   /public
      /images
      /styles
      /javascript
   .htaccess
   index.php


Thoughts and comments on the changes?
Pádraic Brady

http://blog.astrumfutura.com
http://www.survivethedeepend.com
Zend Framework Community Review Team
Zend Framework PHP-FIG Representative
User avatar
Maugrim_The_Reaper
DevNet Master
 
Posts: 2704
Joined: Tue Nov 02, 2004 6:43 am
Location: Ireland

Postby Christopher » Fri Apr 06, 2007 4:19 pm

Sorry for the delay, but there is a lot to cover in these posts. I actually started a couple of times. Hopefully we can take this a small piece at a time going forward.

Maugrim_The_Reaper wrote:Should we setup Subversion somewhere to track this code? Eventually it's be easier to just throw anyone reading a link to grab what's added (after discussion here of course).
Probably.

Maugrim_The_Reaper wrote:Just to note to everyone else reading the AppRequest and AppResponse don't exist yet - don't throw yourself off the deep end if you run this ;).
I changed my code to get rid of those.


I want to comment on your code in sections because it brings up many of the initial questions in this discussion:
Syntax: [ Download ] [ Hide ]
<?php

// Development settings only!!!
ini_set('display_errors', 1);
error_reporting(E_ALL|E_STRICT);
By putting these settings first they cannot use config setting to set error reporting -- such as on for development site but off for the live site. Should they set after config is defined?

Syntax: [ Download ] [ Hide ]
/**
 * Set the /application path. This can either be the suggested
 * option of a path below the webroot, or a relative path from
 * the webroot (if distributing online).
 */

// $applicationPath = './application'; // if "/application" is in webroot
$applicationPath = '/path/to/application/directory';
Again, because this is before the configuration information a separate variable is used. That also means that the path cannot be changed by the config.

Syntax: [ Download ] [ Hide ]
// Basic setup (adjust timezone as needed)
date_default_timezone_set('America/Los_Angeles');
I have started to see date_default_timezone_set() in many demos. What are the pros/cons of having this setting explicit? Is it required or a php.ini setting?

Syntax: [ Download ] [ Hide ]
set_include_path(
    '.' . PATH_SEPARATOR
    . $applicationPath . '/user/models' . PATH_SEPARATOR
    . $applicationPath . '/library/Zend/Incubator' . PATH_SEPARATOR
    . $applicationPath . '/library/Zend/Core' . PATH_SEPARATOR
    . get_include_path()
);
Two questions. First is whether the search path should just go to "/application" and then when you want a model you do "models/MyModel.php" or for misc code you do "inc/MyCode.php". Second is whether we should have have this Incubator/Core stuff still? Will it be necessary to stay current or is it just an artifact of the 0.1 - 0.9 process?

I think the path setting is important because it defines conventions within the program code. So it would be good to thing about the ramifications of different settings. I also leads to the question of whether to use _autoload()?

Syntax: [ Download ] [ Hide ]
// Load configuration
require 'Zend/Config/Ini.php';
$Config = new Zend_Config_Ini($applicationPath . '/config/config.ini', 'general');
As I discussed above, perhaps some configuration settings should be done sooner so they can be used in some of the above cases in this file. Also, have you looked into the differences between using a text config file (INI, XML, YAML) as opposed to using a PHP file containing an array of values? An array would also allow its values to be used here before the config object was created.

Syntax: [ Download ] [ Hide ]
// Setup the Registry
require 'Zend/Registry.php';
$Registry = Zend_Registry::getInstance();
$Registry->set('Config', $Config);
We need to set some standards for what the text strings for common objects in the registry are.

Syntax: [ Download ] [ Hide ]
// Detect Base URL from sanitised PHP_SELF Server variable
$_SERVER['PHP_SELF'] = substr($_SERVER['PHP_SELF'], 0, strlen($_SERVER['PHP_SELF']) - @strlen($_SERVER['PATH_INFO']));
$baseUrl = substr($_SERVER['PHP_SELF'], 0, -9); // strips the ending "index.php"
Is this needed or should this be either hard coded or come from the config data? I don't currently do this kind of processing in index.php because it is a set-once-and-it-works value.

Syntax: [ Download ] [ Hide ]
// Get a Front Controller instance
require 'Zend/Controller/Front.php';
$Front = Zend_Controller_Front::getInstance();


// Setup the Front Controller with any application specific settings
$Front->throwExceptions(true)
    ->setBaseUrl($baseUrl)
    ->setControllerDirectory($applicationPath . '/controllers')
    ->setParam('Registry', $Registry)
    ->returnResponse(true);
You use the fluent syntax as opposed to standard method calls. What do people prefer? Is there a performance difference?

Syntax: [ Download ] [ Hide ]
// Dispatch the Request and get the Response!
$Response = $Front->dispatch();


// Echo the Response
echo $Response;
There are some more details about the response that we will probably need to get into later. A big one is whether there is an outer View object that is defined here in index.php?

Maugrim_The_Reaper wrote:Here's the .htaccess:

Syntax: [ Download ] [ Hide ]
RewriteEngine on
RewriteCond %{REQUEST_URI} !/public.*
RewriteRule .* index.php


Slightly different to yours. Maps everything onto index.php as path information, but excludes a specific directory instead for files which may be directly referenced. I find this a bit better than the suggested Zend Framework Manual .htaccess which specifies file extensions one by one.

I am not sure which way is better. I think I would prefer to keep the rules as simple as possible and add additional .htaccess files (with "RewriteEngine off" in it) where you don't want rewriting. The "/public" seems nonstandard to me.

Maugrim_The_Reaper wrote:My own suggested filesystem layout is a bit different. Still retains the important distinction between files below, and above the webroot. Otherwise it's the suggested Zend Framework layout from the Developer Wiki only with all public files sourced from a single "public" directory (to limit Rewrite Conditions in .htaccess). "webroot" is simply the "/demo/petshop" path in the document root of the webserver. Removed the View subdirs until needed. Haven't defined /library/Zend subdirectories on the assumption the framework download/updating will be managed via subversion externals (will post a quickie on that later).

Syntax: [ Download ] [ Hide ]
/application
   /config
   /library
      /Zend
   /user
      /controllers
      /models
      /views
/webroot
   /public
      /images
      /styles
      /javascript
   .htaccess
   index.php


Thoughts and comments on the changes?

Again, I am not sure I like the non-standard "/webroot/public" and /application/user" directories. A separate directory for config files does make sense. I am also wondering about the whole "/library" path. Things could almost be simplified by just putting everything into "/application". It is not as though it will get that that cluttered. An "/Zend" could be a symbolic link. So maybe something like:
Syntax: [ Download ] [ Hide ]
/application
   /config
   /controllers
   /models
   /views
      /filters
      /helpers
      /scripts
   /Zend
/document_root
   /images
   /scripts
   /styles
   .htaccess
   index.php
(#10850)
User avatar
Christopher
Site Administrator
 
Posts: 12686
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Postby Maugrim_The_Reaper » Sun Apr 08, 2007 5:25 pm

By putting these settings first they cannot use config setting to set error reporting -- such as on for development site but off for the live site. Should they set after config is defined?

Again, because this is before the configuration information a separate variable is used. That also means that the path cannot be changed by the config.


Assuming configuration contains such settings, then yes, they should be placed there. I simply hadn't messed with configuration yet since it's a slightly softer subject - there are a dozen and one ways of handling transition from a development to a production environment and this was the simplest for now. It would be better to have something in config settings.

I have started to see date_default_timezone_set() in many demos. What are the pros/cons of having this setting explicit? Is it required or a php.ini setting?


I think it should be removed, but a warning note added. The problem is very few users typically check INI settings and this has never cropped up prior to 5.1 as anything more than an obscure issue. Now using system settings raises E_STRICT errors if it's not set. It really doesn't belong in the application code...

Two questions. First is whether the search path should just go to "/application" and then when you want a model you do "models/MyModel.php" or for misc code you do "inc/MyCode.php". Second is whether we should have have this Incubator/Core stuff still? Will it be necessary to stay current or is it just an artifact of the 0.1 - 0.9 process?

I think the path setting is important because it defines conventions within the program code. So it would be good to thing about the ramifications of different settings. I also leads to the question of whether to use _autoload()?


There is some convention setting attached - using "./application/models" assumes a further convention ruling Model class names and filepath - likely a non-issue until there's actually a Model class to load ;). So it can be stripped out until needed? Incubator and Core simply recognises that the Framework proposal procedures always place stable code in Incubator pending at least one full release cycle. I don't think it's essential for a demo app though.

Is this needed or should this be either hard coded or come from the config data? I don't currently do this kind of processing in index.php because it is a set-once-and-it-works value.


If hard-coded it's not flexible - which becomes an issue for how the application is installed, esp. if a widely distributed application. It's also by definition a user variable, so it should be sanitised for every request. If nothing else it trades a little performance for a little consistency in what future developers get for using it unpredictably.

You use the fluent syntax as opposed to standard method calls. What do people prefer? Is there a performance difference?


I used the fluent interface because it's reasonably clear, and as far as I know has no performance downside. It's a personal preference only.

There are some more details about the response that we will probably need to get into later. A big one is whether there is an outer View object that is defined here in index.php?


Possibly, but some recent changes around Views and the Response object, I think, make it less needful. What exists has already grown a lot of extra functionality likely to cover the majority of needs. If something more is required it will become more obvious as things move on.

I am not sure which way is better. I think I would prefer to keep the rules as simple as possible and add additional .htaccess files (with "RewriteEngine off" in it) where you don't want rewriting. The "/public" seems nonstandard to me.


The problem I have is relying on numerous .htaccess files everywhere where one could suffice. Sticking in public is just a simple device - any other directories can be tagged as needed with clearer naming, or even more specialised rules.

Again, I am not sure I like the non-standard "/webroot/public" and /application/user" directories. A separate directory for config files does make sense. I am also wondering about the whole "/library" path. Things could almost be simplified by just putting everything into "/application". It is not as though it will get that that cluttered. An "/Zend" could be a symbolic link. So maybe something like:


Example looks fine - don't let me quibblin' derail ye ;).
Pádraic Brady

http://blog.astrumfutura.com
http://www.survivethedeepend.com
Zend Framework Community Review Team
Zend Framework PHP-FIG Representative
User avatar
Maugrim_The_Reaper
DevNet Master
 
Posts: 2704
Joined: Tue Nov 02, 2004 6:43 am
Location: Ireland

Postby Ollie Saunders » Sun Apr 08, 2007 6:28 pm

Wow I can see each and every point being raised here involving a fair bit of discussion. I'm not sure I'm prepared to enter into such a massive debate. Anyway what I'm going to do in this post is to outline my solutions to general problems. There is a slight issue in that this is a very public forum

Here follows part of my solution to the issue of poor cohesion in controllers. Generally if you put anything more complex than instanitation logic into controller methods you are going to run into this problem. So the solution is to limit the code to just that. This goes completely against the impression I get of how it is supposed to be done but if this works better than then, clearly, I'm above that. So in my application I have a job controller that looks like this:
Syntax: [ Download ] [ Hide ]
<?php

class JobController extends Nitrogen_Controller_Action

{

    public function indexAction()

    {

        $this->_redirect('Job/list');

    }

    public function listAction()

    {

        $job = $this->_getModel();

        $list = new Nitrogen_List_Job($job, new Nitrogen_Pagination(

            10, $job->getTotalRows(), $this->_getParam('page', 1), '/Job/list/page/%d'

        ));



        $list->setRowHead(array('Job No.', 'Title', 'Password Protected', 'Last File Added On'));



        $list->addLink(new Nitrogen_List_Component('New Job', 'n', '/Job/new'))

             ->addPostAction(new Nitrogen_List_Component('Delete', 'd', 'delete'))

             ->addRowAction(new Nitrogen_List_Component('Verify', 'i', '/Job/verify/id/%d'))

             ->addRowAction(new Nitrogen_List_Component('Modify', 'm', '/Job/modify/id/%d'))

             ->addRowAction(new Nitrogen_List_Component('View', 'v', '/Job/view/%d'))

             ->addColumnFormatter(new Nitrogen_DataFormatter_JobList());



        $list->run();

        $this->view->list = $list;

        $this->render();

    }

    public function newAction()

    {

        $form = new Nitrogen_Form_JobNew($this);

        $form->run();

        $form->getView()->returnToPage = $this->_getParam('fromPage', 1);



        $this->view->form = $form;

        $this->render();

    }

    public function modifyAction()

    {

        $form = new Nitrogen_Form_JobModify($this);

        $form->run();

        $form->getView()->returnToPage = $this->_getParam('fromPage', 1);





        $this->view->form = $form;

        $this->render();

    }

}
Here the Nitrogen_List and Nitrogen_Form objects are notable. These objects very nearly represent the whole actions. In fact the view code for modifyAction() looks like this:
Syntax: [ Download ] [ Hide ]
<?php

$pageTitle = 'Modify Job';

$currentTab = 'Jobs';

$js = array('xpath.js', 'DOMAssistantCSS.js', 'form.js', 'jobNew.js');



include '../app/views/scripts/header.phtml';

echo $this->form->render();

include '../app/views/scripts/footer.phtml';

?>
...and is pretty much delgating straight to the form object.

There is a reasonable code base behind these objects but most of it is application specific and things that aren't application specific and just used to connect the pieces together and keep everything organised. For instance Nitrogen_Form_JobModify extends Nitrogen_Form_JobNew that in turn extends Nitrogen_Form_Abstract that has methods that look like this
Syntax: [ Download ] [ Hide ]
public function setView(Zend_View_Interface $view)

{

    $view->assign(array(

        'errors'            => $this->_errors,

        'notifications'     => $this->_notifications,

        'formErrors'        => $this->_formErrors,

        'formNotifications' => $this->_formNotifications,

        'post'              => $this->_post,

        'get'               => $this->_get,

    ));

    $this->_view = $view;

}

public function run()

{

    /* @var $request Zend_Controller_Request_Http */

    $request = $this->_controllerAction->getRequest();

    if (!$request instanceof Zend_Controller_Request_Http) {

        throw new Nitrogen_Form_Exception('Request from controller action is not HTTP');

    }

    $this->_onInitialize();

    if ($this->isSubmitted()) {

        if ($this->_onSubmit()) {

            $this->_onValid();

        } else {

            $this->_onInvalid();

        }

    } else {

        $this->_onPopulate();

    }

}

abstract public function isSubmitted();

abstract public function render();

protected function _onPopulate() {}

protected function _onInitialize() {}

protected function _onSubmit() {}

protected function _onValid() {}

protected function _onInvalid() {}
That run method is probably one of the most sensible bits of code I've ever written in my life. A template method pattern I believe.

I would like to show you guys more, there's a lot of stuff I've written that is worth talking about. I have plans to get myself a website where I'm going to be publishing individual unit tested, documented and commented classes and class hierarchies for solving individual problems and allowing visitor feedback on them.
User avatar
Ollie Saunders
DevNet Master
 
Posts: 3179
Joined: Tue May 24, 2005 6:01 pm
Location: UK

Postby Maugrim_The_Reaper » Mon Apr 09, 2007 10:47 am

Wow I can see each and every point being raised here involving a fair bit of discussion. I'm not sure I'm prepared to enter into such a massive debate. Anyway what I'm going to do in this post is to outline my solutions to general problems. There is a slight issue in that this is a very public forum


Bear in mind, in a framework application with MVC, discussing the bootstrap (the central point of entry for the C segment) is always a bit more complex than dumping in lots of code. The rest of the discussion will be a lot more interesting ;). A common mistake is that the bootstrap contains too much code, which is better shuffled into Controllers (either subclasses and plugins) so these the last few heavy posts are likely the only large ones you'll see concerning the index.php and htaccess - the rest will resolve these starting points before moving on. Oddly these points do have far reaching consequences - index.php is where ALL REQUESTS go for an application using the ZF so it's worth the extra effort.

Here follows part of my solution to the issue of poor cohesion in controllers. Generally if you put anything more complex than instanitation logic into controller methods you are going to run into this problem. So the solution is to limit the code to just that. This goes completely against the impression I get of how it is supposed to be done but if this works better than then, clearly, I'm above that. So in my application I have a job controller that looks like this:


Controllers aren't poorly cohesive - controller logic is ;). Controllers often get overcoded until you realise the framework is completely open. You can apply refactoring to minimise the amount of code in the usual fashion using subclassing, extraction of classes (basically writing mini-libraries when needed), etc. The key with a framework is to focus on making these types of "extensions" generic so you can re-use them alongside the framework for other applications. In a very big way, it's stamping your own preferred framework needs on top of the generic default classes. In other words - throw out the idea of the framework being so restrictive and don't be afraid to customise it :).

Syntax: [ Download ] [ Hide ]
class JobController extends Nitrogen_Controller_Action

{

    public function indexAction()

    {

        $this->_redirect('Job/list');

    }

    public function listAction()

    {

        $job = $this->_getModel();

        $list = new Nitrogen_List_Job($job, new Nitrogen_Pagination(

            10, $job->getTotalRows(), $this->_getParam('page', 1), '/Job/list/page/%d'

        ));



        $list->setRowHead(array('Job No.', 'Title', 'Password Protected', 'Last File Added On'));



        $list->addLink(new Nitrogen_List_Component('New Job', 'n', '/Job/new'))

             ->addPostAction(new Nitrogen_List_Component('Delete', 'd', 'delete'))

             ->addRowAction(new Nitrogen_List_Component('Verify', 'i', '/Job/verify/id/%d'))

             ->addRowAction(new Nitrogen_List_Component('Modify', 'm', '/Job/modify/id/%d'))

             ->addRowAction(new Nitrogen_List_Component('View', 'v', '/Job/view/%d'))

             ->addColumnFormatter(new Nitrogen_DataFormatter_JobList());



        $list->run();

        $this->view->list = $list;

        $this->render();

    }

    public function newAction()

    {

        $form = new Nitrogen_Form_JobNew($this);

        $form->run();

        $form->getView()->returnToPage = $this->_getParam('fromPage', 1);



        $this->view->form = $form;

        $this->render();

    }

    public function modifyAction()

    {

        $form = new Nitrogen_Form_JobModify($this);

        $form->run();

        $form->getView()->returnToPage = $this->_getParam('fromPage', 1);





        $this->view->form = $form;

        $this->render();

    }

}


General comments:

Use forwarding more than redirecting - the second requires an extra client request, the first is handled internally on the server. You should isolate View in a private member in the Nitrogen_Controller_Action class so there's less getView/$this->view confusion. I'll also note the current manual is woefully out of date on recent ways of accessing the view - it saw a little automation recently. You should consider extracting the List and Form elements (V) into a View Helper class - shouldn't be too much presentation logic in a controller. Basically it would be the same stuff you have here - only pushed into the View layer completely. Only thing your controllers should know is that there is View A, and you render it using Method B - the rest is for the View layer to process.

Thing here is as before - you're extending a generic framework to cope with your specific needs. How many applications need a form builder or Paginator? Reduce the duplication - add it to a re-useable class....

You're on the right road - just take a small turn away from putting these things in Controllers. :).
Pádraic Brady

http://blog.astrumfutura.com
http://www.survivethedeepend.com
Zend Framework Community Review Team
Zend Framework PHP-FIG Representative
User avatar
Maugrim_The_Reaper
DevNet Master
 
Posts: 2704
Joined: Tue Nov 02, 2004 6:43 am
Location: Ireland

Postby Christopher » Mon Apr 09, 2007 11:42 am

These posts are all still too long. Hopefully we can start at the top of the index.php and work down it section by section as I broke down your post above.
Maugrim_The_Reaper wrote:Bear in mind, in a framework application with MVC, discussing the bootstrap (the central point of entry for the C segment) is always a bit more complex than dumping in lots of code. The rest of the discussion will be a lot more interesting ;). A common mistake is that the bootstrap contains too much code, which is better shuffled into Controllers (either subclasses and plugins) so these the last few heavy posts are likely the only large ones you'll see concerning the index.php and htaccess - the rest will resolve these starting points before moving on. Oddly these points do have far reaching consequences - index.php is where ALL REQUESTS go for an application using the ZF so it's worth the extra effort.
I agree, decisions about index.php set the stage for the rest of the application. We especially need to deal with the Response/View as you note below. Getting the Response/View "organized" in index.php can make things easier/simpler in the Action code.

Maugrim_The_Reaper wrote:Controllers aren't poorly cohesive - controller logic is ;). Controllers often get overcoded until you realise the framework is completely open. You can apply refactoring to minimise the amount of code in the usual fashion using subclassing, extraction of classes (basically writing mini-libraries when needed), etc. The key with a framework is to focus on making these types of "extensions" generic so you can re-use them alongside the framework for other applications. In a very big way, it's stamping your own preferred framework needs on top of the generic default classes. In other words - throw out the idea of the framework being so restrictive and don't be afraid to customise it :).
Yes, and this is a big flaw in Zend's thinking. The base Action Controller should be the basis for things like Form and Application controllers -- and needs to be designed to support those things. The ZF Action controller is designed to do some View responsibilities and build Transaction scripts. Those are shortcut premature optimizations. That is mostly because the designers did not seem to either understand or like MVC separations.

Maugrim_The_Reaper wrote:Use forwarding more than redirecting - the second requires an extra client request, the first is handled internally on the server.

And there is nothing wrong with simply $this->listAction(); which eliminates the overhead of a dispatch loop that happens when you do forward. We should come up with guidelines redirect, forward and method call.

Maugrim_The_Reaper wrote:You should isolate View in a private member in the Nitrogen_Controller_Action class so there's less getView/$this->view confusion. I'll also note the current manual is woefully out of date on recent ways of accessing the view - it saw a little automation recently. You should consider extracting the List and Form elements (V) into a View Helper class - shouldn't be too much presentation logic in a controller. Basically it would be the same stuff you have here - only pushed into the View layer completely. Only thing your controllers should know is that there is View A, and you render it using Method B - the rest is for the View layer to process.
The View is the part that needs the most attention. It will be the biggest part of our discussion and the part of the design where people vary the most in naming and thinking about the parts. It is the part where we need to support a couple of different options. If our design is good it should be obvious to people how similar other options are to the one they are currently using. That will hopefully allow some non-stubborn programmers to embrace other options in a mix-and-match style.

Maugrim_The_Reaper wrote:Thing here is as before - you're extending a generic framework to cope with your specific needs. How many applications need a form builder or Paginator? Reduce the duplication - add it to a re-useable class....
We need to decide when we design a Pager and what Fowler calls an Input Controller (the basis for dealing with the Request that is under Form and Page Controllers). We probably want to air the general issues of Action Controller and then create a couple different implementations of those (to see which style keeps working as we make more design decisions).


So my first question above was the chicken-and-egg situation about configuration data. I have found that it is good to bootstrap config data first/early so most/all settings can be set from config data. But how do you bootstrap the config data? What is the best format for config data -- considering performance, the ability for the data to be generated, and flexibility of describing data? How to deal with multiple configurations (dev/live) -- chunks or separate config files?
(#10850)
User avatar
Christopher
Site Administrator
 
Posts: 12686
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Postby Ollie Saunders » Mon Apr 09, 2007 11:55 am

Oddly these points do have far reaching consequences - index.php is where ALL REQUESTS go for an application using the ZF so it's worth the extra effort.
I wasn't saying otherwise. But it has occurred to me that it will take you some time to reach an agreed consensus... I'm not sure I want to be yet another cook.
In other words - throw out the idea of the framework being so restrictive and don't be afraid to customise it
Consider it thrown out.
You can apply refactoring to minimise the amount of code in the usual fashion using subclassing, extraction of classes (basically writing mini-libraries when needed), etc.
Yeah that's basically what I have done....but I'm now in the situation where I can easily remove ALL of the ZF from my code base and replace it with my own alternatives, albeit based heavily on original zend ones, that I know would definitely be better for my needs during this project and very possibly during future ones as well.
Use forwarding more than redirecting - the second requires an extra client request, the first is handled internally on the server.
I'm not sure that is a particularly great reason to use a forward. I want my users to know what page they are on and if its labelled "list" in the URL surely that is better than nothing at all. Also URLs get confusing when you have parameters: so instead of /Job/list/page/1 you just have /Job/page/1 which looks like you're on the "page" page alternatively you have /Job/index/page/1 but then I can imagine my users asking me what the hell is index? In summary, I'm not convinced.
You should isolate View in a private member in the Nitrogen_Controller_Action class so there's less getView/$this->view confusion.
This is an interesting point. Having a view within a view does seem a little silly when the first one is pretty much delegating straight to another. I opted to keep it that way though because it gives me more flexibility and I can have views dedicated to single responsibilities.
You should consider extracting the List and Form elements (V) into a View Helper class - shouldn't be too much presentation logic in a controller.
I don't believe I have any presentation logic in my controller. How could you reasonably change any of the code in the controller action methods to go in the presentation layer?
Thing here is as before - you're extending a generic framework to cope with your specific needs. How many applications need a form builder or Paginator? Reduce the duplication - add it to a re-useable class....
These are formed from reusable classes.
User avatar
Ollie Saunders
DevNet Master
 
Posts: 3179
Joined: Tue May 24, 2005 6:01 pm
Location: UK

Postby Christopher » Mon Apr 09, 2007 7:46 pm

ole wrote:But it has occurred to me that it will take you some time to reach an agreed consensus... I'm not sure I want to be yet another cook.
I don't think it is cooks at this point. Think of it as applying brains to a problem. I seriously doubt that a consensus will come out of it. The goal should be that those who participate will gain a deeper understanding of the ZF.
(#10850)
User avatar
Christopher
Site Administrator
 
Posts: 12686
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Postby sike » Tue Apr 10, 2007 1:42 pm

arborint wrote:So my first question above was the chicken-and-egg situation about configuration data. I have found that it is good to bootstrap config data first/early so most/all settings can be set from config data. But how do you bootstrap the config data? What is the best format for config data -- considering performance, the ability for the data to be generated, and flexibility of describing data? How to deal with multiple configurations (dev/live) -- chunks or separate config files?


we should use the Zend_Config stuff (http://framework.zend.com/manual/en/zend.config.adapters.ini.html) wich supports different config scenarios and gives us the choice of choosing our prefered format like plain php, ini, xml or some other custom things.

Syntax: [ Download ] [ Hide ]
require_once 'Zend/Config/Ini.php';

$config = new Zend_Config_Ini('/path/to/config.ini', 'staging');


the only thing that has to be decided is how to select the appropriate configuration. i have worked with different setups in the past and got used to two options :

1. choose the configuration based on the server ip:
Syntax: [ Download ] [ Hide ]
switch($_SERVER['SERVER_ADDR'])

  {

        case '192.168.0.1':

                        define('APPLICATION_MODE', 'live');

                        break;

                       

        case '192.168.0.2':

                        define('APPLICATION_MODE', 'staging');

                        break;

                       

        default:

                        define('APPLICATION_MODE', 'develop'); 

                        break;

  }

this work's only for different servers but could be adapted to use other settings like the domain name to distinguish the different settings.

2. have a configuration file (eg. applicationmode.config.php) wich only consists of something like
Syntax: [ Download ] [ Hide ]
define('APPLICATION_MODE', 'develop');

and is not part of the usual deployment process (that is it's not in version control) but is a required file. if it doesn't exist the application dies.


cheers
Chris
sike
Forum Commoner
 
Posts: 84
Joined: Wed Aug 02, 2006 8:33 am

Postby sike » Tue Apr 10, 2007 1:47 pm

arborint wrote:
Syntax: [ Download ] [ Hide ]
// Get a Front Controller instance
require 'Zend/Controller/Front.php';
$Front = Zend_Controller_Front::getInstance();


// Setup the Front Controller with any application specific settings
$Front->throwExceptions(true)
    ->setBaseUrl($baseUrl)
    ->setControllerDirectory($applicationPath . '/controllers')
    ->setParam('Registry', $Registry)
    ->returnResponse(true);
You use the fluent syntax as opposed to standard method calls. What do people prefer? Is there a performance difference?


i am a big fan of fluent interfaces as long as they make sense. in this particular case i don't think it does :) because it just saves me from typing $this without providing any other benefits - so i would say stay with standard method calls.

cheers
Chris
sike
Forum Commoner
 
Posts: 84
Joined: Wed Aug 02, 2006 8:33 am

Postby Christopher » Tue Apr 10, 2007 2:20 pm

sike wrote:we should use the Zend_Config stuff (http://framework.zend.com/manual/en/zend.config.adapters.ini.html) wich supports different config scenarios and gives us the choice of choosing our prefered format like plain php, ini, xml or some other custom things.

I agree. A question I have is how to deal with complex config stuff like you show using INI,XML,YAML. It seem like we should consider using a PHP script for config (using only the base Zend_Config class) and set conventions for that.

sike wrote:the only thing that has to be decided is how to select the appropriate configuration. i have worked with different setups in the past and got used to two options :

1. choose the configuration based on the server ip:
this work's only for different servers but could be adapted to use other settings like the domain name to distinguish the different settings.

2. have a configuration file (eg. applicationmode.config.php) wich only consists of something like
and is not part of the usual deployment process (that is it's not in version control) but is a required file. if it doesn't exist the application dies.

These are exactly the kinds of issues I want to deal with. The first question for me is how to specify the path to the config file when we want the path information in the config data. It seems like there are two options:
1. configuration: a variable or constant defined before the config is loaded,
2. convention: standard relative place for config file, such as config.php always being in the same directory as index.php.

sike wrote:i am a big fan of fluent interfaces as long as they make sense. in this particular case i don't think it does :) because it just saves me from typing $this without providing any other benefits - so i would say stay with standard method calls.
I tend to not use fluent style, but am completely open to using it. These are the kinds of little conventions that I am looking to define. So we might want to comment where we make each standard/fluent decision so people can understand the thinking behind it.
(#10850)
User avatar
Christopher
Site Administrator
 
Posts: 12686
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Postby Maugrim_The_Reaper » Tue Apr 10, 2007 4:19 pm

typing $this without providing any other benefits


Typing $this takes far too long - I could use the saved time typing a comment! ;) I think the benefits of a fluent interface are two fold, and I agree saved typing isn't one of them. It's affords a little extra readability, and it reads like a natural language (only one noun, not a Peter says this, Peter says that concoction). Again - it's a preference. I think it makes sense for the Front Controller to have a "block" feel about it since the expressions are minimal at the moment and likely to get longer. If two people to my one it gone - consider it gone :). Power to the people...

arborint wrote:I agree. A question I have is how to deal with complex config stuff like you show using INI,XML,YAML. It seem like we should consider using a PHP script for config (using only the base Zend_Config class) and set conventions for that.


Zend_Config allows division of a single config file - easy enough to categorise configuration categories. The development vs production difference is slightly different - one is defunct and even a little dangerous if allowed in production. Admittedly I'm a fan of ant and Phing so you can guess my answer involves dual files, one with the notorious .dist extension part, the other being deleted before being pushed to a production server. Outside that - separate files and manual replacement should be enough. Plus a sprinkling of hard-coded controls to prevent errors (triggered by the production server's IP perhaps).

Zend_Config doesn't care whether the file is PHP, INI or XML. YAML support is outstanding (I'm pushing for it). Someone benchmarked which was faster recently - I'll try digging up the reference. INI is the simplest though. I'd start as simple as possible - let it evolve as needed and we can re-visit a cleaner method if required.

Path to config files? I prefer convention where possible for a single Path. Easier for Phing to work it's magic ;). In general I always prefer convention and automation to most other options for these decisions - I know "building" PHP sounds OTT, but it's a simple exercise in defining what's supposed to happen to turn subversion tags into production ready code, automating those steps, using the automation to integrate frequently (testing) and continuing to celebrate your obvious laziness as a programmer :). A basic Phing config file is only a few lines long for a config replacement/renaming task.
Pádraic Brady

http://blog.astrumfutura.com
http://www.survivethedeepend.com
Zend Framework Community Review Team
Zend Framework PHP-FIG Representative
User avatar
Maugrim_The_Reaper
DevNet Master
 
Posts: 2704
Joined: Tue Nov 02, 2004 6:43 am
Location: Ireland

Postby Ollie Saunders » Tue Apr 10, 2007 5:22 pm

It's affords a little extra readability, and it reads like a natural language
I agree with this.

I also prefer convention over configuration. 37Signals will back me up on that one.
User avatar
Ollie Saunders
DevNet Master
 
Posts: 3179
Joined: Tue May 24, 2005 6:01 pm
Location: UK

Next

Return to PHP - Theory and Design

Who is online

Users browsing this forum: No registered users and 0 guests