Skip to content

Plugins

Overview

4SEF builds SEF URLs for a specific component by using a dedicated piece of code, tailored for that component. It comes with support for all native Joomla content types, plus some commonly used 3rd-party extensions.

For component without a specific support plugin, 4SEF fallbacks to using the component own router.php file, normally meant for use by Joomla SEF URLs system. This process is automatic and will work in most cases.

If you want to provide a more refined set of URLs for a component you use or built, you can create a SEF URL driver for 4SEF to do so.

All the code samples below include copyright, names and date from Weeblr. Although they won't affect how the code works, you should change them to your own name, date and relevant information as needed.

The driver

A driver is at minimum a single PHP class file, which you can store anywhere you want. It can be part of your own component for instance, or inside a dedicated plugin (see paragraph below, Loading your driver).

The driver extends a base class provided by 4SEF and has 3 main roles:

  • build the SEF URL based on the non-sef variables, similar to Joomla's own router.php file
  • build the "normalized" non-sef URL. Most of that is done by 4SEF already, but you may want to remove some component-specific query variables.
  • optionally, determine if a SEF URL should be left non-SEF, which is sometimes handy and save space

Study the built-in drivers

Building SEF URLs for your component is really specific to the component itself, we can only provide general guidance and how to integrate your driver. It's best to look at the built-in drivers, for Joomla components, found in the /plugins/system/forsef/platform/extensions folder.

Driver sample code

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
<?php
/**
 * Project: 4SEF
 *
 * @package          4SEF
 * @copyright        Copyright Weeblr llc - 2022
 * @author           Yannick Gaultier - Weeblr llc
 * @license          GNU General Public License version 3; see LICENSE.md
 * @version          1.0.0
 * @date             2022-06-03
 */

namespace Weeblr\Forsef\Platform\Extensions;

use Joomla\CMS\Uri;

use Weeblr\Forsef\Data;
use Weeblr\Forsef\Helper;

use Weeblr\Wblib\Forsef\Wb;
use Weeblr\Wblib\Forsef\System;
use Weeblr\Wblib\Forsef\Factory;

// no direct access
defined('_JEXEC') || die();

/**
 * Sample plugin to build URL for a com_sample component.
 *
 * - Drop the "com_" at the start from the component to name the class
 * - Below are only the most important methods. For more advanced logic,
 * be sure to check the built-in plugins in
 *
 * /plugins/system/forsef/platform/extensions
 *
 */
class Sample extends Base
{
    /**
     * @var Helper\Platform
     */
    private $platformHelper;

    /**
     * Stores factory instance.
     *
     * @param string $option  Extension this applies to, in com_xxx format.
     * @param array  $options Can inject custom factory and platform.
     */
    public function __construct($option, $options = [])
    {
        parent::__construct($option, $options);

        $this->platformHelper = $this->factory
            ->getA(Helper\Platform::class);
    }

    /**
     * Builds the SEF URL for a non-sef.
     *
     * @param Uri\Uri $uriToBuild
     * @param Uri\Uri $platformUri
     * @param Uri\Uri $originalUri
     *
     * @return \array
     * @throws \Exception
     */
    public function build($uriToBuild, $platformUri, $originalUri)
    {
        $sefSegments = parent::build($uriToBuild, $platformUri, $originalUri);

        // Read whatever variables you need to build your SEF URL.
        $view   = $uriToBuild->getVar('view');
        $layout = $uriToBuild->getVar('layout');
        $id     = $uriToBuild->getVar('id');
        $Itemid = $uriToBuild->getVar('Itemid');

        // and add strings to the $sefSegments array to build the URL.
        // $sefSegments[] = 'something'

        // Add a / as the last element of the sefSegments array to prevent the
        // .html suffix to be appended to the URL later on (if set in 4SEF configuration).
        // $sefSegments[] = '/';

        return $sefSegments;
    }

    /**
     * Participate in building a normalized non-sef URL based on an incoming URI. Query vars values are URL-encoded.
     * Stripping slugs, sorting vars and possibly other things are taken care globally. Only plugin-specific
     * vars processing should happen here. For instance, stripping pagination variables if the plugin
     * handles pagination dynamically.
     *
     * @param array $vars
     * @return array
     */
    public function buildNormalizedNonSef($vars)
    {
        return $this->nonSefHelper->stripFeedVars(
            parent::buildNormalizedNonSef(
                $vars
            )
        );
    }

    /**
     * Check if passed URI is for an extension configured to be left non-sef.
     *
     * @param Uri\Uri $uriToBuild
     * @return bool
     */
    public function shouldLeaveNonSef($uriToBuild)
    {
        // read whatever variables you need to decide whether a
        // URL must be left non-sef
        $view   = $uriToBuild->getVar('view');
        $task   = $uriToBuild->getVar('task');
        $layout = $uriToBuild->getVar('layout');
        $a_id   = $uriToBuild->getVar('a_id');

        // then check these variables and return true
        // if this URL must be left non-sef.

        // by default, all URLs are made SEF
        return false;
    }
}

Loading your driver

As your driver is just a specific PHP class, 4SEF will detect and use it automatically when required, as long as this PHP file is included once.

There are 3 ways to do that, as described below. We suggest to build a real Joomla plugin to wrap your driver, this is the most convenient way for the final users, for updating, etc, although all 3 options are perfectly viable.

Include your class in your extension

You can simply do a include_once my_4sef_driver.php anywhere in your own extension to make the driver available. If it's named correctly (see sample above), 4SEF can detect and use it.

Driver must be loaded early enough

Joomla can start building SEF URLs very early in the page rendering process. If you load your driver yourself that way, it should be done early, for instance at the onAfterRoute event, or even earlier.

Respond to the 4SEF plugins load event

Right before it needs to build its first SEF URL, 4SEF will trigger an event to let you include your driver. You can load your driver by adding the following code to your extension:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php

// Your extension own code here

// Include use statement for 4SEF factory class
use Weeblr\Forsef\Factory as ForsefFactory;


// somewhere in your extension, add the following to respond to 4SEF event:
ForsefFactory::get()->getThe('hook')->add(
            'forsef_on_load_plugins',
            function () {
                // include your plugin file here

                // Sample code to 
                $filename = '/absolute/path/to/your/driver/componentname.php';
                if (file_exists($filename))
                {
                    include_once $filename;
                }
            }
        );

Driver must be loaded early enough

Just as in the previous solution, you should add your event responder early on, likely at the Joomla onAfterInitialise event.

Build a Joomla 4SEF plugin

You can build a real Joomla plugin to wrap and deliver your driver to users. This is quite simple in fact, and will take care of loading your driver early enough.

4SEF drivers are to be installed, and can be seen, in the forsef Joomla plugin category.

Here is the structure of such a plugin:

Directory structure of a 4SEF URL plugin

The /plugin/sample.php file contains your driver, as described in the Driver sample code paragraph above.

Here is the manifest file, sample.xml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="utf-8"?>
<extension type="plugin" group="forsef" method="upgrade">
    <name>PLG_FORSEF_SAMPLE</name>
    <author>Yannick Gaultier - Weeblr llc</author>
    <creationDate>2022-06-03</creationDate>
    <copyright>Copyright Weeblr llc - 2022</copyright>
    <license>GNU General Public License version 3; see LICENSE.md</license>
    <authorEmail>[email protected]</authorEmail>
    <authorUrl>https://weeblr.com/</authorUrl>
    <version>1.0.0</version>
    <description>PLG_FORSEF_SAMPLE_XML_DESCRIPTION</description>
    <files>
        <filename plugin="sample">sample.php</filename>
        <folder plugin="sample">plugin</folder>
    </files>
</extension>

and here is the main Joomla plugin file, sample.php:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<?php
/**
 * Project: 4SEF
 *
 * @package          4SEF
 * @copyright        Copyright Weeblr llc - 2022
 * @author           Yannick Gaultier - Weeblr llc
 * @license          GNU General Public License version 3; see LICENSE.md
 * @version          1.0.0
 * @date             2022-06-03
 */

defined('_JEXEC') or die;

use Weeblr\Forsef\Factory as ForsefFactory;

use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\DispatcherInterface;

/**
 * 4SEF - A loader for a 4SEF SEF URL plugin.
 *
 * @since  1.0.0
 */
class PlgForsefSample extends CMSPlugin
{
    /**
     * @var string location of plugin file from
     */
    private $pluginFileName = '/plugin/sample.php';

    /**
     * Constructor
     *
     * @param DispatcherInterface  &$subject    The object to observe
     * @param array                 $config     An optional associative array of configuration settings.
     *                                          Recognized key values include 'name', 'group', 'params', 'language'
     *                                          (this list is not meant to be comprehensive).
     *
     * @since   1.5
     */
    public function __construct(&$subject, $config = array())
    {
        parent::__construct($subject, $config);

        ForsefFactory::get()->getThe('hook')->add(
            'forsef_on_load_plugins',
            function () {
                $filename = __DIR__ . $this->pluginFileName;
                if (file_exists($filename))
                {
                    include_once $filename;
                }
            }
        );
    }
}