Custom Component Development - Joomla

Joomla! employs a specific naming scheme, which is used by all components. Each
component in the system has a unique name with no spaces. The code is split into
two folders, each bearing the component name prefixed by com_. The component
in this book will be called reviews. Therefore, you will have to create two folders
named com_reviews:

Create one in the folder named components for the front end.

Create one in the folder named components within the administrator

folder for the back end.
When the component is loaded from the front end, Joomla! will look for a file
with the component's unique name ending in a .php extension. Within the
components/com_reviews folder, create the reviews.php file. Similarly, running
it in the back end assumes the presence of a file prefaced with admin. followed
by the component name and ending in .php. Add the file admin.reviews.php in
administrator/components/com_reviews. Leave both the files empty for
the moment.Assuming this is the case, you can load the component's front
end by opening http://localhost/joomla/index.php?option=com_reviews in
your browser. At this point, the screen should be essentially blank, apart from the
common template elements and modules. To make this component slightly more
useful, open reviews.php and add the following code, then refresh the browser:code:defined( '_JEXEC' ) or die( 'Restricted access' );echo '

Restaurant Reviews

';

For the back end, drop this code into
administrator/components/com_reviews/admin.reviews.php:

Go to
http://localhost/joomla/administrator/index.php?option=com_reviews
and compare your result

Registering Your Component in the Database

We will perform this registration using the following query. It is assumed that your
database prefix is jos_. If not, replace jos_ with the prefix you chose. If you prefer
to work with direct SQL statements on a command-line interface, enter the following
query in your console:

If you prefer to use a GUI or web-based database manager such as phpMyAdmin,
enter Restaurant Reviews for name, option=com_reviews for link and
admin_menu_link, Manage Reviews for admin_menu_alt, com_reviews for
option, and js/ThemeOffice/component.png for admin_menu_img. Leave all of
the other fields blank. The fields menuid, parent, ordering, and iscore will default
to 0, while enabled will default to 1.(with same above structure)

After the record is successfully entered, go to any page in the back end and refresh
it. When you move the mouse cursor over the Components menu you should see the
new option: Restaurant Reviews

Now that the component is registered, you can also create a link for the front end.
Go to Menus | Main Menu and click New. From this screen, select Restaurant
Reviews. Enter Reviews as the Name. The New Menu Items screen will be observed:Now click Save and go to the front end. You should now see Reviews listed as
an option in left Main Menu.

Creating ToolbarsThroughout the back end, all the core components implement toolbars with similar
buttons for saving, deleting, editing, and publishing items. You can use these buttons
in your component so that frequent administrators will have a seamless experience.
To start, create the toolbar.reviews.html.php file in the
administrator/components/com_reviews folder and enter in the following code:code:defined( '_JEXEC' ) or die( 'Restricted access' );
class TOOLBAR_reviews {function _NEW() {
JToolBarHelper::save();
JToolBarHelper::apply();
JToolBarHelper::cancel();
}
function _DEFAULT() {
JToolBarHelper::title( JText::_( 'Restaurant Reviews' ),
'generic.png' );
JToolBarHelper::publishList();
JToolBarHelper::unpublishList();
JToolBarHelper::editList();
JToolBarHelper::deleteList();
JToolBarHelper::addNew();
}
}Now Add the following code into toolbar.reviews.php in the
administrator/components/com_reviews folder:

Before we set up an interface for entering reviews, we need to create a place in
the database where they will go. We will start with a table where one row will
represent one review in the system. Assuming that your database prefix is
jos_ (check Site | Configuration | Server if you are unsure), enter the following
query into your SQL console:CREATE TABLE 'jos_reviews'
(
'id' int(11) NOT NULL auto_increment,
'name' varchar(255) NOT NULL,
'address' varchar(255) NOT NULL,
'reservations' varchar(31) NOT NULL,
'quicktake' text NOT NULL,
'review' text NOT NULL,'notes' text NOT NULL,
'smoking' tinyint(1) unsigned NOT NULL default '0',
'credit_cards' varchar(255) NOT NULL,
'cuisine' varchar(31) NOT NULL,
'avg_dinner_price' tinyint(3) unsigned NOT NULL default '0',
'review_date' datetime NOT NULL,
'published' tinyint(1) unsigned NOT NULL default '0',
PRIMARY KEY ('id')
);If you're using phpMyAdmin, create new table name as jos_reviews and let it generate 13 fields with the same above structure.(Be sure you make the field id into an automatically incremented primary key:)

Creating a Table Class

We could write individual functions to take care of the queries necessary to add,
update, and delete the reviews. However, these are rudimentary functions that
you would prefer not to write. Fortunately, the Joomla! team has already done this
for you. The JTable class provides functions for creating, reading, updating, and
deleting records from a single table in the database.
To take advantage of JTable, we need to write an extension of it specific to
jos_reviews. In the /administrator/components/com_reviews folder, create
a folder named tables. In this folder, create the review.php file and enter the
following code:defined('_JEXEC') or die('Restricted access');class TableReview extends JTable{var $id = null;var $name = null;var $address = null;var $reservations = null;var $quicktake = null;var $review = null;var $notes = null;var $smoking = null;var $credit_cards = null;var $cuisine = null;var $avg_dinner_price = null;var $review_date = null;var $published = null;function __construct(&$db){parent::__construct( '#__reviews', 'id', $db );}}The TableReview class inherits the bind(), store(), load(), and delete(),
functions among others. These four functions allow you to manage records in the
database without writing a single line of SQL.

Creating the Review Form

With a database table now in place, we need a friendly interface for adding reviews
into it. To start, let's create a form for entering the review data. As we did with the
toolbar files, we want to separate the HTML output from our processing logic. The
PHP code necessary for configuring the form will be in admin.reviews.php while
admin.reviews.html.php will contain the actual HTML output. Open admin.
reviews.php and replace the contents with the following code:

Now create the admin.reviews.html.php file and add the following code:

Now Point your browser to http://localhost/joomla/administrator/index.
php?option=com_reviews&task=add and you should see the Detail Form screen:

Processing the DataOnce the data in the form is filled out and the admin clicks the Save button, we need
to save the information into the database. To start, create saveReview() in admin.
reviews.php: code:

At the moment, the switch() statement in admin.reviews.php only processes the
add task. Now that we have a form and function in place, we can add a case to save
our data. Add the highlighted code below to the switch():

Save all your files and go to http://localhost/joomla/administrator/index.
php?option=com_reviews&task=add in your browser. You should now be able to
fill out the form and click Save. You should see a screen with message Review Saved!:

Creating a List Screen

Since our admins will not have access to phpMyAdmin, we need to build a screen
that lists all of the reviews in the database. To start, add the following function to
admin.reviews.php:

If all goes well, we pass the array of results into the following member function to be
added to admin.reviews.html.php:

This aids the JavaScript in processing the list.
Once the HTML output code is in place, update your switch() statement in admin.
reviews.php with the following highlighted code. This will add a default case for
when no task is selected:

When you now load http://localhost/joomla/administrator/index.
php?option=com_reviews, a screen similar to the Restaurant Review List should appear:

Editing Records

Instead of writing a whole new set of functions for editing records, we can extend the
existing code. In the admin.reviews.php file under editReview() replace:
$row =& JTable::getInstance('Review', 'Table');
with the following highlighted code:

As we did with the saveReview() function, we get a TableReview object to handle
the data for the record. We also pull in the form variable cid, which is an array of
record IDs. Since we only want to edit one record at a time, we select the first ID in
the array and load the corresponding row. While we're in the admin.reviews.php
file, we should add a case for edit to the switch():

case 'edit':
case 'add':
editReview( $option );
break;

You need to provide links that the user can click to edit the individual records. In the
admin.reviews.html.php file under HTML_reviews::showReviews(), replace
the function to display the name and add the first two bits of highlighted code as seen below:

You will also have to update the toolbar code. First, go to the switch()in the
toolbar.reviews.php file and check for the 'edit' case just above 'add':
case 'edit': case 'add':
TOOLBAR_reviews::_NEW();
break;

Now that the edit function is built, we can add an edit button that will
allow you to alternatively check boxes rather than click the links. Go to the
toolbar.reviews.html.php file and check for the following line in
TOOLBAR_reviews::_DEFAULT():
JToolBarHelper::unpublishList();
JToolBarHelper::editList();
JToolBarHelper::deleteList();
Save all your files and then refresh the page http://localhost/joomla/
administrator/index.php?option=com_reviews. The record should now appear
with a link. Click this link and you should get a screen Edit Details Form with Save,Apply and Cancel tool.

You may have noticed the Apply button in the toolbar on the edit screen. This is
intended to allow people to save their progress and continue editing the record. In
order to make this button function as intended, we will have to make two changes in
the admin.reviews.php file. Modify the switch() as follows:case 'apply':
case 'save':
saveReview( $option, $task );
break;
Add the following highlighted parameter to the function definition:function saveReview( $option, $task )
Then change the last line of saveReview() to the following code that checks the

In a similar way to the back end, the code
require_once( JApplicationHelper::getPath( 'html' ) ); includes in the
reviews.html.php file. We pass
JPATH_ADMINISTRATOR.DS.'components'.DS.$option.DS.'tables' into
Jtable::addIncludePath(); to pull in the table class we wrote for the
administrator portion in the previous chapter. Finally, the switch() function is set
with a default case, which calls a function to display all the published reviews. The
query in this function ensures that only the published reviews are loaded and that
these are reverse‑chronologically ordered by the review date.

Before we reload the page, we need to add the HTML_reviews class for the front end.
In the /components/com_reviews folder, create the file reviews.html.php:

Save all your files and hit refresh in your browser. You should now see a listing of all
the reviews in the system:

Displaying a Review

If you were to click on any of the links at the moment, you would simply see the
same screen again as we have not yet coded anything to handle the view task. For
this, add the following function to the reviews.php file:function viewReview($option){$id = JRequest::getVar('id', 0);$row =& JTable::getInstance('review', 'Table');$row->load($id);if(!$row->published){JError::raiseError( 404, JText::_( 'InvalidID provided' ) );}HTML_reviews::showReview($row, $option);}

The viewReview() function will do everything necessary to load a requested review,
but we still need to add code to call this function. Add this highlighted case of view
to the switch on $task:
switch($task)
{case 'view':
viewReview($option);
break;
default:
showPublishedReviews($option);
break;

We also need to create a display function in our output class. Add the showReview()
function to HTML_reviews in the reviews.html.php file:

After saving all the files, click one of the review links again and you should see a nicely formatted page.

Generating Search-Engine Friendly Links

At this point of time the links to our reviews (http://localhost/joomla/index.

php?option=com_reviews&id=1&task=view&Itemid=1) appear as long GETstrings. Our critics mentioned that they hate seeing links like these. Also, theselinks are not very helpful for search engines attempting to index our site. It wouldbe preferable to have a link like: http://www.ourdomain.com/reviews/view/1instead. To accomplish this, we will define a route to both generate and decodeSearch-Engine Friendly (SEF) links. Before we write any code, wewill have to go to the administrator back end and enable SEF links. Go toSite | Configuration and make sure Search Engine Friendly URLs is set to Yes. If

you're using Apache as your webserver and have mod_rewrite enabled, you

can also set Use mod_rewrite to Yes; this will entirely remove index.php fromyour URLs. With mod_rewrite enabled, the SEO Settings portion of your GlobalConfiguration screen should look like the following:

Click Save to change the configuration. If you're using mod_rewrite, make sureyou rename htaccess.txt to .htaccess. If you get a message saying that yourconfiguration file is unwritable, open the configuration.php file in the Joomla! rootand set the $sef member variable of JConfig to 1 instead of 0.

Building URL Segments

When creating internal links while building a page in Joomla!, components and
modules will call the JRoute::_() function. This function takes a relative link
as the parameter and returns a SEF version of the link. To build this version,
JRoute::_() first parses the relative link into an array, then removes the option
element and adds its value as the first segment of the new URL. The function will
then look for router.php in the component directory with the same name as
option. If router.php is found, it will be included and the function beginning with
the component name and ending with BuildRoute() will be called; in our case,
ReviewsBuildRoute(). To create this function, go back to the /components/com_
reviews folder and create the file router.php. Fill the file with the following code:

Although we now have router.php in place with a function that will generate SEF
URLs, our component's output functions are not set to use it. Open
/components/com_reviews/reviews.html.php and check for the highlighted code
in the showReviews() member function of HTML_reviews:

The component will now generate SEF URLs according the pattern we set in
ReviewsBuildRoute().

Parsing URL Segments

If you attempt to click on one of the reviews right now, you will get a message like
"Fatal error: Call to undefined function reviewsParseRoute()". In addition to a
function generating SEF URLs for reviews, we need a function capable of decoding
these URLs. Go back to /components/com_reviews/router.php and add the
following function:
function ReviewsParseRoute($segments)
{
$vars = array();
$vars['task'] = $segments[0];
$vars['id'] = $segments[1];
return $vars;
}Once Joomla! determines that the page request is intended for the reviews
component, it will call BuildParseRoute() and pass in an array of the relevant
URL segments. These segments are ordered the same way we set them in
ReviewsBuildRoute(). We initialize an array $vars to hold the variables we return.
Then we set the task and id elements of this array to the first and second elements
of $segments respectively. Finally, we return the array, which Joomla! in turn sets as
request variables. This way, the entire routing process is transparent to the rest of the
code: all of the request variables you would normally expect to be present under a
conventional script call will be there.

Now Save router.php and try clicking on some of the links and pay attention
to the location bar in your browser. You should now notice URLs like
http://www.oursite.com/reviews/view/1 or
http://www.oursite.com/index.php/reviews/view/1. If the URLs look like
http://www.oursite.com/component/reviews/view/1, this just means that you
followed a non-SEF URL; this will clear up as you navigate around.

Adding CommentsMost visitors will take our word for it when we say that a restaurant is great (or
that it isn't). However, there may be a few who disagree. Why not give them an
opportunity to leave comments about their experiences with the restaurant? We'll
need a place to store them, so enter the following SQL command into your
database console: CREATE TABLE 'jos_reviews_comments' (
'id' int(11) NOT NULL auto_increment,
'review_id' int(11) NOT NULL,
'user_id' int(11) NOT NULL,
'full_name' varchar(50) NOT NULL,
'comment_date' datetime NOT NULL,
'comment_text' text NOT NULL,
PRIMARY KEY ('id')
)
If you're using phpMyAdmin, pull up the following screen and enter
jos_reviews_comments as the table name and 6 in the Number of fields section:

After clicking Go, a grid is displayed; fill in details so that it looks like the
following screen:

We also want to add another database class to handle the basic functions. Since we
already have the class for the reviews themselves in administrator/components/
com_reviews/tables, we will add the second one here as well. Create the
comment.php file and add the following TableComment class, making sure that
each column in the table is represented as a member variable:defined('_JEXEC') or die('Restricted access');
class TableComment extends JTable
{
var $id = null;
var $review_id = null;
var $user_id = null;
var $full_name = null;
var $comment_date = null;
var $comment_text = null;
function __construct(&$db)
{
parent::__construct( '#__reviews_comments',
'id', $db );
}
}
?>

Now that we've established a place to hold the comments, a form should be added so
that people can enter their comments in. Open the reviews.html.php file and add
the following function to HTML_reviews:

The showCommentForm() function takes the current component's name, the id of
the review we're displaying, and a name as parameters. The Name is already filled
in the form so that logged-in users do not have to retype it. There is a link return
to the reviews, which routes us back to the reviews component. The task is set to
comment so that the component records the comment. To make sure we attach the
comment to the right review, review_id is set to the current one. We would like to
display the form just beneath the review, so add the following highlighted code to
the viewReview() function in the reviews.php file:if(!$row->published)
{
JError::raiseError( 404, JText::_( 'Invalid ID provided' ) );
}
HTML_reviews::showReview($row, $option);
$user =& JFactory::getUser();
if($user->name)
{
$name = $user->name;
}
else
{
$name = '';
}
HTML_reviews::showCommentForm($option, $id, $name);
Before calling the HTML output function, we need to get the name of the currently
logged-in user (if present). The code $user =& Jfactory::getUser(); sets $user as
a reference to an object for the currently logged-in user. If the user's full name is set
in the name member variable, we capture this value in $name, otherwise $name is set
to a blank string.Save all your files and reload the review. If you are logged in to the front end, your
screen should look like the screenshot below. If you are not logged in, the form will
be displayed, but the Name field will not be filled.

Before we attempt to fill in and submit the comment form, we need to add the
code that will process the input and insert it into the database. Add the following
highlighted code to the switch in the reviews.php file:
switch($task)
{
case 'view':
viewReview($option);
break;case 'comment':
addComment($option);
break;
default:
showPublishedReviews($option);
break;
}Then add the addComment() function to the same file:

Displaying Comments

After saving the code files, you will be able to submit the form and return to the
review. However, nothing will appear to happen as we do not have the code in place
to display the comments. On other websites, you will often see that the content is
directly followed by the comments, which are also followed by a form for adding
more comments. We will follow the same style. Add the following highlighted code
to the reviews.php file, which will get all the comments from the database, go
through them, and then output each one: