From f7f4be03351e4788eb864dd8728de4abd066727d Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Wed, 5 Sep 2018 13:22:42 +0200 Subject: [PATCH] TASK: Add configuration and custom record content --- .../Classes/Controller/ExampleController.php | 2 +- .../Classes/Domain/Model/Address.php | 87 ++++++++ .../Domain/Repository/AddressRepository.php | 29 +++ ..._exampleextension_domain_model_address.php | 101 +++++++++ .../Private/Language/locallang_tca.xlf | 29 +++ .../example_extension/ext_tables.sql | 22 ++ Documentation/source/Configuration.rst | 124 +++++++++++ Documentation/source/CustomRecords.rst | 198 +++++++++++++++++- Documentation/source/conf.py | 1 + 9 files changed, 590 insertions(+), 3 deletions(-) create mode 100644 CodeExamples/localPackages/example_extension/Classes/Domain/Model/Address.php create mode 100644 CodeExamples/localPackages/example_extension/Classes/Domain/Repository/AddressRepository.php create mode 100644 CodeExamples/localPackages/example_extension/Configuration/TCA/tx_exampleextension_domain_model_address.php create mode 100644 CodeExamples/localPackages/example_extension/Resources/Private/Language/locallang_tca.xlf create mode 100644 CodeExamples/localPackages/example_extension/ext_tables.sql diff --git a/CodeExamples/localPackages/example_extension/Classes/Controller/ExampleController.php b/CodeExamples/localPackages/example_extension/Classes/Controller/ExampleController.php index 983a635..ae243d5 100644 --- a/CodeExamples/localPackages/example_extension/Classes/Controller/ExampleController.php +++ b/CodeExamples/localPackages/example_extension/Classes/Controller/ExampleController.php @@ -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!'; } } diff --git a/CodeExamples/localPackages/example_extension/Classes/Domain/Model/Address.php b/CodeExamples/localPackages/example_extension/Classes/Domain/Model/Address.php new file mode 100644 index 0000000..304206d --- /dev/null +++ b/CodeExamples/localPackages/example_extension/Classes/Domain/Model/Address.php @@ -0,0 +1,87 @@ + + * + * 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; + } +} diff --git a/CodeExamples/localPackages/example_extension/Classes/Domain/Repository/AddressRepository.php b/CodeExamples/localPackages/example_extension/Classes/Domain/Repository/AddressRepository.php new file mode 100644 index 0000000..506d745 --- /dev/null +++ b/CodeExamples/localPackages/example_extension/Classes/Domain/Repository/AddressRepository.php @@ -0,0 +1,29 @@ + + * + * 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 +{ + +} diff --git a/CodeExamples/localPackages/example_extension/Configuration/TCA/tx_exampleextension_domain_model_address.php b/CodeExamples/localPackages/example_extension/Configuration/TCA/tx_exampleextension_domain_model_address.php new file mode 100644 index 0000000..6af4230 --- /dev/null +++ b/CodeExamples/localPackages/example_extension/Configuration/TCA/tx_exampleextension_domain_model_address.php @@ -0,0 +1,101 @@ + [ + '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', + ], + ], + ], + ]; +})(); diff --git a/CodeExamples/localPackages/example_extension/Resources/Private/Language/locallang_tca.xlf b/CodeExamples/localPackages/example_extension/Resources/Private/Language/locallang_tca.xlf new file mode 100644 index 0000000..4ee28b2 --- /dev/null +++ b/CodeExamples/localPackages/example_extension/Resources/Private/Language/locallang_tca.xlf @@ -0,0 +1,29 @@ + + + +
+ + + Address + + + Company name + + + Street + + + House number + + + Zip + + + City + + + Country + + + + diff --git a/CodeExamples/localPackages/example_extension/ext_tables.sql b/CodeExamples/localPackages/example_extension/ext_tables.sql new file mode 100644 index 0000000..d340b20 --- /dev/null +++ b/CodeExamples/localPackages/example_extension/ext_tables.sql @@ -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) +); diff --git a/Documentation/source/Configuration.rst b/Documentation/source/Configuration.rst index ffddf59..0ca9121 100644 --- a/Documentation/source/Configuration.rst +++ b/Documentation/source/Configuration.rst @@ -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. diff --git a/Documentation/source/CustomRecords.rst b/Documentation/source/CustomRecords.rst index 5dd7fda..32645f6 100644 --- a/Documentation/source/CustomRecords.rst +++ b/Documentation/source/CustomRecords.rst @@ -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: + + +

{address.companyName}

+
+ {address.street} {address.houseNumber} + {address.zip} {address.city} + {address.country} +
+
+ +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 + } + } + } + diff --git a/Documentation/source/conf.py b/Documentation/source/conf.py index df0fb2c..cf712d6 100644 --- a/Documentation/source/conf.py +++ b/Documentation/source/conf.py @@ -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