17 Commits

13 changed files with 438 additions and 10 deletions

View File

@ -4,7 +4,7 @@ root = true
[*]
indent_style = tab
indent_size = 4
end_of_file = lf
end_of_line = lf
charset = UTF-8
trim_trailing_whitespace = true
insert_final_newline = true

243
README.md
View File

@ -36,6 +36,17 @@ Add the following basic six files:
##### 1. DepotComponent.php
This file contains class for the extension. The class extends MVCComponent.
Compare this version with the original at [Tech Fry Tutorium](https://www.techfry.com/joomla/adding-basic-files-for-component).
```php
namespace KW4NZ\Component\Depot\Administrator\Extension;
use Joomla\CMS\Extension\MVCComponent;
class DepotComponent extends MVCComponent
{
}
```
##### 2. provider.php
This is a special file that tells Joomla how to initialize the component - which
@ -283,3 +294,235 @@ Methods in the table objects are:
1. **delete()** to delete the record from the database.
---
### Get a Form in Joomla component
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
XML file. Then the view file gets the form from the model and layout file
displays the form.
#### XML Form file
**admin/forms/part.xml**
```xml
<?xml version="1.0" encoding="UTF-8"?>
<form>
<field
name="title"
type="text"
label="JGLOBAL_TITLE"
required="true"
/>
</form>
```
Similarly you can add other fields to the XML file.
#### View file
**admin/src/View/Part/HtmlView.php**
This file is similar to the view file added earlier for the "Parts" view.
The view file gets the form from the model in the display() method.
```php
<?php
namespace KW4NZ\Component\Depot\Administrator\View\Part;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
\defined('_JEDEC') or die;
class HtmlView extends BaseHtmlView
{
protected $form;
protected $item;
public function display($tpl = null)
{
$this->form = $this->get('Form');
$this->item = $this->get('Item');
parent::display($tpl);
}
}
```
The code will look for the method **getForm()**
and **getItem()** in the model file.
We need to get the item to get the ID
of the record in case of editing the existing record.
#### Model file
**admin/src/Model/PartModel.php**
We need to extend the model class with the **AdminModel**.
The AdminModel class extends FormModel class. The **getForm()**
method gets the Form object for the edit form.
```php
namespace KW4NZ\Component\Depot\Administrator\Model;
use Joomla\CMS\MVC\Model\AdminModel;
\defined('_JEXEC') or die;
class PartModel extends AdminModel
{
public function getForm($data = array(), $loadData = true)
{
$form = $this->loadForm('com_depot.part',
'part', array('control' => 'jform',
'load_data' => $loadData));
if (empty($form)) {
return false;
}
return $form;
}
}
```
The **loadForm()** and **preprocessForm()** methods are defined in
the FromModel class and the **bind()** method is defined in the
Form class. The first argument (name) of loadForm() is set to
"com_depot.part". The second argument (form xml source) is "part",
and the third argument is the associative array for options.
The form is defined in the source file: **forms/part.xml**
After you have set the $form variable with the Form object,
you check to see if you are loading data to the form.
If you want to pre-load data for the form, you include an element
called "load_data" that is set to a boolean true.
Then, the method calls the loadFormData() method to get the data
for the form. This method gets any previously loaded data from
the session or database.
###### Modifying Form dynamically
Inside the getForm() method, before returning the $form, you can modify the form with many methods of the Form class. You can easily fine-tune your forms dynamically before they are rendered.
#### Layout file - rendering Form
**tmpl/part/edit.php**
After you get the form in the view file ($this->form), the form
is rendered in the layout file (edit.php).
```php
<?php
use Joomla\CMS\HTML\HTMLHelper;
$wa = $this->document->getWebAssetManager();
$wa->useScript('keepalive');
$wa->useScript('form.validate');
?>
<form action="<?= Route::_('index.php?option=com_depot&layout=edit&id=' .
(int) $this->item->id); ?>"
method="post" name="adminForm" id="item-form" class="form-validate">
<?= $this->form->renderField('title'); ?>
<input type="hidden" name="task" value="part.edit" />
<?= HTMLHelper::_('form.token'); ?>
</form>
```
The form validate script is required to submit the for. The renderField() method
of the Form class displays the field - both label and input.
We can access the form using following URL:
```php
administrator/index.php?option=com_depot&view=part&layout=edit
```
It displays edit.php layout file of the Part View. When the form is submitted, the
data is sent to the controller depending upon the action buttons in the toolbar.
Please remember to add the additional folder `form` to the Manifest file "depot.xml", i.e.
just a line before `<folder>services</folder>` :
```xml
<folder>form</folder>
```
---
### Adding administrator's actions (Back-end) - Save and Cancel
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.
These buttons require compound tasks (controller and method). For example,
- Save and Edit: part.apply
- Save and Close: part.save
- Cancel: part.cancel
#### View file
**admin/src/View/Part/HtmlView.php**
In the View file, we create a new method to add a toolbar: `addToolbar()`. The
toolbar hides the sidebar menu on forms, sets the title and adds action buttons.
```php
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Toolbar\Toolbar;
use Joomla\CMS\Toolbar\ToolbarHelper;
protected function addToolbar()
{
Factory::getApplication()->getInput()->set('hidemainmenu', true);
ToolbarHelper::title('Part: Add');
ToolbarHelper::apply('part.apply');
ToolbarHelper::save('part.save');
ToolbarHelper::cancel('part.cancel', 'JTOOLBAR_CLOSE');
}
```
The display() calls this method to include the toolbar.
```php
public function display($tpl = null)
{
$this->form = $this->get('Form');
$this->addToolbar();
parent::display($tpl);
}
```
#### Controller file
**admin/src/Controller/PartController.php***
Now, when these action buttons are clicked, Joomla will look for apply(), save() or cancel()
methods in the Part controller. So, create a controller clss that will extend FormController.
These methods are already defined in the parent class.
```php
use Joomla\CMS\MVC\Controller\FormController;
class PartController extends FormController
{
}
```
#### Model file
**admin/src/model/PartModel.php**
If you click on Save button, it will save the data and redirect to the editing screen. However, the data will not be present in the form. Though, we have set `$loadData` to true, we also need to create a method `loadFormData()` to get the data for the form.
```php
protected function loadFormData()
{
$app = Factory::getApplication();
$data = $app->getUserState('com_depot.edit.part.data', []);
if (empty($data)) {
$data = $this->getItem();
}
return $data;
}
```
First, this method tries to get the data from the session. If it fails to get data from the
session, then it gets data from the database. The `getItem()` method is defined in the
parent class.
Suppose the user has filled out the form to add a new item but has some invalid data in the
form. In this case, the save will not be successful, so the data is not saved in the
database. It would be very frustrating if the user hat to reenter all the fields to fix a
single error. To handle this scenario, you save the entered data in the user's session.
Only after the save is successful, you clear this out of the session.
So, you either have data from the database, which you can get with `getItem()`, or you have
data from the session, which you can get with `getUserSate()`.
---

18
admin/forms/part.xml Normal file
View File

@ -0,0 +1,18 @@
<?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"
/>
</form>

View File

@ -5,4 +5,6 @@
; @license GNU General Public License version 2 or later; see LICENSE.md
; @since 0.0.1
;
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_XML_DESCRIPTION="Depot, the component warehouse"

View File

@ -17,4 +17,4 @@ use Joomla\CMS\MVC\Controller\BaseController;
class DisplayController extends BaseController
{
protected $default_view = 'parts';
}
}

View File

@ -0,0 +1,19 @@
<?php
/**
* @package Depot.Administrator
* @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.4
*/
namespace KW4NZ\Component\Depot\Administrator\Controller;
use Joomla\CMS\MVC\Controller\FormController;
defined('_JEXEC') or die;
class PartController extends FormController
{
}

View File

@ -14,5 +14,4 @@ use Joomla\CMS\Extension\MVCComponent;
class DepotComponent extends MVCComponent
{
}
}

View File

@ -0,0 +1,42 @@
<?php
/**
* @package Depot.Administrator
* @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.3
*/
namespace KW4NZ\Component\Depot\Administrator\Model;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Model\AdminModel;
\defined('_JEXEC') or die;
class PartModel extends AdminModel
{
public function getForm($data = [], $loadData = true)
{
$form = $this->loadForm('com_depot.part', 'part', ['control' => 'jform', 'load_data' => $loadData]);
if (empty($form)) {
return false;
}
return $form;
}
protected function loadFormData()
{
$app = Factory::getApplication();
$data = $app->getUserState('com_depot.edit.part.data', []);
if (empty($data)) {
$data = $this->getItem();
}
return $data;
}
}

View File

@ -15,10 +15,10 @@ use Joomla\Database\DatabaseDriver;
\defined('_JEXEC') or die;
class DepotTable extends Table
class PartTable extends Table
{
function __contruct(DatabaseDriver $db)
function __construct(DatabaseDriver $db)
{
parent::__contruct('#__depot', 'id', $db);
parent::__construct('#__depot', 'id', $db);
}
}
}

View File

@ -0,0 +1,75 @@
<?php
/**
* @package Depot.Administrator
* @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.3
*/
namespace KW4NZ\Component\Depot\Administrator\View\Part;
use Joomla\CMS\Factory;
// use Joomla\CMS\MVC\View\GenericDataException;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Toolbar\Toolbar;
use Joomla\CMS\Toolbar\ToolbarHelper;
\defined('_JEXEC') or die;
/**
* View to edit an article.
*
* @since 1.6
*/
class HtmlView extends BaseHtmlView
{
/**
* The \JForm object
*
* @var \JForm
*/
protected $form;
/**
* The active item
*
* @var object
*/
protected $item;
/**
* Execute and display a template script.
*
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
*
* @return mixed A string if successful, otherwise an Error object.
*
* @throws \Exception
* @since 1.6
*/
public function display($tpl = null)
{
$this->form = $this->get('Form');
$this->item = $this->get('Item');
// if (count($errors = $this->get('Errors'))) {
// throw new GenericDataException(implode("\n", $errors), 500);
// }
$this->addToolbar();
return parent::display($tpl);
}
protected function addToolbar()
{
Factory::getApplication()->getInput()->set('hidemainmenu', true);
ToolbarHelper::title('Part: Add');
ToolbarHelper::apply('part.apply');
ToolbarHelper::save('part.save');
ToolbarHelper::cancel('part.cancel', 'JTOOLBAR_CLOSE');
}
}

27
admin/tmpl/part/edit.php Normal file
View File

@ -0,0 +1,27 @@
<?php
/**
* @package Depot.Administrator
* @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.3
*/
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Router\Route;
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->useScript('form.validate')
->useScript('keepalive');
?>
<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'); ?>
<input type="hidden" name="task" value="part.edit" />
<?= HTMLHelper::_('form.token'); ?>
</form>

View File

@ -9,3 +9,5 @@
*/
?>
<h2>Welcome to my Depot Component!</h2>
<p>Link: <a href="index.php?option=com_depot&view=part&layout=edit&id=1">Part</a></p>

View File

@ -2,12 +2,12 @@
<extension type="component" method="upgrade">
<name>Depot</name>
<author>KW4NZ</author>
<creationDate>2023-10-04</creationDate>
<creationDate>2023-10-06</creationDate>
<copyright>(C) KW4NZ Thomas Kuschel</copyright>
<license>GPL v2 +; see LICENSE.md</license>
<authorEmail>thomas@kuschel.at</authorEmail>
<authorUrl>https://kuschel.at</authorUrl>
<version>0.0.2</version>
<version>0.0.4</version>
<description>COM_DEPOT_XML_DESCRIPTION</description>
<namespace path="src/">KW4NZ\Component\Depot</namespace>
<install> <!-- Runs on install -->
@ -55,6 +55,7 @@
</menu>
</submenu>
<files folder="admin">
<folder>forms</folder>
<folder>services</folder>
<folder>sql</folder>
<folder>src</folder>