Creating content programmatically is a common requirement for developers building importers, custom integrations, or automated publishing systems. However, if you have recently migrated from Joomla 3 to Joomla 4 or 5, you may have noticed that your old scripts no longer work as expected.
In Joomla 4, the introduction of the Workflow component and the modernization of the MVC architecture changed the underlying requirements for record creation. Simply inserting a row into the database is no longer sufficient. In this guide, you will learn the modern, recommended approach to creating articles and how to troubleshoot common issues like articles missing from the admin dashboard.
The Modern Approach: Using the Article Model
The most robust and future-proof way to create an article in Joomla 4 or 5 is to use the MVC Factory. By booting the com_content component and using its internal model, you ensure that all core logic—including asset tracking, alias generation, and workflow initialization—is handled automatically.
Why use the Model?
When you use the ArticleModel, Joomla triggers all relevant system plugins (like onContentAfterSave). This is critical if you use third-party extensions for SEO, sitemaps, or social media sharing that need to know when a new article is created.
use Joomla\CMS\Factory;
// Get the application and boot the content component
$app = Factory::getApplication();
$mvcFactory = $app->bootComponent('com_content')->getMVCFactory();
// Create the Article model
$articleModel = $mvcFactory->createModel('Article', 'Administrator', ['ignore_request' => true]);
// Define your article data
$article = [
'catid' => 2, // The ID of the category
'alias' => 'my-new-article-alias',
'title' => 'My Programmatic Article Title',
'introtext' => '<p>This is the intro text.</p>',
'fulltext' => '<p>This is the full body text.</p>',
'state' => 1, // 1 for Published, 0 for Unpublished
'language' => '*', // All languages
];
// Save the article
if (!$articleModel->save($article)) {
throw new \Exception($articleModel->getError());
}
Why Direct SQL Queries Often Fail
You might be tempted to use a direct SQL INSERT statement for performance reasons. However, developers often encounter a frustrating bug: the article appears in the database but is invisible in the Joomla Administrator dashboard.
This happens because Joomla 4 introduced the Workflow system. For an article to be visible in the backend list view, it must have a corresponding entry in the #__workflow_associations table. Without this association, the administrative interface cannot determine which workflow stage the article is in, and it filters it out of the view.
Fixing the Direct SQL Approach
If you must use direct SQL (for example, in a high-volume migration script), you must manually create the workflow association. Here is how to handle that logic:
// 1. Insert the article into #__content (as you normally would)
// ... [Your SQL Insert Code] ...
// 2. Get the ID of the article you just created
$newArticleId = $db->insertid();
// 3. Define the workflow parameters
$stageID = 1; // Default workflow stage
$extension = 'com_content.article';
// 4. Create the workflow association entry
$query = $db->getQuery(true);
$columns = array('item_id', 'stage_id', 'extension');
$values = array($newArticleId, $stageID, $db->quote($extension));
$query
->insert($db->quoteName('#__workflow_associations'))
->columns($db->quoteName($columns))
->values(implode(',', $values));
$db->setQuery($query);
$db->execute();
Common Pitfalls to Avoid
1. Missing the Asset Table
Joomla uses an ACL (Access Control List) system stored in the #__assets table. If you use direct SQL to insert articles, you bypass the creation of these assets. This can lead to permission errors where users cannot edit the newly created articles. Using the Article Model handles this automatically.
2. Alias Conflicts
Joomla requires unique aliases within the same category. If you programmatically create articles with duplicate titles and don't provide a unique alias, the Model will automatically append a suffix (e.g., -2). Direct SQL will fail or create broken SEF URLs.
3. JTable vs. Modern MVC
While JTable (now Table class) still exists, it is considered a lower-level API than the Model. While it is better than direct SQL, it still doesn't handle workflow associations as gracefully as the Model does in Joomla 4.
Frequently Asked Questions
Does this work in Joomla 5?
Yes. Joomla 5 maintains the same MVC Factory architecture as Joomla 4. The bootComponent method remains the standard way to interact with core components programmatically.
How do I set custom fields programmatically?
When using the ArticleModel->save() method, you can include a com_fields array within your data object. Each key in the array should correspond to the ID of the custom field you wish to populate.
Why do I get a 'failed to start application' error?
This usually happens when you try to run Joomla code in a standalone PHP script without properly bootstrapping the Joomla Framework. Ensure you are executing your code within the context of a Joomla Module, Plugin, or a properly initialized CLI application.
Key Takeaways
- Always prefer the Model: Use
$mvcFactory->createModel()to ensure all system triggers and security checks are performed. - Beware of Workflows: If your articles are "missing" from the backend, check the
#__workflow_associationstable. - Handle Assets: Direct database manipulation is dangerous because it ignores the
#__assetstable, which can break your site's permissions. - Stay Updated: Joomla's API is evolving. Always check the latest developer documentation when moving from Joomla 4 to 5, though the core MVC Factory approach remains stable.