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

heimrichhannot / contao-utils-bundle / 21240556887

22 Jan 2026 07:57AM UTC coverage: 78.93% (+0.2%) from 78.761%
21240556887

push

github

koertho
update changelog

1165 of 1476 relevant lines covered (78.93%)

3.46 hits per line

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

83.33
/src/EntityFinder/Finder.php
1
<?php
2

3
namespace HeimrichHannot\UtilsBundle\EntityFinder;
4

5
use Contao\ContentModel;
6
use Contao\Controller;
7
use Contao\CoreBundle\Framework\ContaoFramework;
8
use Contao\FormModel;
9
use Contao\LayoutModel;
10
use Contao\ModuleModel;
11
use Contao\NewsArchiveModel;
12
use Contao\NewsModel;
13
use Contao\PageModel;
14
use Contao\ThemeModel;
15
use Doctrine\DBAL\Connection;
16
use HeimrichHannot\Blocks\BlockModel;
17
use HeimrichHannot\Blocks\BlockModuleModel;
18
use HeimrichHannot\UtilsBundle\Event\EntityFinderFindEvent;
19
use Symfony\Component\Routing\Exception\InvalidParameterException;
20
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
21
use function Symfony\Component\String\u;
22

23
/**
24
 * @internal
25
 */
26
class Finder
27
{
28

29
    public function __construct(
30
        private readonly EntityFinderHelper       $helper,
31
        private readonly EventDispatcherInterface $eventDispatcher,
32
        private readonly ContaoFramework          $framework,
33
        private readonly Connection $connection,
34

35
    )
36
    {
37
    }
8✔
38

39
    public function find(string $table, int $id): ?Element
40
    {
41
        if (in_array($table, ['find', 'tl_find', 'fallback', 'tl_fallback'])) {
8✔
42
            return null;
1✔
43
        }
44

45
        $method = u(str_starts_with($table, 'tl_') ? substr($table, 3) : $table)->camel()->toString();
8✔
46

47
        if (method_exists($this, $method)) {
8✔
48
            return $this->$method($id);
4✔
49
        }
50

51
        $event = $this->eventDispatcher->dispatch(new EntityFinderFindEvent($table, $id));
4✔
52
        if ($element = $event->getElement()) {
4✔
53
            return $element;
1✔
54
        }
55

56
        return $this->fallback($table, $id);
3✔
57
    }
58

59
    private function fallback(string $table, $idOrAlias): ?Element
60
    {
61
        $model = $this->helper->fetchModelOrData($table, $idOrAlias);
3✔
62

63
        if (null === $model) {
3✔
64
            return null;
1✔
65
        }
66

67
        $elementData = [
2✔
68
            'id' => $model->id,
2✔
69
            'table' => $table,
2✔
70
            'description' => null,
2✔
71
            'parents' => null,
2✔
72
        ];
2✔
73

74
        $this->framework->getAdapter(Controller::class)->loadDataContainer($table);
2✔
75

76
        $dca = &$GLOBALS['TL_DCA'][$table];
2✔
77

78
        if (!empty($model->pid)) {
2✔
79
            if (isset($dca['config']['ptable'])) {
2✔
80
                $elementData['parents'] = (function () use ($model, $dca): \Iterator {
1✔
81
                    yield ['table' => $dca['config']['ptable'], 'id' => $model->pid];
1✔
82
                })();
1✔
83
            } elseif (isset($dca['config']['dynamicPtable']) && isset($dca['fields']['pid']) && $model->ptable) {
2✔
84
                $elementData['parents'] = (function () use ($model): \Iterator {
1✔
85
                    yield ['table' => $model->ptable, 'id' => $model->pid];
1✔
86
                })();
1✔
87
            }
88
        }
89

90
        return new Element(
2✔
91
            $elementData['id'],
2✔
92
            $elementData['table'],
2✔
93
            $elementData['description'],
2✔
94
            $elementData['parents']
2✔
95
        );
2✔
96
    }
97

98
    /**
99
     * @codeCoverageIgnore
100
     */
101
    private function block(int $id): ?Element
102
    {
103
        if (!class_exists(BlockModel::class)) {
104
            return null;
105
        }
106

107
        $model = $this->helper->fetchModelOrData(BlockModel::getTable(), $id);
108

109
        if (null === $model) {
110
            return null;
111
        }
112

113
        return new Element(
114
            $model->id,
115
            BlockModel::getTable(),
116
            'Block ' . $model->title . ' (ID: ' . $model->id . ')',
117
            (function () use ($model): \Iterator {
118
                yield ['table' => ModuleModel::getTable(), 'id' => $model->module];
119
            })()
120
        );
121
    }
122

123
    /**
124
     * @codeCoverageIgnore
125
     */
126
    private function blockModule(int $id): ?Element
127
    {
128
        if (!class_exists(BlockModuleModel::class)) {
129
            return null;
130
        }
131

132
        $model = $this->helper->fetchModelOrData(BlockModuleModel::getTable(), $id);
133

134
        if (null === $model) {
135
            return null;
136
        }
137

138
        return new Element(
139
            $model->id,
140
            BlockModuleModel::getTable(),
141
            'Block module ' . $model->title . ' (ID: ' . $model->id . ')',
142
            (function () use ($model): \Iterator {
143
                /* @phpstan-ignore class.notFound */
144
                yield ['table' => BlockModel::getTable(), 'id' => $model->pid];
145
            })()
146
        );
147
    }
148

149
    private function form(int $id): ?Element
150
    {
151
        $model = $this->helper->fetchModelOrData('tl_form', $id);
2✔
152

153
        if (null === $model) {
2✔
154
            return null;
2✔
155
        }
156

157
        return new Element(
1✔
158
            $model->id,
1✔
159
            'tl_form',
1✔
160
            'Form ' . $model->title . ' (ID: ' . $model->id . ')',
1✔
161
            (function () use ($model): \Iterator {
1✔
162
                foreach (($this->framework->getAdapter(ModuleModel::class)->findByForm($model->id)) ?? [] as $module) {
1✔
163
                    yield ['table' => 'tl_module', 'id' => $module->id];
1✔
164
                }
165
                foreach (($this->framework->getAdapter(ContentModel::class)->findByForm($model->id)) ?? [] as $element) {
1✔
166
                    yield ['table' => 'tl_content', 'id' => $element->id];
1✔
167
                }
168
                foreach ($this->helper->findModulesByInserttag('html', 'html', 'insert_form', $model->id) as $module) {
1✔
169
                    yield ['table' => 'tl_module', 'id' => $module->id];
1✔
170
                }
171
                foreach ($this->helper->findContentElementByInserttag('html', 'html', 'insert_form', $model->id) as $element) {
1✔
172
                    yield ['table' => ContentModel::getTable(), 'id' => $element->id];
1✔
173
                }
174
            })()
1✔
175
        );
1✔
176
    }
177

178
    private function formField(int $id): ?Element
179
    {
180
        $model = $this->helper->fetchModelOrData('tl_form_field', $id);
1✔
181

182
        if (null === $model) {
1✔
183
            return null;
1✔
184
        }
185
        return new Element(
1✔
186
            $model->id,
1✔
187
            'tl_form_field',
1✔
188
            'Form field ' . $model->name . ' (ID: ' . $model->id . ')',
1✔
189
            (function () use ($model): \Generator {
1✔
190
                yield ['table' => FormModel::getTable(), 'id' => $model->pid];
1✔
191
            })()
1✔
192
        );
1✔
193
    }
194

195
    private function module(int $id): ?Element
196
    {
197
        $model = $this->helper->fetchModelOrData(ModuleModel::getTable(), $id);
1✔
198

199
        if (null === $model) {
1✔
200
            return null;
×
201
        }
202

203
        return new Element(
1✔
204
            $model->id,
1✔
205
            ModuleModel::getTable(),
1✔
206
            'Module ' . $model->type . ' (ID: ' . $model->id . ')',
1✔
207
            (function () use ($model): \Generator {
1✔
208

209
                yield ['table' => ThemeModel::getTable(), 'id' => $model->pid];
1✔
210

211
                foreach ($this->framework->getAdapter(ContentModel::class)->findBy(
1✔
212
                    ['tl_content.type=?', 'tl_content.module=?'],
1✔
213
                    ['module', $model->id]
1✔
214
                ) ?? [] as $contentelement) {
1✔
215
                    yield ['table' => ContentModel::getTable(), 'id' => $contentelement->id];
×
216
                }
217

218
                $result = $this->connection->executeQuery(
1✔
219
                    "SELECT id FROM tl_layout WHERE modules LIKE '%:\"".((int) $model->id)."\"%'"
1✔
220
                );
1✔
221
                foreach ($result->fetchAssociative() as $row) {
1✔
222
                    yield ['id' => $row['id'], 'table' => LayoutModel::getTable()];
×
223
                }
224

225
                foreach ($this->helper->findModulesByInserttag('html', 'html', 'insert_module', $model->id) as $id) {
1✔
226
                    yield ['id' => $id, 'table' => ModuleModel::getTable()];
×
227
                }
228

229
                if (class_exists(BlockModuleModel::class) && $blockModules = BlockModuleModel::findByModule($model->id)) {
1✔
230
                    foreach ($blockModules as $blockModule) {
×
231
                        yield ['table' => BlockModuleModel::getTable(), 'id' => $blockModule->id];
×
232
                    }
233
                }
234
            })()
1✔
235
        );
1✔
236
    }
237

238
    /**
239
     * @codeCoverageIgnore
240
     */
241
    private function news(int $id): ?Element
242
    {
243
        if (!class_exists(NewsModel::class)) {
244
            return null;
245
        }
246

247
        $model = $this->helper->fetchModelOrData(NewsModel::getTable(), $id);
248

249
        if (null === $model) {
250
            return null;
251
        }
252

253
        return new Element(
254
            $model->id,
255
            NewsModel::getTable(),
256
            'News ' . $model->headline . ' (ID: ' . $model->id . ')',
257
            (function () use ($model): \Generator {
258
                yield ['table' => NewsArchiveModel::getTable(), 'id' => $model->pid];
259
            })()
260
        );
261
    }
262

263
    /**
264
     * @codeCoverageIgnore
265
     */
266
    private function newsArchive(int $id): ?Element
267
    {
268
        if (!class_exists(NewsArchiveModel::class)) {
269
            return null;
270
        }
271

272
        $model = $this->helper->fetchModelOrData(NewsArchiveModel::getTable(), $id);
273

274
        if (null === $model) {
275
            return null;
276
        }
277

278
        return new Element(
279
            $model->id,
280
            NewsArchiveModel::getTable(),
281
            'News Archive ' . $model->title . ' (ID: ' . $model->id . ')',
282
            (function () use ($model): \Generator {
283
                $modules = $this->helper->findModulesByTypeAndSerializedValue(
284
                    'newslist',
285
                    'news_archives',
286
                    [$model->id]
287
                );
288
                if ($modules) {
289
                    foreach ($modules as $module) {
290
                        yield ['table' => ModuleModel::getTable(), 'id' => $module->id];
291
                    }
292
                }
293
            })()
294
        );
295
    }
296

297
    private function listConfig(int $id): ?Element
298
    {
299
        $model = $this->helper->fetchModelOrData('tl_list_config', $id);
1✔
300
        if ($model === null) {
1✔
301
            return null;
1✔
302
        }
303

304
        return new Element(
1✔
305
            $model->id,
1✔
306
            'tl_list_config',
1✔
307
            'List config ' . $model->title . ' (ID: ' . $model->id . ')',
1✔
308
            (function () use ($model): \Iterator {
1✔
309
                $t = ModuleModel::getTable();
1✔
310
                $modules = $this->framework->getAdapter(ModuleModel::class)->findBy(["$t.type=?", "$t.listConfig=?"], ['listConfig', $model->id]);
1✔
311
                foreach ($modules as $module) {
1✔
312
                    yield ['table' => ModuleModel::getTable(), 'id' => $module->id];
1✔
313
                }
314
            })()
1✔
315
        );
1✔
316
    }
317

318
    private function listConfigElement(int $id): ?Element
319
    {
320
        $model = $this->helper->fetchModelOrData('tl_list_config_element', $id);
1✔
321
        if (null === $model) {
1✔
322
            return null;
1✔
323
        }
324

325
        return new Element(
1✔
326
            $model->id,
1✔
327
            'tl_list_config_element',
1✔
328
            'List config element ' . $model->title . ' (ID: ' . $model->id . ')',
1✔
329
            (function () use ($model): \Iterator {
1✔
330
                yield ['table' => 'tl_list_config', 'id' => $model->pid];
1✔
331
            })()
1✔
332
        );
1✔
333
    }
334

335
    private function page(int $id): ?Element
336
    {
337
        /** @var PageModel|null $model */
338
        $model = $this->helper->fetchModelOrData(PageModel::getTable(), $id);
×
339
        if (null === $model) {
×
340
            return null;
×
341
        }
342

343
        try {
344
            $path = $model->getFrontendUrl();
×
345
        } catch (InvalidParameterException) {
×
346
            $model->loadDetails();
×
347
            $path = ($model->rootUseSSL ? 'https' : 'http') . '://' . $model->domain . '/' . $model->alias;
×
348
        }
349

350
        return new Element(
×
351
            $model->id,
×
352
            PageModel::getTable(),
×
353
            'Page ' . $model->title . ' (ID: ' . $model->id . ', Type: ' . $model->type . ', DNS: ' . $path . ')',
×
354
            (function () use ($model): \Iterator {
×
355
                if ($model->pid > 0) {
×
356
                    yield ['table' => PageModel::getTable(), 'id' => $model->pid];
×
357
                }
358
            })()
×
359
        );
×
360
    }
361

362

363
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc