• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

heimrichhannot / contao-utils-bundle / 38542847508

14 Oct 2026 09:30AM UTC coverage: 84.066% (+4.2%) from 79.825%
38542847508

Pull #108

github

koertho
address review comments on resolver and tokenizer

- Look up stemmer and normalizer in explicit methods that check the service
  against its interface, instead of a generic helper
- Transliterate letters without a decomposition into base letter and accent
  (æ, ø, œ, ð, þ, ł, đ, ı), accented letters were already folded

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pull Request #108: add search vector field

392 of 407 new or added lines in 16 files covered. (96.31%)

1588 of 1889 relevant lines covered (84.07%)

8.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.0
/src/EventListener/DcaField/SearchVectorFieldListener.php
1
<?php
2

3
namespace HeimrichHannot\UtilsBundle\EventListener\DcaField;
4

5
use Contao\CoreBundle\DependencyInjection\Attribute\AsHook;
6
use Contao\DataContainer;
7
use HeimrichHannot\UtilsBundle\Dca\SearchVectorField;
8
use HeimrichHannot\UtilsBundle\SearchVector\SearchVectorIndexer;
9

10
class SearchVectorFieldListener extends AbstractDcaFieldListener
11
{
12
    #[AsHook('loadDataContainer')]
13
    public function onLoadDataContainer(string $table): void
14
    {
15
        $registrations = SearchVectorField::takePendingRegistrations($table);
4 ✔
16

17
        // A later registration would silently replace the earlier one
18
        if (\count($registrations) > 1) {
4 ✔
19
            throw new \LogicException(\sprintf(
1 ✔
20
                'Table "%s" registers %d search vector fields (%s), but only one search vector field per table is supported.',
1 ✔
21
                $table,
1 ✔
22
                \count($registrations),
1 ✔
23
                \implode(', ', \array_map(static fn ($registration): string => $registration->getFieldName(), $registrations))
1 ✔
24
            ));
1 ✔
25
        }
26

27
        if (!$configuration = SearchVectorField::getRegistrations()[$table] ?? null) {
3 ✔
28
            return;
1 ✔
29
        }
30

31
        // Machine-written, so it is part of no palette and hidden from the "show" view and the version diff
32
        $field = [
3 ✔
33
            'eval' => ['doNotShow' => true],
3 ✔
34
            'sql' => ['type' => 'text', 'notnull' => false],
3 ✔
35
        ];
3 ✔
36

37
        $this->applyDefaultFieldAdjustments($field, $configuration);
3 ✔
38

39
        $GLOBALS['TL_DCA'][$table]['fields'][$configuration->getFieldName()] = $field;
3 ✔
40

41
        // The table is only known at runtime, so the callbacks cannot be registered with attributes
42
        $GLOBALS['TL_DCA'][$table]['config']['onsubmit_callback'][] = [self::class, 'onSubmitCallback'];
3 ✔
43
        $GLOBALS['TL_DCA'][$table]['config']['oncopy_callback'][] = [self::class, 'onCopyCallback'];
3 ✔
44
        $GLOBALS['TL_DCA'][$table]['config']['onrestore_version_callback'][] = [self::class, 'onRestoreVersionCallback'];
3 ✔
45
    }
46

47
    public function onSubmitCallback(DataContainer $dc): void
48
    {
49
        if ($dc->id) {
3 ✔
50
            $this->rebuild($dc->table, (int) $dc->id, $dc);
2 ✔
51
        }
52
    }
53

54
    public function onCopyCallback(int|string $insertId, DataContainer $dc): void
55
    {
56
        $this->rebuild($dc->table, (int) $insertId);
2 ✔
57
    }
58

59
    public function onRestoreVersionCallback(string $table, int|string $id, int|string $version, array $data): void
60
    {
61
        $this->rebuild($table, (int) $id);
2 ✔
62
    }
63

64
    private function rebuild(string $table, int $id, ?DataContainer $dc = null): void
65
    {
66
        $indexer = $this->container->get(SearchVectorIndexer::class);
2 ✔
67

68
        if ((SearchVectorField::getRegistrations()[$table] ?? null)?->isAsync()) {
2 ✔
69
            $indexer->queue($table, $id);
1 ✔
70
        } else {
71
            $indexer->index($table, $id, $dc);
1 ✔
72
        }
73
    }
74

75
    public static function getSubscribedServices(): array
76
    {
NEW
77
        $services = parent::getSubscribedServices();
×
NEW
78
        $services[] = SearchVectorIndexer::class;
×
79

NEW
80
        return $services;
×
81
    }
82
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc