To get advanced, human friendly SEF URLs in a 3rd party component together with SEF Advance on Joomla! / Mambo, the component should utilitize a SEF extension using standards established by SEF Advance API.

This guide will introduce you to the process of building a SEF extension for a simple 3rd party component and share some tips & tricks for creating more advanced ones.

Analysing 3rd party component

Let's say we have a 3rd party component called myDir which is a simple directory displaying category links. When clicked on a category, a set of belonging entry links are displayed and there is, finaly, the entry view. The URLs, with SEF turned on, look like:
http://www.domain.com/option,com_mydir/Itemid,25/
http://www.domain.com/option,com_mydir/Itemid,25/catid,3/
http://www.domain.com/option,com_mydir/Itemid,25/catid,3/id,7/
We would like to have nicer, more intuitive and search engine friendly, keywords rich URLs. Let's see how we can achieve that with an extension for SEF Advance.

Creating extension file

First thing we do is to create an extension file called sef_ext.php and place it in the component's main directory. We can copy the example sef_ext.php file included in SEF Advance package or just create an empty file, name it sef_ext.php and upload it to:
./components/com_mydir/
Now that the file exists there, SEF Advance will pick up the extension and use it for creating and reverting SEF URLs. Let's go on and see what the file should look like.

SEF extension content

The sef_ext.php should contain a class named sef_componentname. This class should contain 2 methods: create() for creating the links from the non-sef link string and revert() for reverting the SEF URL request to one understandable to Joomla.
In our case the extension file will be something like:

<?php
class sef_mydir {
	function create ($string) {
		$sefstring = '';
		return $sefstring;
	}
	function revert ($url_array, $pos) {
		$QUERY_STRING = '';
		return $QUERY_STRING;
	}
}
?>

As we can see the methods take some arguments as input and return some values. Let's see what those variables stand for.

Creating SEF links

The create() function is called every time the link to our component is displayed on your Joomla site. It takes one input argument which is a string containg the non-sef link to our component. In our example it is something like:
index.php?option=com_mydir&amp;Itemid=25&amp;catid=3&amp;id=7
Our create() function now has to translate this link to something prettier and return it to SEF Advance. We should note that SEF Advance has automatically built link http://www.domain.com/mydir/ to this point. All we have to do is to return the rest of the link which will be appended. To begin with we can do something like this:

<?php
	function create ($string) {
		// $string == "index.php?option=com_mydir&Itemid=$Itemid
		//			&catid=$catid&id=$id"
		$sefstring = '';
		if (stristr($string, '&catid=')) {
			$temp = explode('&catid=', $string);
			$temp = explode('&', $temp[1]);
			$catid = $temp[0];
			$sefstring .= "$catid/";
		}
		if (stristr($string, '&id=')) {
			$temp = explode('&id=', $string);
			$temp = explode('&', $temp[1]);
			$id = $temp[0];
			$sefstring .= "$id/";
		}
		// $sefstring == "$catid/$id/"
		return $sefstring;
	}
?>

What we do is that we extract the $catid and $id variables from the link string and append them to our new, SEF link string. Our complete link will now look like: http://www.domain.com/mydir/3/7/
Not exactly human friendly yet but shorter and easier than what we began with. Let's now take a look on how we take care of URL requests when links we just created are clicked on.

Reverting SEF requests

The revert() function is called when URL request to our component is sent. Let's say someone clicked on the link we created above. SEF Advance will understand it's a request to our myDir component and pass it forward for translation. Two arguments are passed to revert(): $url_array being an array containing the request split by slash and $pos being an offset in this array where our "virtual directories" begin. Since Joomla can be installed in a physical subdirectory on the server we can't be sure which array elements are real directories and which are our variables, that's why we need this offset. Our first variable always starts at $pos+2. Continuing on our example we will do something like:

<?php
	function revert ($url_array, $pos) {
		$QUERY_STRING = '';
		if (isset($url_array[$pos+2]) && $url_array[$pos+2]!='') {
			// .../mydir/$catid/
			$catid = $url_array[$pos+2];
			$_GET['catid'] = $_REQUEST['catid'] = $catid;
			$QUERY_STRING .= "&catid=$catid";
		}
		if (isset($url_array[$pos+3]) && $url_array[$pos+3]!='') {
			// .../mydir/$catid/$id/
			$id = $url_array[$pos+3];
			$_GET['id'] = $_REQUEST['id'] = $id;
			$QUERY_STRING .= "&id=$id";
		}
		// $QUERY_STRING == "catid=$catid&id=$id";
		return $QUERY_STRING;
	}
?>

As you can see we take the variable from the request URL array according to offset, register it as GET request and append it to query string which is automatically appended to the global one already prepared by SEF Advance. After the function is executed the request to our SEF link will get translated to:
index.php?option=com_mydir&Itemid=25&catid=3&id=7
and the component will display the right page. That's cool, but we still have numbers in our SEF links. Let's see how we can improve the look by making keywords appear in our SEF URLs instead of numbers.

Creating keywords-rich SEF URLs

Let's say the catid=3 coresponds to a category called "Sport Cars" and id=7 is an entry called "Porsche". Then we very much want those keywords to be present in URLs to our component. Here is how we can get it:

<?php
	function create ($string) {
		$database = JFactory::getDBO();
		// $string == "index.php?option=com_mydir&Itemid=$Itemid
		//			&catid=$catid&id=$id"
		$sefstring = '';
		if (stristr($string, '&catid=')) {
			$temp = explode('&catid=', $string);
			$temp = explode('&', $temp[1]);
			$catid = $temp[0];
			$query = "SELECT name FROM #__mydir_categories "
				."WHERE id='$catid'";
			$database->setQuery($query);
			$category = $database->loadResult();
			$sefstring .= sefencode($category).'/';
		}
		if (stristr($string, '&id=')) {
			$temp = explode('&id=', $string);
			$temp = explode('&', $temp[1]);
			$id = $temp[0];
			$query = "SELECT name FROM #__mydir_entries WHERE id='$id'";
			$database->setQuery($query);
			$entry = $database->loadResult();
			$sefstring .= sefencode($entry).'/';
		}
		// $sefstring == "$category/$entry/"
		return $sefstring;
	}
?>

What we do here is that we query the database and get the category and entry name based on its IDs. Then we append that name to our link instead of numbers. Note that we run the string through a special function called sefencode() before we append it to the link. This function is declared in SEF Advance and, besides encoding the special characters, it takes care of converting to lower-case and replaces space char all according to SEF Advance configuration settings. Finally, we get links like:
http://www.domain.com/mydir/sport-cars/porsche/

Reverting keywords-rich SEF requests

When above link is clicked on we have to convert names back to corresponding IDs in our revert() function which should look like this:

<?php
	function revert ($url_array, $pos) {
		$database = JFactory::getDBO();
		$QUERY_STRING = '';
		if (isset($url_array[$pos+2]) && $url_array[$pos+2]!='') {
			// .../mydir/$category/
			$category = sefdecode($url_array[$pos+2]);
			$query = "SELECT id FROM #__mydir_categories "
				."WHERE name='$category'";
			$database->setQuery($query);
			$catid = $database->loadResult();
			$_GET['catid'] = $_REQUEST['catid'] = $catid;
			$QUERY_STRING .= "&catid=$catid";
		}
		if (isset($url_array[$pos+3]) && $url_array[$pos+3]!='') {
			// .../mydir/$category/$entry/
			$entry = sefdecode($url_array[$pos+3]);
			$query = "SELECT id FROM #__mydir_entries "
				."WHERE name='$entry' AND catid='$catid'";
			$database->setQuery($query);
			$id = $database->loadResult();
			$_GET['id'] = $_REQUEST['id'] = $id;
			$QUERY_STRING .= "&id=$id";
		}
		// $QUERY_STRING == "catid=$catid&id=$id";
		return $QUERY_STRING;
	}
?>

Note that we use function sefdecode() to convert the string back before we use it in database query. When looking for entry ID by its name I also used category ID to limit the results because there might be entries with same name in different ctegories. We can add another conditions to this query like published=1 etc. Also note that for Joomla 1.0 and Mambo the $database object is globally defined while in Joomla 1.5 we have to create an object within function. Finally we get the same non-sef query string as in revert example with numbers above.

Now that we are done here is the source code of this guide's complete extension.

Tips & tricks

You can define a string to be used instead of the default component string. So, instead of
http://www.domain.com/mydir/sport-cars/porsche/ we can have
http://www.domain.com/vehicles/sport-cars/porsche/ by entering mydir=>vehicles to Custom component strings field in SEF Advance configurations.

When building SEF URLs you might want to distinct variables by order. If that's not possible you could use various tricks, like for example using is_numeric() function to distinct numeric values like pagination from names and titles.

Additionally take a look on SEF extensions written by us or others for more hints on creating nice extensions. Have fun and, if you create a working extension, don't forget to share it on our forums.

About us

We provide high quality Joomla components created by a co-founder and original core developer of Joomla. For over a decade, our products have been used by more than 20.000 webmasters around the world.

Stay in touch