ll_anthology Extension 

A TYPO3 extension that provides a generic content display system for listing and viewing records from any configured repository with advanced filtering capabilities.

Overview 

The ll_anthology extension creates a flexible plugin system that can display content from any TYPO3 repository in both list and single view modes. It dynamically loads repositories based on configuration and provides pagination, advanced filtering, template customisation, and routing capabilities.

Features 

  • Dual Display Modes: List view with pagination and single record view
  • Dynamic Repository Loading: Configurable to work with any TYPO3 repository
  • Advanced Filtering System: Search, category, and date filtering with multiple display modes
  • Flexible Template System: Custom template paths and template name overrides
  • Pagination Support: Configurable items per page and pagination links
  • SEO-Friendly: Automatic page title generation from record data
  • Route Enhancement: Built-in route enhancers for clean URLs

Requirements 

  • TYPO3 CMS ^13.4
  • PHP 8.2+
  • Composer
  • FluidTYPO3 VHS ^7.1

Documentation 

For detailed setup and configuration instructions, see the documentation:

  • Documentation/00.%20Quick%20start - Complete setup walkthrough
  • Documentation/10.%20Enable%20a%20model - Connecting your data models
  • Documentation/20.%20Routing - URL configuration for clean URLs
  • Documentation/30.%20Adding%20sitemap%20items - SEO integration
  • Documentation/40.%20Templates - Customising the output
  • Documentation/50.%20Custom%20filters - Creating custom filtering options

Quick start 

This guide provides a comprehensive overview for getting the Anthology extension running, assuming you have an existing TYPO3 model you wish to display.

Installation 

Install the extension via Composer:

composer require liquidlight/typo3-anthology
Copied!

Create or edit Configuration/TypoScript/setup.typoscript in your site package or extension:

@import 'EXT:ll_anthology/Configuration/TypoScript/setup'
Copied!

Automated setup 

The quickest and easiest way to get started with Anthology is using the anthology:setup command:

1. Run the command 

./vendor/bin/typo3 anthology:setup [site_package_path]
Copied!

Replace [site_package_path] with the path to either your site package, or the extension you are modifying.

Once the command has completed, it is recommended you manually review the generated configuration for accuracy before proceeding.

2. Add the generated configuration 

If it is not already configured, you must include the generated sitemap.typoscript and [model_name].typoscript files in your site package or extension's TypoScript file:

@import './TypoScript/modules'
Copied!

And also add the generated YAML configuration to your site configuration YAML

imports:
  -
    resource: 'EXT:site_package/Configuration/Sites/[ModelName].yaml'
Copied!

Manual setup 

1. Repository Configuration 

For the extension to function, you must add the AsAnthologyRepository attribute to your model's repository.

use LiquidLight\Anthology\Attribute\AsAnthologyRepository;
use TYPO3\CMS\Extbase\Persistence\Repository;

#[AsAnthologyRepository('tx_myextension_domain_model_item')]
class ItemRepository extends Repository {
    ...
}
Copied!
  • AsAnthologyRepository's tableName argument must be the name of your database table and TCA.

2. Add and Configure the Plugin 

  1. Navigate to the Page module and add the Anthology content element from the Plugins tab.
  2. Configure the plugin settings:

    General Tab

    • Mode: Choose List for the main view or Single for a detail page.
    • Model name: Select your model (e.g., "My Extension Items"). This list is populated based on your TypoScript configuration.
    • Single View Page: For a List plugin, link to the page where the Single view is located.

    Display Tab

    • Items Per Page: Set the number of items for pagination (e.g., 10).
    • Enable Pagination: Activate or deactivate the pagination.

    Filters Tab

    • Assign pre-configured filter records to the plugin.

3. Routing (for Detail Pages) 

To create user-friendly URLs for your detail pages, add a route enhancer to your site's config.yaml:

routeEnhancers:
  # Single view with slug-based URLs
  ItemSingle:
    type: Extbase
    limitToPages:
      - 123  # Your single view page UID
    extension: LlAnthology
    plugin: AnthologyView
    routes:
      # Single record by slug
      - routePath: '/{record}'
        _controller: 'Anthology::single'
    defaultController: 'Anthology::view'
    aspects:
      record:
        type: PersistedAliasMapper
        tableName: tx_myextension_domain_model_item
        routeFieldName: slug
Copied!

This requires a slug field in your model's table.

For further information, and to add pagination route enhancers to the list view, refer to the ./20.%20Routing documentation.

4. Sitemap 

To include your detail pages in the XML sitemap, add a sitemap provider to your TypoScript:

plugin.tx_seo.config.xmlSitemap.sitemaps {
    my_items {
        provider = TYPO3\CMS\Seo\XmlSitemap\RecordsXmlSitemapDataProvider
        config {
            table = tx_myextension_domain_model_item
            sortField = crdate
            lastModifiedField = tstamp
            pid = 123  # Storage page UID
            url {
                pageId = 456  # Single view page UID
                fieldToParameterMap {
                    uid = tx_llanthology_anthologyview[record]
                }
                additionalGetParameters {
                    tx_llanthology_anthologyview.action = single
                    tx_llanthology_anthologyview.controller = Anthology
                }
            }
        }
    }
}
Copied!

See the 30.%20Adding%20sitemap%20items guide for more details.

5. Templates 

The extension will automatically use templates from your model's extension directory. To override a template, copy it from ll_anthology/Resources/Private/ to the corresponding path in your own extension (e.g., my_extension/Resources/Private/Partials/List/Record.html) and modify it.

Enable a model 

The Anthology extension is designed to be a generic tool for displaying records from any database table. To achieve this, you must explicitly tell the extension which model you want it to work with. The ./00.%20Quick%20start guide provides a brief overview, whilst this guide explains the concepts in more detail.

Making a model available involves three key parts: the TCA, the Repository, and the TypoScript configuration.

1. The TCA File 

At a minimum, you need a ctrl section in your TCA file with a title. This title is what will appear in the plugin's "Model name" selection box.

Here is a minimal example for a hypothetical table named tx_myextension_domain_model_item:

Configuration/TCA/tx_myextension_domain_model_item.php

<?php
return [
    'ctrl' => [
        'title' => 'My Extension Items',
        'label' => 'title',
        'tstamp' => 'tstamp',
        'crdate' => 'crdate',
        // ... other necessary ctrl properties
        'iconfile' => 'EXT:my_extension/Resources/Public/Icons/Item.svg',
    ],
    // ... rest of your TCA ...
];
Copied!

2. The Repository 

To fetch the data from your table, the Anthology extension needs a corresponding Extbase repository. This repository must have the LiquidLight\Anthology\Attribute\AsAnthologyRepository attribute with the corresponding TCA/table name argument.

Classes/Domain/Repository/ItemRepository.php

<?php

declare(strict_types=1);

namespace Vendor\MyExtension\Domain\Repository;

use LiquidLight\Anthology\Attribute\AsAnthologyRepository;
use TYPO3\CMS\Extbase\Persistence\Repository;

#[AsAnthologyRepository('tx_myextension_domain_model_item')]
class ItemRepository extends Repository
{
    // You can add custom finder methods here if needed
}
Copied!

3. Select the Model in the Plugin 

After completing the steps above and clearing the cache, your new model will be available for selection in the Anthology plugin.

  1. Edit the Anthology content element on your page.
  2. Navigate to the General tab.
  3. Click on the Model name dropdown menu.
  4. You should now see "My Extension Items" (the title from your TCA file) as an option.

Once you select it and save the content element, the plugin will start to query and display the records from your tx_myextension_domain_model_item table.

Routing 

To create user-friendly, human-readable URLs for the detail view of your records, you need to configure routing for the Anthology plugin.

This guide explains how to set up a route enhancer for the Anthology extension's single record view and list view pagination.

YAML Configuration 

Routing is configured in the main config.yaml file for your TYPO3 site.

Here is an example of a route enhancer for an Anthology detail page:

routeEnhancers:
  # Single view with slug-based URLs
  [UniqueIdentifier]:
    type: Extbase
    limitToPages:
      - 123
    extension: LlAnthology
    plugin: AnthologyView
    routes:
      # Single record by slug
      - routePath: '/{record}'
        _controller: 'Anthology::single'
    defaultController: 'Anthology::view'
    aspects:
      record:
        type: PersistedAliasMapper
        tableName: tx_myextension_domain_model_item
        routeFieldName: slug
Copied!
  • [uniqueIdentifier] must be replaced by a unique value; this can be anything so long as it is unique.

And a more general configuration for list view pagination:

routeEnhancers:
  # Anthology list view - handles pagination
  AnthologyList:
    type: Extbase
    limitToPages:
      - 123
      - 456
    extension: LlAnthology
    plugin: AnthologyView
    routes:
      # List view
      - routePath: '/'
        _controller: 'Anthology::list'
      # List view with pagination
      - routePath: '/page-{page}'
        _controller: 'Anthology::list'
        _arguments:
          page: 'currentPage'
    defaultController: 'Anthology::list'
    defaults:
      page: '1'
    aspects:
      page:
        type: StaticRangeMapper
        start: '2'
        end: '1000'

Copied!

The list view configuration can be reused across all instances of the Anthology list view as it is not dependent upon a model. Add the list view page UIDs to the limitToPages array.

Each single view requires a route configuration per-model as it requires the tableName configuration. Single view page UIDs can be added to the limitToPages array.

Important Considerations 

  • Slugs: For the PersistedAliasMapper to work, your table must have a dedicated field to store the URL slug. You are responsible for generating and saving the unique slugs for your records.
  • Site Configuration: The YAML above needs to be placed under the routeEnhancers key within your site's configuration.
  • Clearing Caches: After adding or changing routing configuration, you must clear the caches in the TYPO3 backend for the changes to take effect.

Adding sitemap items 

To ensure that search engines can discover and index all the detail pages of your anthology records, it is important to include them in your website's XML sitemap.

This guide will show you how to configure a sitemap provider for the records displayed by the Anthology extension.

The Sitemap Provider 

A sitemap provider is responsible for collecting a list of URLs for a specific type of content and adding them to the XML sitemap. TYPO3 provides a generic RecordsXmlSitemapDataProvider that can be configured to work with any database table.

TypoScript Configuration 

plugin.tx_seo.config.xmlSitemap.sitemaps {
	[model_identifier] {
		provider = TYPO3\CMS\Seo\XmlSitemap\RecordsXmlSitemapDataProvider
		config {
			table = tx_myextension_domain_model_item
			sortField = crdate
			lastModifiedField = tstamp
			pid = 123
			url {
				pageId = 456
				fieldToParameterMap {
					uid = tx_llanthology_anthologyview[record]
				}
				additionalGetParameters {
					tx_llanthology_anthologyview.action = single
					tx_llanthology_anthologyview.controller = Anthology
				}
			}
		}
	}
}
Copied!

The [model_identifier] must be replaced with a unique value; this can be anything so long as it is unique. The table, pid and pageId values must be replaced with the relevant values.

Configuration Breakdown 

  • [model_identifier]: A unique key for your sitemap provider. This can be any descriptive name (e.g., my_items, news_articles, products).
  • provider: Uses the standard RecordsXmlSitemapDataProvider from the TYPO3 core SEO extension.
  • config: Contains the specific settings for the provider:

    • table: The name of the table your anthology is displaying (must match your repository configuration).
    • sortField: How the records should be sorted in the sitemap. crdate (creation date) is a sensible default.
    • lastModifiedField: Field used to determine when the record was last modified. Usually tstamp.
    • pid: The UID of the storage page where your records are stored. The provider will only select records from this page.
    • url: Defines how to construct the URL for each record:

      • pageId: The UID of the page that will handle the single view (your detail page).
      • fieldToParameterMap: Maps database fields to URL parameters. Here, the uid field is mapped to the tx_llanthology_anthologyview[record] parameter.
      • additionalGetParameters: Additional parameters required for the Anthology plugin to work correctly.

Final Steps 

  1. Add the TypoScript configuration above to your site's setup.typoscript file.
  2. Adjust the following values to match your specific setup:

    • [model_identifier]: Choose a unique name for your sitemap
    • table: Your model's database table name
    • pid: The UID of the page where your records are stored
    • pageId: The UID of your single view page
  3. Clear all caches in the TYPO3 backend.
  4. If you're using route enhancers (recommended), ensure your routing configuration is set up correctly as described in the 20.%20Routing guide.

Note: If you're using route enhancers for clean URLs, you may need to adjust the fieldToParameterMap and additionalGetParameters to match your routing configuration. The example above works with the default Anthology plugin parameters.

After these steps, TYPO3 will automatically include the URLs for all your published anthology records in the XML sitemap. You can verify this by accessing your sitemap at https://www.your-site.com/sitemap.xml.

Templates 

The Anthology extension uses the Fluid templating engine to render its output. This allows for full control over the HTML markup. You can customise every aspect of the plugin's appearance by overriding the default templates.

Default Template Structure 

The extension's templates are located in its Resources/Private/ directory:

  • Templates/: Contains the main template files for each action (e.g., List.html, View.html).
  • Partials/: Contains reusable snippets of code that are used in the main templates. This is where the most common customisations are made.

    • List/Record.html: Renders a single record in the list view.
    • List/Pagination.html: Renders the pagination widget.
    • List/Filters.html: Renders the container for the filters.
    • Filter/...: Contains the templates for the different filter types (e.g., Date.html, Search.html).
  • Layouts/: Defines the overall HTML structure of the templates.

Overriding Templates 

There are two main ways to override the default templates.

2. Manual TypoScript Overrides 

You can also explicitly tell the Anthology plugin where to find your templates using TypoScript:

plugin.tx_llanthology {
    view {
        templateRootPaths.123456789 = EXT:my_site_package/Resources/Private/Templates/
        partialRootPaths.123456789 = EXT:my_site_package/Resources/Private/Partials/
        layoutRootPaths.123456789 = EXT:my_site_package/Resources/Private/Layouts/
    }
}
Copied!

Available Variables 

Inside the templates, you have access to several variables:

  • paginator: A paginator object that contains the records for the current page (paginator.paginatedItems).
  • pagination: The pagination object for building the page links.
  • filters: A list of the configured filter objects.
  • record: In the singleAction view and the List/Record.html partial, this variable holds the current record being displayed.
  • settings: The settings array from the plugin's FlexForm.

Custom filters 

The Anthology extension includes a powerful and extensible filtering system. Whilst it comes with several common filter types out of the box (Search, Category, and Date), you can create your own custom filter types to meet the specific needs of your project.

This guide will walk you through the process of creating, registering, and using a new custom filter.

1. The Components of a Filter 

A filter consists of three main parts:

  1. A Filter Class: The PHP class that contains the filtering logic.
  2. A FlexForm File: An XML file that defines the configuration options for the filter (optional).
  3. A Fluid Template: An HTML file that renders the filter's frontend interface.

We will create a simple "Tag" filter as an example.

2. The Filter Class 

The filter class is responsible for applying the actual query constraint. It must implement the FilterInterface (or extend the provided AbstractFilter), and must have the AsAnthologyFilter attribute.

Although not required, it is recommended to prefix the name of your filter with the extension in order to avoid clashes with other filters.

Classes/Domain/Filter/TagFilter.php

<?php

declare(strict_types=1);

namespace Vendor\MyExtension\Domain\Filter;

use LiquidLight\Anthology\Attribute\AsAnthologyFilter;
use LiquidLight\Anthology\Domain\Filter\AbstractFilter;
use LiquidLight\Anthology\Domain\Filter\FilterInterface;
use LiquidLight\Anthology\Domain\Model\Filter;
use TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface;
use TYPO3\CMS\Extbase\Persistence\QueryInterface;

#[AsAnthologyFilter('myextension_tag')]
class TagFilter extends AbstractFilter implements FilterInterface
{
    protected const LABEL = 'My Custom Tag Filter';

    public static function getConstraint(
        Filter $filter,
        QueryInterface $query
    ): ?ComparisonInterface {
        // If no tag is selected in the frontend, don't apply any constraint
        if (empty($filter->getParameter())) {
            return null;
        }

        // 'tags' is the property on our model that we want to filter by.
        // This comes from the FlexForm setting for this filter.
        $property = $filter->getParsedSettings()['property'];

        return $query->like($property, '%' . $filter->getParameter() . '%');
    }
}

Copied!
  • LABEL: This constant defines the human-readable name of your filter, which will be shown in the backend. Whilst not required, this should refer to an XLIFF file.
  • getConstraint(): This is the core method. It receives the Filter model (which contains its settings and the current value from the frontend) and the Extbase Query object. It should return a ConstraintInterface object that will be added to the database query.

3. The FlexForm Configuration 

The FlexForm defines the settings that are available for your filter in the backend. This is an XML file.

Configuration/FlexForms/Filter/Tag.xml

<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<T3DataStructure>
    <sheets>
        <sDEF>
            <ROOT>
                <sheetTitle>Settings</sheetTitle>
                <type>array</type>
                <el>
                    <settings.property>
                        <label>Property to filter on</label>
                        <config>
                            <type>input</type>
                            <eval>trim,required</eval>
                        </config>
                    </settings.property>
                </el>
            </ROOT>
        </sDEF>
    </sheets>
</T3DataStructure>
Copied!

This simple FlexForm adds a single text input field where the editor can specify which property of the model (e.g., tags) this filter should apply to.

4. The Fluid Template 

This template renders the filter on the website. It's a standard Fluid template.

Resources/Private/Partials/Filter/Tag.html

<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers">
    <f:form.textfield
        name="filter[{filter.uid}]"
        value="{filter.parameter}"
        class="form-control"
        placeholder="Enter a tag..."
    />
</html>
Copied!
  • filter: A filter object is available in the template, containing all its properties and settings.
  • name="filter[{filter.uid}]": It is crucial that the name attribute follows this format. This allows the Anthology extension to correctly identify the value for each filter.
  • value="{filter.parameter}": This ensures that when the form is submitted, the selected value is pre-filled.

5. Configuring the Filter 

Register the FlexForm 

To provide customisable options for your filter, you need to create a FlexForm. This is done in the TCA for the filter record. If no further configuration options are required, this is not necessary.

Configuration/TCA/Overrides/tx_anthology_domain_model_filter.php

<?php
// Add this to your extension's TCA override file
$GLOBALS['TCA']['tx_anthology_domain_model_filter']['columns']['settings']['config']['ds']['tag'] =
    'FILE:EXT:my_extension/Configuration/FlexForms/Filter/Tag.xml';
Copied!

Register the Template 

Finally, ensure the template is found by placing it in your extension's Resources/Private/Partials/Filter/ directory. As explained in the 40.%20Templates guide, the Anthology extension will automatically find templates in the extension that provides the model.

6. Using the Custom Filter 

After clearing the cache, you can now use your new filter:

  1. In the TYPO3 backend, create a new "Anthology Filter" record.
  2. In the Filter Type dropdown, you should now see "My Custom Tag Filter".
  3. Select it. The FlexForm you created will appear, asking for the "Property to filter on". Enter the name of the field on your model (e.g., tags).
  4. Give the filter a Title (e.g., "Filter by Tag").
  5. Save the filter record.
  6. Edit your Anthology plugin on the content page, go to the Filters tab, and add your newly created filter.

Now, your custom tag filter will appear on the frontend, allowing users to filter your records.