The Joomla! Community Magazine™

Joomla! 3.0 Extension Development Series: More Functionality

Written by | Monday, 01 April 2013 00:00 | Published in 2013 April
The Joomla 3.x development series has been progressing steadily for several months now. At this point you should have a fairly good grasp of the basics of extension development and hopefully you've begun writing code of your own. This article will continue to expand your knowledge of code and the use of Bootstrap in your development. Don't forget to review the past articles in this series.

Step 0: Make Coffee

At this point we have spent several articles together as we have built this component. I am confident at this point you are aware of the first step. Before beginning on writing code or structuring the next bit of the process it’s important to continue the habits we’ve started from the beginning. Find your favorite cup and fill it with the liquid calmness of your choice. If this is new to you, I’d recommend reviewing the previous articles, start at the beginning and work your way through the series so far. I’ll wait here for you.

Ok! welcome back. In this article we’ll take a look at some of the other features involved in Lendr. You will notice I’ve added more code to many of the files that were empty before. Rather than spending time reviewing each of them I will focus only on those areas where new functionality or concepts are introduced. You should be able to easily understand the code based on the previous articles. Now we will continue in our series by writing the various modal windows necessary for the Lendr component.

Step 1: Create the modal windows

In this article we take a more of a relaxed approach and work on some additional features and detail work. First you will notice that we’ve added more modal windows. We’ve done all our modal windows the same way (utilizing the Bootstrap technique). In order to give a bit more detail I’ll explain the two options and then detail the option we’ve chosen. First you can load a view and layout via AJAX and using the typical Joomla method which although using a modal window loads an entire view file when a button or link is clicked. This has been historically the standard method for Joomla to load modal windows (think about them a bit like iframes). The second method is to load the modal window details onto the page when the page is initially loaded however the modal window (in essence, a div containing the view) is hidden by default on the page load and is not visible until activated by a link or button click.

I’ve chosen this second method for our modals within Lendr for a couple of reasons. By adding the modal div into the page on load but keeping it hidden this puts all the page load speed into the initial page load. While this may make you think the page will load slower initially this is a quite miniscule addition. On the other hand, by already loading the HTML into the page when the button or link is clicked the modal appears instantly (since it has already been loaded). This tends to make the page feel as though it has loaded incredibly fast. While it may be a personal preference, the quickness is one that I notice and therefore prefer. A second reason for choosing this modal loading method resides in the fact that there are only a few data fields being added to the modal. Because the requested data is minimal it is not a very difficult task to assign those variables to the modal on the fly instead of requesting an entire page via AJAX. Again, this is a bit of a personal preference in regards to speed.

Let’s look at the lines of code necessary to include a modal into our page. First, we’ll look at the html.php file for the “container” view. This might also be considered the parent view for the modal window we plan to load.

html.php
_lend.php
book.php

I will continue to use this code repeatedly to load modal windows throughout Lendr. You’ll notice there are two different calls I will make to load modals. One is a direct loading of a modal window (this is when no additional data is needed in the modal) and the second uses a javascript call to load the modal window (I’ll use this second method when I want to add a variable to the modal before displaying it).

You can find the rest of the modal files (similar in structure) in our Github repository.

Step 2: Writing the lending and returning functionality

Now that we have our modal windows loading and displaying additional information we want to begin adding functionality to Lendr through the use of these modals. First we’ll look at the actual core process of lending and returning. In case you’re wondering, we’re not building any advanced tracking or monitoring system into Lendr (this may be something I’ll look at in greater detail later if it’s requested). In Lendr we’ll simply let you lend a book and mark it as returned when you receive it back. There are a couple of files related to the lending and returning process. We’ll look at each in more detail below:

Controller

First we shall review the lend.php controller file which serves as the main controller for both lending and returning.

joomla_root/components/com_lendr/controllers/lend.php

<div class="modal-body">
  <div class="row-fluid">
    <form id="reviewForm">
      <input class="span12" type="text" name="title" placeholder="" />
      <textarea class="span12" placeholder="" name="review" rows="10"></textarea>
      <input type="hidden" name="user_id" value="" />
      <input type="hidden" name="view" value="review" /> 
      <input type="hidden" name="book_id" value="" />
      <input type="hidden" name="model" value="review" />
      <input type="hidden" name="item" value="review" />
      <input type="hidden" name="table" value="review" />
    </form>
  </div>
</div>
<div class="modal-footer"> </div>

Model

In the controller we’re again performing only a single task (execute) and in this task we pass the data and the request directly to the Book model which will handle the lend function call. Below is the lend function located within the book model.

joomla_root/components/com_lendr/models/book.php

public function lend($data = null)
  {
    $data = isset($data) ? $data : JRequest::get('post');
 
    if (isset($data['lend']) && $data['lend']==1)
    {
      $date = date("Y-m-d H:i:s");
 
      $data['lent'] = 1;
      $data['lent_date'] = $date;
      $data['lent_uid'] = $data['borrower_id'];
 
      $waitlistData = array('waitlist_id'=> $data['waitlist_id'], 'fulfilled' => 1, 'fulfilled_time' => $date, 'table' => 'Waitlist');
      $waitlistModel = new LendrModelsWaitlist();
      $waitlistModel->store($waitlistData);
    } else {
      $data['lent'] = 0;
      $data['lent_date'] = NULL;
      $data['lent_uid'] = NULL;
    }
    
    $row = parent::store($data);    
    
    return $row;
 
  }

In this function you’ll notice we handle both the lending and returning. Notice that when a book is lent successfully we also mark the accompanying waitlist item as fulfilled.

Javascript

This is the associated javascript functions used with lending and returning. As I mentioned earlier I am creating a function to first load the modal (so I can inject variables into the modal window). The second function is used to actually lend the book. I am using a jQuery AJAX call to pass the form data to the lend controller (listed above). If the controller/model executes successfully then the modal window is closed.

joomla_root/components/com_lendr/assets/js/lendr.js

function loadLendModal(book_id, borrower_id, borrower, waitlist_id)
{
  jQuery("#lendBookModal").modal('show');
  jQuery('#borrower_name').html(borrower);
  jQuery("#book_id").val(book_id);
  jQuery("#borrower_id").val(borrower_id);
  jQuery("#waitlist_id").val(waitlist_id);
}
 
function lendBook()
{
  var lendForm = {};
  jQuery("#lendForm :input").each(function(idx,ele){
    lendForm[jQuery(ele).attr('name')] = jQuery(ele).val();
  });
  
  jQuery.ajax({
    url:'index.php?option=com_lendr&controller=lend&format=raw&tmpl=component',
    type:'POST',
    data:lendForm,
    dataType:'JSON',
    success:function(data)
    {
      if ( data.success )
      {
        jQuery("#lendBookModal").modal('hide');
      }
    }
  });
}

There are several different places where you may notice the lack of error messages. We’ll be writing all of these at once in our clean up article still to come.

Step 3: Adding wishlists, waitlists, and reviews

There are three areas related to books that we are going to work on. The wishlist and waitlist functionality is pretty simple. Both of these will simply load a modal and add the specific book to either the wishlist or waitlist of the user. Again, this code is similar in most aspects to the other modal code and lender codes listed above. Because the review code has a bit more custom work than the other two we’ll focus on the code involved with the review process.

Note: You can review both the wishlist and waitlist code in the GitHub repository.

Originally the intention was to utilize the review controller for the new reviews, however upon further thought I decided that technically a new review should follow the same “add” controller as other parts of the system. This involved a bit of a rewrite on the add controller to properly route the data to the correct model.

A review is created from a modal window. The modal loads a form which holds the title and summary of the review. Hidden fields track the book and user submitting the review. The form for a new review is below.

joomla_root/components/com_lendr/views/review/tmpl/_add.php

<div id="newReviewModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="newReviewModal" aria-hidden="true">
  <div class="modal-header">
    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
    <h3 id="myModalLabel"<>?php echo JText::_('COM_LENDR_ADD_REVIEW'); ?></h3>
  </div>
  <div class="modal-body">
  <div class="row-fluid">
    <form id="reviewForm">
      <input class="span12" type="text" name="title" placeholder="<?php echo JText::_('COM_LENDR_TITLE'); ?>" />
      <textarea class="span12" placeholder="<?php echo JText::_('COM_LENDR_SUMMARY'); ?>" name="review" rows="10"</textarea>
      <input type="hidden" name="user_id" value=">?php echo $this->user->id; ?>" />
      <input type="hidden" name="view" value="review" />
      <input type="hidden" name="book_id" value="<?php echo $this->book->book_id; ?>" />
      <input type="hidden" name="model" value="review" />
      <input type="hidden" name="item" value="review" />
      <input type="hidden" name="table" value="review" />
    </form>
  </div>
  </div>
  <div class="modal-footer">
    <button class="btn" data-dismiss="modal" aria-hidden="true"><?php echo JText::_('COM_LENDR_CLOSE'); ?<>/button>
    <button class="btn btn-primary" onclick="addReview()"><?php echo JText::_('COM_LENDR_ADD'); ?></button>
  </div>
</div>
Note: We are passing the table and other fields necessary to route to the appropriate model and function.

Below is the javascript associated with the review process.

joomla_root/components/com_lendr/assets/js/lendr.js

//add a review
function addReview()
{
  var reviewInfo = {};
  jQuery("#reviewForm :input").each(function(idx,ele){
    reviewInfo[jQuery(ele).attr('name')] = jQuery(ele).val();
  });
 
  jQuery.ajax({
    url:'index.php?option=com_lendr&controller=add&format=raw&tmpl=component',
    type:'POST',
    data:reviewInfo,
    dataType:'JSON',
    success:function(data)
    {
      if ( data.success ){
        console.log(data.html);
        jQuery("#review-list").append(data.html);
        jQuery("#newReviewModal").modal('hide');
      }else{
 
      }
    }
  });
}

In this function we perform a couple of actions. First, we use a nifty jQuery loop to get all the form data to pass through to the controller. After that submit the form and wait for the response. If the response is successful then we append the response to the review list and hide the modal window. This means I am passing the full html row back from the ajax call. Below is how that is handled in the updated controller.

joomla_root/components/com_lendr/controllers/add.php

<?php defined( '_JEXEC' ) or die( 'Restricted access' ); 
class LendrControllersAdd extends JControllerBase
{
  public function execute()
  {
    $app      = JFactory::getApplication();
    $return   = array("success"=>false);
 
    $modelName  = $app->input->get('model', 'Book');
    $view       = $app->input->get('view', 'Book');
    $layout     = $app->input->get('layout', '_entry');
    $item       = $app->input->get('item', 'book');
 
    $modelName  = 'LendrModels'.ucwords($modelName);
 
    $model = new $modelName();
    if ( $row = $model->store() )
    {
      $return['success'] = true;
      $return['msg'] = JText::_('COM_LENDR_SAVE_SUCCESS');
      $return['html'] = LendrHelpersView::getHtml($view, $layout, $item, $row);
    }else{
      $return['msg'] = JText::_('COM_LENDR_SAVE_FAILURE');
    }
    echo json_encode($return);
  }
}

The html that is returned to the javascript is loaded from the Helper file. You’ll notice we pass the view, layout, item, and row to the getHtml function. It is necessary at this point to review the helper file function for getHtml.

joomla_root/components/com_lendr/helpers/view.php

function getHtml($view, $layout, $item, $data)
{
  $objectView = LendrHelpersView::load($view, $layout, 'phtml');
  $objectView->$item = $data;
  
  ob_start();
   echo $objectView->render();
   $html = ob_get_contents();
  ob_clean();
    
  return $html;
}

This view serves a dual purpose. First it loads a partial view located earlier in the View Helper file, and then once loaded it renders that view out to a variable. This variable is then returned to be passed back to the javascript which in turn will append it to the page.

This concludes the review process. View all the related files directly in the GitHub repository.

Step 4: Searching books

The search process is an interesting one to evaluate. There are again a couple of options that can be pursued. Either you can incorporate a full functioning search system into your component or you can take advantage of the system already built into Joomla in the Finder component, module, and plugins. While it may not be appropriate in all instances to use the Finder system, in the Lendr component we will take this opportunity to integrate with Finder. By integrating with Finder we will first simplify the amount of code and structure that must be added to Lendr, and secondly we will also be able to demonstrate the code necessary to create a plugin for a new content type with Finder.

The plugin I write for Finder is a simple one and certainly does not demonstrate all the typical capabilities of the plugin system. Below is the XML file associated with the Finder plugin we are writing (I call it Smart Search - Books).

books.xml
books.php

These functions are used by Finder to index the correct table, load the data and route it correctly in the search results. Because of the level of this tutorial I am not going to go into too much detail regarding each of these functions nor will I be loading up the secondary route helpers which are often used to make the component language specific and other additional features. If you are interested in learning more about the search plugin system leave a message in the comments or contact me for more information.

Step 5: Wrapping up

In this article we’ve covered quite a few functions, structural design, and additional ideas which can be applied to any other component. We’ve looked at how to best utilize Bootstrap for modals, we’ve taken a deeper look at javascript functionality and use and then we’ve expanded our use beyond components to integration with a secondary plugin. I have purposely not included every file written or every function modified simply for the purpose of keeping the focus of this article on the thought process and concepts surrounding component development and not individual functions which may be more readily discerned from previous tutorials. If for any reason you have questions about any of the code written don’t hesitate to ask.

In the next article we will begin wrapping things up and finalizing the code. We will cover the administrator interface, proper use of live updates on the page when an AJAX call is completed, and we will begin cleaning up the code, removing unnecessary files, and looking at ways we can simplify or reduce our code.

Download

Download the component as it exists to this point from the Github repository.

Download

Visit the full tutorial site: http://lendr.websparkinc.com/

In the next tutorial we will begin wrapping things up, adding the admin interface and cleaning up the code.

Read 97058 times
Tagged under Developers, English
David Hurley

David Hurley

David Hurley is an open source advocate and travels most weeks of the year to speak around the world on topics of tech, PHP and open source software. He is the Community Manager for Joomla - the second largest content management system in the world. He is also a member of the Production Leadership Team and the Framework maintainers. David writes semi-obsessively at http://dbhurley.com and is an active partner in several businesses.