SitePoint Sponsor

User Tag List

Results 1 to 24 of 24

Thread: MVC Dashboard

  1. #1
    SitePoint Member
    Join Date
    Mar 2005
    Posts
    18
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    MVC Dashboard

    First, let's start with the code:

    PHP Code:
    <?php
    class DashboardController extends Controller
    {
        function 
    indexAction()
        {
            
    /* Here's the problem, the dashboard is a collection of information from 
                other models. So we have to load multiple models. That just seems
                icky. */
            
    $example1 loadModel('Example1');
            
    $example2 loadModel('Example2');
            
    $example3 loadModel('Example3');
            
    $example4 loadModel('Example4');
            
    $example5 loadModel('Example5');

            
    // Now we get some nifty methods from these separate models
            
    $this->example1 $example1->getFunkyStats();
            
    $this->example2 $example2->getCoolStats();
            
    $this->example3 $example3->getRatherNeatStats();
            
    // etc

            
    loadView('dashboard.tpl');
        }
    }
    ?>
    Nothing about this looks right, but I'm completely lost on any other options. I'm thinking, my implementation must be wrong and I've stared at it too long to figure out why.

  2. #2
    Non-Member melancholic's Avatar
    Join Date
    Nov 2004
    Location
    Australia
    Posts
    447
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi,

    Perhaps you should refactor so that you separate the collection of information from the other models into one object.

    PHP Code:
     class Statistics
     
    {
         function 
    getFunkyStas();
         function 
    getCoolStats();
         function 
    getRatherNeatStats();
     } 
    I don't know what the architecture of the application is and how specialised the example(n) models are, so this is a long shot, but perhaps you could refactor so that examples could either compose or aggregate the stats object and shape it as needed.


    Regards,

  3. #3
    SitePoint Zealot
    Join Date
    Oct 2004
    Location
    Worcester
    Posts
    138
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I would start by giving responsibility for displaying to the widget in question. i.e the widget should do the work of picking up it's data then be able to display it. The display function should be standardised to something like render()

    Then it should be possible to register widgets with the dashboard object. The dashboard could also have a render() function that iterates over the registered widgets calling each one's render() function.

    IndexAction() then just collects the dashboard from a registry and calls it's render() function.

    Dunno if it would work in your context though

  4. #4
    SitePoint Member
    Join Date
    Mar 2005
    Posts
    18
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'll throw in 2 models to give an idea of how they are restricted.

    PHP Code:
    <?php
    class Example1Model extends ActiveRecord
    {
        function 
    getFunkyStats()
        {
            
    // queries the AR to get specific info and returns.
        
    }

        function 
    getCoolStats()
        {
            
    // Same as above
        
    }
    }
    ?>
    Second model:
    PHP Code:
    <?php
    class Example2Model extends ActiveRecord
    {
        function 
    getCoolStats()
        {
            
    // queries the AR to get specific info and returns.
        
    }

        function 
    getRatherNeatStats()
        {
            
    // Same as above
        
    }
    }
    ?>
    Each model represents a single controller and a set of tables in the database that relationally make sense (example1, example1_entries, example1_comments) for example. Each model can also contain all, some or entirely different methods than the others (most share some general ones).

    So, implementing a registry or a statistics class seems unlikely. Perhaps the culprit is too much restriction on my model? Perhaps I'm a little too focused on implementing my design around MVC instead of implementing MVC (or any other architecture) around my design.

  5. #5
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    If you have one or more Models assocc. with a Controller, then what I suggest is that you use the Composite to control how you are able to access each Models data. So, as you recurse over this Composite structure, the parent most Model picks up the data from any children that given parent Model has - the children themselves are also Models you understand?

    Thus, as your recursion move upwards towards the root Model, you have an accumalation of your Modelled data, ready to be rendered to the View via a Visitor; The accumalation works as the data from any given Model is passed to it's parent most Model, and so on as you move upwards towards each/from parent yes?

    If you want to do something in particular with a given data set (from a specific Model) before it gets rendered, then just pass another Visitor to the Composite (Model) in question.

  6. #6
    SitePoint Guru
    Join Date
    May 2005
    Location
    Finland
    Posts
    608
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Dr Livingston
    If you have one or more Models assocc. with a Controller, then what I suggest is that you use the Composite to control how you are able to access each Models data.
    It occurred to me that this problem might actually be the one where applying a Composite hierarchy would indeed be the correct solution. I was wondering when you'd show up.

  7. #7
    SitePoint Member
    Join Date
    Mar 2005
    Posts
    18
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Dr Livingston
    If you have one or more Models assocc. with a Controller, then what I suggest is that you use the Composite to control how you are able to access each Models data. So, as you recurse over this Composite structure, the parent most Model picks up the data from any children that given parent Model has - the children themselves are also Models you understand?
    I'm having a bit of trouble with the implementation here. Unfortunately I can't see how to implement this outside of a flat stack, which seems to defy the entire purpose of a composite in the first place. Blame my utter lack of experience in using design patterns, or my one-demensional brain .

    Here's my flat stack implementation:

    PHP Code:
    <?php
    $model 
    = new CompositeModel();

    $model->attach( new Exmaple1Model('getFunkyStats'));
    $model->attach( new Exmaple1Model('getCoolStats'));

    $model->attach( new Example2Model('getFunkyStats'));
    $model->attach( new Example2Model('getCoolStats'));

    $model->attach( new Example3Model('getFunkyStats'));
    $model->attach( new Example3Model('getCoolStats'));
    $model->attach( new Example3Model('getRatherNeatStats'));

    $model->execute();
    ?>
    When you get a chance, would you mind a quick example of how you would use this type of example with a true Composite?

  8. #8
    Put your best practices away. The New Guy's Avatar
    Join Date
    Sep 2002
    Location
    Canada
    Posts
    2,087
    Mentioned
    1 Post(s)
    Tagged
    1 Thread(s)
    Your load module function could accept arrays.
    "A nerd who gets contacts
    and a trendy hair cut is still a nerd"

    - Stephen Colbert on Apple Users

  9. #9
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    It's a flat stack as there is no parent <> child relationship, so you need something like this,

    PHP Code:
    $root = new BaseModel();
    $root -> attach( new ModelA() ); // deals with pulling each row of data from database
    $root -> attach( new ModelB() ); // deals with paged results, ie Page 1 of 3
    $root -> attach( ... other Models ... ); 
    You would use a Factory or Builder to create the structure for you, in which case you get to the point whereby you notice that you can actually re-use certain fragments of the Composite structure, so you separate that particular Composite and all it's children out.

    So, you attachment is something like this,

    PHP Code:
    public function attachIComposite $composite ) {
    $this -> children[$composite -> getId()] = $composite;
    $composite -> setParent$this );

    I use an Interface for your information,

    PHP Code:
    interface IComposite {
    public function 
    getId();
    public function 
    hasChildren();
    public function 
    getChildren();
    public function 
    attachIComposite $composite );
    }

    // also for the models
    interface IModel extends IComposite {
    public function 
    gather$sets = array() );

    When you need to accumalate the data sets you need something like this (off the top of my head as I don't have any script at hand, so beware)...

    PHP Code:
    public function gather$sets= array() ) {
    foreach( 
    $this -> getChildren() as $child ) {
    $sets[$child -> getId()] = $child -> gather();
    }
    return 
    $sets;

    Do note however, that in the Composite::gather( $sets = array() ); that you only actually need to gather for the child Models, and not the actual Model it's self in question, as the gathering for the Model in question, is done from it's parent - that is a gotcha to watch out for

    Anyways, hope this helps you out?

  10. #10
    simple tester McGruff's Avatar
    Join Date
    Sep 2003
    Location
    Glasgow
    Posts
    1,690
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    This is ridiculous. Don't use Composite.

  11. #11
    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)
    The dashboard example is a complex view, accessing multiple model components. Theese kinds of views can generally be aggregated at the view level or at the controller level. If you do the latter, you get more reusability since you can easier reuse the sub-components in other contexts. If you aggregate at the view level, you don't get as much reuse, but on the other hand, you have more flexibility to tailor the resulting view, because the different parts can be aware of eachother.
    If you aggregate at the controller level, you'll generally have fewer (possible just one) model components in use per controller component, whereas if you aggregate at the view level, you'll pull all the used model components in a single controller component.
    There is no best way in deciding between the two strategies - it depends on what you want to archieve.

  12. #12
    SitePoint Guru
    Join Date
    May 2005
    Location
    Finland
    Posts
    608
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by McGruff
    This is ridiculous. Don't use Composite.
    Yeah. I must've misinterpreted something at first for thinking it would be a valid approach. To be frank, I'm not seeing the problem here. As kyberfabrikken said, there aren't many ways to do this, you just have to pick one.

  13. #13
    simple tester McGruff's Avatar
    Join Date
    Sep 2003
    Location
    Glasgow
    Posts
    1,690
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    As best as I can make out, the original problem is that there are a bunch of domain objects providing data for display and nothing for the controller to do except fire the starter gun for the view. All a view object needs to do is get the others into the same scope as a template file and bingo you're done.
    Quote Originally Posted by kyberfabrikken
    Theese kinds of views can generally be aggregated at the view level or at the controller level.
    Is an MVC controller allowed to have this kind of detailed knowledge of a view?

  14. #14
    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 Ezku
    To be frank, I'm not seeing the problem here.
    Apologies if I was a bit abrupt I've got some issues with the good Dr which we don't need to go into.

  15. #15
    SitePoint Guru
    Join Date
    May 2005
    Location
    Finland
    Posts
    608
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by McGruff
    Apologies if I was a bit abrupt I've got some issues with the good Dr which we don't need to go into.
    Not at all - I believe I know the gripe you're alluding to. The phrase you quoted was referring to the topic; I'm not seeing a real problem here.

  16. #16
    SitePoint Member
    Join Date
    May 2006
    Location
    Saratov, Russia
    Posts
    13
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I would call models' methods in a separate view class:

    PHP Code:
    class DashboardController extends Controller
    {
        function 
    indexAction()
        {
            
    $model1 =& new Model1;
            
    $model2 =& new Model2;
            
    $model3 =& new Model3;
            
    $model4 =& new Model4;
            
    $model5 =& new Model5;

            
    $this->view =& new DashboardView
                
    $model1$model2$model3$model4$model5
            
    );
        }
    }

    class 
    DashboardView extends TemplateView
    {
        function 
    DashboardView( &$model1, &$model2, &$model3, &$model4, &$model5 )
        {
            
    $this->setTemplate'dashboard.tpl' );

            
    $this->set'funkyStats'$model1->getFunkyStats() );
            
    $this->set'coolStats'$model2->getCoolStats() );
            
    $this->set'ratherNeatStats'$model3->getRatherNeatStats() );
            
    // ...
        
    }


  17. #17
    SitePoint Member
    Join Date
    Mar 2005
    Posts
    18
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by McGruff
    As best as I can make out, the original problem is that there are a bunch of domain objects providing data for display and nothing for the controller to do except fire the starter gun for the view. All a view object needs to do is get the others into the same scope as a template file and bingo you're done.
    I agree, which is why I was confused about the Composite suggestion. Again, I'm no expert when it comes to design patterns, but I was always under the asssumption that a Composite was useful for a tree structure, which this certainly is not. I think my real problem was getting a little one demensional 1 model : 1 controller.

    Quote Originally Posted by ArtGor
    I would call models' methods in a separate view class:
    That is definately the direction I'm headed as well. Originally, I had the following model design:

    Global Model
    - ActiveRecord
    -- Child Model

    Obviously loading the ActiveRecord and (to a lessor extent the Global Model) is unneccesary overhead. Since I want to avoid loading the entire model when I want to use (at most) 3 methods in each model I'm thinking this might be the end result (ignore the poorly named methods and variables, I'm going by the seat of my pants):

    PHP Code:
    <?php
    class Example1Model extends ActiveRecord
    {
        private 
    $modelExtension;  

        function 
    __construct()
        {
            
    $this->modelExtension loadExtendedModel('Example1Stats');
        }

        
    // A whole bunch of methods that are useless to the dashboard

        // A function that IS useful to the dashboard
        
    function getRatherNeatStats()
        {
            return 
    $this->modelExtension->getRatherNeatStats();
        }
    }
    ?>
    In essence, the DashBoard constructor can load JUST the Example1Stats model, while the Example1 controller can load the entire Example1 model. Probably not ideal, but seems more intuitive then what I was dealing with originally.

    Thanks everyone for the suggestions, but if anyone else is interested do chime in.

  18. #18
    simple tester McGruff's Avatar
    Join Date
    Sep 2003
    Location
    Glasgow
    Posts
    1,690
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Maybe the "global model" isn't as bad as you think. Or maybe it is... Can you tell me more about it?

  19. #19
    SitePoint Member
    Join Date
    Mar 2005
    Posts
    18
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by McGruff
    Maybe the "global model" isn't as bad as you think. Or maybe it is... Can you tell me more about it?
    I'll give an example (this is not a real world example, but will give you an idea of what I am doing witht he global model):

    PHP Code:
    <?php

    class Model
    {
        
    // Here's a method I use all over the place
        
    protected function getMenuText($language)
        {
            return 
    child::findAll("SELECT * FROM menu_lang WHERE lang='{$language}'");
        }
    }

    class 
    ActiveRecord extends Model
    {
         public function 
    findAll($query)
         {
               
    // grab any data that matches $query from the database
         
    }
    }

    class 
    Example1 extends ActiveRecord
    {
         
    // we know what this does
    }
    ?>
    Where I run into the problem is loading 5 or 6 models means having 5 or 6 global models and 5 or 6 ActiveRecords. Perhaps the Global Model wouldn't be THAT bad, because it's fairly small (most applications I envision using very few global methods for data access), so I can handle that, and a good remedy might be passing ActiveRecord to each model, instead of having it a child of each.

    So perhaps this may be a better design:

    PHP Code:
    <?php
    class Model
    {
        public function 
    __construct($dbObj)
        {
            
    $this->dbObj $dbOjb;
        }
    }


    class 
    Example1Model extends Model
    {
        public function 
    __construct($dbObj)
        {
            
    parent::__construct($dbObj);
    }
    ?>
    This way, I can split models up (like have the "extended" model for methods which may be used in the dashboard). I can then have my Global Controller create the database object and pass it to the model. However, this seems to mean creating extra steps I don't need to create. So I have yet to decide how exactly to implement this without comitting the crime of repetition.

  20. #20
    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 McGruff
    Is an MVC controller allowed to have this kind of detailed knowledge of a view?
    I think yes. MVC describes the division of labour between layers - not components. The controller will need to have some level of knowledge of both the model and the view.

    I agree that composing at the controller level is better OO code, since it decouples and encapsulates. The cost is increased complexity, so if you don't need that, I think it's overdesign. Thus if I'm in doubt, I would generally start with the composite view, but I would be quick to refactor to a composite controller if the design pulls me down.
    As mentioned, there are rare situations where I would choose the composite view for technical reasons - namely if the sub-parts need to be aware of eachother.

  21. #21
    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 JustinH
    I'll give an example (this is not a real world example, but will give you an idea of what I am doing witht he global model)
    OK so the "global" model class isn't global in the sense of being in the global scope but rather a base class with some methods common to derived classes? That won't change how the objects are created. You'd still be doing $foo = new Foo(...) irrespective of whether Foo inherits from another class or not. I'm not really seeing the problem though. Can you give me some more specific examples?

    An ActiveRecord would correspond to a single db row and would have a bunch of getters and setters. If it doesn't have any domain logic it's just a RowDataGateway. The two are very similar.

  22. #22
    SitePoint Member
    Join Date
    Mar 2005
    Posts
    18
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by McGruff
    OK so the "global" model class isn't global in the sense of being in the global scope but rather a base class with some methods common to derived classes?
    True

    That won't change how the objects are created. You'd still be doing $foo = new Foo(...) irrespective of whether Foo inherits from another class or not. I'm not really seeing the problem though. Can you give me some more specific examples?

    An ActiveRecord would correspond to a single db row and would have a bunch of getters and setters. If it doesn't have any domain logic it's just a RowDataGateway. The two are very similar.
    The only reason I'm hesitant is because inhereting means creating the ActiveRecord and Model classes on each instance, whereas passing it to the model would result in using a reference to the object (and thus, less memory overhead... theoretically). Perhaps the optimization will be limited by loading the Model class 5 times, but certainly the ActiveRecord class shouldn't have 5 instances, especially since the constructor creates the actual db connection.

    I do have a tendency to do a lot of memory optimization when I'm writing in interpreted languages, so it's quite possible I'm reading to much into the memory consumption.

    As an aside, ActiveRecord pertains to the data collection method used by Ruby on Rails, not the pattern (although, they are similar as you mentioned). However, for the sake of clarity, I really need to stop using it as a class name .

    Here's a cleaner example:

    PHP Code:
    <?php
    class Model
    {
        
    // superclass for all models
    }

    class 
    RowDataGateway extends Model
    {
        
    // database stuff
    }

    class 
    Example(n)Model extends RowDataGateway
    {
        
    // all sub-models
    }
    ?>
    Now assuming we use the code above as an example:
    PHP Code:
    <?php
    class DashboardController
    {
        public function 
    indexAction()
        {
            
    $model1 = new Example1Model();
            
    $model2 = new Example2Model();
            
    $model3 = new Example3Model();
            
    $model4 = new Example4Model();
            
    $model5 = new Example5Model();

            
    // load the view and put the models into the proper scope.
        
    }
    }
    ?>
    That's 5 active RowDataGateway classes and 5 active Model classes, when only one is neccesary. Versus this:

    PHP Code:
    <?php
    ...
        
    $model1 = new Example1Model($RowDataGateway$SuperModel);
    ...
    ?>
    I won't use a singleton (and a global registry seems to be a perverted singleton to me) so my options seem to be limited in this case.

  23. #23
    SitePoint Enthusiast
    Join Date
    Jul 2004
    Location
    Finland
    Posts
    73
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    IMO your Models are just basic ActiveRecord classes, so why cant you do something like this?

    PHP Code:
    <?php
    class DashboardController extends Controller
    {
        function 
    indexAction()
        {
            
    $dashb =  loadModel('Dashboard');
            
    $this->example1 $dashb->getFunkyStats();
            
    // etc
            
    loadView('dashboard.tpl');
        }
    }



    class 
    Dashboard{
        
        function 
    __construct(){
            
    $this->example1 = new Example2Model();
            
    $this->example2 = new Example2Model();
           
    // ...
           //Or pass factory and use it to create instances.

        
    }
        
        function 
    getFunkyStats(){
            
    $this->example1->getFunkyStats();
        }
    }
    ?>

  24. #24
    SitePoint Member
    Join Date
    May 2006
    Location
    Saratov, Russia
    Posts
    13
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by JustinH
    certainly the ActiveRecord class shouldn't have 5 instances, especially since the constructor creates the actual db connection.
    I'd suggest establishing the database connection in the controller:

    PHP Code:
    class DashboardController 

        private 
    $db;

        public 
    __construct()
        {
            
    $this->db = new Db;
        }

        public function 
    indexAction() 
        { 
            
    $model1 = new Example1Model$this->db ); 
            
    $model2 = new Example2Model$this->db ); 
            
    $model3 = new Example3Model$this->db ); 
            
    $model4 = new Example4Model$this->db ); 
            
    $model5 = new Example5Model$this->db ); 

            
    // load the view and put the models into the proper scope. 
        




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
  •