SitePoint Sponsor

User Tag List

Page 1 of 2 12 LastLast
Results 1 to 25 of 35
  1. #1
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    mvc and filtered view

    Hi,

    I wanted to ask you, how do you work in such a situation

    Let's assume, I have a news component.
    This component displays a html table with news

    So, I have NewsController, NewsModel, NewsView (or just php template, doesn't matter)

    Now, let's say, that above the news table, I'd like to place a small form (method="GET"), which could be used to filter the results in that news table (many different controls to set the filter).

    The question is, how would you handle such a scenario in the MVC pattern
    There might be different query altering scenarios
    1. Filtering (additional conditions to WHERE clause)
    2. Sorting (additional conditions to ORDER BY clause)
    3. Paging (additional conditions to LIMIT/OFFSET clause)

    All of these points have some view (forms/hrefs etc) and all of them modify a generic query.

    Thanks

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

    Can't remember the actual specifics off hand at the moment, but take a look over at Martin Fowlers site; What you are looking for are the forthcoming contributions made by Martin himself, and others, in regards to the material for his next PoEAA book

    There are a number of patterns discussed which (if I remember) cover what your looking for...

  3. #3
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ok, I'll try to take a look there, thanks.
    I just hoped, somebody already used it here... It isn't anything uncommon, is it?

  4. #4
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    As you have realized yourself, the main point here is the query. In applications, we normally identify model with persistent state. This is the type of state, that constitutes the core part in out business logic. Components in this layer will normally model real world objects or concepts, and therefore is often referred to as the model layer. In web-applications, the model layer will normally be persisted in a relational database.

    While persistent state is the primary pivot of the application, it is not the only type of state. As the user navigates the application, she is presented for a series of screens. The knowledge about which screen the user is currently experiencing can be thought of as a state. This state is not reflected by changes to the business model, so the type of state we're dealing with is of a different nature. We can call it presentational state.

    Presentational state can be preserved in 2 ways; At the client or at the server (a combination of the two is also possible). The location part of a URI (everything before the questionmark) is a way to transport presentational state. The URI is stored at the clientside as bookmarks, in the browser history or simply as links in the hypertext document. So the location is a clear example of client preserved state. The rest of the URI can also be used for this purpose. The querystring is automatically parsed and can be read from the $_GET array, so this information can be propagated through the URI's.
    For most appliances, client side state is the best solution. It has a number of benefits, such as allowing the browser to navigate forth and back. It's limitation lies in the querystring itself - Only shorter strings can safely be transported this way (http://classicasp.aspfaq.com/forms/w...arameters.html). Another problem is that PHP doesn't have an api for creating url's (only for reading them), which means that your have to propagate the information manually.

    Server side state on the other hand is fairly easy to work with. PHP has a very convenient api for storing state in what is called sessions. For this reason, it may be tempting to use server side state for situations, where client side state is more appropriate. As a rule of thumb, the same rules applies to client vs. server state, as with GET vs. POST request types. Idempotent requests (Eg. requests, that can't change persistent state) should have their state preserved at the client. Requests that (potentially) changes persistent state, can be transported via POST and preserved in session until it can be written to the persistent state.

    A good candidate for server side presentational state is a <form> that changes persistent state - For example by inserting a new row in a table. The form can be temporarily persisted in session, until it is valid and can be persisted. If the form is invalid, it can be re-displayed with a message asking the user to correct any errors.
    Not all forms are good candidates for server side state. A search query - even a complex one - is much better transported over uri (preserved at the client). The query form also fits with our previous assumption, in that this type of form is idempotent.

    REST (Representational State Transfer) is an attempt to describe and use the http protocol. The central idea is that presentational state should always be preserved at the client. In reality, I find that this isn't always practical, but it's still an important goal, even if it's utopian.

    Back to your original question; Filtering, sorting and pagination are all different types of presentational states. From an MVC perspective, you can treat the presentational state like any other type of state. This means that the state belongs to your model. You could create an object to encapsulate the query. This object would have certain attributes to direct the filtering, sorting and pagination.
    PHP Code:
    class Query
    {
        public 
    $sortby "id";
        public 
    $offset 0;

    For sake of simplicity, I have limited this example to just two properties, but you can expand on it to include filtering etc.

    The controller would update the query object, and the view would read from it. Cutting it this way, the controller and view can be reused for multiple datasources. It would be convenient to put a function to pull the resultset from the query - the view will use this function.
    PHP Code:
    class Query
    {
        ...

        function 
    select() {
            
    // @todo query db and return result ...
        
    }

    Likewise, we could create an spi for the controller to use.
    PHP Code:
    class Query
    {
        ...

        function 
    import($data) {
            
    // @todo do some validation here
            
    if (isset($data['sortby'])) {
                
    $this->sortby $data['sortby'];
            }
            if (isset(
    $data['offset'])) {
                
    $this->offset $data['offset'];
            }
        }

    The controller will now simply call $query->import($_GET) for client preserved state. If you prefer server preserved state (which would be a bad choice in this case), you could pass $_SESSION to import().

    The view pulls data from select(), but since we want to preserve state at the client, we need to propagate the state to the next request over URL's. The model (query) contains the information, so putting a method on there is one posibility. This does bind the model to the view, which should sound some alarm bells. If you feel uncomfortable with this, you could move the url-building responsibility to a separate component.

    PHP Code:
    class UrlBuilder
    {
        protected 
    $state;

        function 
    __construct($state) {
            
    $this->state $state;
        }

        function 
    url($args = Array()) {
            
    $args['sortby'] = array_key_exists('sortby'$args) ? $args['sortby'] : $this->state->sortby;
            
    $args['offset'] = array_key_exists('offset'$args) ? $args['offset'] : $this->state->offset;

            
    $q "";
            foreach (
    $args as $key => $value) {
                
    $q .= ($q == "") ? "?" "&";
                
    $q .= rawurlencode($key)."=".rawurlencode($value);
            }
            return 
    $q;
        }

    The view would use the url-builder for creating links that preserves state. This type of component is called a view helper.

    PHP Code:
    Sort by:
    <ul>
        <li><a href="<?php echo htmlentities($urlbuilder->url(Array("sortby" => "id"))); ?>">id</a></li>
        <li><a href="<?php echo htmlentities($urlbuilder->url(Array("sortby" => "name"))); ?>">name</a></li>
        <li><a href="<?php echo htmlentities($urlbuilder->url(Array("sortby" => "date"))); ?>">date</a></li>
    </ul>
    <table>
    <?php foreach ($query->select() as $row) : ?>
    <tr>
    <?php foreach ($row as $col) : ?>
    <td><?php echo htmlentities($col); ?></td>
    <?php endforeach; ?>
    </tr>
    <?php endforeach; ?>
    </table>
    The url-builder will need to know about the URL-mapping scheme, so it has strong ties to the controller (in particular the front controller), and it also needs to know about the model. The latter can be dealt with by supplying an api between model and url-builder.

    PHP Code:
    class Query
    {
        ...

        function 
    export() {
            return Array(
                
    'sortby' => $this->sortby,
                
    'offset' => $this->offset,
            );
        }

    PHP Code:
    class UrlBuilder
    {
        ...

        function 
    url($args = Array()) {
            
    $q "";
            foreach (
    array_merge($this->state->export(), $args) as $key => $value) {
                
    $q .= ($q == "") ? "?" "&";
                
    $q .= rawurlencode($key)."=".rawurlencode($value);
            }
            return 
    $q;
        }

    There still remains an exercise of putting the pieces together. The easiest solution in the short run is to put the responsibility in the controller. The problem with using the controller like this, is that it binds the controller and view strongly together. If this is not an issue for you (and it probably isn't), you can accept this violation of MVC separation. If you want a proper solution, you will either need to manually write a lot of redundant code or use a separate assembly layer.

  5. #5
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks for so comprehensive response. I took me some time to grasp the idea. Please corret me if I understood all this in a wrong way.

    So, the controller pases the _GET stuff to the model. Now, the model responsibility is the validation of this data, based on some rules (eg array of allowed 'order by' values etc.)

    Now, the validation passes and model has a new state definition (eg. order by: id, filter by: name = 'lala', pager: limit 10, offset 20).

    So, when the view asks for the data, the query (model) is rebuilt using the new state. Thew new state is also available to the view (through some method), so that things like selected fields in the filter form are updated too etc.

    Assuming I'm using procedural templates, where should I create instances of view helpers? As they need the model to be passed in their constructors, should I just create them in the appropriate controller? Thanks

  6. #6
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I think that about sums it up, yes.

    Quote Originally Posted by dan7
    Assuming I'm using procedural templates, where should I create instances of view helpers? As they need the model to be passed in their constructors, should I just create them in the appropriate controller?
    You have options here, but the simplest thing to do is to put methods on the controller that creates the components used by the view. So it could look something like this ;
    PHP Code:
    class MyController
    {
        protected 
    $_query;
        protected 
    $_builder;

        function 
    getQuery() {
            if (!isset(
    $this->_query)) {
                
    $this->_query = new Query();
            }
            return 
    $this->_query;
        }

        function 
    getUrlBuilder() {
            if (!isset(
    $this->_builder)) {
                
    $this->_builder = new UrlBuilder($this->getQuery());
            }
            return 
    $this->_builder;
        }

        function 
    execute() {
            
    $m $this->getQuery();
            
    $m->import($_GET);
            return 
    $this->render("myview.tpl.php");
        }

        function 
    render($template) {
            
    ob_start();
            include(
    $template);
            return 
    ob_get_clean();
        }

    The view would then access the shared objects (query + urlbuilder) through those methods ;
    PHP Code:
    ...
        <li><a href="<?php echo htmlentities($this->getUrlBuilder()->url(Array("sortby" => "id")));
    ...
    Ultimately, theese factory methods are a bit misplaced in the controller, so you could consider moving them out into a separate component. This type of component is called a service locator or registry ;
    PHP Code:
    class Registry
    {
        protected 
    $_query;
        protected 
    $_builder;

        function 
    getQuery() {
            if (!isset(
    $this->_query)) {
                
    $this->_query = new Query();
            }
            return 
    $this->_query;
        }

        function 
    getUrlBuilder() {
            if (!isset(
    $this->_builder)) {
                
    $this->_builder = new UrlBuilder($this->getQuery());
            }
            return 
    $this->_builder;
        }

    PHP Code:
    class MyController
    {
        public 
    $registry;

        function 
    __construct($registry) {
            
    $this->registry $registry;
        }

        function 
    execute() {
            
    $m $this->registry->getQuery();
            
    $m->import($_GET);
            return 
    $this->render("myview.tpl.php");
        }

        function 
    render($template) {
            
    ob_start();
            include(
    $template);
            return 
    ob_get_clean();
        }

    PHP Code:
    ...
        <li><a href="<?php echo htmlentities($this->registry->getUrlBuilder()->url(Array("sortby" => "id")));
    ...
    That's quite a lot of syntax, so you might want to do some tricks to shorten on the typing. For one thing, you could make the registry available automatically, in the render() method. You could also implement the magic __get() method on the registry. Theese two tricks should be able to shorten the view code to this :
    PHP Code:
    ...
        <li><a href="<?php echo htmlentities($registry->urlBuilder->url(Array("sortby" => "id")));
    ...

  7. #7
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks.
    I remember this registry stuff from your previous posts.
    Looking at your first example, do I really need those methods like getQuery etc?
    Is it wrong to just create those objects in a constructor (especially, that I know I will need them)?
    Something like
    PHP Code:
    class SomeController {
            
        public function 
    controller($db) {

            
    $this->model = new Model($db);
            
    $this->urlbuilder = new UrlBuilder($this->model);
            
    $this->pager = new Pager($this->model);

        }   


    And one other thing. You don't explicitly call components in the view, but you rather use additional methods, like $this->getUrlBuilder()->dosomething() rather than just $this->urlbuilder->dosomething()
    Is it just your personal taste or something more?

    Thanks

  8. #8
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by dan7
    Is it wrong to just create those objects in a constructor (especially, that I know I will need them)?
    ...
    And one other thing. You don't explicitly call components in the view, but you rather use additional methods, like $this->getUrlBuilder()->dosomething() rather than just $this->urlbuilder->dosomething()
    Is it just your personal taste or something more?
    That's really two sides of the same issue.
    In this case, the view and controller happen to use the same model component, but that's coincidental. The view uses the urlbuilder, but the controller doesn't, so the controller shouldn't really be creating an instance of urlbuilder, until the view asks for it. For what the controller knows, the view might never do this, so the creation could be wasted.
    In more general terms, the controller can never know which dependencies (model or helpers) the view has. That's why I delegate the creation of objects in functions - to postpone the creation until someone asks for them. It's also why those functions doesn't really belong in the controller, but ought to be moved into a separate component (the registry).

  9. #9
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken
    In more general terms, the controller can never know which dependencies (model or helpers) the view has. That's why I delegate the creation of objects in functions - to postpone the creation until someone asks for them. It's also why those functions doesn't really belong in the controller, but ought to be moved into a separate component (the registry).
    Hmm. I'm already passing some objects to the controller through it's constructor (like db, httpcontext, translator). And I'm confused. Is it right or wrong considering the quote above?
    db is never used by controller, it's only passed to model
    translator is used only by the view
    httpcontext however may be used by a controller..

  10. #10
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I wouldn't call it wrong per se, but I think it's basically a bit clumsy. If you pass objects in through several constructors, it's a lot of places to change something, when you decide to add a new dependency, or even just to change an existing one.
    It's not as much a problem, that relates directly to MVC, as it's a more general problem about how to get things "wired" up correctly without using global scope. A registry is a simple solution to this issue, and works remarkably well in a dynamic language like PHP. You could also use a dependency injection container to archive the same type of separation, but that's even more complex than a registry.

  11. #11
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Uhhh. That means rewriting each component now
    I checked your older posts and registry class looked a bit different. In this thread it serves objects on demand but earlier, it was just used to pass objects around and they were created in index.php

    When I look at the classes, I rather see a need of a mix of service locator and registry. Some objects are needed on demand (models, urlbuilders, translators), but others are needed asap (session, httpcontext). The question is, how to reconcile these things together.

    So, could we just try to solve it based on the real examples?
    Here's some old code.
    index.php
    PHP Code:

    require 'db.php';

    $httpcontext = new httpcontext(new Session($db));
    $translator = new Translator ($db);
    $page = new Controller($db$httpcontext$translator); 
    Ok, most of them COULD be started on demand, but session should be started asap, right? And if so, the DB too.

    Here's some code from this thread
    PHP Code:
    class Registry
    {
        protected 
    $_query;
        protected 
    $_builder;

        function 
    getQuery() {
            if (!isset(
    $this->_query)) {
                
    $this->_query = new Query();
            }
            return 
    $this->_query;
        } 
    I assume, that _query is actually a model? I understand it;'s an example, but if I want to write realy registry class, I would probably have to write something like that?
    PHP Code:
    class Registry
    {
        protected 
    $model;

        function 
    getModel($model) {
            if (!isset(
    $this->model[$model])) {
                
    $this->model[$model] = new $model;
            }
            return 
    $this->model[$model];
        } 
    But, if I create the model this way I would have to additionally pass it the registry object as well, right? So, the model would have access to DB or Session.

    Uhhh, I think I'm really lost now. Where What When

  12. #12
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by dan7
    Uhhh. That means rewriting each component now
    And it's for sure not the last time that will happen. There are good news however - An assembly layer takes the heat out of shaving for you.

    Quote Originally Posted by dan7
    When I look at the classes, I rather see a need of a mix of service locator and registry. Some objects are needed on demand (models, urlbuilders, translators), but others are needed asap (session, httpcontext). The question is, how to reconcile these things together.
    Yes - This have had me confused as well. The basic premise of these assembly patterns (registry, locator, DI-container) is that the object can be created ignorant of it's creator. But in reality, you need to pass a factory around to avoid using globals - even with a DI-container. Every part of your code, that creates or uses other objects at runtime, will need a reference to the factory.

    Quote Originally Posted by dan7
    I checked your older posts and registry class looked a bit different. In this thread it serves objects on demand but earlier, it was just used to pass objects around and they were created in index.php
    You can implement a registry in several ways. I was trying to be pedagogical , rather than pulling in a full featured component. That may have been a bad idea.

    Quote Originally Posted by dan7
    So, could we just try to solve it based on the real examples?
    Certainly - I'll take offset in the code I use myself then. For some time now, I have been using the following implementation : http://svn.sourceforge.net/viewvc/ko...hp?view=markup
    It's part of konstrukt, but it has no ties to the rest of the code, so it can be used stand alone. There's a test case too, which may be helpful to illustrate the syntax/usage of the methods, if I'm going too fast over it : http://svn.sourceforge.net/viewvc/ko...hp?view=markup

    I'll just give a quick explanation of the workings of the registry. The basic functionality is a plain registry, combined with a pluggable factory. The registry is the simplest part. Basically, you call get(<classname>) and it returns an instance of classname. Call it twice and get the same object back. This can be used to replace globals or singletons. The get method is accompanied with a set() method that does what you would expect.

    To avoid creating all objects in advance, the registry has a factory. Rather than creating the objects in advance, this factory is called the first time an instance is requested - eg. when get() is called the first time. The application can register different factories (called constructors) for different classes. The code is intelligent enough to use a parent-class constructor if no constructor exists for a particular class. The default constructor is used if no fitting constructor is found. A constructor is basically a callback, which takes three arguments; $classname : string, $args : array, $registry : Registry. The third argument allows the constructor to use the registry to resolve dependencies for the class being created.

    Back to the concrete example. Looking first at index.php from your post, we can see a number of objects being created. We want to avoid doing that in advance, so let's encapsulate the creation in constructors and register them with the registry ;

    PHP Code:
    require_once 'k/registry.php';
    $registry = new k_Registry();
    $registry->registerConstructor(
        
    "DB",
        
    "createDB"
    );
    function 
    createDB($className$args$registry) {
        return new 
    PDO("mysql:dbname=testdb;host=127.0.0.1""root""secret");
    }
    $registry->registerConstructor(
        
    "Session",
        
    "createSession"
    );
    function 
    createSession($className$args$registry) {
        return new 
    Session($registry->get("DB"));

    At this point neither the session object, nor the DB have been created - we merely registered the constructors for them. So if at some point our code needs the session, we can use the following code:
    PHP Code:
    $session $registry->get("Session"); 
    This will invoke first the createSession() function, which in turn will call createDB().

    Let's register the other shared objects:
    PHP Code:
    $registry->registerConstructor(
        
    "HttpContext",
        
    "createHttpContext"
    );
    function 
    createHttpContext($className$args$registry) {
        return new 
    HttpContext($registry->get("Session"));
    }
    $registry->registerConstructor(
        
    "Translator",
        
    "createTranslator"
    );
    function 
    createTranslator($className$args$registry) {
        return new 
    Translator($registry->get("DB"));

    And our index.php would now look something like the following (Notice that I put the registration in a separate file, registry.php):
    PHP Code:
    require_once 'registry.php'// contains the above code
    $page = new Controller($registry->get("DB"), $registry->get("HttpContext"), $registry->get("Translator")); 
    But the controller didn't actually need the DB or the Translator objects - it's merely passing theese on to satisfy contained objects' dependencies. So let's pass the registry instead:
    PHP Code:
    require_once 'registry.php';
    $page = new Controller($registry->get("HttpContext"), $registry); 
    But we can do even better than that. Let's assume that the controller inherits from an abstract baseclass (AbsController), with the same signature, we can register a constructor for the abstract baseclass, and that way hide the creation of the controller as well:
    PHP Code:
    abstract class AbsController
    {
        function 
    __construct($httpcontext$registry) {
            
    $this->httpcontext $httpcontext;
            
    $this->registry $registry;
        }
    }

    ...

    class 
    Controller extends AbsController
    {

    PHP Code:
    $registry->registerConstructor(
        
    "AbsController",
        
    "createController"
    );
    function 
    createController($className$args$registry) {
        return new 
    $className($registry->get("HttpContext"), $registry);

    PHP Code:
    require_once 'registry.php';
    $page $registry->create("Controller"); 
    Let's implement the rest of the example, using the registry:
    PHP Code:
    $registry->registerConstructor(
        
    "Query",
        
    "createQuery"
    );
    function 
    createQuery($className$args$registry) {
        return new 
    Query();
    }
    $registry->registerConstructor(
        
    "UrlBuilder",
        
    "createUrlBuilder"
    );
    function 
    createUrlBuilder($className$args$registry) {
        return new 
    UrlBuilder($registry->get("Controller"));

    Controller will now look like the following:
    PHP Code:
    class Controller extends AbsController
    {
        function 
    execute() {
            
    $m $this->registry->get("query");
            
    $m->import($_GET);
            return 
    $this->render("myview.tpl.php");
        }

        function 
    render($template) {
            
    ob_start();
            include(
    $template);
            return 
    ob_get_clean();
        }

    And the view becomes:
    PHP Code:
    Sort by:
    <ul>
        <li><a href="<?php echo htmlentities($this->registry->get("urlbuilder")->url(Array("sortby" => "id"))); ?>">id</a></li>
        <li><a href="<?php echo htmlentities($this->registry->get("urlbuilder")->url(Array("sortby" => "name"))); ?>">name</a></li>
        <li><a href="<?php echo htmlentities($this->registry->get("urlbuilder")->url(Array("sortby" => "date"))); ?>">date</a></li>
    </ul>
    <table>
    <?php foreach ($this->registry->get("query")->select() as $row) : ?>
    <tr>
    <?php foreach ($row as $col) : ?>
    <td><?php echo htmlentities($col); ?></td>
    <?php endforeach; ?>
    </tr>
    <?php endforeach; ?>
    </table>
    The code for Query and UrlBuilder are unchanged.

    This code is basically doing the same as we started out with, but all the creational details have been removed from out application code and put in a single place (registry.php). Not only does this make our code a lot leaner (and thus easier to read), but it also makes it a lot easier to change the signature of the objects' constructors. For example, let's say that you now realize that Query needs access to the DB (Not a far-fetched example actually). Before, you would have to change a lot of places, to pass the DB to Query. Now, all you have to do is edit the function createQuery:
    PHP Code:
    function createQuery($className$args$registry) {
        return new 
    Query($registry->get("DB"));

    And finally, since we have removed any direct creation of objects, we can easily replace one class with another - This is particular helpful when writing unit tests, but also a good aid in refactoring code.

  13. #13
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks for the response. Now it's more clear, but I still have a few questions about your examples and some regarding mine.

    I understand using the registry, but this way, it will contain ALL the functions (model creators, other stuff) for the application, even though the currently running code needs only a fraction of those. Is that right?
    I will have at least tens of model function (+ lots of others). How do you do it? You just write them all?

    Another question is about objects, that have parameters depending on the context. Good example is UrlBuilder. It needs different models in different situations, so I can't simply register a function like
    PHP Code:
    $registry->registerConstructor(
        
    "UrlBuilder",
        
    "createUrlBuilder"
    );
    function 
    createUrlBuilder($className$args$registry) {
        return new 
    UrlBuilder($registry->get("Controller"));

    because $registry->get("Controller") will vary between different actions, pages etc.

    And some question about refactoring, based on the example.

    controller.php + index.tpl.php
    PHP Code:
    class Controller {
        
        function getMenu() {
            $menu = new MenuController();
            $menu->importMessages();
            return $menu->execute();
        }   

        function execute() {
            return $this->render('index.tpl.php');
        }            

    }       

    ...

    <body>

        <?php echo $this->getMenu(); ?>
        
    </body>
    would become something like that, using registry ?

    PHP Code:
    class Controller {
        
        function execute() {
            $menu = $this->registry->get('MenuController');
            $this->importMessages($menu->getMessages());

            return $this->render('index.tpl.php');
        }   
    }    

    ...

    <body>

        <?php echo $this->registry->get('MenuController')->execute(); ?>

    </body>
    Thanks a lot!

  14. #14
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by dan7
    I understand using the registry, but this way, it will contain ALL the functions (model creators, other stuff) for the application, even though the currently running code needs only a fraction of those. Is that right?
    I will have at least tens of model function (+ lots of others). How do you do it? You just write them all?
    Yes. However - Most model components will inherit from a common baseclass, so I'd just register a constructor for the baseclass (Like I did with AbsController vs. Controller in the previous post). It may sound like a lot of manual work to do it this way, but in reality it's not a problem. After all, you're just removing code from your application in putting it somewhere else. If anything, it reduces the overall amount of code (By removing duplicates).

    Quote Originally Posted by dan7
    Another question is about objects, that have parameters depending on the context. Good example is UrlBuilder. It needs different models in different situations, so I can't simply register a function like
    Well, you probably won't have a concrete class named Query, just like you won't have a concrete class called Controller. They would be called something like NewsQuery and NewsController respectively. Same thing with UrlBuilder - You would probably have a NewsUrlBuilder, extending from UrlBuilder. Now you can use the registry, without conflicting.

    Quote Originally Posted by dan7
    PHP Code:
    <body>

        <?php echo $this->registry->get('MenuController')->execute(); ?>

    </body>
    I would personally prefer to have the view ask the current controller to do the job for it, rather than calling a controllers execute() directly from inside a view. Eg. keep the view as it was, but use the registry from within the controllers getMenu() method. It gives the controller some saying in the matter, but I suspect that it's more a matter of personal preference.

  15. #15
    SitePoint Guru
    Join Date
    May 2005
    Location
    Finland
    Posts
    608
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Off Topic:

    Quote Originally Posted by kyberfabrikken
    PHP Code:
    $registry->registerConstructor(
        
    "UrlBuilder",
        
    "createUrlBuilder"
    );
    function 
    createUrlBuilder($className$args$registry) {
        return new 
    UrlBuilder($registry->get("Controller"));

    Anyone up for closures in PHP?

  16. #16
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks for the answers. I have one more question.
    I'm wondering about this code
    PHP Code:
    function createTranslator($className$args$registry) {
        return new 
    Translator($registry->get("DB"));

    So, that means passing db object to the Translator as a parameter. Wouldn't it be easier to just pass the single registry object to every class, that needs anything from the registry?
    so, do rather
    PHP Code:
    function createTranslator($className$args$registry) {
        return new 
    Translator($registry);

    And then
    PHP Code:
    class Translator {
        
        public function 
    __contruct($registry) {
            
    $this->db $registry->get('DB');
        } 
    or just use it when needed
    PHP Code:
    class Translator {
        
        public function 
    __contruct($registry) {
            
    $this->registry $registry
        
    }

        public function 
    getStuff() {
           
            
    $this->registry->get('DB')->prepare('select * from tmp');
            
    // or something shorter, like $this->registry->DB

        

    I see, that $registry->get would be called a few times more in this case (assuming the translator would be used a few times per request), but is there any more reasons, to pass different objects instead of single registry object (or sometimes both)?

    Thanks

  17. #17
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Good question - I kind of expected that you'd ask.

    Strictly speaking, there is no logical reason for not simply passing the registry and let each component pull what it needs - it may even be beneficial performance wise, because it delays the creation of the dependency even further.

    The reason why I try to pass the concrete dependencies in the constructor, rather than just the registry is for semantics. If the dependencies are passed explicitly in the constructor, it's clear to see which dependencies a given component has by looking at the code - You could even use type hints to emphasize it further.

    If you pull the components from the registry, the dependencies are only clear at runtime. If you need to find out which dependencies a given component has, you have to read through the source code, line by line.
    For this reason, I think it's a good idea to be so explicit as possible.

  18. #18
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Oh yeah. I should have thought about it Thanks

  19. #19
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken
    Well, you probably won't have a concrete class named Query, just like you won't have a concrete class called Controller. They would be called something like NewsQuery and NewsController respectively. Same thing with UrlBuilder - You would probably have a NewsUrlBuilder, extending from UrlBuilder. Now you can use the registry, without conflicting.
    This thing still strikes me. No, I wouldn't have NewsUrlBuilder etc. Why would I? UrlBuilder makes only one thing. It's not different for articles or news. It just builds urls for given arguments and given model (passed in the constructor).
    So, what would be the purpose of creating tens of classes, that do the same? Except that it would work nice with k_registry. It's not a problem to do, but I feel it's a bit overblown in this case... What do you think? Any other way to do this (while still working with k_registry)? Thanks

  20. #20
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Oh, one more thing.
    PHP Code:
    ...
        <li><a href="<?php echo htmlentities($this->getUrlBuilder()->url(Array("sortby" => "id")));
    ...
    I'm starting to be a bit tired of such code in my html - $this->getUrlBuilder()->url(Array("sortby" => "id"))
    The main reason is, that my html begins to be unreadable because of too much looong php calls.

    Is it ok to use some helper class, that would user UrlBuilder class and create an array with links etc? Something like
    PHP Code:
    class Sorter {
                    
        public function 
    __construct($urlbuilder$model) {
            
    $this->urlbuilder $urlbuilder;
            
    $this->model $model;
        }   

        public function 
    getHeader() {
         
            
    $header = array();
            foreach (
    $this->model->columns() as $column) {
                
    $header[$column] = $this->UrlBuilder->url(Array("sortby" => "id"));
            }
            return 
    $header;
        }    

    and then in my template
    PHP Code:
    <table>

        <tr>
        
        <?php foreach ($this->registry->Sorter->getHeader() as $link): ?>

            <th><a href="<?php echo htmlentities($link); ?>">something</a></th>

        <?php endforeach; ?>

        </tr>

    </table>
    It's easier to read IMHO. Or maybe it would better to write separate View class and make such things there? Or wrap something in thew controller? What are your thoughts about it?

    Thanks

  21. #21
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by dan7
    This thing still strikes me. No, I wouldn't have NewsUrlBuilder etc. Why would I? UrlBuilder makes only one thing. It's not different for articles or news. It just builds urls for given arguments and given model (passed in the constructor).
    So, what would be the purpose of creating tens of classes, that do the same? Except that it would work nice with k_registry. It's not a problem to do, but I feel it's a bit overblown in this case... What do you think? Any other way to do this (while still working with k_registry)? Thanks
    I think it's a matter of style. The extra classes does add some extra flexibility - It provides a place where you can change stuff for one case, without affecting the others. The question may be whether you need this flexibility. If you don't think so, I suggest that you merge the urlbuilder with one of the other components. You'll loose a bit of flexibility, but gain some simplicity. It may be a good trade off.
    What I do myself, is to put the urlbuilder functionality as part of the controller. The view will hold a reference back to the controller, which it can ask to build the url's for it. In this case, the controller can be said to act as a helper for the view.

    Quote Originally Posted by dan7
    Or maybe it would better to write separate View class and make such things there?
    Probably. I normally don't do that for html applications, because the view logic is normally pretty trivial, so I'd just put it in the template, or as a function on the controller (which the view can call back to). This is a coupling of view-controller and as such contradicts the MVC dogma, yet not as bad as if the controller was pushing data to the view (The view is the initiator here).
    If you prefer a cleaner design, you could have a view class, which contains all the view logic. This adds another component to the mix, but it helps to separate controller and view more from each other.

  22. #22
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken
    I think it's a matter of style. The extra classes does add some extra flexibility - It provides a place where you can change stuff for one case, without affecting the others. The question may be whether you need this flexibility. If you don't think so, I suggest that you merge the urlbuilder with one of the other components. You'll loose a bit of flexibility, but gain some simplicity. It may be a good trade off.
    What I do myself, is to put the urlbuilder functionality as part of the controller. The view will hold a reference back to the controller, which it can ask to build the url's for it. In this case, the controller can be said to act as a helper for the view.
    Thanks. OK, I decided to put urlbuilder in the controller, instead of making additional view class. But now I'm not sure, how should I tell the urlbuilder which model to use? I wanted to put this method in an abstract controller. So, should I call this method from the view with model name as parameter? for example
    PHP Code:
    <a href="<?php echo $this->urlbuilder('somemodel',Array("sortby" => id")); ?>">
    and then in the controller
    PHP Code:
    function urlbuilder($model,$args) {
        
    $m $this->registry->create($model);
        
    // some code
        
    return $q;

    I know I could create the instance of the model in controller, like $this->model = new Model(), but this way I would be tied to this model while presenting the data in the view... The key point was to have this method written only once in the abstract class, otherwise I could go with extending separate urlbuilder classes like in the examples mentioned before

    Well, I can't say I like the solution above..

    Have you got some better idea? Thanks!

  23. #23
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by dan7
    So, should I call this method from the view with model name as parameter?
    Or you could pass the actual model instance.

    Quote Originally Posted by dan7
    Well, I can't say I like the solution above..
    It's rather verbose, but it does the job of keeping things separate. You could relax on the V/C separation in trade off for some simpler communications.
    Keep in mind though, that this case is probably about as complex as it gets - Forms and datalists are complicated matters, and in order to maintain full flexibility, you need quite complex code. Looking at most web applications, I'd say that these two scenarios make up nearly all cases of client side state. You can make some generic baseclasses, and reuse the implementation for trivial cases, and in the cases where you need customization (often in the public part of the site), you would probably want the full flexibility anyway.

  24. #24
    SitePoint Enthusiast
    Join Date
    Jul 2004
    Location
    Finland
    Posts
    73
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by dan7
    PHP Code:
    <a href="<?php echo $this->urlbuilder('somemodel',Array("sortby" => id")); ?>">
    and then in the controller

    Have you got some better idea? Thanks!
    Do you really need to generate all urls dynamically? I used to do that too, but then I became sane again and decided to just hard-type urls in my templates.

    PHP Code:
    <a href="?somemodel/sortby/<?=$id;?>">Sort</a>
    Nice and clean. And if neened, I can easily change all url's I want using the "File Search" function of Eclipse.

  25. #25
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by JaskaS
    Do you really need to generate all urls dynamically? I used to do that too, but then I became sane again and decided to just hard-type urls in my templates.
    Yeah, I need to. Otherwise I would probably go your way, because it's clean...


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
  •