Tools to build a Component - 4: Built-in Extras and some Setup Tools
In this episode we will expand our example component, mainly by using features that are built-in into Joomla. And we show some tools that can help us setting up a custom component.
Our journey until now
In this series we want to show some tools that can help to build components for Joomla. The first three episodes were mainly to introduce the example and show some implementations of it without building a component.
- In episode one we introduced our example, an event schedule, for instance for a Joomla Day. We explored the basic entities and made an implementation with core functionality (mainly additional fields (1)). We were not yet building a component and didn’t use any tools other than Joomla itself.
- In episode two we showed an embedded implementation, using a Content Construction Kit (CCK) and Application Builder (Seblod and Fabrik). So, we still didn’t build a component, but we used some tools.
- In episode three we finally started building a component. We did that “by hand”, not using specific tools (2) to build Joomla components. We also spent some time on relationships between entities and how those are implemented in Joomla.
So we have built our example in four different ways now: core/additional fields, Seblod, Fabrik, and a basic component. This is how the event schedule looks like in the last episode (using some data from the Dutch JoomlaDagen 2024):
Multiple event schedules
In our basic component we now have:
- Events: things that happen during some time in a day, like a presentation at a conference. They can be of some type, for instance beginners, advanced etc.
- Actors: events are “done” by actors. For instance, at a music festival the performances (events) are done by bands (actors).
- Sections: events that occur with overlapping time in the schedule are placed in a different section. The rooms in the above schedule are an example of sections.
- Containers: different schedules can be put into containers. They are implemented as tabs. In the above example we have two tabs for the two days of Dutch JoomlaDagen, the Friday and the Saturday.
Say we want to have such schedules for multiple years or maybe even for completely different events (3)… how can we let those different schedules coexist? We need something to store those different editions. We will therefore add:
- Event Schedules: different editions of some festival or conference.
We add an event schedule as an extra entity. Each event schedule has its own containers, sections, events etc. That means that in all tables we used until now, we’ll have to add an eventschedule_id
as foreign key.
In a Joomla component we have a special place for such an update: in the administrator side, in the /sql/updates/mysql folder (4), we add a database migration file for each version of our component in which we update the database schema (5).
In this way we start with empty containers, sections, events, event types and actors for each event schedule. As an enhancement we might later add a feature to easily copy those data from one event schedule to another.
Adding built-in bells & whistles
There are a lot of features built-in in Joomla that we can use relatively easily in our own component. The (randomly ordered) list provided down here is not even complete. But we don’t need everything everywhere, so to not bloat your component it is wise to not automatically put everything in it, but only the features that are needed for your entities. In our case we will only implement a handful of features in our minimal component.
Some built-in features should always be used for security reasons (and are left out of this list). For instance the use of a token to check against XSS attacks.
1. Language strings
We already used language strings (6) in our example component.
Joomla’s AdminController automatically creates some language strings for bulk actions, like COM_EXTENSIONNAME_N_ITEMS_CHECKED_IN_1
. The suffix is auto-generated and is language dependent. For English you can have suffixes _1
(= one item, singular), _0
(no items, plural) and _MORE
(multiple items, plural). When using multiple entities, they would all get the same language string, using “item” instead of the name of the entity. In the controller you can define an entity-specific prefix for the language strings: $this->text_prefix
. You can make it different for each entity, like $this->text_prefix = ‘COM_EXTENSIONNAME_ENTITYNAME’ (
7).
2. General parameters
You can define general parameters for a component. You can give those parameters values by clicking on “Options”. What general variables we want to have in our component is defined in config.xml in the administrator side of the component, using Joomla’s XML form definition.
Retrieving those general component parameters in your component is done with the ComponentHelper:
$params = ComponentHelper::getParams('com_eventschedule');
For menu-parameters add fields to <fields name="params">
in the frontend default.xml file in the template layout folder. Menu parameters are added to the component parameters, overriding the component parameters with the same name. If you need the component parameters plus the menu parameters, retrieve them from the Site Application:
$params = Factory::getApplication()->getParams());
3. Item ordering
When an entity needs to be put in an order, we add an “ordering” field (integer) to the database table. Don’t do it automatically everywhere, but look where it could be useful. In our example for instance we want an ordering for containers, sections and event types, because we want to be able to display them in some specific order.
in the template layout of the listing in the Administrator side you can add a drag & drop ordering field. In the heading of your table:
<th scope="col" class="w-1 text-center d-none d-md-table-cell">
<?php echo HTMLHelper::_('searchtools.sort', '', 'a.ordering', $listDirn, $listOrder, null, 'asc', 'JGRID_HEADING_ORDERING', 'icon-sort'); ?>
</th>
In that column:
<td class="text-center d-none d-md-table-cell">
<?php
$iconClass = '';
if (!$canChange) {
$iconClass = ' inactive';
} elseif (!$saveOrder) {
$iconClass = ' inactive" title="' . Text::_('JORDERINGDISABLED');
}
?>
<span class="sortable-handler<?php echo $iconClass ?>">
<span class="icon-ellipsis-v" aria-hidden="true"></span>
</span>
<?php if ($canChange && $saveOrder) : ?>
<input type="text" name="order[]" size="5" value="<?php echo $item->ordering; ?>" class="width-20 text-area-order hidden">
<?php endif; ?>
</td>
The code above is from com_content and also includes some variables $canChange
and $saveOrder
, $listDrn
, $listOrder
that you have to initialise first. The javascript-code for the drag & drop with Ajax-saving has to be added as well as the Ajax handling of this ordering in PHP.
So, on top of /administrator/components/com_content/tmpl/articles/default.php you’ll see a.o.:
if ($saveOrder && !empty($this->items)) {
$saveOrderingUrl = 'index.php?option=com_content&task=articles.saveOrderAjax&tmpl=component&' . Session::getFormToken() . '=1';
HTMLHelper::_('draggablelist.draggable');
}
You won’t find the saveOrderAjax()
method directly in the ArticlesModel: this method is part of the Joomla\CMS\MVC\Controller\AdminController
and is already baked into Joomla core for you to use in your own extensions.
4. Alias handling
In many Joomla database tables you can see an “alias” field. Most of the time it is a string that you can use in an URL (only lower case letters, numbers and dashes). It can also be used for SEO-purposes. In our event schedule example you might use it for actors or maybe for events. It depends: do you want to possibly have an URL with this entity?
To automatically generate a string useful for an URL there is a handy small string-utility built into Joomla to do that for you: \Joomla\CMS\Filter\OutputFilter::stringUrlSafe($yourString)
.
5. Locking, check-out
To prevent two users from trying to edit the same article at the same time each article has a checked_out
database field to indicate whether or not it is in use. The item is “locked” from editing by another user as soon as you mark it as checked out. In order to do this with your own entities, you only have to place a checked_out
and a checked_out_time
field in your database table. Then Joomla’s Table-object will automatically check your item out or in.
Don’t do it for every entity. A shopping cart or a review for instance will only be edited by one user.
6. Filters and search in the list view
In the list-view we can add filters and a search-field for our items. Again, Joomla has a lot built in that we can use. To apply filters and search do the following (8):
- Add a filter_xxx form, where xxx is the name of the form you want to add the filter to.
- If you want a search field, add a text field with name “search” to that form.
Joomla\MVC\Model\ListModel
providesgetFilterForm()
andgetActiveFilters()
methods. We use them in the View (9) to get the filter information.- Put the form in the default.php template layout, rendering the
joomla.searchtools.default
layout by usingJoomla\CMS\Layout\LayoutHelper
. - In the model, put the results of the user’s filter choice in the state and use it in the
getListQuery()
method.
7. Custom Form Fields
In the last episode we implemented our example event schedule with the available core form fields. But there are many more possibilities to create your own custom form fields (10). For relationships for instance we used sql fields. When using them with the multiple selection possibilities, you don’t have such a nice interface as we have for tags. It is however very easy to make your own custom form field for it: just extend from a ListField. For the options you can use the same query as we did for our sql field.
N.B. If you need the database in your custom form field, just use $this->getDatabase()
. Then you’ll get the database that you injected into your MVCFactory (that injects it into your Model, that injects it into the FormFactory, that puts it into the form and that finally sets the database driver into your FormField). So you are all set, dependency injection got you covered. Still, you can often see something like Factory::getContainer()->get(DatabaseInterface::class)
(11). Conjuring it from the container as a global is a service locator anti pattern. Or even worse Factory::getDbo()
(12). Some developers would obviously love to go back to the good ol’days of global $mainframe
😉
8.Column sorting in the list view
You can add links to the table column headers in order to give users the possibility to sort on that column. Put HTMLHelper::_('searchtools.sort', 'COM_MYEXTENSION_MYVIEW_TABLEHEAD_MYFIELDNAME', 'fieldname', $listDirn, $listOrder); ?>
in the table head. If clicked, put the submitted data (on what column do you want to sort and in what direction) in the model state and use it in the getListQuery()
method for the correct sorting of the records.
9. Trash
Instead of just deleting an item, you can put it in trash, so it might be restored. In order for this to happen you need a “published” column in your database. Trashed items will have a published value of -2. You can also archive items: the published value will be 2 then (13). Use the task “trash” instead of “delete” and everything will be handled automatically.
10. Batch processing
Some actions can be performed on multiple items: copy, move, assign language or rights, add tags. You can add that to your own components: add a batch button in your view, handle the request in the controller, execute the batch processes in a model and redirect back to your view (14).
11. Categories
Joomla’s categories are hierarchical (= nested). You can assign one category to an item of an entity (15). In order to use those core categories in your custom component (16):
- Add a field in the database table to store the category id (and update the sql installation and update files).
- Add a record to the category table for an “uncategorised” category of your extension (17). Add this to the updated sql too.
- Add a menu-link to your components manifest in order to show the categories for this component in the backend.
- Add a field of type=”category” to the input-form for your entity.
- Add such a field to a filter form too if you want to filter on categories in your list view.
- Because com_categories is now a dependency of your component, add a CategoryFactory to your component in the components’ service provider.
- Add the CategoryServiceInterface to Extension/YourComponent.php and add the CategoryServiceTrait to implement the interface.
- Update list-queries in models if you can filter on categories.
- If you want access control on the category level, add a category-section in access.xml in the backend.
- In the frontend add a CategoryService in order to map the used extension, fieldnames and database-table to the categories component.
12. Tags
Joomla’s tags are nested, just like categories. Contrary to Joomla’s categories you can add multiple tags to an item of an entity. Tags and entities have an n:n relation. The tags are stored in the #__tags
table and the junction table is #__contentitem_tag_map
(18). Storing and retrieving the tags is done in the \Joomla\CMS\Table\Table
-object. The tags component is based on the Unified Content Model (UCM), so mapping in the junction table is between records of the #__ucm_content
table and the #__tags
table. The #__ucm_content
table has copies of the records that have tags. The original table of the entity (= “content type” in the jargon of UCM) can be found in the #__content_types
table via the core_type_alias
(in the format: <component name>.<entity name>.
The tags-functionality dependency is not injected in the component and hence not found in the service provider of your component. Communication with the Table-object is mostly done via the Taggable behaviour plugin.
In order to use tags in your component:
- In the Table-object: implement the TaggableTableInterface by using the TaggableTableTrait.
- Add a field of type=”tag” to the input-form for your entity.
- Add such a field to a filter form too if you want to filter on tags in your list view.
- We need to add the entity in the
#__content_types
table. We’ll do it in an installation script (19) (and/or in the updated sql files). - Add filtering on tags to the
getListQuery()
method of the backend list model. - In the view add the right language attribute to the tags using the
setFieldAttribute()
method of the form. - In order for the batch processing to also be used for the tags, we insert a form field. With the help of this field it is possible to select a tag that will be assigned to all selected items.In the template layout render the field with the
joomla.html.batch.tag
layout by usingJoomla\CMS\Layout\LayoutHelper
(20).
13. Pagination
When a lot of items have to be displayed in a list view, you can divide it into chunks, so you get several pages with a part of the list.
- In the list view add a
$pagination
variable, to put the pagination object in. You get it with$model->getPagination()
. - Display the pagination footer:
<?php echo $this->pagination->getListFooter(); ?>
in the default.php template layout. - You don’t add the pagination variables (limit and limitstart) to the query in the list model: the query will be “decorated” with them in the final call in
_getList()
. State is also set in the parent ListModel, that’s why you should callparent::loadUserState()
in theloadUserState()
method of your list model.
14. Form validation
Joomla provides both client-side and server-side validation out of the box.
- Client-side validation (21) is performed in the browser using Javascript which is downloaded with the web page.
- Server-side validation (22) is performed in PHP on the server, when the form fields are sent in the HTTP POST request.
15. Access Control Lists (ACL)
We have two types of access control in Joomla:
- Viewing access: who can see what content? (23)
- Permissions to do specific actions on the content. (24)
Basically all rights, both viewing access and action permissions, are connected to user groups. User groups with specific viewing access rights are put in a so-called Viewing Access Level.
If you want viewing access on the level of individual items of entities:
- Add an “access” column (unsigned integer) to your entity’s table. It is a foreign key to the
#__viewlevels
table. - Add an “access” field,
type="accesslevel"
, to your entity’s input form. - Restrict the list query for that entity to only show items for which view access is allowed for that user.
Actions for the component are defined in the access.xml file. You are not restricted to the core actions (create, edit, edit.own, delete, etc.), but can also define your own actions, specific for your component. Permissions for those actions are allowed or denied in the options of your component; you’ll have to add a permissions fieldset with a type="rules" field in your config.xml file.
In order to get action permissions for the individual items of an entity (25):
- Add an
asset_id
in the database table of your entity. - The
asset_id
is set in the\Joomla\CMS\Table\Table
class, helped by the\Joomla\CMS\Table\Asset
class. In order to do that the Table class needs some information about your entity, provided by overriding_getAssetName()
,_getAssetTitle()
and_getAssetParentId()
in your entity’s Table class.
16. Additional fields
The possibility for site administrators to create their own fields can also be added to your custom component (26). Contrary to categories, language associations or routing, those fields are implemented via plugins, not injected as a dependency in your component and hence not found in the service provider of your component. This dependency is “conveniently” hidden.
To have additional fields available in your own component:
- Add the FieldServiceInterface in Extension/YourComponent by implementing two methods:
getContexts()
andvalidateSections()
. Here you define for which entities in which views you want to have additional fields. - Add menu-links to your component’s manifest-file for fields and field groups, so the administrator can easily add new fields.
- The additional fields are automatically added to the form and rendered in the backend view template by the Fields system plugin. Using UITab in your view template renders the additional fields in the same way as in Joomla’s core extensions.
- The additional fields are rendered in a standard way by the Fields system plugin. If administrators want their own custom fields rendered in a special way, they can make a template override.
All current core additional field types are available in your component. If you install more additional fields, they are also available for your component. And if you want to have some specialised additional fields, you can even make custom additional fields, just by adding a plugin.
17. Multilingual association
Just like with articles you can associate translations of items of an entity for several languages (27). To use it in your own component:
- Add a “language” column (
char(7)
) in your entity’s database table to store the language code for that item. - In the input form of the entity add a “language” field,
type="contentlanguage"
to select what language this item is. - Add the AssociationServiceInterface in Extension/YourComponent by using the AssociationServiceTrait.
- In your services/provider.php set an AssociationsHelper (= an AssociationExtensionInterface) in your component using the
setAssociationExtension()
method from the AssociationServiceTrait (28). - Add an AssociationsHelper with three protected variables (
$extension
,$itemTypes
and$associationsSupport
), supply a value for those variables (put your entity name in the$itemTypes
array) and add three public methods:getAssociations()
,getItem()
andgetType()
.
18. Hide table columns
You can have users choose to hide columns in the listing. Use the WebAssetManager to add the table.columns
script (29).
19. Versioning
You can have versions of the entities in your own component (30) in the same way as you can have versions of articles (31). The versions are managed in com_contenthistory, which works together with the Unified Content Model (UCM) and uses the #__content_types
table. The version-information is stored in the #__history
table; in Joomla 3 this table was called #__ucm_history
. The versioning is handled while storing (or deleting) a record in the Table-object.
The versioning-functionality dependency is not injected in the component and hence not found in the service provider of your component. Communication with the Table-object is mostly done via the Versionable behaviour plugin. Just like with tags.
To get versioning in your component:
- Add the name of your component and your entity in the
#__content_types
table. Also add some options to add Labels to the pop-up windows (see documentation). - Add 2 fields in the general options of your component (config.xml): save_history and history_limit. Set a default maximum number of versions to keep.
- Add a public
$typeAlias = 'com_<your component name>.<entity name>';
- Add a Versions button to your edit form.
- Add Version Note field to form (not to the database table of the entity!)
20. Adding toolbar buttons
In the backend view you define what buttons you want to display. You are not limited to the standard Joomla buttons (save, save & close, close etc.) to add to the toolbar in your component. If you need to do something that is special for your component, you can make your own buttons that trigger your custom actions in the controller.
Based on the action permissions buttons are displayed or not. You have to code that yourself, using the rights a user has.
21. Hits, rating
Won’t discuss the usefulness of it, but there is standard functionality to count the number of hits of an item and also to give a rating to some content item.
The hits are incremented by the \Joomla\CMS\MVC\Table\Table
object. You’ll have to use a column named “hits” in your database table (32). The Table-object has a hit()
method to handle the hits. The Table-object is normally used in a model, so create a hit()
method in your model; get the table object and call $table->hit()
(33). You can then call $model->hit()
from your controller or view as soon as you display your entity.
The display of rating is handled by the Vote content-plugin, but only works for articles in com_content. If you want the same functionality in your own component, you’ll have to make your own plugin, but inspiration can be found in this Vote content-plugin (34).
22. Router / SEF URLs
URLs on the website can be made a bit more human readable, generally known as “search engine friendly”, by exchanging ids and other coded stuff to reach the correct content (35).
23. Workflow
You can add a custom workflow to your component (36). A workflow has stages and transitions. An entity item can be in a stage and from that stage you can transition to another stage. Both stages and transitions are tied to ACL-permissions (= have an asset_id
) and you are completely free to create them for your own needs.
Because of backwards compatibility with the publishing workflow we still had in version 3 of our CMS, Joomla is shipped with a hybrid implementation mimicking the older static states of published, unpublished, trashed, archived and featured. Some plugins then take care of filling in a state-field in com_content.
You can use this traditional publishing workflow, just be sure you have a “state” field in your entity. But in most cases it is probably better to create your own custom stages and transitions and use the dynamic possibilities of the workflow component.
24. Metadata
A site can have metadata tags and you can put that in your own component too. Joomla 5.0 introduced new tools to help add Schema.org compliant metadata (37).
25. Email cloak
It is a standard piece of Javascript to hide your email address from directly scraping. Could save you some spam (or at least: did so some years ago). If you just want to hide an email address in some view output you can use echo JHtml::_('email.cloak',
(38). If you want to hide an email address that is somewhere in a text, you’ll have to put that text through a content plugin, triggered with onContentPrepare.
26. Finder
If you have a search-possibility on your site, you are using com_finder, formally known as “smart search”. You can also index the content of your own component: you have to write a finder plugin for that. See the plugins under “/finder” for inspiration for plugins for different content types.
27. Help pages
Of course you provide documentation for your component. In Joomla’s core backend we have help-pages. It is a good idea to make help pages for your own component: then the users / administrators can easily find the relevant information (39).
28. Action logs
In Joomla’s backend all kinds of actions will be logged. Handy to see who did what lately. You can add actions from your own component by writing an actionlog plugin. See the /actionlog/joomla plugin for inspiration. Or maybe you have some extensions installed that have an actionlog plugin, like AkeebaBackup.
29. Extra views: modules
Huh, modules as an extra feature of a component, what the heck? Well, look at the bigger picture to see how modules are related to components. Originally in Joomla you can only display one component on a page. To get some extra pieces of content on a page modules were invented. Often they just offer an extra (mini-)view of your component. You can see that very nicely in the new Articles module: it uses the model of the articles component. In fact we only show extra views of the articles component.
So, if you need extra views of your component on a page where a component is already shown: modules are your friend. You can pack them together with your component in one package.
30. Extensions of a component: plugins
Plugins were primarily meant to give the possibility to extend parts of your component. For instance if you have some shopping cart where all kinds of payment methods can be added. With some clear instructions others can also provide custom plugins.
31. Web services / API
There is a standard JSON-API webservice in Joomla and you can add it to your own component too. This standard makes use of your backend models and it is rather simple to have that same backend functionality available as a web service.
If you only need to GET some simple JSON -output of your content that can be consumed as a web service, you don’t have to output the full JSON-API with links, data, relationships etc. You could use the Joomla\CMS\MVC\View\JsonView
(without “Api” in its name). Then you also don’t have to take the built-in pagination into account with its standard limit of 20 items (40).
32. CLI
When actions for your component have to be done in a CRON-job, it might be handy to make some CLI extension for it (41).
Tools that can help
A. Setup Basic Files
In this section we will look at some smaller tools that can help to scaffold some basic files for an extension.
A1. create-joomla-extension
https://github.com/dgrammatiko/create-joomla-extension
This Node/npm based CLI tool will generate a basic boilerplate of a component, module, plugin, library or template. The command is expected to be run inside a directory that will become the source of your code. I tried out version 1.0.1 (on Windows 11).
After some questions for basic user info, that will be used in the manifest file you can choose what kind of extension you want to build (component, module, plugin or template). If you create a component, its name is asked:
And some files for a basic (frontend) component are made. No entities, just a DisplayController and a default view. In the admin-side only the necessary files to install a component (manifest, service provider and language files):
After running npm install
, npm run init
and npm run build
a complete Joomla (4.4.0) instance is fetched, put in the www-directory and the scaffolded files for our test-component are symlinked into the Joomla instance.
The basic media files for the component are also made (and symlinked):
This setup is intended to get started with a component (or other extension), discover-install it in a Joomla installation. Work on it, while testing it out in Joomla and finally build the zip to distribute the package. The package is created with an npm run release
command. The setup files are a very bare minimum without any entity.
A2. JEXT
https://github.com/ahamed/jext-cli (42) (1.0.0-beta-1. Last update: last year)
This only works on Linux & Mac.
You first make a component without any views. You give the component a name and provide some basic information for the manifest.
Next you make views per entity. You can generate singular and plural views in both frontend and backend. You can choose which of the 6 possible combinations of those views you want. So both for frontend and backend you can have 0, 1 or 2 views (of one entity). The entities are not related and only a basic CRUD interface is provided.
You cannot adjust the fields of the entities; they all have the same fields. A title and description are the only basic fields. The rest of the fields are for the system and some “bells and whistles”: id, asset_id, alias, published, checked_out, checked_out_time, ordering, access, language, created, created_by, modified and modified_by.
B. Some other handy tools
In this section we will look at some other smaller tools that can also be helpful in developing an extension.
B1. JoRobo
https://github.com/joomla-projects/jorobo (43)
Robo is a cross platform PHP task runner to automate common tasks. JoRobo is a collection of Robo-scripts for Joomla-related tasks:
- Build a distributable package from your extension files.
- Add symlinks to a Joomla installation to locally use your extension in Joomla. Very handy during development. The extension is then installed via discovery. In Windows the command line application must have administrator rights in order to be able to make symlinks.
- Run tests for your extension.
In the standard JoRobo scripts it is assumed that your files are all in the /src directory and then in the directories as used in a Joomla instance, like administrator/components/com_<extension name> etc. The language files are assumed to be in the src/language folder (or src/administrator/language for the administrator side), not in the extension itself; that is a good practice, as Joomla is moving in this direction of centralising the language files.
When using an extension via symlinks, don’t uninstall the extension,
for you might run the risk that your original files will be deleted.
There are also tasks in JoRobo to generate extension skeletons, but those tasks are still empty and not (yet) useful.
B2. Language strings
When crafting a Joomla extension “by hand”, one of the challenges is to get all language strings in the language file. Most of the time you’ll have a language strings file open, to - on the fly - gather all the language strings that you as developer are using in your code.
When you forget to put language strings in your file, they won’t be translated. Of course you can check every page for untranslated strings. And Joomla’s language debug setting can also help with that. But still: you’ll have to check every page, also for all edge cases, warnings and error messages.
I found a small Python script to scan for language strings that were not used in an .ini language file. Will translate it to PHP and put it in the repository for this series. When code for a component is generated by tools we will show in the next episodes of this series, then the language files are automatically built.
B3. Joomla downloader
https://github.com/JoomlaLABS/Joomla_Downloader (last update: 10 months ago)
This is a small handy PHP script I sometimes use when I want to quickly prepare some Joomla instances with different versions. Put the script in a directory under your localhost and run /<directory name>/joomla_downloader.php.
Next month
In the next episode we are starting with an online tool to generate a complete component:
Component Creator.
Resources
Building a component:
- Joomla! Programmers Documentation, our ever expanding online source of information.
- Book: “Developing Extensions for Joomla! 5” by Carlos Cámara.
- Online book: "Joomla Extension Development"by Nicholas Dionysopoulos.
- Robbie Jackson’s tutorial: “Developing an MVC Component”. Made for Joomla 3, but there is a lot that is still useful.
- Look at core components. They are supposed to be an example for your own code.
- Book: "Developing Extensions: Step by step to an working Joomla extension" by Astrid Günther. Out of sale at the moment; hopefully a new version will be published.
Some smaller tools from this article:
- create -joomla-extension (Javascript): some scaffolding, symlinks and packaging.
- JExt (PHP-CLI): some scaffolding, only Linux and Mac.
- JoRobo (PHP-CLI): packaging, symlinks and other tasks (for instance: running tests).
- A script to gather missing language strings (Python).
- Joomla Downloader (PHP script to easily download and unpack different versions of Joomla)
Articles in this series about Tools to build a Component:
- Episode 1: Introduction and Core Solution
- Episode 2: An Embedded Application in Joomla
- Episode 3: Creating a Component
- Episode 4: Built-in extras and some setup tools (this article)
Extra material like the code for our example event schedule component can be found in the repository for this series.
Notes
- In episode one I explained why I use the term “additional fields” instead of “custom fields”. I use “form fields” for the fields that we use in our forms in the components we build. Both additional fields and form fields can be standard and custom.
- I use general coding tools, like a code editor (IDE) and git, but in this series we’ll show some tools specifically for building Joomla components.
- The word “event” is ambiguous here: until now we used it for the presentations of a conference, but in this sentence I used the word event suddenly for the whole conference.
- This folder is for MySql. Joomla also supports PostGreSQL and in order to be useful in all Joomla installations your component should also provide installation files and updates for PostGreSQL. In practice it is very uncommon to do that.
- Update database schema: see https://manual.joomla.org/docs/building-extensions/install-update/installation/manifest#sql. N.B.: the update-files are used when updating an already installed component. When installing the component for the first time, only the sql installation file is used. See https://stackoverflow.com/questions/45261640/sql-update-file-for-each-version-and-what-is-the-content#answer-46533958
- For the use of language constants see https://manual.joomla.org/docs/general-concepts/multilingual. Also see Carlos's book pages 124-127.
- In Joomla core you can see this text_prefix in action in com_banners, with banners and clients as different entities.
It was also used in a former version of the new JED-component. Look at the plural controllers (Emails, Extensions, Reviews, Tickets) in https://github.com/joomla/com_jed4/tree/main/public_html/administrator/components/com_jed/src/Controller; but in the (still unfinished) newer version https://github.com/joomla-projects/Joomla-Extension-Directory/tree/main/src/administrator/components/com_jed/src/Controller thisCOM_BANNERS_BANNERS_N_ITEMS_ARCHIVED_1="Banner archived." COM_BANNERS_BANNERS_N_ITEMS_CHECKED_IN_1="Banner checked in." COM_BANNERS_BANNERS_N_ITEMS_CHECKED_IN_MORE="%d banners checked in." COM_BANNERS_BANNERS_N_ITEMS_DELETED="%d banners deleted." COM_BANNERS_BANNERS_N_ITEMS_DELETED_1="Banner deleted." COM_BANNERS_CLIENTS_N_ITEMS_ARCHIVED_1="Client archived."COM_BANNERS_CLIENTS_N_ITEMS_CHECKED_IN_1="Client checked in." COM_BANNERS_CLIENTS_N_ITEMS_CHECKED_IN_MORE="%d clients checked in." COM_BANNERS_CLIENTS_N_ITEMS_DELETED="%d clients deleted." COM_BANNERS_CLIENTS_N_ITEMS_DELETED_1="Client deleted."
$text_prefix
is left out. - Filters and search in list form: see Carlos’s book page 86-89 for code example. If you happen to have Astrid’s book: chapter 21.
- Call those getters directly on the model instead of using the AbstractView get() method, as that will be deprecated, see https://github.com/joomla/joomla-cms/pull/44162.
- Custom form fields: see https://manual.joomla.org/docs/general-concepts/forms-fields/custom-fields-overview and https://manual.joomla.org/docs/general-concepts/forms-fields/example-custom-fields
- Or
Factory::getContainer()->get('DatabaseDriver')
. It is for instance used in Carlos's book (pag.94) and by Component Creator. - For instance in Astrid’s book, in boilerplate code and in Joomla Component Builder.
- You can see the possible values for the published field in
\Joomla\CMS\MVC\Controller\AdminController
, line 200:$data = ['publish' => 1, 'unpublish' => 0, 'archive' => 2, 'trash' => -2, 'report' => -3];
but I’ve never seen the -3 value being used. - Batch processing: see https://docs.joomla.org/J3.x:Developing_an_MVC_Component/Adding_a_batch_process
- There are extensions that allow multiple categories, for instance Hikashop. That is done with their own implementation of categories, not with the core com_categories. See Carlos's book page 113 for advantages and disadvantages of developing your own category system.
- Categories in your custom component: see Carlos's book pages 110-117, including adding custom form fields to your extension’s categories. If you have Astrid’s book: chapters 16 “Set Up Categories in Backend” and 32 “View by Categories”. For some general information about categories see https://manual.joomla.org/docs/general-concepts/categories/
- With core Joomla categories you can only add categories to an extension, not to one entity (content type) of an extension.
- N:n relation: see episode 3 for information about many-to-many relations and the use of a junction table.
- Update tables in installation script: see Astrid’s book paragraph 36.1.2.3. for an example of how to add the content-type record in the installation script.
- Batch processing tags: see Astrid’s book paragraph 36.1.2.7.
- See https://manual.joomla.org/docs/general-concepts/forms/client-side-validation
- See https://manual.joomla.org/docs/general-concepts/forms/server-side-validation
- See https://manual.joomla.org/docs/general-concepts/acl/acl-access/ for an introduction
- See https://manual.joomla.org/docs/general-concepts/acl/acl-permissions/ for an introduction
- Especially see https://docs.joomla.org/J3.x:Developing_an_MVC_Component/Adding_ACL. This excellent documentation was made for Joomla 3, but the ACL hasn't changed since then. Just imagine the namespaced version (
\Joomla\CMS\Table\Table
) instead of the old classnames (JTable
). - Adding additional fields: see Carlos’s book pages 117-123, Astrid’s book chapters 18 (backend) and 19 (frontend). And the Joomla 3 tutorial https://docs.joomla.org/J3.x:Developing_an_MVC_Component/Adding_Custom_Fields.
- Multilingual association: see https://docs.joomla.org/J3.x:Multilingual_Associations/Developers and https://docs.joomla.org/J3.x:Developing_an_MVC_Component/Adding_Associations (for Joomla 3, so read namespaces instead of the old JClassNames). In addition to this we now also have to inject the associations software into the component (dependency injection in the service provvider).
- AssociationsHelper in service provider: see the services/provider.php of com_content as an example.
- Adding
table.columns
script: see https://manual.joomla.org/docs/building-extensions/components/table-columns - Adding versioning: see https://docs.joomla.org/J3.x:Developing_an_MVC_Component/Adding_Versioning and https://docs.joomla.org/Using_Content_History_in_your_Component (both for Joomla 3). The
#__history table
was still called#__ucm_history
in Joomla 3. The behaviour/versionable plugin also didn’t exist yet; in Joomla 4/5 you don’t have to explicitly add the observers anymore. - See https://www.joomlashack.com/blog/joomla/content-versions/ how to use versioning for articles.
- Hits in Table-object: see https://docs.joomla.org/Using_the_JTable_class
- See for instance the hit() method in
\Joomla\Component\Content\Site\Model\ArticleModel
. - Handling of the submitted rating can be seen in the frontend
ArticleController->vote()
andArticleModel->storeVote()
methods. - Using the router: see Carlos’s book pages 77-81, https://manual.joomla.org/docs/general-concepts/routing/ and Astrid’s book chapter 33.
- Workflow: see https://docs.joomla.org/Publishing_Workflow_Implementation for implementation (“stage” was still called “state” back then and the default Joomla publishing workflow was not yet implemented). Also see: https://community.joomla.org/gsoc-2017/publishing-workflow.html and https://community.joomla.org/gsoc-2017/publishing-workflow.html, also from 2017. Later JCM articles: https://magazine.joomla.org/all-issues/march-2021/joomla-4-the-new-publishing-workflow-feature (2021) and https://magazine.joomla.org/all-issues/january-2022/explore-the-core-workflows (2022). For some background, here are some tutorials (both from 2020) how to use the workflow feature with com_content: https://www.joomlart.com/blog/the-all-new-joomla-4-workflow-feature-explained and https://dj-extensions.com/blog/joomla-4/joomla-4-workflow-explained. There is no actual workflow implementation tutorial for custom components; I’ve put it on my list…
- See https://kevinsguides.com/guides/webdev/joomla/content/joom-schema/ for adding schema.org compliant meta-tags.
- See https://docs.joomla.org/How_to_cloak_email_addresses
- Help pages: see Astrid’s book, chapter 28.
- JSON view web services: a tutorial for that can be found on https://manual.joomla.org/docs/web-services-api/json-response-format/. Web Services for Akeeba extensions are also an example of the use of this “bare” JSON-format.
- See https://docs.joomla.org/J4.x:Writing_A_CLI_Application/en
- Here is a description of the possibilities of JEXT: https://dev.to/ahamed_/create-your-own-joomla-4-component-using-the-jext-cli-application-o5h
- JoRobo: also see JCM October 2023 https://magazine.joomla.org/all-issues/october/jorobo-how-to-professionelly-manage-extension-development
Some articles published on the Joomla Community Magazine represent the personal opinion or experience of the Author on the specific topic and might not be aligned to the official position of the Joomla Project
By accepting you will be accessing a service provided by a third-party external to https://magazine.joomla.org/
Comments