SitePoint Sponsor

User Tag List

Page 3 of 6 FirstFirst 123456 LastLast
Results 51 to 75 of 138
  1. #51
    SitePoint Wizard DougBTX's Avatar
    Join Date
    Nov 2001
    Location
    Bath, UK
    Posts
    2,498
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Dr Livingston
    This part still could be refactored as SWITCH is a bad code smell
    You might be able to get off with something like this:

    PHP Code:
    class FrontController
    {

        var 
    $commands = array(
            
    'add'  => 'UserAddCommand',
            
    'edit' => 'UserEditCommand',
            
    'list' => 'UserListCommand'
        
    );
        
        function 
    run($page)
        {
            
    $command = new $this->commands[$page];
            
    $command->execute();

            
    $view $command->getView();
            
    $view->render();
        }


    The bad smell for me was the repitition of "$command = ...; break;". Much better to replace that with a simple "return" imo. ("$this->" is also very ugly imo, and is the thing I like the least about PHP.)

    I get the feeling that there is lots of over-engeneering going on, when simpler solutions would work. Look at the number of Java projects out there at the moment going under the "better than J2EE" banner. (It is interesting watching the arguments between the hardcore Java croud and the Rails people!)

    I'd much rather do these things like this:
    http://www.sitepoint.com/forums/show...97&postcount=9

    Happy coding peiople,
    Douglas
    Hello World

  2. #52
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I was thinking a Chain of Responsibility as the refactor, which I use at the moment in several varieties.

    But then again, I don't really have a clue of what I'm talking about, so I could be wasting your time.

    Again.

    EDIT:

    Your link has some interesting ideas, particularly the Alpha and Beta class examples

    Would be interesting to explore this some more, if you've had the time to expand on this?

  3. #53
    SitePoint Wizard DougBTX's Avatar
    Join Date
    Nov 2001
    Location
    Bath, UK
    Posts
    2,498
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Dr Livingston
    I was thinking a Chain of Responsibility as the refactor, which I use at the moment in several varieties.
    Have a go anyway, I'd like to see what an "official" refactor would look like

    (also, just changed the code above to make it clearer what it might be doing)

    Douglas
    Hello World

  4. #54
    SitePoint Wizard DougBTX's Avatar
    Join Date
    Nov 2001
    Location
    Bath, UK
    Posts
    2,498
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Dr Livingston
    Would be interesting to explore this some more, if you've had the time to expand on this?
    Short answer: download Rails. http://www.rubyonrails.com/

    Longer answer: as far as I'm concerned, that's practically all there is to MVC in PHP. Off the top of my head, there are only two things which need added to the example: pre/post filters, and a clever way to get @params ported.

    I think @params can be translated to a pre-filter which sets $this->params = array(), so that really means there is only one thing that needs done I need to do a little bit of testing to see how static class access works wrt inheritance in PHP4/5, but it should be quite trivial to do.

    Edit: see: http://www.sitepoint.com/forums/show...91#post1822691

    Douglas
    Last edited by DougBTX; Apr 12, 2005 at 13:10.
    Hello World

  5. #55
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    This is an interesting discussion because it get to the heart of dispatching. Perhaps a more flexible implementation might be:
    PHP Code:
    class Controller {
    var 
    $children = array();
    var 
    $default_child '';

        function 
    execute() {
            
    $class $this->getChild($_GET['page']);
            
    $command = new $class();
            
    $command->execute();

            
    $view $command->getView();
            
    $view->render();
        }

        function 
    setChild($action$object) {
            
    $this->children[$action] = $object;
        }

        function 
    getChild($action) {
            if (
    $this->children[$action]) {
                return 
    $this->children[$action];
            } else {
                return 
    $this->default_child;
            }
        }

        function 
    setDefaultChild($action) {
            
    $this->default_child $action;
        }

    }

    $c = new Controller();
    $c->setChild('add''UserAddCommand');
    $c->setChild('edit''UserEditCommand');
    $c->setChild('list''UserListCommand');
    $c->setDefaultChild('list');
    $c->execute(); 
    A few comments:

    - Needs code added to include() the file containing the command class. I think a Lazy Load "ObjectHandle" class would be a good direction to go.

    - To make it a real Front Controller you either want it to create class/file names from the $action or look them up in a map. Passing a mapper object to this controller would allow you to do either.

    - There is not loop here. It would be nice if the base controller could be an Intercepting Filter or a Chain of Responsibility. Then you could extend it to make a Front Controller, Application Controller or Page Controller.

    - Should pass a Request object to the controller rather than the hard coded $_GET['page']. Also should be able to specify the Request param on which we will dispatch.

    - It should allow a Model to be passed to the View, but also allow Model-less and View-less controllers (e.g. Filters).

    - And of course error handling beyond the default child.
    Christopher

  6. #56
    SitePoint Addict
    Join Date
    May 2003
    Location
    Calgary, Alberta, Canada
    Posts
    275
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by arborint
    This is an interesting discussion because it get to the heart of dispatching. Perhaps a more flexible implementation might be:
    This is almost exactly what I use in many projects. Like you mention in your comments you should be passing in a request object (or similar). I also pass in a template object(similar to the view used here). Passing in the template object allows easier testing because it can be mocked.

  7. #57
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I also pass in a template object(similar to the view used here). Passing in the template object allows easier testing because it can be mocked.
    I am interested in your thoughts here. The traditional MVC way is to pass Request and Reponse objects to the Controller. I think there is general agreement on using a Request object (I may include one if I post updated code).

    WACT, for example, uses a ResponseModel object which I assume means that they pass data along, but not any View stuff (which I assume is handled separately).

    You are proposing passing a Template object. I assume that the goal is to simplify the Response to a Template View which makes sense in PHP. (Perhaps you could tweek my example to show this)

    I'm just wondering which way makes sense for the Response? And whether there is an implementation that allows either a Model based response or a VIew based response?
    Christopher

  8. #58
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    The Chain of Responsibility does play a key role I feel with a Front Controller, take for example this piece of Java...

    PHP Code:
    // code example 4.8
    public class ControllerServlet extends HttpServlet {
      private 
    HashMap actions;
      public 
    void init() throws ServletException {
        
    actions = new HashMap();
        
    CreateUserAction cua = new CreateUserAction(model);
        
    actions.put(cua.getName(), cua);
        
    //... create and add more actions
      
    }
      public 
    void doPost(HttpServletRequest req,
                 
    HttpServletResponse resp)
        
    throws IOExceptionServletException {

        
    // First identify operation "op" from URL.
        // method getOperation() is defined elsewhere.
        
    String op getOperation(req.getRequestURL());
        
    // Then find and execute corresponding Action
        
    Action action = (Action)actions.get(op);
        
    Object result null;
        try {
            
    result action.perform(req);
        } catch (
    NullPointerException npx) {
            
    //... handle error condition: no such action
        
    }
        
    // ... Use result to determine next view (see next section)
      
    }
    //... other methods...

    On the point of a Request class, here is my version arborint, but feel free to post your version as well

    PHP Code:
    class HttpRequest {
            private 
    $cookies = array();
            private 
    $sessions = array();
            private 
    $parameters = array();
            
            public function 
    __construct() {
                foreach( 
    $this -> sanitise$_COOKIE ) as $parameter => $value ) {
                    
    $this -> cookies[strtolower$parameter )] = $value;
                }
                foreach( 
    $this -> sanitise$_SESSION ) as $parameter => $value ) {
                    
    $this -> sessions[strtolower$parameter )] = $value;
                }
                foreach( 
    $this -> sanitisearray_merge$_GET$_POST ) ) as $parameter => $value ) {
                    
    $this -> parameters[strtolower$parameter )] = $value;
                }
            }
            
            public function 
    setSession$session$value ) {
                
    $this -> sessions[strtolower$session )] = $this -> sanitise$value );
            }
            
            public function 
    getCookie$cookie ) {
                if( 
    $this -> hasCookie$cookie ) ) {
                    return 
    $this -> cookies[$cookie];
                }
                return 
    false;
            }
            
            public function 
    getSession$session ) {
                if( 
    $this -> hasSession$session ) ) {
                    return 
    $this -> sessions[$session];
                }
                return 
    false;
            }
            
            public function 
    getParameter$parameter ) {
                if( 
    $this -> hasParameter$parameter ) ) {
                    return 
    $this -> parameters[$parameter];
                }
                return 
    false;
            }
            
            private function 
    sanitise$parameters ) {
                if( 
    is_array$parameters ) ) {
                    
    $tmp = array();
                    foreach( 
    $parameters as $parameter => $value ) {
                        if( 
    get_magic_quotes_gpc() ) {
                            
    $value stripslashes$value );
                        }
                        
    $tmp[$parameter] = $value;
                    }
                    return 
    $tmp;
                }
            }
            
            private function 
    hasCookie$cookie ) {
                return 
    array_key_exists$cookie$this -> cookies );
            }
            
            private function 
    hasSession$session ) {
                return 
    array_key_exists$session$this -> sessions );
            }
            
            private function 
    hasParameter$parameter ) {
                return 
    array_key_exists$parameter$this -> parameters );
            }
        }

        class 
    HttpResponse {
        } 
    I'm still pondering on the Response class, in regards to what responsibilities that are required. Could do with some help if available?

  9. #59
    SitePoint Addict
    Join Date
    May 2003
    Location
    Calgary, Alberta, Canada
    Posts
    275
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    A HttpResponse in java or a ResponseModel in WACT, its all the same really. Just different evolutions of the same concept. Im not really proposing that you have to pass a template object in, but usually if you want to be able to unit test your controller you need a way to examine the output. A bit of dependency injection to reference the "in" terminology.

    Here's some modified code from Perdure that has a front controller. This was more test driven so the $_GET array doesnt get passed through to the controllers, and the default only gets a template instead of a Controller at this point but its pretty easily modified.
    Attached Files Attached Files

  10. #60
    simple tester McGruff's Avatar
    Join Date
    Sep 2003
    Location
    Glasgow
    Posts
    1,690
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Dr Livingston
    ... SWITCH is a bad code smell
    What do you mean?

  11. #61
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    The code I have been using for the Request is like this:
    PHP Code:
    class apl_Request {
    var 
    $request;

    function 
    Request() {
        if (! 
    strcasecmp($_SERVER['REQUEST_METHOD'], 'POST')) {
            
    $this->request =& $_POST;
        } else {
            
    $this->request =& $_GET;
        }
        
    $this->request['PATH_INFO'] = $_SERVER['PATH_INFO'];
        if (! 
    get_magic_quotes_gpc()) {
            
    $this->removeSlashes($this->request);
        }
    }

    function 
    removeSlashes(&$str) {
        if (
    is_array($str)) {
            foreach (
    $str as $key => $val) {
                if (
    is_array($val)) {
                    
    $this->removeSlashes($val);
                } else {
                    
    $array[$key] = stripslashes($val);
                }
           }
        } else {
            
    $str stripslashes($str);
        }
    }

    function 
    get($name) {
        return 
    $this->request[$name];
    }

    function 
    set($name$value) {
        if (
    $name$this->request[$name] = $value;
    }


    I don't include Session or Cookies in the Request. I have considered them data sources, but yours is an interesting idea.

    Adding the Request class to the code above.
    PHP Code:
    class Controller {
    var 
    $children = array();
    var 
    $default_child '';

        function 
    Controller($action_param) {
            
    $this->action_param $action_param;
        }
        function 
    execute($request) {
            
    $class $this->getChild($request->get('page'});
            
    $command = new $class();
            
    $command->execute();

            
    $view $command->getView();
            
    $view->render();
        }

        function 
    setChild($action$object) {
            
    $this->children[$action] = $object;
        }

        function 
    getChild($action) {
            if (
    $this->children[$action]) {
                return 
    $this->children[$action];
            } else {
                return 
    $this->default_child;
            }
        }

        function 
    setDefaultChild($action) {
            
    $this->default_child $action;
        }

    }

    $c = new Controller();
    $c->setChild('add''UserAddCommand');
    $c->setChild('edit''UserEditCommand');
    $c->setChild('list''UserListCommand');
    $c->setDefaultChild('list');
    $c->execute(new Request()); 
    Though I am still not too happy with the execute() method in the Controller. It has that hard coded 'page' value and still no loop capability and it doesn't look like something that you could make other controllers out of.
    Christopher

  12. #62
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Actually, the equivalent of the WACT ResponseModel in Java are attributes, which are stored on the request. (Writing to the request seems illogical to me -- WACT doesn't allow it.) Mojavi mirrors Java in this respect. In struts, the equivalent of the ResponseModel is the ActionForm (plus attributes). WebWork calls it a stack. I think this concept of a flexible or extensible data container or workspace to collect data is inherent to web MVC. It is definitely a pattern because it evolves independently again and again.

    I think extensibility and flexibility is essential to the concept, but Marcus (or was is Jason?) suggested that this might be the Magic Container anti-pattern. I'm not so sure. I think it might be a variant of the Blackboard pattern where independent components use a common, flexible and extensible data structure to collaborate on a task and remain loosely coupled. The struts ActionForm was not flexible or extensible and they ended up creating one that is (DynaBean). An anti-pattern or just dynamic typing?

    In applications that don't adhere to the web's request/response cycle, I think this concept can be called an application model or presentation model. However, this concept doesn't seem to evolve into a separate explicit class in these architectures. For example dotnet and Prado attempt to hide the explicit request/response cycle of HTTP by using javascript and POST variables to transmit session state between requests. These frameworks don't explicitly exhibit this pattern. (But I think they tend to implement MVC in only the most liberal definition of MVC.)

    Most template engines have a flexible, extensible "Magic Container" style API for collecting data, so I don't see any major problem with using one for this purpose. I think there are some advantages to having the object both separate from the request object and separate from the view object, but combining it with the request has been proven to work, so I suspect that combining it with the view will too.

  13. #63
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks for info and clarification. I agree that writing to the Request seems illogical and am interested in how you handle it.

    With WACT, is the ResponseModel passed all the way through to a View object outside of all Controllers? I guess I am wondering who writes to it and who (finally) reads from it. I think the Struts (Mojavi) style is that the Controller manages the View so it is more contained.
    Christopher

  14. #64
    SitePoint Member
    Join Date
    Aug 2004
    Location
    Germany
    Posts
    21
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Back to the problem :-) Forget the switch, in a complex application it is a dictionary (DB, XML-File ...) ... :-)

    @arborint, @Dr Livingston ... @all :-)

    Is action chaining really "the solution"? It is complex, isn't it?!

    I took a look at Pattern-Oriented Software Architecture - A System of Patterns and Fowler and ... They all uses simple examples (not mvc with a complex page with with banner, footer some boxes ...).

    I summarize key features of MVC from "A System of Patterns":
    (It helps me focusing the problem :-))

    Changes to the user interface should be easy, and even possible at
    run-time.

    Supporting different 'look and feel' standards or porting the user
    interface should not affect code in the core of the application.

    The separation of the model from view and controller components
    allows multiple views of the same model.

    Model:

    - The model component encapsulates core data and functionality. The
    model is independent of specific output representations or input
    behavior.

    Responsibility:
    Provides functional core of the application.
    Registers dependent views and controllers.
    Notifies dependent components about data changes.

    View:

    - View components display information to the user. A view obtains the
    data from the model. There can be multiple views of the model.
    - Each view has an associated controller component. (!?)
    Each view creates a suitable controller. There is a one-to-one relationship between views and controllers. Views often offer functionality that allows controllers to manipulate the display.

    Responsibility:
    - Creates and initializes its associated controller.
    - Displays information to the user.
    - Implements the update procedure.
    - Retrieves data from the model.

    Controller:

    - The user interacts with the system solely through controllers.

    Responsibility:
    - Accepts user input as events.
    - Translates events to service requests for the model or display requests for
    the view.
    - Implements the update procedure, if required.
    A very nice, but simple MVC example from Harry:
    => http://www.phppatterns.com/index.php...leview/19/1/1/

    PHP Code:
    <?php
    /**
     *  Controls the application
     */
    class ProductController {
        var 
    $model;
        var 
    $view;

        
    //! A constructor.
        /**
        * Constucts a new ProductController object
        * @param $model an instance of the ProductModel class
        * @param $getvars the incoming HTTP GET method variables
        */
        
    function ProductController (& $dao) {
            
    $this->model=& new ProductModel($dao);
        }
    }

    class 
    ProductItemController extends ProductController {
       
    //! A constructor.
        /**
        * Constucts a new ProductItemController object
        * @param $model an instance of the ProductModel class
        * @param $getvars the incoming HTTP GET method variables
        */
        
    function ProductItemController (& $dao,$getvars=null) {
            
    ProductController::ProductController($dao);
            
    $this->view=& new ProductItemView($this->model,$getvars['id']);
        }

        function & 
    getView () {
            return 
    $this->view;
        }
    }

    class 
    ProductTableController extends ProductController {
       
    //! A constructor.
        /**
        * Constucts a new ProductTableController object
        * @param $model an instance of the ProductModel class
        * @param $getvars the incoming HTTP GET method variables
        */
        
    function ProductTableController (& $dao,$getvars=null) {
            
    ProductController::ProductController($dao);
            if ( !isset (
    $getvars['rowsperpage']) )
                
    $rowsperpage=20;
            if ( !isset (
    $getvars['rownum']) )
                
    $getvars['rownum']=1;
            
    $this->view=& new ProductTableView($this->model,
                                        
    $rowsperpage,
                                        
    $getvars['rownum']);
        }

        function & 
    getView () {
            return 
    $this->view;
        }
    }
    ?>

    <?php
    /**
     *  Fetches "products" from the database
     */
    class ProductModel {
        
    /**
        * Private
        * $dao an instance of the DataAccess class
        */
        
    var $dao;

        
    //! A constructor.
        /**
        * Constucts a new ProductModel object
        * @param $dbobject an instance of the DataAccess class
        */
        
    function ProductModel (&$dao) {
            
    $this->dao=& $dao;
        }

        
    //! A manipulator
        /**
        * Tells the $dboject to store this query as a resource
        * @param $start the row to start from
        * @param $rows the number of rows to fetch
        * @return void
        */
        
    function listProducts($start=1,$rows=50) {
            
    $this->dao->fetch("SELECT * FROM products LIMIT ".$start.", ".$rows);
        }

        
    //! A manipulator
        /**
        * Tells the $dboject to store this query as a resource
        * @param $id a primary key for a row
        * @return void
        */
        
    function listProduct($id) {
            
    $this->dao->fetch("SELECT * FROM products WHERE PRODUCTID='".$id."'");
        }

        
    //! A manipulator
        /**
        * Fetches a product as an associative array from the $dbobject
        * @return mixed
        */
        
    function getProduct() {
            if ( 
    $product=$this->dao->getRow() )
                return 
    $product;
            else
                return 
    false;
        }
    }
    ?>

    <?php
    /**
     *  Binds product data to HTML rendering
     */
    class ProductView {
        
    /**
        * Private
        * $model an instance of the ProductModel class
        */
        
    var $model;

        
    /**
        * Private
        * $output rendered HTML is stored here for display
        */
        
    var $output;

        
    //! A constructor.
        /**
        * Constucts a new ProductView object
        * @param $model an instance of the ProductModel class
        */
        
    function ProductView (&$model) {
            
    $this->model=& $model;
        }

        
    //! A manipulator
        /**
        * Builds the top of an HTML page
        * @return void
        */
        
    function header () {
            
    $this->output=<<<EOD
    <!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">
    <html>
    <head>
    <title> Our Products </title>
    <style>
    body { font-size: 13.75px; font-family: verdana }
    td { font-size: 13.75px; font-family: verdana }
    .title { font-size: 15.75px; font-weight: bold; font-family: verdana }
    .heading {
        font-size: 13.75px; font-weight: bold;
        font-family: verdana; background-color: #f7f8f9 }
    .nav { background-color: #f7f8f9 }
    </style>
    </head>
    <body>
    <div align="center" class="title">Our Products</div>
    EOD;
            
    $this->output.="\n<div align=\"right\"><a href=\"".
                
    $_SERVER['PHP_SELF']."\">Start Over</a></div>\n";

        }

        
    //! A manipulator
        /**
        * Builds the bottom of an HTML page
        * @return void
        */
        
    function footer () {
            
    $this->output.="</body>\n</html>";
        }
    }

    class 
    ProductItemView extends ProductView {
        
    /**
        * Private
        * $productID ID of product to render
        */
        
    var $productID;

        
    //! A constructor.
        /**
        * Constucts a new ProductView object
        * @param $model an instance of the ProductModel class
        */
        
    function ProductItemView (&$model,$productID) {
            
    ProductView::ProductView($model);
            
    $this->productID=$productID;
        }

        
    //! A manipulator
        /**
        * Renders a single product
        * @return void
        */
        
    function productItem() {
            
    $this->model->listProduct($this->productID);
            while ( 
    $product=$this->model->getProduct() ) {
                
    $this->output.="<p><b>Name</b>:".$product['PRODUCTNAME']."</p>".
                    
    "<p><b>Price</b>:".$product['UNITPRICE']."</p>".
                    
    "<p><b># In Stock</b>:".$product['UNITSINSTOCK']."</p>";
                if ( 
    $product['DISCONTINUED']==) {
                    
    $this->output.="<p>This product has been discontinued.</p>";
                }
            }
        }

        
    //! An accessor
        /**
        * Returns the rendered HTML
        * @return string
        */
        
    function display () {
            
    $this->header();
            
    $this->productItem();
            
    $this->footer();
            return 
    $this->output;
        }
    }

    class 
    ProductTableView extends ProductView {
        
    /**
        * Private
        * $rowsperpage number of results per page
        */
        
    var $rowsPerPage;
        
    /**
        * Private
        * $rownum begin display of rows at this ID
        */
        
    var $rowNum;

        
    //! A constructor.
        /**
        * Constucts a new ProductView object
        * @param $model an instance of the ProductModel class
        */
        
    function ProductTableView (&$model,$rowsPerPage=20,$rowNum=1) {
            
    ProductView::ProductView($model);
            
    $this->rowsPerPage=$rowsPerPage;
            
    $this->rowNum=$rowNum;
        }

        
    //! A manipulator
        /**
        * Renders a product table
        * @return void
        */
        
    function productTable() {
            
    $this->model->listProducts($this->rowNum,$this->rowsPerPage);
            
    $this->output.="<table width=\"600\" align=\"center\">\n<tr>\n".
                    
    "<td class=\"heading\">Name</td>\n".
                    
    "<td class=\"heading\">Price</td>\n</tr>\n";
            while ( 
    $product=$this->model->getProduct() ) {
                
    $lastID=$product['PRODUCTID'];
                
    $this->output.="<tr>\n<td><a href=\"".$_SERVER['PHP_SELF'].
                    
    "?view=product&id=".$product['PRODUCTID']."\">".
                    
    $product['PRODUCTNAME']."</a></td>".
                    
    "<td>".$product['UNITPRICE']."</td>\n</tr>\n";
            }
            
    $this->output.="<tr class=\"nav\">\n";
            if ( 
    $this->rowNum && $this->rowNum $this->rowsPerPage ) {
                
    $this->output.="<td><a href=\"".$_SERVER['PHP_SELF'].
                    
    "?view=table&rownum=".($this->rowNum-$this->rowsPerPage).
                    
    "\"><< Prev</a></td>";
            } else {
                
    $this->output.="<td>&nbsp;</td>";            
            }
            if ( 
    $this->rowsPerPage <= ( $lastID $this->rowNum ) ) {
                
    $this->output.="<td><a href=\"".$_SERVER['PHP_SELF'].
                    
    "?view=table&rownum=".($this->rowNum+$this->rowsPerPage).
                    
    "\">Next >></a></td>";
            } else {
                
    $this->output.="<td>&nbsp;</td>\n";            
            }
            
    $this->output.="</tr>\n</table>\n";
        }

        
    //! An accessor
        /**
        * Returns the rendered HTML
        * @return string
        */
        
    function display () {
            
    $this->header();
            
    $this->productTable();
            
    $this->footer();
            return 
    $this->output;
        }
    }
    ?>

    This looks so easy (HMVC), but ... :


    I think about an easy example like that from Harry, but for a complex page ...


    For example:

    "A product page:"

    The bird schould be the product :-)

    => User starts a request to the controller: www.feinding.de/index.php?page=xboxproduct

    => Controller selects product modell
    => Controller selects product-"Website"-View

    But the view is complex ... not only a product is shown ....:
    (Banner, Footer, Navigation, Login Box, Search Box ...)


    Some of the view parts are complex objects, think about login box:
    A loginbox could have different states: 1.) Login Form 2.) Hello Mr. Sambox ...


    The HMVC Modell says:
    "Every part (Header, Footer, boxes etc.) of the view is a (Sub-) MVC Modell" ?!


    Sounds so easy :-)

    The picture of the HMVC modell shows that the ParentController chooses the SubMVC model ... So no way arround ActionsChaining?!

    The SubMVC-Modells are so View related ... For example if the user chooses not the "Product-Website-View" but instead the "Product-Minimal-Cellphone-Website-View" (Product is shown, but without some boxes ...)

    There is a conflict:
    My mind says SubMVC-Modells are View related but the picture above says the controller composed the complex View by choosing the View with the SubMVC-Modells ... Very Confusing ...

    Any ideas ? :-)



    A Solution (perhaps ?!):

    http://java.sun.com/blueprints/guide...5.html#1078938



    The Java guys use a SREENFLOWMANAGER! Is that a solution?!
    Instead of ActionChaining?!
    Want somebody post a nice php example for a screenflowmanager? :-)





    I took a look at the ruby Rails :-) They have a solution :-) (View related):

    http://manuals.rubyonrails.com/read/chapter/72

    With components, this is no longer the case. Instead of altering the WeblogController to gather the Image, you can now add a method to the ImageController to handle this. Let’s say you call it “recent_image”. At this point, you’d also define a view for recent_image that only returned an HTML snippet displaying that Image in some fashion. Then, in the Weblog view, you’d simply add something like: <%= render_componentcontroller => 'image', :action => 'recent_image') %>. When this point in the view was reached, Rails would use the ImageController to gather the data from the Image model, populate that view, and return it, in place, within the view for your WeblogController.
    -----------------
    http://www.xbox-profis.de :: The german Xbox Community

  15. #65
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Very long post, but unfortunately you will have difficulty avoiding this problem. A strength of MVC is that complex and simple things are solved in the same way. This makes it good for teams and good for complex sites.

    As far a HMVC, from the things I have read from Selkirk there are some advantages to having hierarchical controllers like HMVC suggests. Seethis post
    Christopher

  16. #66
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    What do you mean?
    http://today.java.net/today/2003/11/...uredSmells.pdf

    This PDF has a list of bad code smells, namely the one on SWITCH. But to answer your question, I came across this bad code smell regarding SWITCH in this very forum, but I can't remember who it was that made the statement.

    Apparently though, the avoidance of SWITCH should be obvious

  17. #67
    eschew sesquipedalians silver trophy sweatje's Avatar
    Join Date
    Jun 2003
    Location
    Iowa, USA
    Posts
    3,749
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I think like most things, you need to look at your own situation and apply common sense. The BadSmell with case statement usually applies to
    a) substancial blocks of logic within each case
    and b) the same switch cases repeated in several locations in your code (DRY?)

    If one or both of the two statements above are true, you code may be ripe for refactoring to TemplateMethod, Strategy or State patterns.

    The simple switch statement for dispatching, with one line per case is probably fine as is, IMHO
    Jason Sweat ZCE - jsweat_php@yahoo.com
    Book: PHP Patterns
    Good Stuff: SimpleTest PHPUnit FireFox ADOdb YUI
    Detestable (adjective): software that isn't testable.

  18. #68
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Umm...

    Maybe I've just taken a defiant approach against SWITCH?

  19. #69
    SitePoint Wizard DougBTX's Avatar
    Join Date
    Nov 2001
    Location
    Bath, UK
    Posts
    2,498
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Dr Livingston
    Maybe I've just taken a defiant approach against SWITCH?
    Switch isn't all bad, though I like the "conditionals considered evil" view posted here: http://cgi.bramwell.plus.com/krblog/2004/07/if.html

    I don't much like normal switches in PHP because I can never figure out how to indent the 'breaks' nicely!

    Douglas
    Hello World

  20. #70
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Back on topic (you can attribute the off topic discussion partly to me I think), about this part and the comment made by arborint,

    PHP Code:
    ...
    function 
    execute($request) {
            
    $class $this->getChild($request->get('page'});
            
    $command = new $class();
            
    $command->execute();

            
    $view $command->getView();
            
    $view->render();
        } 
    ... 
    Not sure how to solve this myself, but something like this,

    PHP Code:
    ...
    // fixed typo btw
    $class $this -> getChild$request -> getPage() ); 
    ... 
    What about this?

    EDIT:

    View:

    - View components display information to the user. A view obtains the
    data from the model. There can be multiple views of the model.
    - Each view has an associated controller component. (!?)
    Each view creates a suitable controller. There is a one-to-one relationship between views and controllers. Views often offer functionality that allows controllers to manipulate the display.
    True, there could be one or more views of a model, and I believe that view helpers have something to do with this? I'm not too clear on a view creating a controller, as I thought it was the other way around.

    Also, of my understanding of MVC, a view isn't manipulated by a controller but more likely a model. At the moment, I have a controller that selects a template, but also this controller can execute child controllers as well, and thus they select their template, and so on until there is no more controllers (a leaf node).

    But it appears I've been wrong in the past, so maybe someone can add to my thoughts?

  21. #71
    SitePoint Wizard DougBTX's Avatar
    Join Date
    Nov 2001
    Location
    Bath, UK
    Posts
    2,498
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Dr Livingston
    Also, of my understanding of MVC, a view is manipulated by a ... model.
    When you have a model which acts as a real-time data source where the view has to be updated whenever the model changes, this would make sense. But HTTP isn't real-time, it is request-responce. Coding around that fact will just lead to lots of code which doesn't provide useful abtractions, imo.

    Douglas
    Hello World

  22. #72
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I think the Controller selecting the View and Model and passing the Model to the View is the most straightforward. I also keeps the dependencies straight.
    Christopher

  23. #73
    simple tester McGruff's Avatar
    Join Date
    Sep 2003
    Location
    Glasgow
    Posts
    1,690
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Dr Livingston
    Also, of my understanding of MVC, a view isn't manipulated by a controller but more likely a model.

    But it appears I've been wrong in the past...
    If you make comments without understanding the subject you will only confuse people. Best not to, I think.

  24. #74
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'm just wondering why there is no distinction GET, POST when selecting what todo..

    Its just ive seen some implementations which seeming array_merge($_GET, $_POST) into a single array, which appears to mean that it be possible to do everything via GET,
    which if I remember correctly is against HTTP spec regulations recommendations?

    PHP Code:
    class Controller 
    var 
    $children = array(); 
    var 
    $default_child ''

        function 
    Controller($action_param) { 
            
    $this->action_param $action_param
        } 
        function 
    execute($request) { 
            
    $class $this->getChild($request->getMethod(), $request->get('page')); 
            
    $command = new $class(); 
            
    $command->execute(); 

            
    $view $command->getView(); 
            
    $view->render(); 
        } 

        function 
    setChild($method$action$object) { 
            
    $this->children[$method][$action] = $object
        } 

        function 
    getChild($method$action) { 
            if (
    $this->children[$method][$action]) { 
                return 
    $this->children[$method][$action]; 
            } else { 
                return 
    $this->default_child
            } 
        } 

        function 
    setDefaultChild($action) { 
            
    $this->default_child $action
        } 



    $c = new Controller(); 
    $c->setChild('POST''add''UserAddCommand'); 
    $c->setChild('POST''edit''UserEditCommand'); 
    $c->setChild('GET''list''UserListCommand'); 
    $c->setDefaultChild('list'); 
    $c->execute(new Request()); 

  25. #75
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'm just wondering why there is no distinction GET, POST when selecting what todo..
    There are a couple of way to make the distinction. Above I did it in the Request.
    PHP Code:
        if (! strcasecmp($_SERVER['REQUEST_METHOD'], 'POST')) {
            
    $this->request =& $_POST;
        } else {
            
    $this->request =& $_GET;
        } 
    You have implemented a separate set of commands depending on get/post. I'm not sure I see a use for that in what I do. Either I don't accept the request because it is not the correct request method or I accept it either way if the parameters are correct.

    Under what circumstances would you see two sets of commands depending on request method?
    Christopher


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •