TASK: Add configuration and custom record content
This commit is contained in:
parent
e47a16656d
commit
f7f4be0335
9 changed files with 590 additions and 3 deletions
|
@ -31,6 +31,6 @@ class ExampleController extends ActionController
|
|||
// Comment the code out, to use fluid template from
|
||||
// "Resources/Private/Templates/Example/Example.html"
|
||||
|
||||
// return 'Hello world!';
|
||||
return 'Hello world!';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
namespace Workshop\ExampleExtension\Domain\Model;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
|
||||
|
||||
class Address extends AbstractEntity
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $companyName;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $street;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $houseNumber;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $zip;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $city;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $country;
|
||||
|
||||
public function getCompanyName(): string
|
||||
{
|
||||
return $this->companyName;
|
||||
}
|
||||
|
||||
public function getStreet(): string
|
||||
{
|
||||
return $this->street;
|
||||
}
|
||||
|
||||
public function getHouseNumber(): string
|
||||
{
|
||||
return $this->houseNumber;
|
||||
}
|
||||
|
||||
public function getZip(): string
|
||||
{
|
||||
return $this->zip;
|
||||
}
|
||||
|
||||
public function getCity(): string
|
||||
{
|
||||
return $this->city;
|
||||
}
|
||||
|
||||
public function getCountry(): string
|
||||
{
|
||||
return $this->country;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Workshop\ExampleExtension\Domain\Repository;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Extbase\Persistence\Repository;
|
||||
|
||||
class AddressRepository extends Repository
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
<?php
|
||||
|
||||
return (function (
|
||||
$extensionKey = 'example_extension',
|
||||
$tableName = 'tx_workshopexampleextension_domain_model_address'
|
||||
) {
|
||||
$extensionLanguagePrefix = 'LLL:EXT:example_extension/Resources/Private/Language/locallang_tca.xlf:';
|
||||
$coreLanguagePrefix = 'LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:';
|
||||
|
||||
return [
|
||||
'ctrl' => [
|
||||
'label' => 'company_name',
|
||||
'default_sortby' => 'company_name',
|
||||
'tstamp' => 'tstamp',
|
||||
'crdate' => 'crdate',
|
||||
'cruser_id' => 'cruser_id',
|
||||
'title' => $extensionLanguagePrefix . 'address',
|
||||
'delete' => 'deleted',
|
||||
'enablecolumns' => [
|
||||
'disabled' => 'hidden',
|
||||
'starttime' => 'starttime',
|
||||
'endtime' => 'endtime'
|
||||
],
|
||||
'searchFields' => 'company_name, street, city'
|
||||
],
|
||||
'interface' => [
|
||||
'showRecordFieldList' => 'company_name, street, house_number, zip, city, country'
|
||||
],
|
||||
'palettes' => [
|
||||
'address' => [
|
||||
'showitem' => implode(',', [
|
||||
'street, house_number',
|
||||
'--linebreak--',
|
||||
'zip, city',
|
||||
'--linebreak--',
|
||||
'country',
|
||||
]),
|
||||
],
|
||||
],
|
||||
'types' => [
|
||||
'0' => [
|
||||
'showitem' => implode(',', [
|
||||
'--div--;' . $coreLanguagePrefix . 'general',
|
||||
'company_name;;address',
|
||||
]),
|
||||
],
|
||||
],
|
||||
'columns' => [
|
||||
'company_name' => [
|
||||
'label' => $extensionLanguagePrefix . 'company_name',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'max' => 255,
|
||||
'eval' => 'trim,required',
|
||||
],
|
||||
],
|
||||
'street' => [
|
||||
'label' => $extensionLanguagePrefix . 'street',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'max' => 255,
|
||||
'eval' => 'trim,required',
|
||||
],
|
||||
],
|
||||
'house_number' => [
|
||||
'label' => $extensionLanguagePrefix . 'house_number',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 10,
|
||||
'max' => 255,
|
||||
'eval' => 'trim,required',
|
||||
],
|
||||
],
|
||||
'zip' => [
|
||||
'label' => $extensionLanguagePrefix . 'zip',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 10,
|
||||
'max' => 255,
|
||||
'eval' => 'trim,required',
|
||||
],
|
||||
],
|
||||
'city' => [
|
||||
'label' => $extensionLanguagePrefix . 'city',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'max' => 255,
|
||||
'eval' => 'trim,required',
|
||||
],
|
||||
],
|
||||
'country' => [
|
||||
'label' => $extensionLanguagePrefix . 'country',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'max' => 255,
|
||||
'eval' => 'trim,required',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
})();
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<file source-language="en" datatype="plaintext" original="messages" date="2018-09-05T08:51:01Z" product-name="CodeExamples">
|
||||
<header/>
|
||||
<body>
|
||||
<trans-unit id="address" xml:space="preserve">
|
||||
<source>Address</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="company_name" xml:space="preserve">
|
||||
<source>Company name</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="street" xml:space="preserve">
|
||||
<source>Street</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="house_number" xml:space="preserve">
|
||||
<source>House number</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="zip" xml:space="preserve">
|
||||
<source>Zip</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="city" xml:space="preserve">
|
||||
<source>City</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="country" xml:space="preserve">
|
||||
<source>Country</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
22
CodeExamples/localPackages/example_extension/ext_tables.sql
Normal file
22
CodeExamples/localPackages/example_extension/ext_tables.sql
Normal file
|
@ -0,0 +1,22 @@
|
|||
CREATE TABLE tx_exampleextension_domain_model_address (
|
||||
uid int(11) unsigned NOT NULL auto_increment,
|
||||
pid int(11) unsigned DEFAULT '0' NOT NULL,
|
||||
|
||||
crdate int(11) unsigned DEFAULT '0' NOT NULL,
|
||||
cruser_id int(11) unsigned DEFAULT '0' NOT NULL,
|
||||
tstamp int(11) unsigned DEFAULT '0' NOT NULL,
|
||||
hidden tinyint(3) unsigned DEFAULT '0' NOT NULL,
|
||||
deleted tinyint(3) unsigned DEFAULT '0' NOT NULL,
|
||||
starttime int(11) unsigned DEFAULT '0' NOT NULL,
|
||||
endtime int(11) unsigned DEFAULT '0' NOT NULL,
|
||||
|
||||
company_name varchar(255) DEFAULT '' NOT NULL,
|
||||
street varchar(255) DEFAULT '' NOT NULL,
|
||||
house_number varchar(255) DEFAULT '' NOT NULL,
|
||||
zip varchar(255) DEFAULT '' NOT NULL,
|
||||
city varchar(255) DEFAULT '' NOT NULL,
|
||||
country varchar(255) DEFAULT '' NOT NULL,
|
||||
|
||||
PRIMARY KEY (uid),
|
||||
KEY parent (pid)
|
||||
);
|
|
@ -1,14 +1,138 @@
|
|||
Configuration
|
||||
=============
|
||||
|
||||
There are many different ways and places in TYPO3 for configuration. We will cover
|
||||
the different places and types of configuration, including when to use a certain way.
|
||||
|
||||
By following the API you also make sure modern approaches like configuration loaders
|
||||
will work with your extensions.
|
||||
|
||||
Places of configuration
|
||||
-----------------------
|
||||
|
||||
The following places exist to configure a TYPO3 Extensions:
|
||||
|
||||
PHP
|
||||
^^^
|
||||
|
||||
PHP via :file:`LocalConfiguration.php` and :file:`AdditionalConfiguration.php`.
|
||||
|
||||
Some extensions allow to place configuration in custom files, e.g. EXT:realurl. I
|
||||
would call that a bad practice as you have arbitrary places to check for certain
|
||||
configurations.
|
||||
|
||||
Instead use existing places like the two mentioned files above. This is done by
|
||||
either using:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
return [
|
||||
'EXTCONF' => [
|
||||
'extkey' => [
|
||||
// options
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
This way you can access the configuration via ``$GLOBALS['EXTCONF']['extkey']``.
|
||||
|
||||
Or by providing a :file:`ext_conf_template.txt` in the root of your Extension.
|
||||
The content is TypoScript as documented at :ref:`t3coreapi:extension-options`.
|
||||
Afterwards you can access the options through an API.
|
||||
|
||||
TypoScript
|
||||
^^^^^^^^^^
|
||||
|
||||
TypoScript and Flexforms are merged by Extbase, so you do not have to do any
|
||||
additional work and combine both. They are available as Variable ``{settings}`` in
|
||||
all Templates. Also inside the Controller they are available as array in
|
||||
``$this->settings`` out of the box.
|
||||
|
||||
.. admonition:: Task
|
||||
|
||||
Add some settings and print them in Template and Controller.
|
||||
|
||||
The configuration via TypoScript has to be located at a specific path:
|
||||
|
||||
.. code-block:: typoscript
|
||||
:linenos:
|
||||
|
||||
// For all frontend plugins of the extension
|
||||
plugin {
|
||||
tx_exampleextension {
|
||||
settings {
|
||||
// The configuration goes here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For a specific frontend plugin of the extension
|
||||
plugin {
|
||||
tx_exampleextension_pluginkey {
|
||||
settings {
|
||||
// The configuration goes here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For Backend Modules
|
||||
module {
|
||||
tx_exampleextension {
|
||||
settings {
|
||||
// The configuration goes here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Extbase itself already has some configuration options available via TypoScript, some
|
||||
are mentioned at :ref:`t3extbasebook:typoscript_configuration` section of Extbase
|
||||
book.
|
||||
|
||||
.. tip::
|
||||
|
||||
The whole ``settings`` array is passed into all templates, layouts and partials.
|
||||
This way it's possible for integrators to provide arbitary information.
|
||||
|
||||
Also it's possible to insert a plugin via TypoScript. In that case the settings can
|
||||
be provided only for that instance:
|
||||
|
||||
.. code-block:: typoscript
|
||||
:linenos:
|
||||
|
||||
lib.instance = USER
|
||||
lib.instance {
|
||||
userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run
|
||||
extensionName = ExampleExtension
|
||||
pluginName = pluginkey
|
||||
vendorName = Workshop
|
||||
settings {
|
||||
testKey = testValue
|
||||
}
|
||||
}
|
||||
|
||||
Flexforms
|
||||
^^^^^^^^^
|
||||
|
||||
Flexforms are like TCA, which will be covered later on. The format is XML instead of
|
||||
PHP and saved inside the database field ``pi_flexform`` of the ``tt_content`` record.
|
||||
This way editors are able to adjust provided settings within a plugin record.
|
||||
|
||||
Custom
|
||||
^^^^^^
|
||||
|
||||
Do whatever you want, e.g. use yaml or TypoScript by calling the parser for contents
|
||||
from anywhere.
|
||||
|
||||
When to use which
|
||||
-----------------
|
||||
|
||||
The Flexform approach provides the best UX as it uses the known UI of TYPO3 inside a
|
||||
record.
|
||||
|
||||
The TypoScript provided the best UX when integrators have to deploy configuration or
|
||||
configuration is necessary on multiple pages. Also if the plugin is inserted directly
|
||||
via TypoScript.
|
||||
|
||||
The PHP approach is best suited for instance wide configuration, which nearly never
|
||||
exists. Things like API Keys might depend on the current Domain or Website, and there
|
||||
can be multiple in a single TYPO3 instance.
|
||||
|
|
|
@ -1,14 +1,208 @@
|
|||
Custom records
|
||||
==============
|
||||
|
||||
The basics are behind us, now let's get deeper into the system and create a new
|
||||
record type, like ``tt_address`` which can be displayed through our plugin.
|
||||
|
||||
TCA
|
||||
---
|
||||
|
||||
.. admonition:: Task
|
||||
|
||||
Create necessary TCA for our new record.
|
||||
|
||||
Before we can do anything with Extbase, we need to configure TYPO3. The TCA (=Table
|
||||
Configuration Array) contains configuration for each database table. TYPO3 will
|
||||
generate the list view and edit forms from this configuration.
|
||||
|
||||
Extbase uses this configuration for mapping and database queries, together with
|
||||
relation handling.
|
||||
|
||||
TYPO3 provides a rich documentation about the TCA at
|
||||
https://docs.typo3.org/typo3cms/TCAReference/. That's why this section is empty, all
|
||||
information are available there.
|
||||
|
||||
One thing to notice is that Extbase uses "Convention over Configuration". While we
|
||||
can configure Extbase to map a Model to a specific database table, we can auto match
|
||||
them. For a Model ``Workshop\ExampleExtension\Domain\Model\Address``, the database
|
||||
table would be ``tx_exampleextension_domain_model_address``. So this will be
|
||||
our database table name for our example.
|
||||
|
||||
Also each property within the model is written lowerCamelCase, while the database
|
||||
columns are written snake_case.
|
||||
|
||||
Our new record will be an address record with the following fields:
|
||||
|
||||
* Company Name
|
||||
|
||||
* Street
|
||||
|
||||
* House number
|
||||
|
||||
* Zip
|
||||
|
||||
* City
|
||||
|
||||
* Country
|
||||
|
||||
.. note::
|
||||
|
||||
By default new records are only allowed on pages of type "Folder".
|
||||
|
||||
ext_tables.sql
|
||||
--------------
|
||||
|
||||
.. admonition:: Task
|
||||
|
||||
Create necessary sql for our new record.
|
||||
|
||||
.. admonition:: Task
|
||||
|
||||
Create some records, edit them, play around.
|
||||
|
||||
Once the TCA is provided, we need to create the table in our Database.
|
||||
Each extension can provide a :file:`ext_tables.sql` in the root directory. Within the
|
||||
install tool and TYPO3 Console, you can update the database schema to match the
|
||||
current necessary structure of all extensions.
|
||||
|
||||
If multiple extensions adjust the same field, the last one in load order is used.
|
||||
|
||||
The example :file:`ext_tables.sql` is:
|
||||
|
||||
.. literalinclude:: ../../CodeExamples/localPackages/example_extension/ext_tables.sql
|
||||
:language: sql
|
||||
|
||||
Model
|
||||
-----
|
||||
|
||||
.. admonition:: Task
|
||||
|
||||
Create Model representing our records.
|
||||
|
||||
Once we are able to create and edit records in TYPO3 backend, we are ready to go with
|
||||
Extbase. First we need a representation of our Data. This is done with a Model, in
|
||||
our case this has to match the table name and is called
|
||||
``Workshop\ExampleExtension\Domain\Model\Address`` and located at
|
||||
:file:`Classes/Domain/Model/Address.php`.
|
||||
|
||||
Each model is a PHP class structure like:
|
||||
|
||||
.. literalinclude:: ../../CodeExamples/localPackages/example_extension/Classes/Domain/Model/Address.php
|
||||
:language: php
|
||||
:linenos:
|
||||
:lines: 1-4,24-32,58-61,87
|
||||
|
||||
Repository
|
||||
----------
|
||||
|
||||
.. admonition:: Task
|
||||
|
||||
Create Repository to access records.
|
||||
|
||||
In order to get, update or delete our records, we need a repository. This will return
|
||||
the models for us. The repository is another class which can be completely empty:
|
||||
|
||||
The file is located at :file:`Classes/Domain/Repository/AddressRepository.php`:
|
||||
|
||||
.. literalinclude:: ../../CodeExamples/localPackages/example_extension/Classes/Domain/Repository/AddressRepository.php
|
||||
:language: php
|
||||
:linenos:
|
||||
:lines: 1-4, 24-
|
||||
|
||||
The parent class already provides all necessary methods for daily use cases.
|
||||
|
||||
Controller
|
||||
----------
|
||||
|
||||
Model
|
||||
-----
|
||||
.. admonition:: Task
|
||||
|
||||
Provide available records to template.
|
||||
|
||||
In order to provide records in form of models to our template, we first need an
|
||||
instance of our repository:
|
||||
|
||||
.. code-block:: php
|
||||
:linenos:
|
||||
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||
use Workshop\ExampleExtension\Domain\Repository\AddressRepository;
|
||||
|
||||
class ExampleController extends ActionController
|
||||
{
|
||||
/**
|
||||
* @var AddressRepository
|
||||
*/
|
||||
protected $addressRepository;
|
||||
|
||||
public function __construct(AddressRepository $addressRepository)
|
||||
{
|
||||
$this->addressRepository = $addressRepository;
|
||||
}
|
||||
}
|
||||
|
||||
With the above code we only can create instances of the controller if an instance of
|
||||
the Repository is provided.
|
||||
|
||||
Extbase itself will analyse dependencies inside ``__construct`` and will provide
|
||||
instances. This is called Dependency Injection and works in three different ways with
|
||||
Extbase. The above one is the preferred as this is not Extbase specific but will also
|
||||
work in other PHP Frameworks and without any Dependency Injection at all.
|
||||
|
||||
We then can call the accordingly method to fetch records, which then can be assigned
|
||||
to the view:
|
||||
|
||||
.. code-block:: php
|
||||
:linenos:
|
||||
|
||||
class ExampleController extends ActionController
|
||||
{
|
||||
public function exampleAction()
|
||||
{
|
||||
$this->view->assign('addresses', $this->addressRepository->findAll());
|
||||
}
|
||||
}
|
||||
|
||||
The ``AddressRepository`` extends the base ``Repository`` class and inherits some
|
||||
methods, e.g. ``findAll``.
|
||||
|
||||
Template
|
||||
--------
|
||||
|
||||
With our records in our template, we can iterate over them to display them.
|
||||
|
||||
:file:`Resources/Private/Templates/Example/Example.html`:
|
||||
|
||||
.. code-block:: html
|
||||
:linenos:
|
||||
|
||||
<f:for each="{addresses}" as="address">
|
||||
<h2>{address.companyName}</h2>
|
||||
<address>
|
||||
{address.street} {address.houseNumber}
|
||||
{address.zip} {address.city}
|
||||
{address.country}
|
||||
</address>
|
||||
</f:for>
|
||||
|
||||
Configure storage pid
|
||||
---------------------
|
||||
|
||||
We should not see any addresses yet, that’s due to the generated database query by
|
||||
Extbase. If no storage pid is configured, Extbase will fetch records from pid 0.
|
||||
|
||||
Within the content element we can select arbitrary "Record Storage Page" entries to
|
||||
use for records.
|
||||
|
||||
We could also configure the pid via TypoScript:
|
||||
|
||||
.. code-block:: typoscript
|
||||
:linenos:
|
||||
|
||||
plugin {
|
||||
tx_exampleextension {
|
||||
persistence {
|
||||
storagePid = 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -308,6 +308,7 @@ texinfo_documents = [
|
|||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {
|
||||
't3coreapi': ('https://docs.typo3.org/typo3cms/CoreApiReference/', None),
|
||||
't3extbasebook': ('https://docs.typo3.org/typo3cms/ExtbaseFluidBook/', None),
|
||||
}
|
||||
|
||||
# Allow inline PHP highlighting
|
||||
|
|
Loading…
Reference in a new issue