views:

93

answers:

2

Most PHP MVC systems follow a pattern where a request is routed to a specific controller action, and then the controller sets a bunch of variables for use in the view.

When you're in an agency/services work environment that uses a lot of dynamic HTML for UI elements, this patterns leads to a lot of javascript being generated with view variables

<script type="text/javascript">
    jQuery(document).ready(function(){
        $('#ui-element).init(
            {
                'param1':<?=$this->param1;?>,
                'param2':<?=$this->param2;?>,                   
            }
        );
    });
</script>

While this works, I've found it leads to views with a horrible spaghetti mix of HTML, PHP and Javascript. It also offends a certain class of front-end developer who thinks all javascript should be included in external files.

So, what are your patterns/practices to deal with this problem? Specifically, when you want to provide a default set of data for a Javascript widget in a PHP MVC framework, how do you do it while keeping things clean and modular? Is it just a matter of discipline, or are there specific design patterns that can force modularity** here, while still giving talented client-side developers a markup centric environment to work in.

+2  A: 

EDIT: Well you dont have to get that elaborate either you could simple have a php class/function that just proxies to ob_start/ob_get_clean and then stores the js somewhere then outputs the js elsewhere. you dont necessarily have to support or integrate the functionality of the library through php...

if could be somthing as simple as something like the following:

class UnobtrusiveJsHelper {
  protected static $_instance;
  protected $_js = array();
  protected $_ready = array();

  public static function getInstance()
  {
  }

  public static function setInstance(UnobtrusiveJsHelper $instance)
  {
  }

  public function captureStart($key = null)
  {
    if(null !== $key)
    {
      $this->_js[$key] = null;
    }
    ob_start();
  }

  public function captureEnd($key = null)
  {
    if(null !== $key)
    {
      $this->_js[$key] = ob_get_clean();
      return;
    }
    $this->_js[] = ob_get_clean();

   public function __toString()
   {
      return $this->dumpJs() . $this->_dumpReady();
   }

   public function dumpJs(array $attributes = null)
   {
      if(!empty($this->_js))
      {
         return "<script type=\"text/javascript\">". implode("\n", $this->_js) . "</script>";
      }

      return null;
   }

   public function dumpReady(array $attributes = null)
   {
      if(!empty($this->_js))
      {
         return '<script type="text/javascript">$(document).ready(function(){'. implode("\n", $this->_js) . '});</script>';
      }

      return null;
   }

}

and then in your controller: $js = UnobtrusiveJsHelper::getInstance();

and in your view:

<?php $js->captureStart(); ?>
  var myjsvariable = 0;
<?php $js->captureEnd();

and in your layout (assumign two-step view here): <?php echo isset($js) ? $js : null ?>


This is what you use a helper for. For example in Zend_Framework all these little onLoad/Ready snippets are added to a stack. Then they are all ouput at once in a single place in the head.

I have a special helper i use for jQ that does something similar under Symfony as well.

These allow to things like $jq->setVar('myjsvar', 1); then when i dump this i get something like:

var myjsvar = 1; in a script tag in the head.

Take a look at ZendX_Jquery and Zend_Dojo and their respective view helper classes for a good example of the functionality.

prodigitalson
I like this approach personally and if everyone's on board it's great, but I've found that it ends up hindering talented client side developers because 1. They have these non-local blobs in their views 2. There's an up-front cost of writing the helper that comes when you want to incorporate some new jQuery/Dojo/Third party element. Generating HTML/Javascript with PHP (which is what helpers do) seems to require more development time than a pure view-based approach. Still, useful information, so thank you!
Alan Storm
A: 

A lot of this discussion is about best practices. Honestly, if < 100 lines, IMHO, what you're doing is fine. A different approach would just to be to call a function from the view and have the js as seperate file, like this (with appropriate defaults):

<script src='/js/widgets.js'></script>

<script type="text/javascript">
    jQuery(document).ready(function(){
         showSomeWidget(<?=$this->name ?> , <?=$this->place ?> );
    }
</script>

You could also use some jQuery templating plugin like Dojo but often I find more trouble than its worth.

timpone