TASK: Add update record, without validation
This commit is contained in:
parent
f7f4be0335
commit
89b63c3f43
12 changed files with 370 additions and 44 deletions
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace Workshop\ExampleExtension\Controller;
|
||||
|
||||
/*
|
||||
* 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\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');
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -6,4 +6,9 @@
|
|||
'pluginkey',
|
||||
'Example Plugin'
|
||||
);
|
||||
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin(
|
||||
'Workshop.ExampleExtension',
|
||||
'Address',
|
||||
'Address Plugin'
|
||||
);
|
||||
})();
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<?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-10-01T11:16:33Z" product-name="CodeExamples">
|
||||
<header/>
|
||||
<body>
|
||||
<!-- Form Labels -->
|
||||
<trans-unit id="labels.companyName" xml:space="preserve">
|
||||
<source>Company name</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="labels.street" xml:space="preserve">
|
||||
<source>Street</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="labels.houseNumber" xml:space="preserve">
|
||||
<source>House number</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="labels.zip" xml:space="preserve">
|
||||
<source>Zip</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="labels.city" xml:space="preserve">
|
||||
<source>City</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="labels.country" xml:space="preserve">
|
||||
<source>Country</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
|
@ -0,0 +1,24 @@
|
|||
<h3>Editing: {address.companyName}</h3>
|
||||
|
||||
<f:form action="update" object="{address}" name="address">
|
||||
<f:for each="{
|
||||
0: 'companyName',
|
||||
1: 'street',
|
||||
2: 'houseNumber',
|
||||
3: 'zip',
|
||||
4: 'city',
|
||||
5: 'country'
|
||||
}" as="propertyName">
|
||||
<p>
|
||||
<label for="{propertyName}">
|
||||
{f:translate(id: 'labels.{propertyName}')}
|
||||
</label>
|
||||
<br>
|
||||
<f:form.textfield property="{propertyName}" id="{propertyName}" />
|
||||
<br>
|
||||
</p>
|
||||
</f:for>
|
||||
|
||||
<br>
|
||||
<f:form.submit value="Update" />
|
||||
</f:form>
|
|
@ -0,0 +1,10 @@
|
|||
<f:for each="{addresses}" as="address">
|
||||
<h3>{address.companyName}</h3>
|
||||
<address>
|
||||
{address.street} {address.houseNumber}
|
||||
{address.zip} {address.city}
|
||||
{address.country}
|
||||
</address>
|
||||
|
||||
<f:link.action action="edit" arguments="{address: address}">Edit</f:link.action>
|
||||
</f:for>
|
|
@ -8,4 +8,15 @@
|
|||
'Example' => 'example'
|
||||
]
|
||||
);
|
||||
|
||||
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
|
||||
'Workshop.ExampleExtension',
|
||||
'Address',
|
||||
[
|
||||
'Address' => 'index, edit, update'
|
||||
],
|
||||
[
|
||||
'Address' => 'update'
|
||||
]
|
||||
);
|
||||
})();
|
||||
|
|
|
@ -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
|
||||
--------------------
|
||||
|
|
|
@ -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:
|
||||
|
||||
<f:for each="{addresses}" as="address">
|
||||
<h2>{address.companyName}</h2>
|
||||
<address>
|
||||
{address.street} {address.houseNumber}
|
||||
{address.zip} {address.city}
|
||||
{address.country}
|
||||
</address>
|
||||
</f:for>
|
||||
:lines: 1-7,10
|
||||
|
||||
Configure storage pid
|
||||
---------------------
|
||||
|
|
|
@ -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:
|
||||
|
||||
<f:form action="update" object="{address}" name="address">
|
||||
<f:form.textfield property="companyName" />
|
||||
<f:form.submit value="Update" />
|
||||
</f:form>
|
||||
|
||||
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.
|
||||
|
||||
|
|
3
Documentation/source/_static/css/custom.css
Normal file
3
Documentation/source/_static/css/custom.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
.admonition-task {
|
||||
border-left-color: #ad7526;
|
||||
}
|
|
@ -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')
|
||||
|
|
Loading…
Reference in a new issue