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.
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 within the Backend 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 TYPO3 uses convention over
configuration, so the TCA for this table is placed within
Configuration/TCA/tx_exampleextension_domain_model_address.php
within our
Extension.
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:
Once we finished the TCA, we already can create new records. Only saving them does not work, as we didn’t setup the database yet.
Note
By default new records are only allowed on pages of type “Folder”.
Task
Create necessary sql for our new record.
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 ext_tables.sql
in the root directory. Within the
admin tools 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 ext_tables.sql
is:
1 2 3 4 5 6 7 8 | CREATE TABLE tx_exampleextension_domain_model_address (
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,
);
|
All further TYPO3 specific fields, like uid
and pid
are added by TYPO3 CMS since v9.
Before v9, the file would look like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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)
);
|
We should now be able to create and save new records within the TYPO3 backend. Also existing records should be listed, searchable and editable.
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
Classes/Domain/Model/Address.php
.
Each model is a PHP class, structured like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?php
namespace Workshop\ExampleExtension\Domain\Model;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
class Address extends AbstractEntity
{
/**
* @var string
*/
protected $companyName;
public function getCompanyName(): string
{
return $this->companyName;
}
}
|
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 Classes/Domain/Repository/AddressRepository.php
:
1 2 3 4 5 6 7 8 9 10 | <?php
namespace Workshop\ExampleExtension\Domain\Repository;
use TYPO3\CMS\Extbase\Persistence\Repository;
class AddressRepository extends Repository
{
}
|
The parent class already provides all necessary methods for daily use cases.
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <?php
namespace Workshop\ExampleExtension\Controller;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
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;
}
|
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:
1 2 3 4 5 6 | class AddressController extends ActionController
public function indexAction()
{
$this->view->assign('addresses', $this->addressRepository->findAll());
}
|
The AddressRepository
extends the base Repository
class and inherits some
methods, e.g. findAll()
.
With our records in our template, we can iterate over them to display them.
Resources/Private/Templates/Address/Index.html
:
1 2 3 4 5 6 7 8 | <f:flashMessages />
<f:for each="{addresses}" as="address">
<h3>{address.companyName}</h3>
<address>
{address.street} {address.houseNumber}
{address.zip} {address.city}
{address.country}
<f:link.action action="edit" arguments="{address: address}">Edit</f:link.action>
|
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:
1 2 3 4 5 6 7 | plugin {
tx_exampleextension {
persistence {
storagePid = 2
}
}
}
|
You might have noticed that the above controller is not the same as in our first example. We therefore can add the controller to the existing plugin or add a new plugin for this controller.
I would recommend to create a new plugin, to separate things. The process is not explained again. If you struggle, take a look at Add first Plugin again.
Once everything is set up, the following should be possible:
Sounds like a lot of work for a small benefit? Right. If all you have to achieve is this, you should not use Extbase but “pure” TYPO3. But we will extend the Extension in the next step. Also this is about a first simple Extbase Extension, not how to use TYPO3 the right way.