Calien - XLS Exporter 

Extension key

xlsexport

Package name

calien/xlsexport

Version

5.0

Language

en

Author

Markus Hofmann, Frank Berger

License

This document is published under the Creative Commons BY 4.0 license.

Rendered

Wed, 22 Jul 2026 08:04:29 +0000


A backend module that lets editors download preconfigured database query results as spreadsheet files (xlsx, xls, ods or csv). Exports are described declaratively in page TSconfig and translated into a TYPO3 QueryBuilder query.


Introduction 

What the extension does and why the export format changed in version 5.

Installation 

Install via Composer and activate the site set.

For editors 

Download an export from the backend module.

Configuration 

The TSconfig export format for integrators, mapped to the QueryBuilder.

Developer 

The PSR-14 events that let you shape the generated spreadsheet.

Migration 

Breaking changes and how to move an export from version 4 to version 5.

Changelog 

Notable changes to this extension.

Table of contents:

Introduction 

Table of contents

What it does 

The XLS Exporter adds a backend module (Web > XLS Exporter) that offers editors one or more preconfigured exports for the currently selected page. Each export runs a database query and streams the result to the browser as a spreadsheet file. The editor picks a file name and one of the supported formats — xlsx, xls, ods or csv — and downloads the file.

Exports are defined by integrators in page TSconfig. A single definition describes which table to read, which columns to select, how to filter and join, and which column headers to write — see Configuration.

Declarative exports 

Version 5 introduces a declarative export format: a definition describes what to read — table, columns, filters and joins — and the extension builds the query for it with the TYPO3 QueryBuilder. Integrators no longer write SQL statements by hand, which makes exports easier to read, maintain and extend. See Migration from version 4 for how to convert an export from version 4.

Features 

  • Read any table, with SELECT, WHERE, JOIN / LEFT JOIN / RIGHT JOIN and a record count, all expressed as TSconfig.
  • Output as xlsx, xls, ods or csv via PhpSpreadsheet.
  • Delivered as a site set, so integrators enable it per site.
  • Three PSR-14 events to shape the generated spreadsheet — see Developer.

Installation 

Table of contents

Requirements 

  • TYPO3 13.4 LTS or 14.3 LTS
  • PHP 8.2, 8.3, 8.4 or 8.5

Install with Composer 

composer require calien/xlsexport
Copied!

Activate the site set 

The extension ships an example export as the site set XLS Exporter (calien/xlsexport). Assign it to a site to make the example available on that site's pages:

config/sites/<my-site>/config.yaml
dependencies:
  - calien/xlsexport
Copied!

The set delivers the example page TSconfig under mod.web_xlsexport. Replace it with your own exports as described in Configuration. The backend module itself is always available under Web > XLS Exporter and needs no set to work.

For editors 

Table of contents

Downloading an export 

  1. Open the module Web > XLS Exporter.
  2. Select the page whose exports you want in the page tree. The module lists every export configured for that page, together with the number of records it currently contains.
  3. For the export you want, adjust the suggested File name if needed and choose a Format (xlsx, xls, ods or csv).
  4. Select Export. The file is generated and downloaded to your computer.

An export whose record count is 0 cannot be downloaded — its button is disabled.

If the module shows the message "Please choose a page on the left", no page is selected yet. If a page has no exports, the module says so; if a single export is misconfigured, it is skipped and a warning naming it is shown, while the remaining exports stay available.

Configuration 

Exports are configured in page TSconfig below mod.web_xlsexport. Every direct sub-key is one export offered by the backend module; the key is also used to reference the export internally:

EXT:my_extension/Configuration/page.tsconfig
mod.web_xlsexport {
    # "orders" is the export key
    orders {
        label = Orders of this page
        table = tx_myext_order
        select {
            10 = uid
            20 = order_number
        }
        fieldLabels {
            10 = ID
            20 = Order number
        }
    }
}
Copied!

Each definition is translated one-to-one into a TYPO3 QueryBuilder query: values are bound with QueryBuilder::createNamedParameter() and column names are quoted with QueryBuilder::quoteIdentifier().

Export options 

These are the keys of a single export definition below mod.web_xlsexport.<key>.

Name Type Default
string
string
list
list
string '*'
list
string xlsx
string
list
list
list
list

table

table
Type
string
Required

true

The database table to read from — the argument of QueryBuilder::from().

alias

alias
Type
string

Optional table alias, passed as the second argument of QueryBuilder::from(). Use it when a join needs to reference this table under a short name.

select

select
Type
list
Required

true

The columns to read, as a numeric TSconfig list (10, 20, …). Each value is one column passed to QueryBuilder::select(). Prefix with the table or alias (tt_content.uid) when selecting across joins.

selectLiteral

selectLiteral
Type
list

Additional raw select expressions passed to QueryBuilder::selectLiteral(), for example COUNT(*) AS amount. Unlike select, these are not quoted — only use integrator-controlled values here.

count

count
Type
string
Default
'*'

The column counted for the record number shown next to the export in the module, using QueryBuilder::count(). * counts all rows.

fieldLabels

fieldLabels
Type
list
Required

true

The column headers written into the first row of the spreadsheet, as a numeric list aligned by position with select.

format

format
Type
string
Default
xlsx

The default spreadsheet format: xlsx, xls, ods or csv. Editors can override it per download.

label

label
Type
string

The label shown for the export in the module. May be a plain string or an LLL: reference. Defaults to the table name.

where

where
Type
list

The filter conditions — see Filtering rows.

join

join
Type
list

Inner joins — see Joining tables.

leftJoin

leftJoin
Type
list

Left joins — see Joining tables.

rightJoin

rightJoin
Type
list

Right joins — see Joining tables.

Filtering rows 

Table of contents

where is a numeric list of conditions that are combined with AND and passed to QueryBuilder::where(). Each condition maps to one method of the ExpressionBuilder:

where {
    10 {
        fieldName = pid
        parameter = ###CURRENT_ID###
        expressionType = eq
        type = int
    }
}
Copied!

fieldName

fieldName
Type
string
Required

true

The column the condition applies to.

expressionType

expressionType
Type
string
Required

true

The comparison operator, see Expression types.

parameter

parameter
Type
string or list

The value to compare against. A scalar for most operators, or a list for in. The special value ###CURRENT_ID### is replaced with the uid of the page selected in the module.

type

type
Type
string

The parameter type keyword, see Parameter types. Not needed for isNull/isNotNull or when isColumn is set.

isColumn

isColumn
Type
boolean

Set to 1 to treat parameter as a column identifier (quoted with quoteIdentifier()) instead of a value. Use this to compare two columns, typically in a join condition.

Expression types 

expressionType ExpressionBuilder method Meaning
eq eq() equal to
neq neq() not equal to
lt lt() less than
lte lte() less than or equal to
gt gt() greater than
gte gte() greater than or equal to
in in() value in a list (use an int_array/string_array type)
inSet inSet() value contained in a comma-separated column
isNull isNull() column is NULL
isNotNull isNotNull() column is not NULL

Parameter types 

The type keyword selects the \TYPO3\CMS\Core\Database\Connection parameter type used to bind the value:

type Connection constant
int Connection::PARAM_INT
string Connection::PARAM_STR
bool Connection::PARAM_BOOL
null Connection::PARAM_NULL
lob Connection::PARAM_LOB
int_array Connection::PARAM_INT_ARRAY (for in with integers)
string_array Connection::PARAM_STR_ARRAY (for in with strings)

Joining tables 

join, leftJoin and rightJoin are numeric lists of joins, mapped to QueryBuilder::join(), leftJoin() and rightJoin(). A join reads from an existing table/alias, joins to another table (optionally under toAlias), and its where list forms the ON condition using the same syntax as Filtering rows:

join {
    10 {
        from = tt_content
        to = pages
        toAlias = pages
        where {
            10 {
                fieldName = tt_content.pid
                parameter = pages.uid
                expressionType = eq
                # compare the two columns, no value binding
                isColumn = 1
            }
        }
    }
}
Copied!

from

from
Type
string
Required

true

The table or alias the join starts from.

to

to
Type
string
Required

true

The table being joined in.

toAlias

toAlias
Type
string

Optional alias for the joined table. Defaults to to.

where

where
Type
list

The join condition, using the condition syntax from Filtering rows.

Complete example 

The following export lists the content elements of the current page together with the title of their page:

EXT:my_extension/Configuration/page.tsconfig
mod.web_xlsexport {
    content {
        label = Page content elements
        table = tt_content
        format = xlsx
        select {
            10 = tt_content.uid
            20 = tt_content.header
            30 = tt_content.CType
            40 = pages.title
        }
        fieldLabels {
            10 = ID
            20 = Header
            30 = Type
            40 = Page
        }
        count = *
        where {
            10 {
                fieldName = tt_content.pid
                parameter = ###CURRENT_ID###
                expressionType = eq
                type = int
            }
        }
        join {
            10 {
                from = tt_content
                to = pages
                toAlias = pages
                where {
                    10 {
                        fieldName = tt_content.pid
                        parameter = pages.uid
                        expressionType = eq
                        isColumn = 1
                    }
                }
            }
        }
    }
}
Copied!

Developer 

While the spreadsheet is generated, the extension dispatches three PSR-14 events that let you shape the output without replacing the writer. All three live in the namespace \Calien\Xlsexport\Event\Export and are dispatched by \Calien\Xlsexport\Service\SpreadsheetWriteService. Register a listener with the \TYPO3\CMS\Core\Attribute\AsEventListener attribute.

AlternateFirstColumnInSheetEvent 

Dispatched once, before anything is written, to decide the first spreadsheet column the header and rows start in. The default is A. Use it to leave room for a logo or leading columns your own listener fills.

Mutable

yes, via setFirstColumn(string $firstColumn)

EXT:my_extension/Classes/EventListener/MoveExportStart.php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\EventListener;

use Calien\Xlsexport\Event\Export\AlternateFirstColumnInSheetEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;

final class MoveExportStart
{
    #[AsEventListener]
    public function __invoke(AlternateFirstColumnInSheetEvent $event): void
    {
        $event->setFirstColumn('B');
    }
}
Copied!

AlternateHeaderLineEvent 

Dispatched before the header row is written, carrying the field labels from fieldLabels and the export key. Use it to translate, reorder or extend the column headers.

Mutable

yes, via setHeaderFieldLabels(array $headerFieldLabels)

Read

getHeaderFieldLabels(), getConfiguration() (the export key)

#[AsEventListener]
public function __invoke(AlternateHeaderLineEvent $event): void
{
    if ($event->getConfiguration() === 'orders') {
        $event->setHeaderFieldLabels(['#', 'Order number', 'Total']);
    }
}
Copied!

ManipulateRowEntryEvent 

Dispatched for every data row before it is written, carrying the raw row (as fetched from the database), the header labels and the export key. Use it to format values, resolve foreign keys to readable labels, or add computed columns.

Mutable

yes, via setRow(array $row)

Read

getRow(), getFieldLabels(), getConfigurationKey()

#[AsEventListener]
public function __invoke(ManipulateRowEntryEvent $event): void
{
    $row = $event->getRow();
    $row['created'] = date('Y-m-d', (int)($row['created'] ?? 0));
    $event->setRow($row);
}
Copied!

Migration from version 4 

Version 5 is a hard break. There is no automatic upgrade path, because the SQL statements of a version 4 export cannot be translated to the declarative format without knowing their intent. Read the breaking changes, then rewrite each export.

Breaking changes 

Supported versions 

TYPO3 12 support is dropped. Version 5 requires TYPO3 13.4 LTS or 14.3 LTS and PHP 8.2 to 8.5, and depends on PhpSpreadsheet 3.

Export format and TSconfig namespace 

The export format changed completely. The check, list and export SQL statements are gone; an export is now the declarative definition described in Configuration. The TSconfig namespace also changed:

Version 4 Version 5
mod.tx_xlsexport.settings.exports.<key> mod.web_xlsexport.<key>

Extension points 

The Extbase controller was replaced by a plain backend controller. The legacy $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['xlsexport']['alternateQueries'] hooks and the old events (AddColumnsToSheetEvent, AlternateCheckQueryEvent, AlternateExportQueryEvent, the former AlternateHeaderLineEvent and ManipulateCellDataEvent) were removed. Use the three PSR-14 events under \Calien\Xlsexport\Event\Export instead — see Developer.

Rewriting an export 

Take the export query of a version 4 definition and express its parts as configuration: the FROM table becomes table, the selected columns become select, and each WHERE clause becomes a condition. The %d placeholder that was filled with the page id becomes ###CURRENT_ID###.

Version 4
mod.tx_xlsexport.settings.exports {
    addresses {
        label = Addresses
        table = tt_address
        export (
            select uid, first_name, last_name from tt_address
            where pid = %d and deleted = 0 and hidden = 0
        )
        exportfields {
            10 = uid
            20 = first_name
            30 = last_name
        }
        exportfieldnames {
            10 = ID
            20 = First name
            30 = Last name
        }
    }
}
Copied!
Version 5
mod.web_xlsexport {
    addresses {
        label = Addresses
        table = tt_address
        select {
            10 = uid
            20 = first_name
            30 = last_name
        }
        fieldLabels {
            10 = ID
            20 = First name
            30 = Last name
        }
        where {
            10 {
                fieldName = pid
                parameter = ###CURRENT_ID###
                expressionType = eq
                type = int
            }
        }
    }
}
Copied!

Changelog 

Table of contents

5.0.0 - Declarative, QueryBuilder-based exports 

A rewrite that introduces a declarative export format and modernises the extension for TYPO3 13 and 14.

Breaking 

See Migration from version 4 for the full list and how to convert an export.

  • Exports are now declared as structured page TSconfig and translated into a TYPO3 QueryBuilder query, replacing the previous check/list/export SQL statements.
  • The TSconfig namespace changed from mod.tx_xlsexport.settings.exports to mod.web_xlsexport.
  • TYPO3 12 support was dropped; the extension now requires TYPO3 13.4 or 14.3 and PHP 8.2 to 8.5.
  • The Extbase controller, the SC_OPTIONS hooks and the previous events were removed.

Features 

  • Improved query building in TSconfig: read any table with filters, joins and literal select expressions.
  • The record count in the module is derived automatically from the export definition, instead of a separate query.
  • Configurable file name and export type (xlsx, xls, ods, csv) per download.
  • The example export ships as a site set.
  • Three PSR-14 events (Developer) to shape the generated spreadsheet.
  • A misconfigured export is skipped with a warning instead of breaking the whole module overview.