ADD fields, field manipulators

This commit is contained in:
Thomas Kuschel 2023-10-14 21:15:09 +02:00
parent 436734cd4c
commit d122312ddd
8 changed files with 387 additions and 23 deletions

194
README.md
View File

@ -17,7 +17,7 @@ developing our project. The first run with simply renaming entries from a
copy of the component **com_banners** did not work as expected.
So let us start at the very beginning:
### Adding basic files for component (b1_basic_backend)
## Adding basic files for component (b1_basic_backend)
With the git branch **b1_basic_backend**
Add the following basic six files:
@ -124,7 +124,7 @@ the directory /admin/language/en-GB/ naming it
---
### Creating and managing Joomla database (b2_database)
## Creating and managing Joomla database (b2_database)
With the new git branch **b2_database** we continue our workflow.
@ -295,7 +295,7 @@ Methods in the table objects are:
---
### Get a Form in Joomla component
## Get a Form in Joomla component (b3_form)
The **Form** class of Joomla is used to create complex forms with flexible
layouts and dynamic properties. First, the form fields are defined in the
@ -440,7 +440,7 @@ just a line before `<folder>services</folder>` :
---
### Adding administrator's actions (Back-end) - Save and Cancel
## Adding administrator's actions (Back-end) - Save and Cancel (b4_actions)
In the form View, you can add action buttons like "Save" and "Cancel" to submit the form
or cancel the editing respectively. These buttons are added to the toolbar.
@ -526,3 +526,189 @@ So, you either have data from the database, which you can get with `getItem()`,
data from the session, which you can get with `getUserSate()`.
---
## Automatic handling of fields (b5_field_manipulation)
Values of some fields in the form can be automatically handled. There is no need to fill in
the values while creating or editing a record.
For example, `alias` can be generated from the `title` (or here, from the component's name),
dates can be set to the current date or to `null`, user id can be obtained from current
logged in user. Further may you also need to check validity of some fields like "should not
be empty", `alias` should be unique, etc.
The data submitted by the form needs to be modified before saving. This can be done at
various places:
- in the **Model class** by oeverriding the `save()` method or `preparetable()` method.
- in the **Table class** by overriding the `bind()`, `check()`, or `store()` methods.
#### 1. Model file
**admin/src/Model/PartModel.php
```php
public function save($data)
{
/* Add code to modify data before saving */
return parent:save($data);
}
```
It is better to perform automatic handling of fields in the Table class as the data can be
saved not only from administration, but also from frontend, API or by any other means.
##### Example generating Alias
Alias is generated from the `component_name` (or any other field) using **OutputFilter** class
method.
```php
if (empty($data['alias']))
{
if (Factory::getConfig()->get('unicodeslugs') == 1) {
$data['alias'] = OutputFilter::stringURLUnicodeSlug($data['component_name']);
} else {
$data['alias'] = OutputFilter::stringURLSafe($data['component_name']);
}
}
```
##### Ordering
The ordering value is calculated by finding the maximum value in the column and then
incrementing it.
```php
/* if it is 0 -> get the max + 1 value */
if (!$data['ordering']) {
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select('MAX(ordering)')
->from('#__depot');
$db->setQuery($query);
$max = $db->loadResult();
$data['ordering'] = $max + 1;
}
```
#### 2. bind()
The `bind()` splits the article text or description into intro text and full text based on read
more tag in the content. This method also converts fields data from arrays to JSON for
saving into the database.
```php
public function bind($array, $ignore = '')
{
if (isset($array['attribs']) && \is_array($array['attribs'])) {
$registry = new Registry($array['attribs']);
$array['attribs'] = (string) $registry;
}
return parent::bind($array, $ignore);
}
```
#### 3. check()
The check() checks whether title of the article is not empty. This method also sets the alias,
hits, publishing dates.
```php
public function check()
{
try {
parent::check();
}
catch (\Exception $e) {
$this->setError($e->getMessage());
return false;
}
if (trim($this->title) == '') {
$this->setError('Title (title) is not set.');
return false;
}
if (trim($this->alias) == '') {
$this->alias = $this->title;
}
$this->alias = ApplicationHelper::stringURLSave($this->alias, $this->language);
// Ensure any new items have compulsory fields set
if (!$this->id) {
// Hits must be zero on a new item
$this-hits = 0;
}
// Set publish_up to null if not set
if (!$this->publish_up) {
$this->publish_up = null;
}
// Set publish_down to null if not set
if (!$this->publish_down) {
$this->publish_down = null;
}
// Check the publish down date is not earlier than publish up.
if (!is_null($this->publish_up) &&
!is_null($this->publish_down) &&
$this->publish_down < $this->publish_up) {
// swap the dates
$temp = $this->publish_up;
$this->publish_up = $this->publish_down;
$this->publish_down = $temp;
}
return true;
}
```
#### 4. store()
The `store()` sets created and modified dates, created by and modified by users, and also
checks for unique alias.
```php
public function store($updateNulls = true)
{
$app = Factory::getApplication();
$date = Factory::getDate()->toSql();
$user = Factory::getUser();
if (!this->created) {
$this->created = $date;
}
if (!this->created_by) {
$this->created_by = $user->get('id');
}
if ($this->id) {
// Existing item
$this->modified_by = $user->get('id');
$this->modified = $date;
} else {
// Set modified to created date if not set
if (!$this->modified)) {
$this->modified = $this->created;
}
// Set modified_by to created_by user if not set
if (empty($this->modified_by)) {
$this->modified_by = $this->created_by;
}
}
// Verify that the alias is unique
$table = $app->bootComponent('com_depot')->getMVCFactory()
->createTable('Part','Administrator');
if ($table->load(['alias' => $this->alias]) &&
($table->id != $this->id || $this->id == 0)) {
$this->setError('Alias is not unique.');
if ($table->state == -2) {
$this->setError('Alias is not unique. The item is in trash.');
}
return false;
}
return parent::store($updateNulls);
}
```
---

View File

@ -1,18 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<form>
<field
name="component_name"
type="text"
label="COM_DEPOT_FIELD_COMPONENT_NAME_LABEL"
description="COM_DEPOT_FIELD_COMPONENT_NAME_DESC"
required="true"
/>
<field
name="id"
type="text"
label="JGLOBAL_FIELD_ID_LABEL"
class="readonly"
default="0"
readonly="true"
/>
<fieldset
name="details"
label="COM_DEPOT_DETAILS"
addruleprefix="KW4NZ\Component\Depot\Administrator\Rule"
addfieldprefix="KW4NZ\Component\Depot\Administrator\Field"
>
<field
name="component_name"
type="text"
label="COM_DEPOT_FIELD_COMPONENT_NAME_LABEL"
description="COM_DEPOT_FIELD_COMPONENT_NAME_DESC"
required="true"
autofocus="1"
/>
<field
name="quantity"
type="number"
label="COM_DEPOT_FIELD_QUANTITY_LABEL"
description="COM_DEPOT_FIELD_QUANTITY_DESC"
default="0"
/>
<field
name="quantity_exp"
type="number"
label="COM_DEPOT_FIELD_QUANTITY_EXP_LABEL"
description="COM_DEPOT_FIELD_QUANTITY_EXP_DESC"
default="0"
/>
<field
name="id"
type="text"
label="JGLOBAL_FIELD_ID_LABEL"
class="readonly"
default="0"
readonly="true"
/>
<field
name="alias"
type="text"
label="JFIELD_ALIAS_LABEL"
description="JFIELD_ALIAS_DESC"
hint="COM_DEPOT_FIELD_ALIAS_PLACEHOLDER"
size="40"
/>
<field
name="manufacturer_id"
type="sql"
label="COM_DEPOT_FIELD_SELECT_MANUFACTURER"
query="SELECT id, CONCAT( name_short, ' (', name_long, ')') AS title FROM #__depot_manufacturer ORDER BY title"
key_field="id"
value_field="title"
required="true"
>
<option value="">COM_DEPOT_SELECT_YOUR_OPTION</option>
</field>
<field
name="state"
type="list"
label="JSTATUS"
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>
</fieldset>
</form>

View File

@ -5,6 +5,16 @@
; @license GNU General Public License version 2 or later; see LICENSE.md
; @since 0.0.1
;
COM_DEPOT_FIELD_ALIAS_PLACEHOLDER="Auto-generate from component name"
COM_DEPOT_FIELD_COMPONENT_NAME_DESC="The name of the component is unique. Please do not enter special characters or umlauts."
COM_DEPOT_FIELD_COMPONENT_NAME_LABEL="Component Name"
COM_DEPOT_FIELD_QUANTITY_LABEL="Quantity"
COM_DEPOT_FIELD_QUANTITY_DESC="Enter here the current number of components"
COM_DEPOT_FIELD_QUANTITY_EXP_LABEL="Quantity Exponent"
COM_DEPOT_FIELD_QUANTITY_EXP_DESC="Exponent (10^x of the number, usually 0 i.e. 10⁰)"
COM_DEPOT_FIELD_SELECT_MANUFACTURER="Manufacturer"
COM_DEPOT_LEGEND_DETAILS="Component Details"
COM_DEPOT_SELECT_YOUR_OPTION="Select your option"
COM_DEPOT_TAB_NEW_PART="New Component"
COM_DEPOT_TAB_EDIT_PART="Component Details"
COM_DEPOT_XML_DESCRIPTION="Depot, the component warehouse"

View File

@ -49,3 +49,33 @@ INSERT INTO `#__depot` (`component_name`,`alias`,`description`,`quantity`,`creat
`ordering`,`state`,`manufacturer_id`) VALUES
('1N5404','1n5404','diode, rectifier 3A',9,'2023-09-25 15:00:00',1,1,1),
('1N4148','1n4148','diode, general purpose',1234,'2023-09-25 15:15:15',2,1,2);
DROP TABLE IF EXISTS `#__depot_manufacturer`;
CREATE TABLE `#__depot_manufacturer` (
`id` SERIAL,
`name_short` CHAR(25) CHARACTER SET ascii COLLATE ascii_general_ci NULL DEFAULT NULL
COMMENT 'unique manufacturer name or abbriviation',
`name_long` VARCHAR(1024) NOT NULL DEFAULT '',
`url` VARCHAR(1024) NOT NULL DEFAULT '',
`created` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
`created_by` INT(10) UNSIGNED NOT NULL DEFAULT 0,
`checked_out` INT(11) NOT NULL DEFAULT 0,
`checked_out_time` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
`modified` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
`modified_by` INT(10) UNSIGNED NOT NULL DEFAULT 0,
`description` VARCHAR(4000) NOT NULL DEFAULT '',
`state` TINYINT(4) NOT NULL DEFAULT 0,
`image` VARCHAR(1024) NOT NULL DEFAULT '',
`access` TINYINT(4) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `name_short` (`name_short`)
) ENGINE=InnoDB
AUTO_INCREMENT=0
DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO `#__depot_manufacturer` (`name_short`, `name_long`, `url`,
`description`, `image`) VALUES
('TSC','Taiwan Semiconductor','https://www.taiwansemi.com',
'Diodes, ECAD Models, ICs, MOSFETs, Protection Devices, AEC-Q qualified',''),
('ST','STMicroelectronics','https://www.st.com',
'Microprocessors, Audio ICs, OPamps, Diodes, Memories, MEMS, NFCs, Transistors, Wireless, Automotive electronics, etc.','');

View File

@ -0,0 +1,35 @@
-- @package Depot.SQL MariaDB -- UPDATE to 0.0.5
-- @subpackage com_depot
-- @author Thomas Kuschel <thomas@kuschel.at>
-- @copyright (C) 2023 KW4NZ, <https://www.kuschel.at>
-- @license GNU General Public License version 2 or later; see LICENSE.md
-- @since 0.0.5
CREATE TABLE IF NOT EXISTS `#__depot_manufacturer` (
`id` SERIAL,
`name_short` CHAR(25) CHARACTER SET ascii COLLATE ascii_general_ci NULL DEFAULT NULL
COMMENT 'unique manufacturer name or abbriviation',
`name_long` VARCHAR(1024) NOT NULL DEFAULT '',
`url` VARCHAR(1024) NOT NULL DEFAULT '',
`created` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
`created_by` INT(10) UNSIGNED NOT NULL DEFAULT 0,
`checked_out` INT(11) NOT NULL DEFAULT 0,
`checked_out_time` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
`modified` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
`modified_by` INT(10) UNSIGNED NOT NULL DEFAULT 0,
`description` VARCHAR(4000) NOT NULL DEFAULT '',
`state` TINYINT(4) NOT NULL DEFAULT 0,
`image` VARCHAR(1024) NOT NULL DEFAULT '',
`access` TINYINT(4) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `name_short` (`name_short`)
) ENGINE=InnoDB
AUTO_INCREMENT=0
DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO `#__depot_manufacturer` (`name_short`, `name_long`, `url`,
`description`, `image`) VALUES
('TSC','Taiwan Semiconductor','https://www.taiwansemi.com',
'Diodes, ECAD Models, ICs, MOSFETs, Protection Devices, AEC-Q qualified',''),
('ST','STMicroelectronics','https://www.st.com',
'Microprocessors, Audio ICs, OPamps, Diodes, Memories, MEMS, NFCs, Transistors, Wireless, Automotive electronics, etc.','');

View File

@ -10,6 +10,7 @@
namespace KW4NZ\Component\Depot\Administrator\Model;
use Joomla\CMS\Application\ApplicationHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Model\AdminModel;
@ -39,4 +40,25 @@ class PartModel extends AdminModel
return $data;
}
public function save($data)
{
/* Add code to modify data before saving */
// if (Factory::getConfig()->get('unicodeslugs') == 1) {
// $data['alias'] = OutputFilter::stringURLUnicodeSlug($data['component_name']);
// } else {
// $data['alias'] = OutputFilter::stringURLSafe($data['component_name']);
// }
/* replaced by: */
if (empty($data['alias'])) {
$data['alias'] = ApplicationHelper::stringURLSafe($data['component_name']);
}
$result = parent::save($data);
// if ($result) {
// $this->getTable('', 'Administrator')->rebuild(1);
// }
return $result;
}
}

View File

@ -8,6 +8,7 @@
* @since 0.0.3
*/
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
@ -19,9 +20,35 @@ $wa->useScript('form.validate')
<form action="<?= Route::_('index.php?option=com_depot&view=part&layout=edit&id=' . (int) $this->item->id); ?>"
method="post" name="adminForm" id="item-form" class="form-validate">
<?= $this->form->renderField('component_name'); ?>
<?= $this->form->renderField('id'); ?>
<div class="form-horizontal">
<?= HtmlHelper::_(
'bootstrap.startTabSet',
'myTab',
['active' => 'details']
); ?>
<?= HTMLHelper::_(
'bootstrap.addTab',
'myTab',
'details',
empty($this->item->id) ? Text::_('COM_DEPOT_TAB_NEW_PART') :
Text::_('COM_DEPOT_TAB_EDIT_PART')
); ?>
<fieldset class="adminform">
<legend>
<?= Text::_('COM_DEPOT_LEGEND_DETAILS') ?>
</legend>
<div class="row">
<div class="col-12 col-lg-3">
<?= $this->form->renderFieldset('details'); ?>
</div>
<div class="col-12 col-lg-9">
<?= $this->form->getInput('description'); ?>
</div>
</div>
</fieldset>
<?= HtmlHelper::_('bootstrap.endTab'); ?>
<?= HtmlHelper::_('bootstrap.endTabSet'); ?>
</div>
<input type="hidden" name="task" value="part.edit" />
<?= HTMLHelper::_('form.token'); ?>
</form>

View File

@ -7,7 +7,7 @@
<license>GPL v2 +; see LICENSE.md</license>
<authorEmail>thomas@kuschel.at</authorEmail>
<authorUrl>https://kuschel.at</authorUrl>
<version>0.0.4</version>
<version>0.0.5</version>
<description>COM_DEPOT_XML_DESCRIPTION</description>
<namespace path="src/">KW4NZ\Component\Depot</namespace>
<install> <!-- Runs on install -->