Sharing templates between PHP and JavaScript

At work, we had a need to share templates between PHP and JavaScript. We had 2 paths to search results data. The first is from doing a dynamic search using AJAX and the results would be displayed by JavaScript. The second path was a way of browsing through the website and showing search results based on the browse selections. This path was server based and would be generated by PHP.

The search results would be displayed in cards, so we wanted to share the card template between both server side and browser. We saved a copy of the card template in the /public/views directory. This is a sample of what the card template looked like:

<div class="card">
  <div class="card_searchCard">
    <h1 class="card_name clearfix"><a href="{{ url }}">{{ display_name }}</a></h1>
    <div class="card_specialty">
      <span>{{ specialty }}</span>
    </div>
  </div>
  <div class="card__body clearfix">
    <div class="card__address row">
      {{ address1 }}
      {{ address2 }}<br>
      {{ city }}, {{ state }} {{ zip }}
    </div>
  </div>
</div>

To use this template in PHP, we settled on using Mustache PHP. A rough example of how we used it to render our results is:

$mustache = new Mustache_Engine(array(
    'loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__) . '/../../public/views'),
));

$cards = array();
$template   = 'professionalCard';
foreach ($results as $result) {
    $cards[] = $mustache->render($template, $result);
}

This basically takes each result and renders it through the Mustache PHP templating engine. We saved the output of each result as a card and then would use those cards later.

It was trickier to use this from JavaScript, because we had to load the template from the web server to have it available for use. We decided to use MustacheJS for doing the JavaScript rendering. Normally, I would include the template with a script tag. However, if I included the code needed to make the template work in a script tag, the same file would not work with the PHP rendering. So the template files only contained the template with no extra code.

Here are the functions we setup for retrieving the template and rendering it with data:

function render(tmpl_name, tmpl_data) {
  var template;
  template = getTemplate(tmpl_name);
  return Mustache.render(template, tmpl_data);
}
function getTemplate(tmpl_name) {
  if ( !getTemplate.tmpl_cache ) {
    getTemplate.tmpl_cache = {};
  }

  if ( ! getTemplate.tmpl_cache[tmpl_name] ) {
    var tmpl_dir = '/views';
    var tmpl_url = tmpl_dir + '/' + tmpl_name + '.mustache';
    var tmpl_string;
    $.ajax({
        url: tmpl_url,
        method: 'GET',
        async: true,
        success: function(data) {
          tmpl_string = data;
          getTemplate.tmpl_cache[tmpl_name] = _.template(tmpl_string);
        }
    });
  }
  return getTemplate.tmpl_cache[tmpl_name];
}

Here is how we used those functions:

for (var i = 0; i < cardHits.length; ++i) {
  var hit = cardHits[i];
  var rendered_html = render(templ_name, hit);

  // render the hit
  res += rendered_html;
}
$('#hits').html(res);

We chose to make sure the templates were preloaded before they were needed, so we added this document ready function:

<script>
  $( document ).ready(function() {
    getTemplate('professionalCard');
    getTemplate('facilityCard');
  });
</script>

When we didn't preload the templates, we would get errors unless we made the AJAX call to load the template a synchronous call. I didn't like having a blocking function, so opted to load the templates at document ready.

If you have any questions or suggestions on alternate solutions, please leave a comment.

OpenCart multi-store problems

I have been continuing to evaluate OpenCart. One of the reasons OpenCart was selected was for its multi-store capability. I would say they have not finished implementing the multi-store capability yet. If you only use what is built into OpenCart and do not directly update the MySQL tables, the multi-store setup is incomplete. You can define multiple stores and define what inventory is available in each store, but that is the extent of what you can do. You cannot define a separate payment processor for each store or define different shipping methods for each store. If you review the MySQL table for the settings, they do support defining things separately for each store, but the front-end has not been defined for it. There is an article in the forums which explains the problem. There is an independently developed plug-in developed called Multi 201 which does provide this capability that is available for $50 AUD. If you are going to be using multiple stores, this plug-in is a necessity.

Edit – 1/25/2015: Found another problem with the multi-store setup in OpenCart. If a customer registers for one store, they just registered in all stores. I think this is a mistake in design. I don’t know of a work-around for this yet.

OpenCart shopping cart software

I have been doing an evaluation of the OpenCart open source shopping cart software. It is implemented using PHP and MySQL. It is still early, but it appears to be a capable shopping cart. I have worked with Magento in the past and OpenCart compares favorably. OpenCart’s file and table layouts seem reasonable. I got lost when I was looking for items in Magento. The biggest thing that appears to be missing from OpenCart is an API for external interaction. With Magento, I was able to use an API for adding new inventory or retrieving new orders. I haven’t found an exposed API for OpenCart yet. However, the table layouts are straightforward and I was able to write an order export script using MySQL commands and accessing the database directly.

Like I said, it is still early in the evaluation process, but I would recommend adding it to your list of packages to investigate.

PHP JSON POST data quirk

I encountered a strange PHP post data quirk. I was posting data to a PHP script using a jQuery AJAX call. I was posting all the data as a JSON string. When the PHP script was invoked the $_POST variable was totally empty. I also check $_REQUEST and $_GET and there was no data. I checked the headers and could see the data being passed, but it was not showing up in the standard PHP pre-defined variables. I did some googling and finally came up with this command:

$incoming = json_decode(file_get_contents("php://input"));

That ended up as an object with all of the data passed.