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

heimrichhannot / contao-utils-bundle / 13951376717

19 Mar 2025 04:12PM UTC coverage: 79.32% (+5.6%) from 73.745%
13951376717

push

github

web-flow
Forwardport #87 (#93)

* forwardport #87

* updated tests

* refactored some deprecated code into finder

* fix phpstan reports

* update tests

* add more test coverage

* raise test coveage

* fix bug

---------

Co-authored-by: DDEV User <nobody@example.com>

142 of 178 new or added lines in 6 files covered. (79.78%)

2 existing lines in 1 file now uncovered.

1097 of 1383 relevant lines covered (79.32%)

3.28 hits per line

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

94.83
/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\ThemeModel;
14
use Doctrine\DBAL\Connection;
15
use HeimrichHannot\Blocks\BlockModel;
16
use HeimrichHannot\Blocks\BlockModuleModel;
17
use HeimrichHannot\UtilsBundle\Event\EntityFinderFindEvent;
18
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
19
use function Symfony\Component\String\u;
20

21
/**
22
 * @internal
23
 */
24
class Finder
25
{
26

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

33
    )
34
    {
35
    }
8✔
36

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

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

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

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

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

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

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

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

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

74
        $dca = &$GLOBALS['TL_DCA'][$table];
2✔
75

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

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

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

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

107
        if (null === $model) {
108
            return null;
109
        }
110

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

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

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

132
        if (null === $model) {
133
            return null;
134
        }
135

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

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

151
        if (null === $model) {
2✔
152
            return null;
2✔
153
        }
154

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

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

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

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

197
        if (null === $model) {
1✔
NEW
198
            return null;
×
199
        }
200

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

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

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

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

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

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

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

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

247
        if (null === $model) {
248
            return null;
249
        }
250

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

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

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

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

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

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

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

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

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

334

335
}
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