---
title: "Developer"
manual: "Ai3 FAQ"
version: "0.9"
permalink: "https://docs.typo3.org/permalink/wegewerk/ai3_faq:developer@0.9"
source: "Developer/Index.rst"
rendered: "2026-10-23T16:31:05+00:00"
---

# Developer {#developer}

## Architecture {#developer-architecture}

Ai3 FAQ follows the *Ai3 Suite* capability pattern provided by
[`wegewerk/ai3_core`](https://packagist.org/packages/wegewerk/ai3_core):

-   A **Capability** class registers the feature under a unique key.
-   An **Endpoint** class implements the ZAK-AI API call.
-   A **FormEngine element** renders the backend widget.
-   A **backend AJAX controller** handles the generation request.
-   **Bootstrap Package integration** provides accordion functionality.

## PHP API {#developer-php-api}

### ArticlefaqCapability {#developer-articlefaq-capability}

-   **class ArticlefaqCapability**

    -   *Fully qualified name:* `\Wegewerk\Ai3Faq\Domain\Capabilities\ArticlefaqCapability`

    Extends `Wegewerk\Ai3Core\Domain\Capabilities\Capability`. Registered
    via `Configuration/Services.yaml` with the following wiring:

    -   `$key`: `articlefaq`
    -   `$title`: `Article FAQ`
    -   `$endpoint`: `ZakAiFaq`
    -   Tagged as `ai3.capability`

### ZakAiFaq {#developer-zakai-faq}

-   **class ZakAiFaq**

    -   *Fully qualified name:* `\Wegewerk\Ai3Faq\Api\ZakAiFaq`

    Extends `Wegewerk\Ai3Core\Api\AbstractZakAiEndpoint` (implementing
    `ZakAiEndpointInterface`). Wraps `ZakAiClient` to call the
    ZAK-AI `faq` REST endpoint.

    -   **generate(array $options = \[\])**

        Sends page content to the ZAK-AI API and returns the generated
        FAQ data as a JSON string containing question-answer pairs.

        -   *param array $options:*

            Associative array of options:
             *\`\`text\`\` (string, required): The page content text to analyze for FAQ generation.*
             `language` (string, optional): Target language for the FAQ (e.g. 'de').
             *\`\`min\`\` (int|null, optional): Minimum number of questions to generate.*
             `max` (int|null, optional): Maximum number of questions to generate.
             *\`\`keyword\`\` (string|null, optional): Focus keyword.*
             `previous` (mixed, optional): Previous context or suggestions.

        -   *returntype:* string

        The response contains a JSON string with an array of objects with `question` and `answer` properties.

### ArticlefaqController {#developer-ajax-controller}

-   **class ArticlefaqController**

    -   *Fully qualified name:* `\Wegewerk\Ai3Faq\Controller\Ajax\ArticlefaqController`

    Backend AJAX controller (`#[AsController]`), extends
    `Wegewerk\Ai3Core\Controller\Ajax\AbstractAjaxController`.

    -   **getFaq(ServerRequestInterface $request)**

        Handles the AJAX FAQ generation request.

        1.  Reads `page_id` and `language` from the POST body.
        1.  Fetches all page content via `PagesRepository::getPageContent()`.
        1.  Calls `ArticlefaqCapability` endpoint to generate the FAQ.
        1.  Parses the JSON response to extract question-answer pairs.
        1.  Returns a JSON response with `faqData`, `source`, and
            `type: 'faq'` on success, or an error JSON on failure.

        -   *returntype:* ResponseInterface

## FormEngine integration {#developer-form-engine}

The custom FormEngine node `ai3FaqGeneratorElement` is registered in
TCA configuration:

**TCA configuration for the generator element**

```php
$GLOBALS['TCA']['tt_content']['columns']['tx_ai3_faq_generator'] = [
    'exclude' => true,
    'label'   => 'LLL:EXT:ai3_faq/Resources/Private/Language/locallang.xlf:tx_ai3.faq.generator',
    'config'  => [
        'type'       => 'user',
        'renderType' => 'ai3FaqGeneratorElement',
    ],
];
```

-   **class Ai3FaqGeneratorElement**

    -   *Fully qualified name:* `\Wegewerk\Ai3Faq\FormEngine\Ai3FaqGeneratorElement`

    Extends `TYPO3\CMS\Backend\Form\Element\AbstractFormElement`. Renders
    a `<div data-ai3="ai3-faq-container">` with `data-page-id` and
    `data-record-uid` attributes. The Lit-HTML `FaqApp` mounts into
    the inner `<div data-ai3="ai3-faq-app">`.

## TCA Configuration {#developer-tca-configuration}

### Content element registration {#content-element-registration}

The `ai3_faq` content type is registered in `Configuration/TCA/Overrides/tt_content_faq.php`:

**Content element registration**

```php
ExtensionManagementUtility::addTcaSelectItem('tt_content',
    'CType',
    [
        'label'       => 'LLL:EXT:ai3_faq/Resources/Private/Language/locallang.xlf:tx_ai3.faq.label_ctype',
        'value'       => 'ai3_faq',
        'icon'        => 'ai3-faq-icon',
        'description' => 'LLL:EXT:ai3_faq/Resources/Private/Language/locallang.xlf:tx_ai3.faq.label_description',
        'group'       => 'AI3',
    ],
    'textmedia',
    'after',
);
```

### Bootstrap Package integration {#bootstrap-package-integration}

The FAQ element inherits accordion functionality from Bootstrap Package:

**Accordion configuration**

```php
$GLOBALS['TCA']['tt_content']['types']['ai3_faq'] = $GLOBALS['TCA']['tt_content']['types']['accordion'];

\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPiFlexFormValue(
    '*',
    'FILE:EXT:bootstrap_package/Configuration/FlexForms/Accordion.xml',
    'ai3_faq'
);
```

## AJAX route {#developer-ajax-route}

The backend AJAX route is registered in
`Configuration/Backend/AjaxRoutes.php`:

| Route name | Path | Handler |
| --- | --- | --- |
| `ai3_faq_generate` | `/ai3/faq/generate` | `ArticlefaqController::getFaq` |

**Request** (POST, JSON body):

**AJAX request body**

```json
{
    "page_id": 42,
    "language": "de"
}
```

**Response** (success):

**AJAX success response**

```json
{
    "success": true,
    "faqData": [
        {
            "question": "What is this page about?",
            "answer": "This page describes..."
        },
        {
            "question": "How do I use this feature?",
            "answer": "To use this feature, you need to..."
        }
    ],
    "source": "Page content extracted from...",
    "type": "faq"
}
```

**Response** (error):

**AJAX error response**

```json
{
    "success": false,
    "error": "Error message describing what went wrong"
}
```

## JavaScript modules {#developer-javascript}

JavaScript modules are registered via `Configuration/JavaScriptModules.php`
under the `@wegewerk/Ai3Faq/` import map prefix.

-   **\`\`@wegewerk/Ai3Faq/ai3api.js\`\`** -- `Ai3Api` class with
    `generateFaq(pageId, recordUid, language)` that POSTs to the AJAX route
    via TYPO3's `AjaxRequest`.
-   **\`\`@wegewerk/Ai3Faq/faq.js\`\`** -- Lit-HTML `FaqApp` that
    renders the backend widget, handles button clicks, calls `Ai3Api`,
    displays FAQ preview, and manages the accordion data.

### FaqApp Class {#faqapp-class}

<!-- TODO: no Markdown rendering for "js:class" -->

The main JavaScript class that handles the FAQ generation interface.

<!-- TODO: no Markdown rendering for "js:method" -->

Initiates FAQ generation by calling the backend AJAX endpoint.
Shows loading state and handles success/error responses.

<!-- TODO: no Markdown rendering for "js:method" -->

Displays the generated FAQ in a preview table with questions and answers.

<!-- TODO: no Markdown rendering for "js:method" -->

Saves the generated FAQ data to the content element fields and triggers
form submission via `FormEngine.saveDocument()`.

<!-- TODO: no Markdown rendering for "js:method" -->

Updates the hidden AI3 core fields with the generated FAQ data:

-   `tx_ai3_type` \- Set to 'faq'
-   `tx_ai3_source` \- Contains the original page content
-   `tx_ai3_raw` \- Contains the JSON-encoded FAQ data

## Services Configuration {#developer-services-configuration}

The extension's services are configured in `Configuration/Services.yaml`:

**Services.yaml**

```yaml
services:
  _defaults:
    autowire: true
    autoconfigure: true
    public: false

  Wegewerk\Ai3Faq\:
    resource: '../Classes/*'

  Wegewerk\Ai3Faq\Domain\Capabilities\ArticlefaqCapability:
    arguments:
      $key: 'articlefaq'
      $title: 'Article FAQ'
      $endpoint: '@Wegewerk\Ai3Faq\Api\ZakAiFaq'
    tags:
      - { name: 'ai3.capability' }
```

## Event listener {#developer-event-listener}

-   **class AfterFormEnginePageInitializedEventListener**

    -   *Fully qualified name:* `\Wegewerk\Ai3Faq\EventListener\AfterFormEnginePageInitializedEventListener`

    Listens to `AfterFormEnginePageInitializedEvent` (`#[AsEventListener]`).
    Adds the extension's `locallang.xlf` as an inline language label file
    to the `PageRenderer`, making all translation keys available to
    JavaScript.

## Schema.org Integration {#developer-schema-integration}

The extension automatically generates schema.org markup for FAQ pages when
using the [`brotkrueml/schema`](https://packagist.org/packages/brotkrueml/schema) extension. This provides structured
data for search engines and enables rich snippets.

The schema markup uses the `FAQPage` type and includes:

-   Main entity as `FAQPage`
-   Individual `Question` entities for each FAQ item
-   Proper `acceptedAnswer` markup for each answer

## Customization and Extension {#developer-customization}

### Adding Language Support {#adding-language-support}

To add support for additional languages, modify the `getLanguageOptions()`
method in `faq.js`:

**Adding language support**

```javascript
getLanguageOptions() {
     return [
         {
             value: 'de',
             label: 'Deutsch'
         },
         {
             value: 'en',
             label: 'English'
         },
         {
             value: 'fr',
             label: 'Français'
         }
     ];
}
```

### Custom FAQ Processing {#custom-faq-processing}

To customize FAQ processing, extend the `ZakAiFaq` class:

**Custom FAQ endpoint**

```php
class CustomZakAiFaq extends ZakAiFaq
{
    public function generate(array $options = []): string
    {
        $result = parent::generate($options);

        // Custom processing of FAQ data
        $faqData = json_decode($result, true);
        $processedData = $this->customProcessing($faqData);

        return json_encode($processedData);
    }

    private function customProcessing(array $faqData): array
    {
        // Your custom logic here
        return $faqData;
    }
}
```

### Template Customization {#template-customization}

Override the Bootstrap Package accordion template to customize FAQ output:

**Custom accordion template**

```html
<!-- EXT:your_sitepackage/Resources/Private/Templates/ContentElements/Accordion.html -->
<div class="accordion faq-accordion" id="accordion_{data.uid}">
    <f:for each="{accordionItems}" as="item" iteration="iteration">
        <div class="accordion-item">
            <h2 class="accordion-header">
                <button class="accordion-button faq-question" type="button"
                        data-bs-toggle="collapse"
                        data-bs-target="#collapse_{data.uid}_{iteration.cycle}">
                    {item.question}
                </button>
            </h2>
            <div id="collapse_{data.uid}_{iteration.cycle}"
                 class="accordion-collapse collapse">
                <div class="accordion-body faq-answer">
                    <f:format.html>{item.answer}</f:format.html>
                </div>
            </div>
        </div>
    </f:for>
</div>
```
