diff --git a/README.md b/README.md index 760aec2..4c43517 100644 --- a/README.md +++ b/README.md @@ -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 `services` : --- -### 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); +} +``` + +--- diff --git a/admin/forms/part.xml b/admin/forms/part.xml index 5167343..eca411e 100644 --- a/admin/forms/part.xml +++ b/admin/forms/part.xml @@ -1,18 +1,72 @@
- - +
+ + + + + + + + + + + + + + +
diff --git a/admin/language/en-GB/com_depot.ini b/admin/language/en-GB/com_depot.ini index a706a2b..98b5f8e 100644 --- a/admin/language/en-GB/com_depot.ini +++ b/admin/language/en-GB/com_depot.ini @@ -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" diff --git a/admin/sql/install.mysql.utf8.sql b/admin/sql/install.mysql.utf8.sql index 40517fc..235da7c 100644 --- a/admin/sql/install.mysql.utf8.sql +++ b/admin/sql/install.mysql.utf8.sql @@ -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.',''); diff --git a/admin/sql/updates/mysql/0.0.5.sql b/admin/sql/updates/mysql/0.0.5.sql new file mode 100644 index 0000000..e5c5376 --- /dev/null +++ b/admin/sql/updates/mysql/0.0.5.sql @@ -0,0 +1,35 @@ +-- @package Depot.SQL MariaDB -- UPDATE to 0.0.5 +-- @subpackage com_depot +-- @author Thomas Kuschel +-- @copyright (C) 2023 KW4NZ, +-- @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.',''); diff --git a/admin/src/Model/PartModel.php b/admin/src/Model/PartModel.php index 944c6bd..81be2f5 100644 --- a/admin/src/Model/PartModel.php +++ b/admin/src/Model/PartModel.php @@ -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; + } } \ No newline at end of file diff --git a/admin/tmpl/part/edit.php b/admin/tmpl/part/edit.php index 3fc2f28..96b6072 100644 --- a/admin/tmpl/part/edit.php +++ b/admin/tmpl/part/edit.php @@ -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->renderField('component_name'); ?> - form->renderField('id'); ?> - +
+ 'details'] + ); ?> + item->id) ? Text::_('COM_DEPOT_TAB_NEW_PART') : + Text::_('COM_DEPOT_TAB_EDIT_PART') + ); ?> +
+ + + +
+
+ form->renderFieldset('details'); ?> +
+
+ form->getInput('description'); ?> +
+
+
+ + +
\ No newline at end of file diff --git a/depot.xml b/depot.xml index 90878b0..a7d8419 100644 --- a/depot.xml +++ b/depot.xml @@ -7,7 +7,7 @@ GPL v2 +; see LICENSE.md thomas@kuschel.at https://kuschel.at - 0.0.4 + 0.0.5 COM_DEPOT_XML_DESCRIPTION KW4NZ\Component\Depot