From 89b63c3f434acfe11ee864bbb6e8165ac6583f7a Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Mon, 1 Oct 2018 13:51:18 +0200 Subject: [PATCH] TASK: Add update record, without validation --- .../Classes/Controller/AddressController.php | 60 +++++++ .../Classes/Domain/Model/Address.php | 30 ++++ .../TCA/Overrides/tt_content.php | 5 + .../Resources/Private/Language/locallang.xlf | 27 +++ .../Private/Templates/Address/Edit.html | 24 +++ .../Private/Templates/Address/Index.html | 10 ++ .../example_extension/ext_localconf.php | 11 ++ Documentation/source/AddFirstPlugin.rst | 25 ++- Documentation/source/CustomRecords.rst | 49 ++---- Documentation/source/UpdatingRecords.rst | 166 +++++++++++++++++- Documentation/source/_static/css/custom.css | 3 + Documentation/source/conf.py | 4 + 12 files changed, 370 insertions(+), 44 deletions(-) create mode 100644 CodeExamples/localPackages/example_extension/Classes/Controller/AddressController.php create mode 100644 CodeExamples/localPackages/example_extension/Resources/Private/Language/locallang.xlf create mode 100644 CodeExamples/localPackages/example_extension/Resources/Private/Templates/Address/Edit.html create mode 100644 CodeExamples/localPackages/example_extension/Resources/Private/Templates/Address/Index.html create mode 100644 Documentation/source/_static/css/custom.css diff --git a/CodeExamples/localPackages/example_extension/Classes/Controller/AddressController.php b/CodeExamples/localPackages/example_extension/Classes/Controller/AddressController.php new file mode 100644 index 0000000..f63f311 --- /dev/null +++ b/CodeExamples/localPackages/example_extension/Classes/Controller/AddressController.php @@ -0,0 +1,60 @@ + + * + * 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\Mvc\Controller\ActionController; +use Workshop\ExampleExtension\Domain\Model\Address; +use Workshop\ExampleExtension\Domain\Repository\AddressRepository; + +class AddressController extends ActionController +{ + /** + * @var AddressRepository + */ + protected $addressRepository; + + public function __construct(AddressRepository $addressRepository) + { + $this->addressRepository = $addressRepository; + } + + public function indexAction() + { + $this->view->assign('addresses', $this->addressRepository->findAll()); + } + + public function editAction(Address $address) + { + $this->view->assign('address', $address); + } + + public function updateAction(Address $address) + { + $this->addressRepository->update($address); + + $this->addFlashMessage( + $address->getCompanyName() . ' was updated.', + 'Update successfully' + ); + $this->redirect('index'); + } +} diff --git a/CodeExamples/localPackages/example_extension/Classes/Domain/Model/Address.php b/CodeExamples/localPackages/example_extension/Classes/Domain/Model/Address.php index 304206d..dd439b8 100644 --- a/CodeExamples/localPackages/example_extension/Classes/Domain/Model/Address.php +++ b/CodeExamples/localPackages/example_extension/Classes/Domain/Model/Address.php @@ -55,31 +55,61 @@ class Address extends AbstractEntity */ protected $country; + public function setCompanyName(string $companyName) + { + $this->companyName = $companyName; + } + public function getCompanyName(): string { return $this->companyName; } + public function setStreet(string $street) + { + $this->street = $street; + } + public function getStreet(): string { return $this->street; } + public function setHouseNumber(string $houseNumber) + { + $this->houseNumber = $houseNumber; + } + public function getHouseNumber(): string { return $this->houseNumber; } + public function setZip(string $zip) + { + $this->zip = $zip; + } + public function getZip(): string { return $this->zip; } + public function setCity(string $city) + { + $this->city = $city; + } + public function getCity(): string { return $this->city; } + public function setCountry(string $country) + { + $this->country = $country; + } + public function getCountry(): string { return $this->country; diff --git a/CodeExamples/localPackages/example_extension/Configuration/TCA/Overrides/tt_content.php b/CodeExamples/localPackages/example_extension/Configuration/TCA/Overrides/tt_content.php index bca38a0..529b61a 100644 --- a/CodeExamples/localPackages/example_extension/Configuration/TCA/Overrides/tt_content.php +++ b/CodeExamples/localPackages/example_extension/Configuration/TCA/Overrides/tt_content.php @@ -6,4 +6,9 @@ 'pluginkey', 'Example Plugin' ); + \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin( + 'Workshop.ExampleExtension', + 'Address', + 'Address Plugin' + ); })(); diff --git a/CodeExamples/localPackages/example_extension/Resources/Private/Language/locallang.xlf b/CodeExamples/localPackages/example_extension/Resources/Private/Language/locallang.xlf new file mode 100644 index 0000000..e69f7d0 --- /dev/null +++ b/CodeExamples/localPackages/example_extension/Resources/Private/Language/locallang.xlf @@ -0,0 +1,27 @@ + + + +
+ + + + Company name + + + Street + + + House number + + + Zip + + + City + + + Country + + + + diff --git a/CodeExamples/localPackages/example_extension/Resources/Private/Templates/Address/Edit.html b/CodeExamples/localPackages/example_extension/Resources/Private/Templates/Address/Edit.html new file mode 100644 index 0000000..c26a48f --- /dev/null +++ b/CodeExamples/localPackages/example_extension/Resources/Private/Templates/Address/Edit.html @@ -0,0 +1,24 @@ +

Editing: {address.companyName}

+ + + +

+ +
+ +
+

+
+ +
+ +
diff --git a/CodeExamples/localPackages/example_extension/Resources/Private/Templates/Address/Index.html b/CodeExamples/localPackages/example_extension/Resources/Private/Templates/Address/Index.html new file mode 100644 index 0000000..d8869da --- /dev/null +++ b/CodeExamples/localPackages/example_extension/Resources/Private/Templates/Address/Index.html @@ -0,0 +1,10 @@ + +

{address.companyName}

+
+ {address.street} {address.houseNumber} + {address.zip} {address.city} + {address.country} +
+ + Edit +
diff --git a/CodeExamples/localPackages/example_extension/ext_localconf.php b/CodeExamples/localPackages/example_extension/ext_localconf.php index 13dcce3..cd5656e 100644 --- a/CodeExamples/localPackages/example_extension/ext_localconf.php +++ b/CodeExamples/localPackages/example_extension/ext_localconf.php @@ -8,4 +8,15 @@ 'Example' => 'example' ] ); + + \TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin( + 'Workshop.ExampleExtension', + 'Address', + [ + 'Address' => 'index, edit, update' + ], + [ + 'Address' => 'update' + ] + ); })(); diff --git a/Documentation/source/AddFirstPlugin.rst b/Documentation/source/AddFirstPlugin.rst index b6130b4..9cf8c87 100644 --- a/Documentation/source/AddFirstPlugin.rst +++ b/Documentation/source/AddFirstPlugin.rst @@ -31,8 +31,15 @@ available as a new option for the content element *Insert Plugin*. This is done with the following code in file :file:`Configuration/TCA/Overrides/tt_content.php`: -.. literalinclude:: ../../CodeExamples/localPackages/example_extension/Configuration/TCA/Overrides/tt_content.php - :language: php +.. code-block:: php + + (function () { + \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin( + 'Workshop.ExampleExtension', + 'pluginkey', + 'Example Plugin' + ); + })(); Configure Plugin for Frontend ----------------------------- @@ -44,8 +51,18 @@ Configure Plugin for Frontend To actually call some PHP Code when the content element is rendered, we need to configure the plugin in :file:`ext_localconf.php`: -.. literalinclude:: ../../CodeExamples/localPackages/example_extension/ext_localconf.php - :language: php +.. code-block:: php + + (function () { + \TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin( + 'Workshop.ExampleExtension', + 'pluginkey', + [ + 'Example' => 'example' + ] + ); + })(); + Write necessary Code -------------------- diff --git a/Documentation/source/CustomRecords.rst b/Documentation/source/CustomRecords.rst index 32645f6..061aa31 100644 --- a/Documentation/source/CustomRecords.rst +++ b/Documentation/source/CustomRecords.rst @@ -90,7 +90,7 @@ 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 + :lines: 1-4,24-32,63-66,87 Repository ---------- @@ -121,24 +121,10 @@ Controller In order to provide records in form of models to our template, we first need an instance of our repository: -.. code-block:: php +.. literalinclude:: ../../CodeExamples/localPackages/example_extension/Classes/Controller/AddressController.php + :language: 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; - } - } + :lines: 1-4, 24-38,66 With the above code we only can create instances of the controller if an instance of the Repository is provided. @@ -151,16 +137,10 @@ 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 +.. literalinclude:: ../../CodeExamples/localPackages/example_extension/Classes/Controller/AddressController.php + :language: php :linenos: - - class ExampleController extends ActionController - { - public function exampleAction() - { - $this->view->assign('addresses', $this->addressRepository->findAll()); - } - } + :lines: 28-29,40-43,66 The ``AddressRepository`` extends the base ``Repository`` class and inherits some methods, e.g. ``findAll``. @@ -170,19 +150,12 @@ Template With our records in our template, we can iterate over them to display them. -:file:`Resources/Private/Templates/Example/Example.html`: +:file:`Resources/Private/Templates/Address/Index.html`: -.. code-block:: html +.. literalinclude:: ../../CodeExamples/localPackages/example_extension/Resources/Private/Templates/Address/Index.html + :language: html :linenos: - - -

{address.companyName}

-
- {address.street} {address.houseNumber} - {address.zip} {address.city} - {address.country} -
-
+ :lines: 1-7,10 Configure storage pid --------------------- diff --git a/Documentation/source/UpdatingRecords.rst b/Documentation/source/UpdatingRecords.rst index fb8b144..368a817 100644 --- a/Documentation/source/UpdatingRecords.rst +++ b/Documentation/source/UpdatingRecords.rst @@ -1,12 +1,174 @@ +.. highlight:: php + Updating records ================ -Form ----- +For everything we have done so far, you do not need a plugin at all. Custom records +only need :file:`ext_tables.sql` the TCA and TypoScript for rendering. + +Extbase is needed if you provide interaction to the user, e.g. updating or adding +records. + +Even that can nowadays achieved using the system extension "Form". Still we will +cover how to update a record next. + +We need a form where we can adjust the values of the record, e.g. change the +companies name. + +Therefore we will add a new ``editAction()``. This will receive a single ``Address`` +for editing and provides the form. We also will add an ``updateAction()`` which +receives a single ``Address``. This action is the target of the submit form and will +update the database record. + +To start editing an address, we will add an link from :file:`index.html` to +``editAction()``. + +Link to another action +---------------------- + +.. admonition:: Task + + Create a link to the ``editAction()`` providing the "current" Address record. + +TYPO3 provides ViewHelpers to create links to different actions. To insert a Link to +edit a record, you could use the following: + +.. literalinclude:: ../../CodeExamples/localPackages/example_extension/Resources/Private/Templates/Address/Index.html + :language: html + :linenos: + :dedent: 4 + :lines: 9 + +The ViewHelper generates a link, thanks to the current plugin context, all arguments +are prefixed with the plugin namespace. + +Creating Forms +-------------- + +.. admonition:: Task + + Create a form which will show the current values of a single ``Address`` to the + user. + +TYPO3 also provides ViewHelpers to create forms. Of course you could create form with +pure HTML, but TYPO3 / Fluid adds some security aspects and makes things easier. + +E.g. a proper validation that forms were not manipulated are added out of the box. +Also you do not need to take care of proper names of the inputs to allow Extbase to +map incoming inputs. + +A basic form looks like: + +.. code-block:: html + :linenos: + + + + + Persistence ----------- +.. admonition:: Task + + Save changes to database. + +Once the user submits the form, the ``updateAction()`` method within our controller is +called. We therefore have to implement this method and to persist the changes that +were submitted. + +We already have an instance of the accordingly repository within our controller. We +also receive the modified object as an argument within our action. All we have to do +is to update the record within the repository:: + + public function updateAction(Address $address) + { + $this->addressRepository->update($address); + } + +At the end of the "request", Extbase will cleanup everything and persist the updates +to the backend, which in our case is MySQL. + +Redirect +-------- + +.. admonition:: Task + + Redirect back to index. + +All our changes are already saved to database, but the user receives an error that no +template could be found. We actually do not need any template for our +``updateAction()``. Instead we will redirect the user back to the ``indexAction()``. +This way he can check whether the change has effected the output and works as +expected. + +.. note:: + + Following REST, an update returns the updated resource, which is the uri to the + resource, in our case the redirect. + + As Browsers do not support ``PATCH``, which would be the request method, we use + ``POST``, see: https://en.wikipedia.org/wiki/Representational_state_transfer#Relationship_between_URL_and_HTTP_methods + +As we extend ``ActionController`` we can use the following line to redirect to +another action:: + + $this->redirect('index'); + +Cache clearing +-------------- + +Even if the user is redirected, he does not see any difference. That's due to TYPO3 +caching. + +.. admonition:: Task + + Make changes visible in index action. + +Extbase, by default, clears the cache for all updated records. Therefore the page +cache for the pages holding the records is cleared. As our plugin resists on a +different page, we have to configure TYPO3. + +Same is true for plain TYPO3 Backend. As soon as a record is edited, the page cache +is cleared. If this record is displayed on another page, the caching has to be +configured, so this is not Extbase specific. + +The caching can be configured using Page TS Config: + +.. code-block:: typoscript + + TCEMAIN { + clearCacheCmd = 1 + } + +See: https://docs.typo3.org/typo3cms/TSconfigReference/PageTsconfig/TceMain.html#clearcachecmd + +Flash message +------------- + +.. admonition:: Task + + Inform user about what happened. + +We now have a fully working process. Still in a long list of records, the user might +not notice a difference. Also if he leaves the computer and comes back, he will not +know what was done before. + +Extbase has a feature called "Flashmessages" which are also used within TYPO3 +Backend. They inform a user on next page about some thing that happened during +the last request. We could use that to add a message about which record was updated. +This is also just one line within a controller:: + + $this->addFlashMessage( + $address->getCompanyName() . ' was updated.', + 'Update successfully' + ); + Validation ---------- +.. admonition:: Task + + Prevent invalid data. + diff --git a/Documentation/source/_static/css/custom.css b/Documentation/source/_static/css/custom.css new file mode 100644 index 0000000..915c825 --- /dev/null +++ b/Documentation/source/_static/css/custom.css @@ -0,0 +1,3 @@ +.admonition-task { + border-left-color: #ad7526; +} diff --git a/Documentation/source/conf.py b/Documentation/source/conf.py index cf712d6..e5a4a18 100644 --- a/Documentation/source/conf.py +++ b/Documentation/source/conf.py @@ -317,3 +317,7 @@ intersphinx_mapping = { from sphinx.highlighting import lexers from pygments.lexers.web import PhpLexer lexers['php'] = PhpLexer(startinline=True, linenos=1) + +# Add custom css +def setup(app): + app.add_stylesheet('css/custom.css')