As of http://trac.agavi.org/changeset/1609, the output of the inner  
layer is available via the variable $inner in templates. Also  
available are $container for the current execution container and  
$view for the template's view. Like with factories, the names of  
these assigns ("inner", "container" and "view") can be configured  
using the "assigns" parameter of a renderer.

Since the wrapping template now does not have to know the inner  
layer's name anymore, the example can be simplified quite a bit.

Master.php:
<html>
        <head><title>logs!</title></head>
        <body>
<?php echo $inner; ?>
        </body>
</html>

LiveSuccess.php:
<tbody>
<?php foreach($t['messages'] as $message): ?>
        <tr>
                <td class="name"><?php echo $message->getNick()->getNick(); 
?></td>
                <td class="message"><?php echo $message->getMessage(); ?></td>
                <td class="time"><?php echo $message->getMessageDate(); ?></td>
        </tr>
<?php endforeach; ?>
</tbody>

LiveSuccess.wrapper.php:
<div id="roomTitle"><?php echo $t['topic']; ?></div>
<table border="0" cellspacing="0" cellpadding="0">
<?php echo $inner; ?>
</table>

And the code that makes it happen:
public function executeHtml(AgaviRequestDataHolder $rd)
{
        // remember: first, you should have a base view and call a method on
the parent to load the layout instead of doing it here.
        // second, loadLayout() would be enough since "standard" is the
default layout
        $this->loadLayout('standard');

        // now we insert the wrapper template between the decorator and the  
actual content layer
        $i = $this->appendLayer($this->createLayer
('AgaviFileTemplateLayer', 'wrapper'), $this->getLayer('content'));
        $i->setTemplate('LiveSuccess.wrapper');
}


Cheers,

David



Am 29.01.2007 um 14:02 schrieb David Zülke:

> Hi guys,
>
> I thought I'd give you a little example of what the new template
> layer system can do.
>
> We participated in the PHP Throwdown (http://www.phpthrowdown.com)
> and built an IRC bot using Agavi. It's called "Chuckwalla", contains
> a fully fledged IRC library that doesn't suck, has live logs, cool
> web interfaces etc and will be open sourced soon.
>
> One feature is the live logs. You click a channel, and it uses Ajax
> to refresh the contents, so you can follow the discussion in a
> channel. So for the first load, we need a full document, and for
> subsequent XMLHttpRequest, we just want the new messages.
>
> Here is the layout's configuration for output type "html":
> <layout name="standard">
>       <layers>
>               <layer name="content" class="AgaviFileTemplateLayer" />
>               <layer name="decorator" class="AgaviFileTemplateLayer">
>                       <parameter name="template">Master</parameter>
>               </layer>
>       </layers>
> </layout>
>
> Let's assume this is our decorator template:
> <html>
>    <head><title>logs!</title></head>
>    <body>
> <?php echo $slots['content']; ?>
>    </body>
> </html>
>
> And here is the content template (LiveSuccess.php):
> <div id="roomTitle"><?php echo $t['topic']; ?></div>
> <table border="0" cellspacing="0" cellpadding="0">
>       <tbody>
> <?php foreach($t['messages'] as $message): ?>
>               <tr>
>                       <td class="name"><?php echo 
> $message->getNick()->getNick(); ?></td>
>                       <td class="message"><?php echo $message->getMessage(); 
> ?></td>
>                       <td class="time"><?php echo $message->getMessageDate(); 
> ?></td>
>               </tr>
> <?php endforeach; ?>
>       </tbody>
> </table>
>
> Now we also have an output type for the AJAX calls, called "json".
> It's configured like this:
> <layout name="standard">
>       <layers>
>               <layer name="content" class="AgaviFileTemplateLayer" />
>       </layers>
> </layout>
>
> But now we have a problem: If an AJAX call is made, we get back the
> entire inner content template, complete with the <divs> and the
> <table>, but we only want the <tbody> to append to the current table
> (remember, tables can have multiple <tbody> elements).
>
> One approach would be to simply have two templates. The other
> approach would be to split them up and include() the inner portion in
> the non-ajax version, and set the templates differently per output  
> type.
>
> However, there also is a third approach. Right now, we have these
> layers:
> +------------------------+
> | <html> decorator       |
> | +--------------------+ |
> | | <table> content    | |
> | | with <tbody>, <tr> | |
> | +--------------------+ |
> | </html>                |
> +------------------------+
>
> What we can do now is split up the template like we would with the
> include() solution:
> +------------------------+
> | <html> decorator       |
> | +--------------------+ |
> | | <table> wrapper    | |
> | | +----------------+ | |
> | | | <tbody> inner  | | |
> | | | content        | | |
> | | +----------------+ | |
> | | </table>           | |
> | +--------------------+ |
> | </html>                |
> +------------------------+
>
>
> First, the "wrapper" template (LiveSuccess.wrapper.php):
> <div id="roomTitle"><?php echo $t['topic']; ?></div>
> <table border="0" cellspacing="0" cellpadding="0">
> <?php echo $slots['inner']; ?>
> </table>
>
> And the actual "inner" content template (LiveSuccess.php):
> <tbody>
> <?php foreach($t['messages'] as $message): ?>
>       <tr>
>               <td class="name"><?php echo $message->getNick()->getNick(); 
> ?></td>
>               <td class="message"><?php echo $message->getMessage(); ?></td>
>               <td class="time"><?php echo $message->getMessageDate(); ?></td>
>       </tr>
> <?php endforeach; ?>
> </tbody>
>
> For Ajax, everything is fine now. We'll get the <tbody> content. But
> for output type "html", the result will be this:
> <html>
>    <head><title>logs!</title></head>
>    <body>
> <tbody>
>       <tr>
>               <td class="name">John McClane</td>
>               <td class="message">Yippie-kay-yay motherfucker</td>
>               <td class="time">03:27</td>
>       </tr>
> </tbody>
>    </body>
> </html>
>
> What we need now is insert the wrapper IN BETWEEN "decorator" and
> "content" layer.
>
> Remember that the decorator template expects to output $slots
> ['content']. Hence, we actually have to modify the "content" layer to
> display the wrapper template (LiveSuccess.wrapper.php), and then
> prepend another "inner" layer (LiveSuccess.php) to the layer list. We
> do it like this, in the view:
>
> public function executeHtml(AgaviRequestDataHolder $rd)
> {
>       // remember: first, you should have a base view and call a method on
> the parent to load the layout instead of doing it here.
>       // second, loadLayout() would be enough since "standard" is the
> default layer
>       $this->loadLayout('standard');
>
>       // we get the "content" layer and change the template to the wrapper
>       $c = $this->getLayer('content');
>       $c->setTemplate('LiveSuccess.wrapper');
>
>       // and then prepend the actual "inner" content template to the list
>       $i = $this->prependLayer($this->createLayer
> ('AgaviFileTemplateLayer', 'inner'));
>       $i->setTemplate('LiveSuccess');
> }
>
> And that's it! Now we have the desired result:
> <html>
>    <head><title>logs!</title></head>
>    <body>
> <div id="roomTitle"><?php echo $t['topic']; ?></div>
> <table border="0" cellspacing="0" cellpadding="0">
> <tbody>
>       <tr>
>               <td class="name">John McClane</td>
>               <td class="message">Yippie-kay-yay motherfucker</td>
>               <td class="time">03:27</td>
>       </tr>
> </tbody>
> </table>
>    </body>
> </html>
>
>
> IMPORTANT: Some advice regarding base views. In the original email, I
> recommended that you always have a base view you extend from where
> execute() throws an exception. This view would also have base methods
> you can call that load layers, maybe set dynamic slots, or do stuff
> like:
> $this->setAttribute('_contentType', $this->container->getOutputType()-
>> getParameter('Content-Type'));
>
> However, there is a problem here. The whole point of having execute()
> throw an exception is that if there is a request using, say, ajax,
> that sets the output type to, say, "json" because of a routing rule
> like this:
> <route pattern="^XMLHttpRequest$" source="_SERVER
> [HTTP_X_REQUESTED_WITH]" stop="false" output_type="json" />
>
> And you then implement a base executeJson() in the base view... all
> of your views DO serve the json output type, and the normal execute()
> is never called, not even for actions/views that don't implement ajax
> features and should thus get an exception!
>
> To solve that problem, name these base methods differently. I suggest
> you call them "setupHtml", "setupJson" and so on, and then call them
> using $this->setupHtml($rd); etc inside your concrete view's
> executeHtml() method.
>
> Again, if there are any questions, let me know.
>
> Cheers,
>
> David
>
> _______________________________________________
> users mailing list
> [email protected]
> http://lists.agavi.org/mailman/listinfo/users
>


_______________________________________________
users mailing list
[email protected]
http://lists.agavi.org/mailman/listinfo/users

Reply via email to