Tables, or rather Table classes, in Joomla's MVC are an abstraction to a row of an actual database table. They are used internally by Models to create new records, modify existing records and delete old records. It's not exactly an Active Record pattern as it does not really participate in the R (Read) of the CRUD operations — Joomla's models query the database directly for listing multiple records and returning a single record. It also does not handle relationships. Like, at all.
The logic of how Table classes work has not changed between Joomla 3 and 4. They are namespaced and they have the familiar superclasses they extend from:
- \Joomla\CMS\Table\Table
-
A plain old record in a database table.
- \Joomla\CMS\Table\Nested
-
A record in a database table which supports Nested Sets (think how categories work, where categories can be placed under other categories and so on).
The table MUST have the following columns:
-
parent_id
. The primary key of the parent node. Provides adjacency list data for nodes. -
level
. The depth level of the node in the tree. -
lft
. The left value of the node for managing its placement in the nested sets tree. -
rgt
. The right value of the node for managing its placement in the nested sets tree. -
alias
. The alias of this node used to construct the full text path, forward-slash delimited.
Note Nested Sets are very slow when you need to add, remove, and reorder records. It's not necessary to use a Nested table to create a nested structure. In some use cases where writing is very common and the number of items in each nested structure rather limited (e.g. nested comments) it may actually be easier to only store the parent ID of each node and calculate the tree structure in PHP code. The read performance is comparable within a few dozen milliseconds but the write performance can be over 100 times(!!!) faster when you have more than a couple thousand records.
On that note, do remember that Joomla uses a Nested table for the #__assets records. Everything that has its own permissions such as categories, articles, banners, contacts, extensions, custom data types you add with an asset ID, etc also has a record in that table. That's why as a site grows in size operations against these records become increasingly slow.
Unfortunately, this cannot be (easily) solved in Joomla; nested assets are a cornerstone of Joomla access control since version 1.6. The only real solution would be using a graph database which is impractical; neither MySQL nor PostgreSQL, the two database server technologies supported by Joomla and widely found on commercial hosting, have graph features (they are relational databases a.k.a. RDBMS).
So, if you find yourself in a situation where you need nested sets but can't afford the performance hit after adding thousands or millions of records and calculating the tree in memory using PHP code is impractical (and cannot be cached in a sensible manner) ask yourself: am I actually developing for the right database server and platform? Not all software can be written for Joomla and MySQL / PostgreSQL and that's alright. We are software engineers; our job is to find the right tool for the job and use it in the most efficient way possible to make something new and functional.
-
The rest of this section is mostly tips and tricks for using Tables the way Joomla intended — and even more efficiently!
As noted in the MVCFactory
section, you are no longer meant to instantiate Table objects using
static method calls. You are meant to go through MVCFactory's
createTable
method.
In the olden days we'd do something like this:
// Returns the Item table from com_example (ExampleTableItem) $table = \Joomla\CMS\Table\Table::getInstance('Item', 'ExampleTable');
While this still works for old components written with the Joomla 3 MVC, it's part of the backwards compatibility with legacy component code and will eventually be removed. In fact, the only time you are supposed to use this in Joomla 4 is when interfacing an old component which has not been migrated to the Joomla 4 architecture yet.
For components based on the Joomla 4+ MVC this won't work at all! You need to go through the MVCFactory of the extension.
If you are inside a Controller or Model and need to get a Table object instance for a Table class which belongs to your own component you must do:
/** * Gets a NEW table object for the Item table in the Administrator section of the component, e.g. * \Acme\Component\Example\Administrator\Table\ItemTable */ $table = $this->getMVCFactory()->createTable('Item', 'Administrator');
In any other case you will need to boot the component through the application object to get its MVCFactory to create the table instance. For example:
// It's best to have the application object injected to your code instead of using this... $app = \Joomla\CMS\Factory::getApplication(); // Get the table $table = $app->bootComponent('com_example') ->getMVCFactory() ->createTable('Item', 'Administrator');
Going through the MVCFactory makes sure that the table is constructed correctly. Remember, as we saw in the Model section, you can push custom services into any MVC object — including Tables — through a custom MVCFactory decorator. This means that the only forwards compatible method of getting a Table object which will always work is going through the MVCFactory of its component.