Love Fuel?    Donate

FuelPHP Forums

Ask your question about FuelPHP in the appropriate forum, or help others by answering their questions.
A document model
  • Some time ago, I created some methods to manipulate and render various pieces of an html document. It's evolved into a pseudo document model. Originally written for CodeIgniter, I've refactored it for FuelPHP. It uses static containers so you can use it anywhere without intantiating it. You can grab a copy on git hub here - http://fuelphp.com/forums/topics/new_topic/10 Here an example of working with the page title:
    Document::set('title', 'My Site');
    
    // then somewhere else you can do this
    Document::append('title', 'My Site');
    
    //then in the view
    echo Document::render('title');
    
    // and get this
    My Site : Page 2
    
    // you can also prepend a value
    Document::prepend('title', 'Some Text');
    
    // set the title separator like this
    Document::set('separator', ' :: ');
    
    Here's an example of rendering the DTD:
    // in a controller or anywhere else
    Document::set('doctype', 'xhtml11');
    
    //then in the view
    echo Document::render('dtd');
    
    // or you can do this
    echo Document::render('dtd', 'xhtml11');
    
    // or this
    echo Document::render('doctype', 'xhtml11');
    
    How about rendering a complete html opening tag:
    Document::set('doctype', 'xhtml11');
    Document::set('language', 'en');
    Document::set('direction', 'ltr');
    
    echo Document::render('htmlopen');
    
    // to get this based on the doctype, language, and direction
    // (looks like the editor stripped the xmlns but it's there)
    <html xml:lang="en" dir="ltr">
    
    The following Asset methods are wrapped: css, js, and img (for consistency since the original class had something similar).
    // See the FuelPHP documentation for more information
    
    // to add a stylesheet
    Document::css('mycss.css');
    
    // to render stylesheets
    echo Document::css();
    
    You could use it as a global registry:
    // create a global variable
    Document::set('myVar', 'myValue');
    
    // access the global variable
    $myVar = Document::get('myVar');
    
    Create a container as an array and append/prepend values:
    // Create the container
    Document::set('myBucket', array());
    
    // add some values to the container
    Document::append('myBucket', 'myValue1');
    Document::append('myBucket', 'myValue2');
    
    // prepend a value to the container
    Document::prepend('myBucket', 'myValueA');
    
    For more information, see the inline documentation.
  • I'm a bit confused about which situation you would use this in? What is the problem you're trying to solve, wouldn't Views just suffice to render html? I'd love to check out your code which probably holds some answers, but your link links back to the form to make a new topic ;)
  • The document model is a static global container. It has the ability to store, retrieve, and render from anywhere. Like most of Fuel's classes, it doesn't have to be instantiated. You simply use it in place. It all started with the title. I wanted a consistent title layout - a base title, then any additional pieces I wanted to add separated by some form of separator. For example, My Site : Courses : Math. Then there is the DTD. Yes, I can use built-in methods for doing that. But, I have to remember which doctype I'm using for every view I create. Why not store a doctype somewhere and use that to render the DTD. That way, I have a consistent DTD throughout with minimal effort. And then there is the opening html tag. It changes based on doctype, language, direction, etc. Again, a manual process for each view. Same with the character set meta tag. It's dependant on the character set. Now, if I'm using a common layout, everything can be hard-coded into the base layout. Except for the title. But here's the thing. If I drop this in as a package, all I have to do is set doctype, language, character set, etc into a config file or just hard code those into the abstract class properties and I never have to worry about typos when I'm creating common tags that every page needs. With Fuel, I've extended ViewModel to setup my layouts and document properties in the before() method (just one approach of many):
    class ViewModel extends \Fuel\Core\ViewModel
    {
     function before()
     {
      $menu_items = array('menu_items' => Model_Sessions_Title::find('all',
        array(
               'where' => array(
        array('classorder', '>', 0),
        ),
               'order_by' => array('classorder' => 'asc'),
        )
       )
      );
      
      $this->header = View::forge('cms/partials/header');
      $this->main_nav = View::forge('cms/partials/main_nav');
      $this->sidebar_left = View::forge('cms/partials/sidebar_left', $menu_items);
      $this->sidebar_right = null;
      $this->footer = View::forge('cms/partials/footer');
      
      Document::set('doctype', 'xhtml11');
      Document::set('charset', 'UTF-8');
      Document::set('title', 'CAN | Training');
      Document::set('separator', ' | ');
      Document::set('language', 'en');
     }
    }
    
    As you an see, I'm setting up my layout and initializing document properties. Here's what one of my layout views looks like:
    <?php 
    Document::append('title', 'Courses');
    echo Document::render('doctype');
    echo Document::render('htmlopen')
    ?>
    <head>
     <?php echo Document::render('charset')?>
     <?php echo Document::render('title')?>
     <?php echo Document::css('template.css'); ?>
     <?php echo Document::js(array('main.js', 'jsval.js')); ?>
    </head>
    <body id="page_bg">
     <div class="wrapper">
      <?php echo $header ?>
      <?php echo $main_nav ?>
      ...
      <?php echo $sidebar_left ?>
      ...
      <?php echo $content ?>
      ...
      <?php echo $sidebar_right ?>
      ...
      <?php echo $footer ?>
     </div>
    </body>
    </html>
    
    Notice how the first line appends to the title. That can be done anywhere. In a controller, a view, a view model, or anywhere else. That's what started the idea of a document model. But like everything else, you create a feature and then feature creap sets in. Before you know it, you have a swiss army knife of capabilities. Is it the most efficient approach? In terms of system performance, no. But it's not even distinguishable unless you're counting microseconds. But, in terms of development, time, and money, it is defintately an efficient approach. I still have some work to do. I'm not satisfied with how the Asset library works. I'll be tweaking the model to allow prepending as well as appending to css and js containers. The original did that but I decided to simply wrap Asset methods. I may rethink that. And oh yea, since it's a static container model, it's globally accessable so you can pretty much use it anywhere for any type of storage you need. It doesn't have to store just document properties.
  • Thought I'd throw in some history. To show you the power of open source, a very popular CMS uses this concept to alter pieces of the head from anywhere. The concept I'm using predates that. It was some features we added to the parent project after it was forked. Those features eventually wound up in the fork.
  • I decided to add native containers for both css and javascript. We could have done this with existing methods but this gives us some consistency. Here's an example:
    // anywhere in your code:
    Document::set('css', 'template.css');
    Document::set('js', array('main.js', 'jsval.js'));
    
    // in a view or anywhere else:
    
    // add some css
    Document::append('css', 'more_css.css');
    
    // prepend some javascript
    Document::prepend('js', 'i_come_first.js');
    
    //render in the view
    echo Document::render('css');
    echo Document::render('js');
    
    // you can also pass something straight to the render method
    echo Document::render('css', 'mycss.css');
    
    You can accomplish the same thing without using the render method:
    echo Document::css(Document::get('css));
    
    Why did I go to the trouble? Maybe I missed something. But I spent a few hours trying to get this functionality out of the native Asset class. Just couldn't make it do what I wanted it to do.
  • This functionality is exactly what the Asset class is for, and it works fine. I don't see a reason for duplicating it. Perhaps you can explain what you wanted to do, and didn't work? I have a feeling that you've been caught by the lazy rendering mechanism of views, which means that you can't have code in a partial that will update the header or the page template, because they will be rendered first. If you have a template/partial system, you have to make sure the partials are rendered first.
  • I still struggle to understand in which situation you'd want to use this. In what kind of project would stuff like DTD and charsets ever be an issue to define them like this. Even if you use multiple templates, I'm pretty sure that using Views, this is entirely manageable. Like Harro I'm very curious on what you wanted to do, didn't work, and how you came up with this as an answer. What situation, what kind of app? Eager to learn :)
  • It has nothing to do with "getting caught up" in something. Or not understanding something. Or something that didn't work. It's just a piece of my standard arsenal. I like it. It makes my life easier from project to project. I just refactored it for fuel. And it's not even a complete refactor. I probably should have left it alone. It would have saved me a lot of headaches. As far as the examples go, view model, view, partial, template, controller, bootstrap, it all makes no difference to me. It just works (except when I hose something during the refactor). I threw together some stuff for examples. That's all. I've been using fuel for exactly one week. I've spent hours digging through docs and code just to perform a few simple bloody tasks. I shouldn't have to do that. So, when I lose my patience, I plug in something that I know works. When I don't find it in the docs, and I step through code only to discover a dead-end, I tend to use alternative methods. Look, I'm old school. I cut my teeth on assembler, basic, and COBOL. I don't do this stuff for a living anymore. I do it for fun. It keeps my mind sharp and keeps me out of the bars. I'd rather spend all night munching code than laying in bed wondering how quick I can get the chick next to me back home. I suppose fuel is an ok framework. It has some hitches I don't care for but you find that with every framework. I throttled/crashed my dev server at least a dozen times today just messing around with orm. Of all the frameworks I've played with, that has never happened before. I do like hmvc though. That's something I'm just starting to explore. Having never used it before, I've got a lot of plans for it already. But who knows what I'll do next. You never know, I might get a phone call and wind up half way across the country. I should shutup or I'll never stop typing. I'm off to see if I can crash orm again.
  • Too bad the ORM keeps crashing on you. I never had such an experience, especially with FuelPHP, but I'm sure it's quite a pain when it does. Nobody is trying to say that you "don't get it", personally I'm trying to learn why you chose the way you chose. Because I believe that for every different situation / context there is a different solution that'll fit best. I'm just eager to learn what this exact situation was, especially here in the forums as other people might have the same question and might benefit from your awesome ideas as well. At the moment the problem your trying to solve is just a bit vague to me and I think the context of the project you're using it in might shed some light on it and your entire approach. So I'm still keen on getting to know all of this, and I'm sure others are too. For problems with the ORM: there is a specific ORM forum with a bunch of people who'd be glad to help you out.
  • Think in terms of a packaging system. Or a content delivery system. The base model contains metadata and base methods to retrieve the metadata. The child model contains the rendering mechanisms that fire based on the metadata requested. I know it sounds like overkill but there's a method behind the madness. Then end goal is not to chunk pieces of data into html, json, or xml. Don't think of a document as a finished piece. Think of it, like we do in oop, as an object. Not to extend for final assembly, but to serve a request. The request could be anything. It could very well be finished output sent to a browser. That's the response side of the model. It could also be the request itself. How would that work? It gets assembled as metadata that a responder would understand and sent as a request. For example, "I'm sending some data. Send me back the cache of your xyz model assembled for relay to the client. I want it in xhtml, a particular language, and right to left. Or I want it raw. Just send the data. Or, I want it in xml. Or Json. The base model is used to format the request. As opposed to assembling requested data. Here's another example. Suppose you have an advertising site. On that site you collect info from you clients. You plug that info into your system with taxonomy that not only describes what's on your pages but makes it searchable by your visitors. That's great! But suppose you, for a fee, offer that data to other sites. They make a request with some taxonomy terms. You chunk the data into your model. You send a single copy of your standard template and the data. The requester can either assemble it as is or use their own template. The data package is smaller because it only contains raw data and a single template instead of html for every instance of an ad. We're leaning here towards a comprehensive scalable content delivery system. By scalable, I mean the ability to offload work. For example, server A might be a medium sized server taking requests from a client and then offloading some of the work to another server. Or multiple servers. Server B might be an intermediate solution that has lots of horsepower and a huge cache to assemble complex pieces of information for rendering. Server C might have an even bigger cache for serving data. And so on and so on. The document model is the glue holding the entire system together. It's not necessarily needed in a pure client/server environment. But, when you start scaling, it becomes the backbone. I use it because I'm lazy. If someone else can get some use out of it that's fine too. No, it goes way beyond why do I need it to render pages with fuel. If I haven't mentioned it already, I saw a comment something to the effect, "we don't NEED another CMS." I couldn't resist and responded with a tweet. That's right. What we NEED is an open source solution for something much bigger. A content deliver system. A CDS. Not a CMS. A completely different animal.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

In this Discussion