SitePoint Sponsor

User Tag List

Results 1 to 13 of 13
  1. #1
    SitePoint Enthusiast
    Join Date
    Mar 2005
    Location
    Sweden
    Posts
    31
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Composite patterns and attach()

    Hi, I got some help a while ago and now I'm, well on my way into MVC. I have one problem that I can't let go of and I need to clear it out before moving on. For about a week I have searched for an answer to this and I get close to answers but never really get there.

    To add components to the final page I tell a Handler to perform attach(new View("template",$class)), $class being optional.

    My problem is more on the pratical level. This is how the code looks inside an example Handler:

    MusicHandler.php
    PHP Code:
        $page = new PageBuilder();

        
    $page->attach( new View("header.tpl.phtml") );
        
    $page->attach( new View("login.tpl.phtml") );
        
    $page->attach( new View("menu.tpl.phtml") );
        
    //main content start
        
    $page->attach( new View("musicSearch.tpl.phtml") );
        
    $page->attach( new View("music.tpl.phtml",$music) );
        
    //main content end
        
    $page->attach( new View("footer.tpl.phtml") );
            
        
    $page->render(); 
    If you think of each row as blocks that build the website, each block with a <div> around it. Whats a good way of getting a <div> around x number of components/blocks in the main area? I could just add a line (and template file) with one row saying "<div id='content'>" before main content (first comment) and then another row saying "</div>" after the main content (second comment). But this doesnt feel good and if I need more "multiblocks" I'm going to have to create a lot of View-objects just to make a simple page.

    How do you do it in practice? I've seen a lot of theoretical examples displaying simple pages but never an "advanced" aplication of the theory.

    The simplest answer could be if you guys just wanted to post the few rows that constructs your pages (not a simple example site) in one of your handlers, as I just did. Of course only if u use some kind of composite pattern.

    I hope I make some sense, please tell me otherwise... Thanks in advance.

  2. #2
    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)
    You could make a hierarchy of components, rather than just a flat stack. Eg. :
    PHP Code:
    $page = new PageBuilder(); 

    $page->attach( new View("header.tpl.phtml") ); 
    $page->attach( new View("login.tpl.phtml") ); 
    $page->attach( new View("menu.tpl.phtml") ); 
    //main content start 
    $content = new CompositeView("content.tpl.phtml");
    $content->attach( new View("musicSearch.tpl.phtml") ); 
    $content->attach( new View("music.tpl.phtml",$music) ); 
    $page->attach($content); 
    //main content end 
    $page->attach( new View("footer.tpl.phtml") ); 
             
    $page->render(); 

  3. #3
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    > How do you do it in practice? I've seen a lot of theoretical examples displaying simple pages but
    > never an "advanced" aplication of the theory.

    Just as Kyber has suggested, use the Composite View to generate a Parent - the container in your post - and then attach the multiple Child(ren) to the Parent; I see the point of using the flat stack approach to simplify things for you, since a View and a Composite View could share the same Interface, but personally I'd just go for the Composite View full stop.

    One point that I'll add is that where you have a number of components that are shared, and that these components are configurable, what I do is to just execute each and every component in turn, and preserve the output generated for each - this way, you are not overly building the Composite structure, that'd you'd build for the actual page

    With the generated output from these components, you put it to the Response object; Whilst the Response is recuring over the Composite structure, visiting each Controller [MVC], the output returned from the Controller in question, you parse the output, to see if there is a tag in the output, that matches any of those that the tag would represent from the components;

    If there is a match, just replace the tag with the output for that given component. You could attach each component to the required Composite it's self, but I find that this approach is better, but your milage may vary?

  4. #4
    SitePoint Enthusiast
    Join Date
    Mar 2005
    Location
    Sweden
    Posts
    31
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Okey, Ill try the parent-child stuff. But still, is there no problem (mostly performance) in making 20+ view classes for each page? And do you guys really make a new object just for one row of code (for example "<div id='content'>")?

    I need to look into the code more to say anything about what Dr L said. I recognize the idea from other threads though.

    Thanks for your answers! I guess I'm off to rewrite my PageHandler class so that it can manage children in the render-method.

  5. #5
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    > And do you guys really make a new object just for one row of code

    Well I do at least

    The typical page for a web based application can have as many as 8 separate Composites in my case, which doesn't include the menu, which is build separately it's self, from a different Composite structure, so I wouldn't get too hung up on the number of objects you create...

    Once you've build one structure, you can cache it for later which helps as well.

  6. #6
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    > so that it can manage children in the render-method.

    Here, let me help you out

    PHP Code:
    // ... response class
    public function renderIComposite $composite ) {
                
    ob_start();
                
    $this -> traverse$composite );
                echo( 
    ob_get_clean() );
            }
            
            private function 
    traverseIComposite $composite ) { 
                
    $nodes = array();
                
    $children $composite -> getChildren();
                foreach( 
    $children as $child ) { 
                    
    ob_start();
                    
    $this -> traverse$child );
                    
    $nodes[$child -> getId()] = ob_get_clean();
                }
                
    $composite -> execute$this -> context, new Parameters$nodes ) );
            } 
    First of all, this particular renderer will render for PHP in your template, such as

    Code:
    <html>
    <head>
    <?php echo( @$items['header'] ); ?></head><body>
    <p><?php echo( @$items['body'] ); ?></p>
    ...
    </body></html>
    Code:
    <!-- header.tpl -->
    <title>Home Page / Welcome</title>
    <link rel="stylesheet" ... />
    Code:
    <!-- body.tpl -->
    Welcome to our home page, you are certainly most welcome... blah... blah... blurp?
    So, there are 3 Composites in this example, Page, Header and Body, which clearly related to 3 Controllers in other words. Each Controller has it's own ID, such as Page - Parent - or it's two children, Header and Body.

    Anyways, whilst you recurse over the Composite structure, you need to cache and pass on each Composites output generated via the Controller in question; This is why there are two arguements in the ActionHandler::execute( ..., ... ); class method - the second arguement is the cached output of each Composite that was more recently, recursed over.

    When you reach a Child Composites Parent Composite, the output that was cached during the previous recursion, which resides in an array (typically, but in my example above, it's not an array, but typeof IDataspace, but eventually, it's exported to an array anyways), so the output is ECHOed from within that Parent Composites own template... Which may it's self be cached and the recursion begins again until you actually (eventually?) reach the root - Page - Composite.

    If that makes sense? I have some difficulty explaining things you understand

  7. #7
    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 AbraKadabra
    But still, is there no problem (mostly performance) in making 20+ view classes for each page?
    Not at all. You can make hundreds of objects per request without noticable performance-problem. It's sort of an urban legend (especially amongst people new with OO), that objects is a tradeoff in performance, so it should be kept at a minimum. As a generel rule you should never care about this type of optimization. If it improves your design, by all means make another object - Heck, make a dozen of them while you're at it.
    Quote Originally Posted by AbraKadabra
    And do you guys really make a new object just for one row of code (for example "<div id='content'>")?
    Of course you have to strike a certain level of granularity. You could in theory make an object per node in the DOM, but this would be overkill for most applications. But to your questin - yes I do. I wouldn't nescesarily write a new class for it though, but rather reuse a general type (such as your "View")

  8. #8
    SitePoint Enthusiast
    Join Date
    Mar 2005
    Location
    Sweden
    Posts
    31
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Cool, thx a lot. I actually started on my own and wrote something a bit similar. I changed my method name to "traverse" after I read ur reply, a new word for me.

    I did it without ob and I don't really get how u change the "$items[]" into it's respctive template. I'll look into it more since that method could let me get rid of my current "mainStart.tpl.phtml" and "mainEnd.tpl.phtml" and just set a "main.tpl.phtml" when I create the PageBuilder object. Just like kyber said.

    If anyone wants to, please take a look at my start and say if it's a bad start. It works anyway!

    PHP Code:
    function render() {
        if(isset(
    $this->redirect))
            
    header('Location: ' $this->redirect);
        else {
            
    $this->traverse($this->components);
        }
    }

    function 
    traverse($components) {
        foreach(
    $components as $child) {
            
            if(
    is_a($child'PageBuilder'))
                
    $this->traverse($child->components);
                    
            elseif(
    is_a($child'View'))
                
    $child->render();
                    
        }

    I know I could change the "if(is_a($child, 'PageBuilder'))" and put it last as just a simple "else", but this i for clarity atm.

  9. #9
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Does the script you posted above, traverse over each, individual Composite? I get the idea that it must do, since you're only traversing over a flat stack. If the example I posted earlier is giving you some trouble, then another example, such as a Controller would help?

    PHP Code:
    // ... a controller
    public function executeContext $contextIDataspace $dataspace /* array $nodes from the response */ ) {
    // ... 
    $view = new PageView'pathname/to/template/file' );
    $view -> output$dataspace -> export() );
    }
    // ... 
    And then, in the View, the exported dataspace, which was the array $nodes from the Response originally, looks like this,

    PHP Code:
    // ... a view
    public function output$items ) {
    include_once( 
    $this -> template );
    }
    // ... 
    Thus, the representation of $items['...'] in the template(s) above, is taken from the array passed to the View via an export of a dataspace. When you use PHP in the template directly, you need to cache the output generated for each Composite, as otherwise there isn't any other way you could put the cached output, to that given Composites, Parent Composites own template - from within the recursion of the Composite structure anyways

    It took some head scratching whilst I was doing this renderer btw...

    On the other hand though, if you are prepared to remove PHP from the templates, and have tokens to represent your generated output for each Composite, things get slightly easier, and simpler for you, since the Response it's self can retain the generated output instead, so you have this instead

    Code:
    <html>
    <head>
    <tag:header /></head><body>
    <p><tag:body /></p>
    ...
    </body></html>
    Code:
    <!-- header.tpl -->
    <title>Home Page / Welcome</title>
    <link rel="stylesheet" ... />
    Code:
    <!-- body.tpl -->
    Welcome to our home page, you are certainly most welcome... blah... blah... blurp?
    Here is the alternate renderer for you,

    PHP Code:
    // ...
    private function traverseIComposite $composite ) {
                
    $this -> template[$composite -> getId()] = $composite -> execute$this -> context  );
                
    $children $composite -> getChildren();
                
                foreach( 
    $children as $child ) {
                    
    $this -> traverse$child );
                    
    $this -> template[$composite -> getId()] = str_replace
                        
    '<tag:'.$child -> getId().' />'
                        
    $this -> template[$child -> getId()], 
                        
    $this -> template[$composite -> getId()] );
                }
            }
            
            public function 
    renderIComposite $composite ) {
                
    $this -> traverse$composite );
                echo( 
    $this -> template['page'] );
            } 
    Personally I've found that it makes little difference which approach you use, either be it PHP or Tokenised, but at the moment for what I'm doing, I use PHP in the templates, but if for whatever reason you have not got that option, you use the Tokenised approach as another option.

    Hope that helps anyways

  10. #10
    SitePoint Enthusiast
    Join Date
    Mar 2005
    Location
    Sweden
    Posts
    31
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'm sorry I have not answered for a while. I have had a lot to do at university.

    The code I posted (at least what I wrote) is "traversing" if I understand the word correctly. It can handle several "levels" of classes.

    Your use of $items is quite similar to what I use. The difference is that I send an object (model class) to the view and then the view calls getX() methods. Is that unwise?

    I'm sure your solution with str_replace works good, but Id rather not mixing in another "language" in my html files. I do still have some problems with "parent templates" sent to the PageBuilder class on initiation though. But that should be solveable and even if it isn't I'd rather live with having to attach a "mainStart" and "mainEnd".

    A big (friendly) hug for all the help. I'm starting to work some things out now.

  11. #11
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    > The difference is that I send an object (model class) to the view and then the view calls getX()
    > methods. Is that unwise?

    Maybe not... I had that idea myself, but I changed my mind since in my case $items are basically previously generated markup, but if you are going to pass through the Model, I would like to think that you could instead, pass through a Fascade as there may be more than one Model(s)?

    Facade

  12. #12
    SitePoint Enthusiast
    Join Date
    Mar 2005
    Location
    Sweden
    Posts
    31
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Is there ever an ending of the unknown..?

    I understand the issue with several models and even though I have not encountered it yet, I'm sure I will. Thank you for the input once again.

    Ill try to look at it tonight, no more studying this friday night anyway.

  13. #13
    SitePoint Addict
    Join Date
    May 2003
    Location
    The Netherlands
    Posts
    391
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Off Topic:

    Quote Originally Posted by AbraKadabra
    Is there ever an ending of the unknown..?
    No, there isn't. The more you dig, the more unknown left.
    There’s more than one way to skin a cat.


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
  •