What is FOF?

Framework on Framework (or FOF) is a free, open-source rapid application development framework for Joomla CMS. It was developed by Nicholas Dionysopoulos, the lead developer of Akeeba Backup, an extremely popular Joomla component. The distinguishing feature that sets it apart from its counterparts is the promotion of convention-over-configuration when it comes to developing components. In a nutshell, it's an extension of the Joomla Framework. It does not replace it, and unlike other frameworks, it is not standalone.

Why FOF?

Don't Repeat Yourself (D.R.Y)

One of the benefits of using FOF when building components is that it uses the D.R.Y. principle. If you're tired of duplicating files and lines of code whenever you have to develop a component, then FOF is definitely for you. Since FOF promotes convention-over-configuration, a lot of underlying code is implemented automatically (just like magic!) if you follow the conventions set out by the Framework. This becomes a lot clearer when you see the code samples below.

Writing less code saves time. Take the below as an example from Tim Plummer during Sydney's Joomla day in October 2013. He demonstrated the FOF version of the 'Hello world' component called 'Yellow'. Tim did a direct comparison between the number of files and lines of codes needed to create the component in both cases.

Looking at the picture below, you will find that the number of files created and the lines of code written with FOF are significantly reduced by more than 50% when compared with a component developed without FOF.

timplummer

Photo Credit: Tim Plummer ( http://www.timplummer.com.au/)

Backwards compatibility

One of the best FOF features is backwards compatibility. FOF incorporates a clear deprecation and migration path. Imagine being able to use the same component source code in Joomla 2.5 and 3.2 without breaking your component? This saves a lot of time and trouble when it comes to re-writing the custom component that you want to use across both Joomla 2.5 and 3.2.

In Joomla 3.2, FOF is already included as a library in the main install files. One of the objectives of including FOF is to offer both backward compatibility with 2.5 & forward compatibility with 3.x & 4.x versions. This means, one less thing to worry about when upgrading to future Joomla versions! The only exception is versions 3.2 and older where FOF is not standard, and needs to be manually installed.

Media file overrides

Hacking the core code of any component to modify media files is cringe factor for any developer! With FOF, you can override your media files just like you would with template overrides. FOF provides a way to load CSS and JS files via your views template file, or alternatively, simply create a directory inside your template i.e templates/your_template/media/com_sample to override files normally found in media/com_sample.

But wait, there's more!

It doesn't stop there – and no, you don't get free steak knives, but there are other jazzy features in FOF to fall in love with. One notable FOF feature is that you don't need to write separate view files for JSON and CSV views because you can simply add format=csv or format=json at the end of your URL and this generates the view within your chosen format. This is particularly helpful when you want to provide web services for third-party applications. There is also the option to use XML files for views templates, including multiple and single items views, which are rendered automatically in HTML. FOF also provides a sophisticated way of re-using views template files in other views within the same component or other components, while still respecting the template overrides.

Building your first FOF component

The first step is to setup the database table needed for the component. Once the database table has been created, consider the naming convention. Normally, a database table will contain the component's name and the views in plural term. Also, in FOF, there are (magic!) fields that can be defined in the database table to perform basic functions automatically.

In administrator/components/com_hello/sql/install/mysql/install.sql, we add the following sql line:

CREATE TABLE IF NOT EXISTS `#__hello_items` (
  `hello_item_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL,
  `ordering` int(10) NOT NULL DEFAULT '0',
  `created_by` bigint(20) NOT NULL DEFAULT '0',
  `created_on` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `modified_by` bigint(20) NOT NULL DEFAULT '0',
  `modified_on` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `locked_by` bigint(20) NOT NULL DEFAULT '0',
  `locked_on` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`hello_item_id`)
) DEFAULT CHARSET=utf8;

In this example, we will build a component called ‘hello’ with items view. The table name should be #__hello_items. (#__componentname_viewname). The auto increment should also be hello_item_id. Of course, you can override these defaults if preferred. Within the back-end directory, there is a file called FOF.xml i.e administrator/components/com_hello/FOF.xml where default overrides can be defined. You can also use this file as a dispatcher.

<?xml version="1.0" encoding="UTF-8"?>
<FOF>
    <!-- Component back-end options -->
    <backend>
        <!-- Dispatcher options -->
        <dispatcher>
            <option name="default_view">items</option>
        </dispatcher>
    </backend>
     <!-- Component front-end options -->
    <frontend>
        <!-- Dispatcher options – setting the default front-end view  -->
        <dispatcher>
            <option name="default_view">items</option>
        </dispatcher>
        <!-- Options common for all views -->
        <view name="*">
            <!-- The star task sets the default ACL privileges for all tasks. -->
            <acl>
                <task name="*"></task>
            </acl>
        </view>

        <!-- Default Table for all models - instead of the model looking for a table of the same name. To override this, add -->
        <view name="modelname">
            <config>
                <option name="tbl">table name</option>
                <option name="tbl_key">column id</option>
            </config>
        </view>
    </frontend>
</FOF>

The fields enabled, created_by, created_on, modified_by, modified_on, locked_by, and locked_on are the magic fields we speak of, which means they just automatically work! If you save an item for example, it will automatically capture the date it was created, the user who created it, as well as other basic functionality.

Next, we have to make sure we have FOF installed. Note that in Joomla 3.2 and higher, the installation package already has FOF pre-installed so if packaging your component with FOF, be careful not to overwrite it.

If you don’t have FOF installed, you can download the latest version at: https://www.akeebabackup.com/download/FOF.html within the component’s package should be the FOF folder for the files. You’re now ready to define your library in your component’s install script file like:

defined('_JEXEC') or die();
// Load FOF if not already loaded
if (!defined('F0F_INCLUDED'))
{
$paths = array(
        (defined('JPATH_LIBRARIES') ? JPATH_LIBRARIES : JPATH_ROOT . '/libraries') . '/f0f/include.php',__DIR__ . '/FOF/include.php',
    );
    foreach ($paths as $filePath)
    {
        if (!defined('F0F_INCLUDED') && file_exists($filePath))
        {
            @include_once $filePath;
        }
    }
}
// Pre-load the installer script class from our own copy of FOF
if (!class_exists('F0FUtilsInstallscript', false))
{
    @include_once __DIR__ . '/FOF/utils/installscript/installscript.php';
}
// Pre-load the database schema installer class from our own copy of FOF
if (!class_exists('F0FDatabaseInstaller', false))
{
    @include_once __DIR__ . '/FOF/database/installer.php';
}
// Pre-load the update utility class from our own copy of FOF
if (!class_exists('F0FUtilsUpdate', false))
{
    @include_once __DIR__ . '/FOF/utils/update/update.php';
}

Next we need to create the main entry point for our component in: administrator/components/com_hello/hello.php.

defined('_JEXEC') or die();
// Load FOF
include_once JPATH_LIBRARIES.'/f0f/include.php';
if(!defined('F0F_INCLUDED')) {
    JError::raiseError ('500', 'FOF is not installed');
    return;
}
F0FDispatcher::getTmpInstance('com_hello')->dispatch();

We then create our installation file or manifest. This is similar to how we do it for non-FOF components. So in administrator/components/com_hello/hello.xml:

<?xml version="1.0" encoding="utf-8"?>
<extension version="2.5" type="component" method="upgrade">
    <name>Component Name</name>
    <author>Component Author</author>
    <creationDate>2014-05-30</creationDate>
    <description>Component Description</description>
    <files folder="frontend">
        <filename>index.html</filename>
        <filename>hello.php</filename>
        <folder>views</folder>
    </files>
    <!-- Install query  -->
    <install>
        <sql>
            <file charset="utf8" driver="mysql">sql/install.mysql.sql</file>
        </sql>
    </install>
    <!-- Uninstall query  -->
    <uninstall>
        <sql>
            <file charset="utf8" driver="mysql">sql/uninstall.mysql.sql</file>
        </sql>
    </uninstall>
    <!-- Administrator back-end section -->
    <administration>
        <!-- Administration menu -->
        <menu view="cpanel">COM_HELLO</menu>
        <!-- Back-end files -->
        <files folder="backend">
            <folder>sql</folder>
            <folder>views</folder>
            <filename>access.xml</filename>
            <filename>config.xml</filename>
            <filename>FOF.xml</filename>
            <filename>index.html</filename>
            <filename>hello.php</filename>
        </files>
        <!-- Back-end translation files -->
        <languages folder="language/backend">
            <language tag="en-GB">en-GB/en-GB.com_hello.ini</language>
            <language tag="en-GB">en-GB/en-GB.com_hello.sys.ini</language>
        </languages>
    </administration>
</extension>

Then we create our config file similar to how we do it with non-FOF components. So in administrator/components/com_hello/config.xml:

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <fieldset
        name="permissions"
        label="JCONFIG_PERMISSIONS_LABEL"
        description="JCONFIG_PERMISSIONS_DESC"
        >
        <field
            name="rules"
            type="rules"
            label="JCONFIG_PERMISSIONS_LABEL"
            class="inputbox"
            filter="rules"
            component="com_hello
            section="component" />
    </fieldset>
</config>

We also create the access file, (again, this is similar to non-FOF components).

<?xml version="1.0" encoding="utf-8"?>
<access component="com_yellow">
    <section name="component">
        <action name="core.admin" title="JACTION_ADMIN" description="JACTION_ADMIN_COMPONENT_DESC" />
        <action name="core.manage" title="JACTION_MANAGE" description="JACTION_MANAGE_COMPONENT_DESC" />
        <action name="core.create" title="JACTION_CREATE" description="JACTION_CREATE_COMPONENT_DESC" />
        <action name="core.delete" title="JACTION_DELETE" description="JACTION_DELETE_COMPONENT_DESC" />
        <action name="core.edit" title="JACTION_EDIT" description="JACTION_EDIT_COMPONENT_DESC" />
        <action name="core.edit.state" title="JACTION_EDITSTATE" description="JACTION_EDITSTATE_COMPONENT_DESC" />
    </section>
</access>

Next we want to create our views. In creating view classes, the convention is ComponentViewViewname, in this case, it's HelloViewItems and the file name must follow the convention view.format.php. If none is specified, html is the assumed value. Please note that when the view class is missing from the back-end, FOF will attempt to load the view class from the front-end and vice versa. However, if no view class can be found, FOF will look for the default view class with the naming convention ComponentViewDefault in the default/view.format.php file inside your views. eg. administrator/components/com_hello/views/default/view.html.php. If you don’t have a default view, FOF will create a pre-configured instance of FOFView using convention over configuration. This means you can either create your own view class or let FOF take care of it if you don't have one.

In addition, FOF uses default names for list or form for add or edit tasks and are linked to the task being executed. Browse tasks will have default.php or form.default.xml if using xml. Edit will have form.php or form.form.xml and read task has the filename item.php or form.item.xml. In our view which is using browse task, we use the plural form e.g items and for the form we use the singular form i.e item. So now, let's create our items view administrator/components/com_hello/views/items/tmp/form.default.xml:

<?xml version="1.0" encoding="utf-8"?>
<form
    type="browse"
    show_header="1"
    show_filters="1"
    show_pagination="1"
>
    <headerset>
        <header name="hello_item_id" type="rowselect" tdwidth="20" />
        <header name="title" type="fieldsearchable" sortable="true"
            buttons="yes" buttonclass="btn"
        />
        <header name="created_by" type="user" sortable="true" tdwidth="10%" />
        <header name="ordering" type="ordering" sortable="true" tdwidth="10%" />
        <header name="enabled" type="published" sortable="true" tdwidth="8%" />
    </headerset>
    <fieldset name="items">
        <field name="hello_item_id" type="selectrow"/>
        <field name="title" type="text"
            show_link="true"
            url="index.php?option=com_hello&amp;view=item&amp;id=[ITEM:ID]"
            empty_replacement="(no title)"
         />

        <field name="created_by" type="user" sortable="true" tdwidth="10%" />
        <field name="ordering" type="ordering" labelclass="order"/>
        <field name="enabled" type="published"/>
    </fieldset>
</form>

To add new items to our items table, we also have to create our item view. In administrator/components/com_hello/views/items/tmp/form.form.xml:

<?xml version="1.0" encoding="utf-8"?>
<form
    validate="true"
>
    <fieldset name="basic_configuration"
        label="COM_HELLO_ITEMS_GROUP_BASIC"
        description="COM_HELLO_ITEMS_GROUP_BASIC_DESC"
        class="span6"
    >
        <field name="title" type="text"
            class="inputbox"
            label="COM_HELLO_ITEMS_FIELD_TITLE"
            labelclass="hello-label hello-label-main"
            required="true"
            size="50"
         />

        <field name="enabled" type="list" label="JSTATUS"
            labelclass="hello-label"
            description="JFIELD_PUBLISHED_DESC" class="inputbox"
            filter="intval" size="1" default="1"
        >
            <option value="1">JPUBLISHED</option>
            <option value="0">JUNPUBLISHED</option>
        </field>

    </fieldset>
</form>

And before we test, let’s create a language file similar to how we would with non FOF Joomla components. In administrator/language/en-GB/en-GB.com_hello.sys.ini write:

COM_HELLO="Hello World"
HELLO="Hello World"
COM_HELLO_TITLE_ITEMS="Hello World Items"

And in your administrator/language/en-GB/en-GB.com_hello.sys.ini:

COM_HELLO="Hello"
COM_HELLO_CONFIGURATION="Configure Hello"
COM_HELLO_COMMON_NORECORDS="No records were found"
COM_HELLO_ITEMS_FIELD_TITLE="Item"
COM_HELLO_ITEMS_FIELD_CREATED_BY="Created By"
COM_HELLO_ITEMS_FIELD_CREATED_ON="Created On"
COM_HELLO_ITEMS_FIELD_MODIFIED_BY="Modified By"
COM_HELLO_ITEMS_FIELD_MODIFIED_ON="Modified On"
COM_HELLO_LBL_ITEM_SAVED="Your item is saved"
COM_HELLO_CONFIRM_DELETE="Are you sure you want to delete this item"
COM_HELLO_SELECTSTATE="- Select Status -"
COM_HELLO_TITLE_ITEMS="Hello World Items"
COM_HELLO_ITEMS_GROUP_BASIC="Details"
COM_HELLO_TITLE_ITEMS_EDIT="Edit Item"
COM_HELLO_LBL_ITEM_SAVED="Item saved"

After installing your extension, you can now add and seethe items:

Items View

itemview

Add/Edit Form

addedit

If you want to wrap your form with texts and other elements, you can use the php template of your view and load your form into your php template. You create administrator/components/com_hello/views/item/tmp/form.php

<?php
// Show some stuff before the form
?><h1>Hello, world!</h1>
<p>This is printed above the form</p>
<?php
// Show the rendered form
echo $this->getRenderedForm();
?>

If you would like to have the json output for your view, simply add format=json in the url and it will give you the json format of the page. The same applies for csv format.

Also, with FOF, you can package your component in one package to be used and different Joomla versions have the ability to assign a different view template for each Joomla version. For example, if you are on Joomla 2.5 and you want to use a different template from what you have with Joomla 3.2, you can add a suffix to the template name. FOF will look for default.j25.php, default.j2.php and default.php in this order. The same is true when using Joomla 3.2.

As you can see, with just a couple of files, and without creating models and view classes, we have developed a simple ‘hello world’ component. The two best takeaways? You only need to extend the classes when you need to, and basic functionalities are done automatically.

Conclusion

For those new to FOF, developing your first FOF Joomla component might be a bit tricky at first. You can find the documentation in the Akeeba site should you require further information. One handy tip is to find existing components that are already using FOF. You can find the yellow component used by Tim Plummer in his presentation at https://github.com/tuum/yellow-world. Akeeba also has a sample FOF component, which you can download at https://github.com/akeeba/todo-FOF-example. Furthermore, Akeeba has a number of products that are built using FOF which you can download and try.

Lastly, when in doubt, just ask! Join the growing community of FOF users at https://groups.google.com/forum/#!forum/frameworkonframework

Sources:

https://www.akeebabackup.com/documentation/FOF.html
 http://www.timplummer.com.au/13-rapid-application-development-using-akeeba-FOF-joomladay-sydney-2013.html 

Since this blog was published, FoF project has been split into two different projects or enrties. FOF](F-oh-F) which is shipped by joomla itself and is used by some of it's core components, the other one is F0F f-zero-f which is the newer version from akeeba. F0F is always the latest version and FoF is lagging a couple of versions behind.

Reference: https://www.akeebabackup.com/home/news/1558-info-about-fof-and-f0f.html

alarm image