I am creating a new Joomla J4 component for my website. I have previously created components in J3 using Akeeba's Framework on Framework. For J4 I'm working to learn how to create a J4 MVC native component and I'm stuck on this issue. All I want to do is to add fields to a record to track when a record was last modified and who did the modification. (Eventually, I'd like to make use of the record lock functionality as well but that's not part of this question.)
It seems to me that the modified_on and modified_by field values should be updated when the JFORM is being saved (store?), but I don't understand where that happens in the MVC framework.
Can someone explain to me where I need to add the code to make this happen? Or, point me in a difference direction regarding to to make this work? Thanks in advance for your assistance!
For reference, here's my code for this view.
These are the fields I have configured in my db table:
**# Name Type Null Default**
11 modified_by bigint Yes NULL
12 modified_on datetime Yes NULL
This is the JFORM I have setup `
<fieldset name="core">
<field
name="title"
type="text"
label="Client"
required="true"
maxlength="100"
/>
<field
name="description"
type="textarea"
label="Description"
description=""
required="false"
maxlength="300"
/>
<field
name="portal_name"
type="text"
label="Portal Name"
description="Enter the URL for the client portal"
required="true"
maxlength="100"
/>
<field
name="primary_contact"
type="text"
label="Primary Contact"
description=""
required="false"
maxlength="100"
/>
<field
name="email_address"
type="text"
label="Email"
description=""
required="false"
maxlength="100"
/>
<field
name="modified_by"
label="JGLOBAL_FIELD_MODIFIED_BY_LABEL"
class="readonly"
readonly="true"
filter="unset" />
<field
name="modified_on"
type="calendar"
label="Last Modified"
default="NOW"
class="readonly"
showtime="true"
translateformat="true"
format="%d-%m-%Y %h:%i:%s"
readonly="true"
filter="unset"
/>
<field
name="id"
type="text"
label="Client ID"
class="readonly"
default="0"
readonly="true"
/>
<field
name="state"
type="list"
label="Status"
class="form-select-color-state"
default="1"
validate="options"
>
<option value="1">JPUBLISHED</option>
<option value="0">JUNPUBLISHED</option>
<option value="2">JARCHIVED</option>
<option value="-2">JTRASHED</option>
</field>
`
These are the functions in the Model for the page:
namespace Bluewater\Component\bwivisuals\Administrator\Model;
\defined('_JEXEC') or die;
use Joomla\CMS\Date\Date;
use Joomla\CMS\Event\AbstractEvent;
use Joomla\CMS\Factory;
use Joomla\CMS\Filter\InputFilter;
use Joomla\CMS\Filter\OutputFilter;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Form\FormFactoryInterface;
use Joomla\CMS\Helper\TagsHelper;
use Joomla\CMS\Language\Associations;
use Joomla\CMS\Language\LanguageHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\MVC\Model\AdminModel;
use Joomla\CMS\MVC\Model\WorkflowBehaviorTrait;
use Joomla\CMS\MVC\Model\WorkflowModelInterface;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\String\PunycodeHelper;
use Joomla\CMS\Table\TableInterface;
use Joomla\CMS\Tag\TaggableTableInterface;
use Joomla\CMS\UCM\UCMType;
use Joomla\CMS\Versioning\VersionableModelTrait;
use Joomla\CMS\Workflow\Workflow;
use Joomla\Component\Categories\Administrator\Helper\CategoriesHelper;
use Joomla\Component\Fields\Administrator\Helper\FieldsHelper;
use Joomla\Database\ParameterType;
use Joomla\Registry\Registry;
use Joomla\Utilities\ArrayHelper;
/**
* Item Model for a Country.
*
* @since 1.6
*/
class ClientModel extends AdminModel
{
/**
* The prefix to use with controller messages.
*
* @var string
* @since 1.6
*/
protected $text_prefix = 'COM_BWIVISUALS_CLIENT';
/**
* Method to get the record form.
*
* @param array $data Data for the form.
* @param boolean $loadData True if the form is to load its own data (default case), false if not.
*
* @return Form|boolean A Form object on success, false on failure
*
* @since 1.6
*/
public function getForm($data = array(), $loadData = true)
{
// Get the form.
$form = $this->loadForm('com_bwivisuals.client', 'client', array('control' => 'jform', 'load_data' => $loadData));
if (empty($form))
{
return false;
}
return $form;
}
/**
* Method to get the data that should be injected in the form.
*
* @return mixed The data for the form.
*
* @since 1.6
*/
protected function loadFormData()
{
// Check the session for previously entered form data.
$app = Factory::getApplication();
$data = $app->getUserState('com_bwivisuals.edit.client.data', array());
if (empty($data))
{
$data = $this->getItem();
}
return $data;
}
/**
* Method to get a table object, load it if necessary.
*
* @param string $name The table name. Optional.
* @param string $prefix The class prefix. Optional.
* @param array $options Configuration array for model. Optional.
*
* @return Table A Table object
*
* @since 3.0
* @throws \Exception
*/
public function getTable($name = '', $prefix = '', $options = array())
{
$name = 'Clients';
$prefix = 'Table';
if ($table = $this->_createTable($name, $prefix, $options))
{
return $table;
}
throw new \Exception(Text::sprintf('JLIB_APPLICATION_ERROR_TABLE_NAME_NOT_SUPPORTED', $name), 0);
}
}
And this is the layout file, edit.php
\defined('_JEXEC') or die;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
HTMLHelper::_('behavior.formvalidator');
HTMLHelper::_('behavior.keepalive');
?>
<form action="<?php echo Route::_('index.php?option=com_mycomponents&view=client&layout=edit&id=' . (int) $this->item->id); ?>"
method="post" name="adminForm" id="client-form" class="form-validate">
<?php echo LayoutHelper::render('joomla.edit.title_alias', $this); ?>
<div>
<?php echo HTMLHelper::_('uitab.startTabSet', 'myTab', array('active' => 'details')); ?>
<?php echo HTMLHelper::_('uitab.addTab', 'myTab', 'details', Text::_('Details')); ?>
<div class="row">
<div class="col-md-9">
<div class="row">
<div class="col-md-6">
<?php echo $this->form->renderField('description'); ?>
<?php echo $this->form->renderField('portal_name'); ?>
<?php echo $this->form->renderField('primary_contact'); ?>
<?php echo $this->form->renderField('email_address'); ?>
<?php echo $this->form->renderField('modified_by'); ?>
<?php echo $this->form->renderField('modified_on'); ?>
<?php echo $this->form->renderField('id'); ?>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card card-light">
<div class="card-body">
<?php echo LayoutHelper::render('joomla.edit.global', $this); ?>
</div>
</div>
</div>
</div>
<?php echo HTMLHelper::_('uitab.endTab'); ?>
<?php echo HTMLHelper::_('uitab.endTabSet'); ?>
</div>
<input type="hidden" name="task" value="">
<?php echo HTMLHelper::_('form.token'); ?>
</form>
And this is the ClientController file:
namespace Bluewater\Component\bwivisuals\Administrator\Controller;
defined('_JEXEC') or die;
use Joomla\CMS\MVC\Controller\FormController;
/**
* Controller for a single record
*
* @since 1.6
*/
class ClientController extends FormController
{
}
This is the ClientsTable
namespace Bluewater\Component\bwivisuals\Administrator\Table;
\defined('_JEXEC') or die;
use Joomla\CMS\Table\Table;
use Joomla\Database\DatabaseDriver;
/**
* Featured Table class.
*
* @since 1.6
*/
class ClientsTable extends Table
{
/**
* Constructor
*
* @param DatabaseDriver $db Database connector object
*
* @since 1.6
*/
public function __construct(DatabaseDriver $db)
{
parent::__construct('#__bwivisuals_clients', 'id', $db);
$this->setColumnAlias('published', 'state');
}
}
And I tried creating a ClientTable, but that didn't work
namespace Bluewater\Component\bwivisuals\Administrator\Table;
\defined('_JEXEC') or die;
use Joomla\CMS\Table\Table;
use Joomla\Database\DatabaseDriver;
/**
* Featured Table class.
*
* @since 1.6
*/
class ClientTable extends Table
{
/**
* Constructor
*
* @param DatabaseDriver $db Database connector object
*
* @since 1.6
*/
public function store($updateNulls = false) {
$this->modified_by = Factory::getApplication()->getIdentity()->id;
$this->modified_on = date('Y-m-d H:i:s');
return parent::store($updateNulls);
}
}