SitePoint Sponsor

User Tag List

Results 1 to 20 of 20
  1. #1
    SitePoint Member
    Join Date
    Aug 2009
    Posts
    3
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Models in MVC - development tactics?

    Hello everyone,

    I'm starting a large project using the Zend framework and I'm nearly ready to start planning out my "Model". This has to be the 4th time I've coded one of these, and you'd think it would be simple! However, every time I create a data model, I struggle with the architecture. I could really use some advice.

    Basically, I like the idea of keeping the controller clean, like this:

    PHP Code:
    // In the Clients Controller... 

    void listAction()
    {
      
    $clients = new Clients();
      
    $clientList $clients->fetchAll();

      
    $this->view->assign("CLIENT_LIST"$clientList);

    However, in my experience, a call like this will often return more client information than actually needs to be displayed. For example, the $clients->fetchAll() function probably looks like this:

    PHP Code:
    // In the model...
    function fetchAll()
    {
      return 
    $this->db->fetchAll("SELECT * FROM clients");

    Rarely do I ever need every column of a table for display. Let me delve a little deeper. Let's say that my client list is also going to show me the number of Widgets managed by a client. (I'm making this up!) I could try to tack on the Widget count in the controller, but that gets messy and inefficient. For example:

    PHP Code:
    // In the Clients Controller... 

    void listAction()
    {
      
    $clients = new Clients();
      
    $widgets = new Widgets();

      
    $clientList $clients->fetchAll();

      foreach(
    $clentList as $client)
      {
        
    $widget_count $widgets->countByClientId($client['client_id']);
        
    // TODO: add the $widget_count to the $client record here
      
    }
      
    $this->view->assign("CLIENT_LIST"$clientList);

    This doesn't feel right. It feels like I'm putting what should be "join SQL" into the controller. I'd much rather put it in the model, like so:

    PHP Code:

    // In the Clients Controller... 

    void listAction()
    {
      
    $clients = new Clients();
      
    $clientList $clients->fetchAll();

      
    $this->view->assign("CLIENT_LIST"$clientList);
    }

    // In the Clients model...

    function fetchAll()
    {

      
    // I have no idea if this query is valid.  It's just for discussion purposes

      
    return $this->db->fetchAll(
       
    "SELECT c.*, COUNT(w.ID) AS widgetCount
        FROM clients c
        LEFT JOIN widgets w ON w.clientID = c.ID
        GROUP BY c.ID
      "
    );

    So, now my client fetchAll() method not only returns all of the client information, but returns the number of Widgets associated with a client as well. However, I don't always need the Widget count, so this new query makes me cringe.

    Let's pretend that my client list only needs to display the firstName, lastName, and widgetCount. The most efficient query for this would look something like:

    PHP Code:
    SELECT c.firstNamec.lastNameCOUNT(w.ID) AS widgetCount
        FROM clients c
        LEFT JOIN widgets w ON w
    .clientID c.ID
        GROUP BY c
    .ID 
    However, making the query so specialized makes it hard to re-use. That aside, let me run with this idea some more. If you did make this method specialized to return ONLY the required information for the page, the controller and model code could look like this:

    PHP Code:

    // In the Clients Controller... 

    void listAction()
    {
      
    $clients = new Clients();
      
    $clientList $clients->fetchAllForListView();

      
    $this->view->assign("CLIENT_LIST"$clientList);
    }

    // In the Clients model...

    function fetchAllForListView() // <--- new function name!
    {
      return 
    $this->db->fetchAll(
       
    "SELECT c.*, COUNT(w.ID) AS widgetCount
        FROM clients c
        LEFT JOIN widgets w ON w.clientID = c.ID
        GROUP BY c.ID
      "
    );

    So that's one way to do it... although it gets out of hand quickly. If I follow this approach, it means that I may have different functions for getting client information for each action in my controller. But should I? I mean, if each action in my controller displays different information, shouldn't each action fetch different data?

    If I sound way off track, I probably am. I post with the humble knowledge that I'm probably adrift and could use some friendly advice.

    Thanks!

  2. #2
    From space with love silver trophy
    SpacePhoenix's Avatar
    Join Date
    May 2007
    Location
    Poole, UK
    Posts
    4,909
    Mentioned
    96 Post(s)
    Tagged
    0 Thread(s)
    This is the way I pass query results from the model to the controller:

    PHP Code:
            while ($row=$db->dbFetchArray($result)) {
                  
    $array_of_results[] = $row;
              }
              return 
    $array_of_results
    The controller then extracts whatever it needs from the array:

    PHP Code:
    $array_of_results=$this->model->method_in_model ($db);
                  foreach (
    $array_of_results as $results) {
                      echo 
    "<tr>\n";
                      
    extract($results); 
    Community Team Advisor
    Forum Guidelines: Posting FAQ Signatures FAQ Self Promotion FAQ
    Help the Mods: What's Fluff? Report Fluff/Spam to a Moderator

  3. #3
    SitePoint Member
    Join Date
    Aug 2009
    Posts
    3
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks SpacePhoenix. I use a similar method of returning data from my model to my controller. Some of my tables contain text fields (as opposed to varchar, float, int, etc) that hold a lot of data. I'm trying to avoid querying for these fields unless the text is displayed by the view. How would you suggest changing your model to return only the data required for rending the view?

    Thanks!
    - Bret

  4. #4
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,052
    Mentioned
    16 Post(s)
    Tagged
    3 Thread(s)
    You could pass arrays of of what select or deselect. Although, that may get a little tedious if its not implemented in a reusable or inheritable fashion.

  5. #5
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,052
    Mentioned
    16 Post(s)
    Tagged
    3 Thread(s)
    However, making the query so specialized makes it hard to re-use.
    I know exactly what your saying. Which is why I created a System to handle all that for me.

    PHP Code:
    // clients
    Client::find();

    // client and number of widgets
    $clients 
    Client::find(
        array(
            
    'dynamic'=>array(
                
    'widgetCount'=>'COUNT(*)'
            
    )
            ,
    'include'=>'widgets'
            
    ,'group'=>'id'
        
    )
        ,array(
            
    'require'=>false
        
    )
    );

    // get clients number of widegts
    $clients[0]->widgetCount
    Even though the clients table doesn't have a column called widgetCount the model can be modified at run time and essentially overloaded with calculated or aggregate columns. Which makes it simple to run complex queries over and over without a need to write the actual SQL because it is all dynamically created. Normally this type of thing would need to handled through a strategic development methodology, but by working it directly into the find mechanism for the model that can all be avoided. Instead these types of decisions can made in the moment without rippling through the rest of the application.

  6. #6
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,052
    Mentioned
    16 Post(s)
    Tagged
    3 Thread(s)
    I also have a way to select and deselect columns which is relatively straightforward. For instance, if the clients table had a column called password but that didn't need to be selected it could deselected at run time.

    PHP Code:
    Client::find(
        array(
            
    'deselect'=>array('pwd')
        )
    ); 

  7. #7
    From space with love silver trophy
    SpacePhoenix's Avatar
    Join Date
    May 2007
    Location
    Poole, UK
    Posts
    4,909
    Mentioned
    96 Post(s)
    Tagged
    0 Thread(s)
    The way I've the models in my app is to have a separate methods, each method contains a single query and returns its results as necessary. It's probably the most in-efficient way of doing it but it works for me.
    Community Team Advisor
    Forum Guidelines: Posting FAQ Signatures FAQ Self Promotion FAQ
    Help the Mods: What's Fluff? Report Fluff/Spam to a Moderator

  8. #8
    Spirit Coder allspiritseve's Avatar
    Join Date
    Dec 2002
    Location
    Ann Arbor, MI (USA)
    Posts
    648
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by clone45 View Post
    So, now my client fetchAll() method not only returns all of the client information, but returns the number of Widgets associated with a client as well. However, I don't always need the Widget count, so this new query makes me cringe.
    The only way you can fetch only the data you need on every request is to use raw SQL. If you want the benefits of a rich model, you're going to have to deal with the consequences-- and part of that is fetching more data than you need (or setting up specific points to lazy load data you don't need that often). I realize that eager fetching makes you cringe, but until it creates a noticeable performance decrease, I wouldn't worry about it.

  9. #9
    SitePoint Wizard silver trophybronze trophy Cups's Avatar
    Join Date
    Oct 2006
    Location
    France, deep rural.
    Posts
    6,869
    Mentioned
    17 Post(s)
    Tagged
    1 Thread(s)
    I analyzed how often I actually needed to do similar requests, and decided to take a pragmatic stance.

    I settling on creating a passThru() method which simply lets me create a sql statement and pass the results thru to PDO.

    This by-passes PDO's prepared statements, so I only use it if I am cocksure there is no need to filter any of the data.

    The other option I leave myself is to hard-code a method into a gateway.

    getAllWidgetsWithCounts();

    A mixture of those two gets me thru the say, 5&#37;, of non-crud queries - up to now anyway, but I am still learning too.

    Crud-like queries though includes calls like this from single tables;

    $this->StaffManager->getColumns( array( "id", "type_ref", "name", "email" ) );

    Doubtless I will come to laughingly rue this 'pragmatism', but it seems to work for me so far.

    I should stress I only use this for my CMS admin screens - whose models tend to not need so many joins.

  10. #10
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    Off the top of my head (feel free to extend this list), there are 3 types of SELECT query and they should all be handled differently.

    -Select one record based on ID or other indexed field in the table
    -Select a set of records belonging to another record
    -Select a set of records based on criteria

    Abstracting non "select on id" type queries is not a good idea:

    • You'll never realistically be able to support all possible SQL functionality, unions, sub queries, aggregates, any mixture of these and probably end up having to work around the limitations of your query abstraction.
    • Optimising the queries will be far more difficult. Often, for optimisation it's better to add the criteria to the join statement as well as the where. You also need full control over the order in which tables are joined, and join on as many indexed fields as possible
    • You have a new syntax to learn. May not be too bad if you've written the library, but it's going to be far easier to give the code to another developer with plain SQL


    oddz example seems far more complex than writing plain SQL, and there's no way it can generate optimised SQL. There's no way it can choose the best table to use for FROM then consider the best order to join the tables in. Then there's the PHP performance hit, why iterate through the multidimensional array when there's really no need?


    In most cases, the query is actually based on one model, e.g. "I want all the orders made by the user"

    In which case I'll do:

    PHP Code:
    $user = new User(21);
    $orders $user->getOrders(); 
    getOrders() can take a set of specified parameters which may be needed, e.g. limit, offset.

    The function does one specific job, and is reusable, rather than trying to act as a god function.

    For top level queries, which aren't sub records of a single other record, I write the SQL in the controller, or if it needs to be reused, move it to a class. Mostly, however the controller is doing a very specific job. E.g. listing all the records of one type with certain properties which isn't going to be repeated.

  11. #11
    SitePoint Member
    Join Date
    Aug 2009
    Posts
    3
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Wow, thank you everyone for your input. I think I have enough information to decide on a strategy. It's interesting how many solutions there are to this subject. Again, thank you all!! I really appreciate your input.

  12. #12
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,052
    Mentioned
    16 Post(s)
    Tagged
    3 Thread(s)
    Quote Originally Posted by TomB
    oddz example seems far more complex than writing plain SQL, and there's no way it can generate optimised SQL.
    Its is far more complex, but the trade off is flexibility. For example, if any model is modified that modification ripples throughout the rest of the application. The only problem would be if that modification or added column results in a conflict between resolving dynamic fields and model fields.

    The other advantage is that the end row object is a tree based on the number of includes. Normally this could only be achieved by parsing the SQL result for every query where a join occures in the application. Meaning a lot of repeating the same thing where I've managed to completely eliminate it by handling it in a generic fashion.

    The only thing it doesn't support right now is unions. However, it is built to support subqueries in the select, from or where clause.

    PHP Code:
    /*
    * Would return raw select statement object
    */
    $categoriesSubquery Category::find(
        
    'subquery'
        
    ,array(
            
    'empty'=>true // deslect ALL fields including primary key
            
    ,'select'=>'id' // reselect primary key ONLY
            
    ,'status <>'=>// filter on status field
        
    )
    );

    /*
    * Embed subquery in where clause
    * run query and return products in a active category
    */
    $products Product::find(
        
    'all'
        
    ,array(
            
    'category_id NOT IN'=>$categoriesSubquery
        
    )
    ); 
    It all depends on what your goals are.

  13. #13
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    or example, if any model is modified that modification ripples throughout the rest of the application.
    This should be true of any model in an MVC pattern. If anything it's less so with your find function.

    Consider this scenario: We've just introduced a new level of user permissions, every query that filters the users view of items needs to be changed. With a $user->getFoo(); model, only the user class needs to be changed, and a lot of these functions are going to be reused. With your User::find() in the controller, every instance of the query needs to be changed because it's essentially not reusable.


    The other advantage is that the end row object is a tree based on the number of includes. Normally this could only be achieved by parsing the SQL result for every query where a join occures in the application. Meaning a lot of repeating the same thing where I've managed to completely eliminate it by handling it in a generic fashion.
    I'm sure I'm misunderstanding here, but by "end row object" do you mean $products[count($products)-1]? If so that would get rather annoying when iterating through the result set. What does the tree contain?

  14. #14
    SitePoint Member
    Join Date
    Dec 2008
    Posts
    21
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    So, now my client fetchAll() method not only returns all of the client information, but returns the number of Widgets associated with a client as well. However, I don't always need the Widget count, so this new query makes me cringe.
    A familiar problem. I often use countBy method. It takes a field name and a set of values. It returns a hash where the number of items with the value is mapped to the value itself. The way it works is by using IN operator. You can easily write something like that for your own models.

    In your example the result would be as follows:
    Code PHP:
    void listAction(){
        $clients = new Clients();
        $widgets = new Widgets();
     
        $clientList = $clients->fetchAll();
        $ids = array();
        foreach ($clients as $client) {
            $ids[] = $client->id;
        }
        $widgetCounts = $widgets->countBy('clientId', $ids);
     
        $this->view->assign("CLIENT_LIST", $clientList);
    }

    It does not result in less code, but it avoids making queries in a loop. If you do things like that very often, you could make it even simpler by adding an aggregating method:

    Code PHP:
    void listAction(){
        $clients = new Clients();
        $widgets = new Widgets();
     
        $clientList = $clients->fetchAll();
        $widgetCounts = $widgets->countByAttr('clientId', $clientList);
     
        $this->view->assign("CLIENT_LIST", $clientList);
    }
    Caffeine Web Framework - reinventing the wheel since 2004.
    MicroWSS - simple SOAP web services server in Java.

  15. #15
    SitePoint Member
    Join Date
    Dec 2008
    Posts
    21
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I decided to investigate a related idea.

    Technically, you can make a projection using nested queries. Technically, if query optmizer is smart enough, it will be as good as narrowing columns down manually.

    To test the effectiveness of this idea I created an articles table with 11K random entries (dates, titles, text, category, etc.). Row size was around 13Kb, mostly from the `text` field.

    Then I tried these queries in MySQL:
    SELECT id, title FROM `article` LIMIT 1000
    SELECT id, title FROM (SELECT * FROM `article` LIMIT 1000) AS a
    SELECT * FROM `article` LIMIT 1000
    SELECT id, title FROM (SELECT * FROM `article`) AS a LIMIT 1000

    The results were 0.0290 sec, 0.1902 sec, 0.2226 sec, and 19.9389 sec respectively.

    So you can somewhat speed up a query by using a generic projection, but you will be much better off just by writing a query manually. I would expect these results to be different for other databases.
    Caffeine Web Framework - reinventing the wheel since 2004.
    MicroWSS - simple SOAP web services server in Java.

  16. #16
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,052
    Mentioned
    16 Post(s)
    Tagged
    3 Thread(s)
    Quote Originally Posted by TomB
    I'm sure I'm misunderstanding here, but by "end row object" do you mean $products[count($products)-1]? If so that would get rather annoying when iterating through the result set. What does the tree contain?
    Eliminates the need to cycle through the result set removing duplicate/projected data. Instead the system handles this entirely in a very intelligent and efficient generic manor.

    One of the common problems I see pop up at least once every two weeks is someone has ran a query including one or several joins, but they need to remove all projected data. The system of mine is built to run a query and remove that projected data creating a tree.

    A simple example would be a users table and dogs table. If users has many dogs then when users and dogs are joined together if one user exists in the database and has 20 dogs there will be 20 rows returned. Well we don't really want twenty rows what we want is the single user and for it to have its dogs as a property. So that is exactly how have my system work.

    PHP Code:
    // build, run and collect result
    $users User::find(
        array(
            
    'include'=>'dogs' // name of model plural for 1:m relationship
        
    )
    );

    // show all users and their dogs name perhaps
    foreach($users as $user) {

        echo 
    $user->getProperty('name');
        
        
    // this is not a lazy load since the property has allready been defined
        // with each users given collection of dogs
        
        
    foreach($user->getProperty('dogs') as $dog) {    
            echo 
    $dog->getProperty('name');
        }

    There is no possible way to achieve that in less code from outside the library. You would have to iterate through the result manually and recollect it based on whether or not the user already exists in the recollected result. It becomes even more complex if we begin talking joining together 2,3,4,etc tables. Yet it becomes simple when its all handled on a generic level regardless of the number of tables.

    Even less possible is the ability to count the number of dogs each user has. Yet, I am able to overload the model at run time with a dynamic property which makes this much simpler then it ever would be in a normal ActiveRecord system without replicating code.

    PHP Code:
    // build, run and collect result
    $users User::find(
        array(
        
            
    'include'=>'dogs' // name of model plural for 1:m relationship
            
    ,'group'=>'id' // group by users id (primary key)
            
            /*
            * This is where the fun begins!!!
            * overload the property num_dogs into each user object
            * as the count of dogs the user has
            */
            
    ,'dynamic'=>array(
                
    'num_dogs'=>'COUNT(*)'
            
    )
        )
        ,array(
            
    /*
            * left join instead of inner
            * we don't require the user to have a dog
            * to be included in the result set
            */
            
    'require'=>false
        
    )
    );

    // dump data
    foreach($users as $user) {
        echo 
    $user->name;
        
    /*
        * num_dogs is not a field on the users table yet by
        * using a dynamic "fields" we are able to overload
        * the model and thus support aggregates, calculated columns
        * and corelated queries. Even though typical ActiveRecord only supports the direct table fields
        */
        
    echo $user->num_dogs;

    The SQL can be optimized because in its purest form all it provides is a object/array syntax for building SQL statements based on entities rather then tables. The only problem is the processing initially required to build the SQL statement. However, it hasn't become an issue even when I'm putting together nested subquery based SQL statements using this interface. However, for those types of things I pretty much have to write out the SQL then transform it to fit my system. However, the advantage is the projection elimination and tree based result set. Its a give and take like anything else.

  17. #17
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,052
    Mentioned
    16 Post(s)
    Tagged
    3 Thread(s)
    Simple example of hierarchy created with including dogs for users where there is one user who has 20 dogs in the db.

    [ActiveRecordCollection] users
    -------------------------- [ActiveRecord] User
    ------------------------------------------[ActiveRecordCollection] dogs
    -------------------------------------------------------------------- [ActiveRecord] Dog
    -------------------------------------------------------------------- [ActiveRecord] Dog
    -------------------------------------------------------------------- [ActiveRecord] Dog
    -------------------------------------------------------------------- [ActiveRecord] Dog
    -------------------------------------------------------------------- [ActiveRecord] Dog
    -------------------------------------------------------------------- [ActiveRecord] Dog
    -------------------------------------------------------------------- [ActiveRecord] Dog
    -------------------------------------------------------------------- [ActiveRecord] Dog
    -------------------------------------------------------------------- [ActiveRecord] Dog
    -------------------------------------------------------------------- [ActiveRecord] Dog

    Each ActiveRecord also provides a toXML() method which will build a nice XML file regardless the depth of the tree for each root model.

  18. #18
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    Ah, that's quite nice.

    I was considering a system with similar, more OO functionality, but didn't think it would be flexible enough to be any practical use:


    It never got very far, but this was the idea:

    PHP Code:
    $user = new User(1); //initialise the user
    $dogs $user->dogs;

    //What this does, is runs a method based on __get() which knows the user's relationships to other tables, and returns a type of resultset object. Not an array of dog objects.

    //The resultset object implements countable and works like this:
    count($dogs);

    ....
    public function 
    count() {
    //if the result have been fetched, count the number of dogs already fetched. If not, run a count(*) query.
    }
    ....


    //The object also implements iterator, The full query will be run on the first call to rewind() making this possible:
    foreach ($user->dogs as $dog) {

    }


    //Finally, it extends ArrayObject meaning you can do:
    $user->dogs[7//and it will do the query with LIMIT 1 OFFSET 7 if the data hasn't already been fetched. 
    But seeing you've made it work in quite a nice way, I'll probably attempt it.

    I will probably attempt to make a POC attempt at this in the next few days Whether this will be any practical use I have no idea as it only supports a join to a single table, and it needs a better method of array access (think pagination)

  19. #19
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,052
    Mentioned
    16 Post(s)
    Tagged
    3 Thread(s)
    If nothing else its a great exercise.

    This is the class that handles the result set hydration that I'm referring to:

    http://github.com/oddz/active_record...tion_agent.php

    Its been developed from the ground up to work with everything else within my library for efficiency reasons. However, the basic concepts are the same either way. One thing you'll probably quickly discover when attempting to build something similar is the need to carry over the selected columns and what table/model they belong to. Otherwise there isn't any *efficient* way of resolving result fields to the appropriate table. The way I handle that is to build a tree out of the SQL statement itself. Every join which occurs results in a new branch. This makes it possible to cycle through that node hierarchy and transpose it upon the SQL result set. Just something to consider that I had some very big problems with.

    Something its lacking is documentation, but I need to get around to that eventually…

  20. #20
    SitePoint Guru
    Join Date
    May 2005
    Location
    Finland
    Posts
    608
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'm having a bit of trouble seeing where the actual problem is, so let's walk this through.

    Quote Originally Posted by clone45 View Post
    PHP Code:
    // In the Clients Controller... 
    void listAction()
    {
      
    $clients = new Clients();
      
    $clientList $clients->fetchAll();

      
    $this->view->assign("CLIENT_LIST"$clientList);

    However, in my experience, a call like this will often return more client information than actually needs to be displayed.
    This is because your controller, in this case, must know what the view wants and act accordingly. The solution is to have the view be active instead of passive and give it the ability to fetch its own data from the model. This is the real sentiment behind M-V interaction in MVC. Really, why involve the controller?

    Rarely do I ever need every column of a table for display. Let me delve a little deeper. Let's say that my client list is also going to show me the number of Widgets managed by a client. (I'm making this up!) I could try to tack on the Widget count in the controller, but that gets messy and inefficient.

    This doesn't feel right. It feels like I'm putting what should be "join SQL" into the controller. I'd much rather put it in the model
    That is correct. Handling the data should definitely be in the model. If your architecture makes you want to avoid doing the right thing, the architecture is what must be corrected.

    So, now my client fetchAll() method not only returns all of the client information, but returns the number of Widgets associated with a client as well. However, I don't always need the Widget count, so this new query makes me cringe.
    Why? Is it because the query is difficult to construct? We're using Query objects (with Doctrine's more than helpful assistance) to great effect. They are highly reusable and make programmatic query assembly based on changing conditions easy and readable.

    If you did make this method specialized to return ONLY the required information for the page, the controller and model code could look like this––

    ––it gets out of hand quickly. If I follow this approach, it means that I may have different functions for getting client information for each action in my controller. But should I? I mean, if each action in my controller displays different information, shouldn't each action fetch different data?
    There's nothing wrong with having multiple ways to access your data. The view is a certain view at the data, be it the same data or something entirely different. Furthermore, once you move to an active view, the amount of cruft in the controllers required for passing the data and actually deciding which data to pass when is substantially lessened.


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
  •