Work In Progress

This book is currently work in progress. Some sections are not yet written. Thank you for your understanding!

Models

The bulk of the implementation logic for Joomla 4 MVC models is the same as in Joomla 3 MVC.

The Model classes extend from one of the base Joomla MVC Model super-classes:

Joomla\CMS\MVC\Model\BaseModel

The most basic model you can get. It does not connect to a database and does not support form. This is the kind of model you may need to use if you have a static view in your component (e.g. a control panel not implemented with a core Dashboard), if your model deals with data outside of the database (e.g. processing images, talking to a third party API over HTTP, converting files, etc) or if it uses an external or third party library (e.g. in Akeeba Backup the BackupModel uses our Akeeba Engine backup engine library).

The basic service it provide is model state management using the getState, setState and populateState methods.

[Tip]Tip

In a proper MVC implementation the only way the Controller would “talk” to a Model is by setting its state and then reading either the return value of the method it called or inspecting the Model's state.

In Joomla MVC the model state is a weird beast. It is ‘normally’ set by reading it from the session and overriding it from the request parameters using the application's getUserStateFromRequest method.

While this is mostly okay for simple administrator pages listing and editing records you may find yourself in situations where you need to “talk” to the model from the Controller. Instead of passing this information around as HTTP GET/POST parameters — which can be inspected and overridden by a curious or malicious user — I urge you to instead override the display method of your Controller and set the Model's state after instantiating it. This is something you will not see anywhere in the Joomla core code but something you SHOULD be doing in your more complex components to avoid embarrassing and easily preventable security vulnerabilities.

Speaking of which, always validate the data type and values of your model state in your code. Writing secure code requires you to adopt a “trust no-one” stance against any data which is not hard-coded into your extension's code! Do not assume that the data will be safe because (you think that) only a Super User can access a specific view. First of all, your assumption may be wrong. Moreover, your Model may be used outside the View you have in mind, even by third party components, plugins and modules integrating with your extension. Trust no-one, not even your own assumptions!

Joomla\CMS\MVC\Model\BaseDatabaseModel

This is an extension to BaseModel primarily adding database support. You can get the applicable database object with $this->getDbo() (Joomla 4.0 or later 4.x version) or $this->getDatabase() (Joomla 4.2 and later versions, including 5.0 and later).

Beyond that, it lets you dispatch events using the global Event Dispatcher you get with $this->getDispatcher() (Joomla 4.2 and later), get the Joomla user object of the currently logged in user with $this->getCurrentUser() (Joomla 4.2 and later), and get the CacheControllerFactory to talk to Joomla's cache with $this->getCacheControllerFactory() (Joomla 4.2 and later).

This is the most used type of model super-class, either having your models directly extend it or indirectly extend it by using one of its descendant classes.

Joomla\CMS\MVC\Model\FormModel

This is an extension to the BaseDatabaseModel which additionally implements Form handling.

[Important]Important

This IS NOT the class you want to extend in pages which edit existing or create new records! What you are looking for in this case is Joomla\CMS\MVC\Model\AdminModel (yes, even in the frontend!).

This is the kind of Model you are going to use on pages which render a Form using an XML file for collecting information from the user BUT NOT for the purpose of editing or creating a record.

A practical example of this is the Joomla Global Configuration page's model (Joomla\Component\Config\Administrator\Model\ApplicationModel) which does render an XML form with the Global Configuration option. Compare this to the article edit page's model (Joomla\Component\Content\Administrator\Model\ArticleModel) and the difference becomes evident right away!

In your components you might want to use this type of Model in custom configuration pages. For example, you may have a component which post new articles automatically on social media. You may want to create different configurations for Facebook, Twitter, Reddit and whatnot. The page managing each of these configurations, each one using its own XML form file, would very likely use a Model class extending from FormModel instead of AdminModel.

Joomla\CMS\MVC\Model\AdminModel

This is an extension to the FormModel which is designed specifically for creating new and editing existing records.

What is the difference? Whereas FormModel is made to deal with the collection of arbitrary data, the AdminModel is made to create and edit records having the same data shape, defined in a database table. As a result, it works together with a Table object. Most of its methods will be talking to the Table object.

It also supports automatic integration with content plugins and implements the methods you need to save new/existing records, reorder records, change record associations, batch process records, check-in and check-out records to the database, change the publish / trash state, and permanently delete records. Yup, this type of Model basically handles nearly everything you need to manage records in the backend of a site except listing records.

[Note]Note

I had personally found it extremely confusing that the AdminModel (and the corresponding FormController) is responsible for handling all the actions you see available in the toolbar of a records list page. When you think about architecture it does make sense. When you are new to Joomla you might wonder why this is not handled by ListModel (and its corresponding AdminController). This is unfortunately a case of “you do this just because”, not a case of using what you'd intuitively think is the right thing to use. I know, right?!

You may also get confused by the fact that an AdminController has a ListModel whereas a FormController has an AdminModel. It looks like someone had a stroke trying to name things.

Thankfully, no, nobody had a stroke — the weird naming comes from the fact that when forms were introduced in Joomla 1.6 we had to maintain backwards compatibility to Joomla 1.5, thereby causing a class naming mayhem. You know what are the two hardest things in software development? Handling dates, managing backwards compatibility and off-by-one errors!

Joomla\CMS\MVC\Model\ItemModel

This is an extension of the BaseDatabaseModel, designed to display a single record in the frontend.

This is something you will use probably a lot in the frontend of your application when you want to show a single record which cannot be edited. Unlike the FormModel and the AdminModel, this Model class does not have any kind of management code for the records. It just displays them and that's it.

[Tip]Tip

Practically speaking, you might just end up extending your frontend Model from your backend Model which in turn extends from Joomla's AdminModel if you need to provide any kind of frontend administration of your component. There is no point creating two models to talk to the same data, one just to show it and one just to modify it. It also makes no sense to have your frontend Model extend from AdminModel and duplicate your code (WET code) when you can simply extend your backend model and refrain from repeating yourself (DRY code).

Joomla\CMS\MVC\Model\ListModel

This class extends from the BaseDatabaseModel and has the ability to manage forms (you need them for Search Tools a.k.a. list filters) and, crucially, to provide pagination-aware list of records from a database table.

You know how the main Joomla backend user interface pattern is a list of records? Yup, this is the Model which implements it.

[Tip]Tip

In the frontend of your component you will very frequently find yourself needing to list a bunch of items in pagination-aware lists. The traditional Joomla code pattern was to create a frontend Model which extended from BaseDatabaseModel and have the same code to list items copied over from the backend.

Don't do that. It's WET code and I've already mentioned that it's the source of many a bug.

What you will practically find yourself doing is extending the frontend Model from the backend Model. Unlike Joomla 3, this is now possible!

You need to be acutely aware, though, that you will need to override the populateState method in the frontend model. By default, this method will accept any kind of user input for filtering the list of records which could indeed be used by a malicious user to display items they are not supposed to access!

An even better approach is what I hinted to earlier. Override the display method of your Controller. In there, first do a getState on your model (to run the populateState and be done with it), then explicitly set the Model state using its setState method before passing it to your View object. When you do that, the state set by your Controller's code overrides whatever populateState did, thereby mitigating any security risks.

Interfaces and Traits

In Joomla 3 each Model type tried to do everything under the sun and even implement features which might have nothing to do with your component, like tags and versioning.

In Joomla 4 and later versions the default Model classes don't do any of that. That's a good thing because it promotes the computer science principle called Separation of Concerns. If you need additional features you will be doing a bit of object composition.

For those with a Computer Science background, you may wonder how you can do object composition in a language like PHP which, unlike C for example, does not allow classes to inherit (extend) from multiple classes. The answer is by using Traits provided by Joomla.

\Joomla\CMS\Versioning\VersionableModelTrait

Implements support for Versions (record history).

Only applicable to models extending from AdminModel. This requires the Behaviour - Versionable plugin to be published. The corresponding Table must implement the \Joomla\CMS\Versioning\VersionableTableInterface interface and the corresponding Controller must use the \Joomla\CMS\Versioning\VersionableControllerTrait trait.

\Joomla\CMS\MVC\Model\WorkflowBehaviorTrait

Implements support for Workflows (prescribed steps for state changes in records).

Only applicable to models extending from AdminModel. The model must implement the \Joomla\CMS\MVC\Model\WorkflowModelInterface interface.