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

heimrichhannot / contao-utils-bundle / 13965748749

20 Mar 2025 09:13AM UTC coverage: 79.29% (-0.03%) from 79.32%
13965748749

push

github

koertho
adjust rector config

1095 of 1381 relevant lines covered (79.29%)

3.28 hits per line

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

4.35
/src/Command/EntityFinderCommand.php
1
<?php
2

3
/*
4
 * Copyright (c) 2022 Heimrich & Hannot GmbH
5
 *
6
 * @license LGPL-3.0-or-later
7
 */
8

9
namespace HeimrichHannot\UtilsBundle\Command;
10

11
use Contao\ArticleModel;
12
use Contao\ContentModel;
13
use Contao\Controller;
14
use Contao\CoreBundle\Framework\ContaoFramework;
15
use Contao\LayoutModel;
16
use Contao\ModuleModel;
17
use Contao\PageModel;
18
use Contao\ThemeModel;
19
use Doctrine\DBAL\Connection;
20
use HeimrichHannot\UtilsBundle\EntityFinder\EntityFinderHelper;
21
use HeimrichHannot\UtilsBundle\EntityFinder\Finder;
22
use HeimrichHannot\UtilsBundle\Event\ExtendEntityFinderEvent;
23
use Symfony\Component\Console\Attribute\AsCommand;
24
use Symfony\Component\Console\Command\Command;
25
use Symfony\Component\Console\Input\InputArgument;
26
use Symfony\Component\Console\Input\InputInterface;
27
use Symfony\Component\Console\Output\OutputInterface;
28
use Symfony\Component\Console\Style\SymfonyStyle;
29
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
30

31
#[AsCommand(
32
    name: 'huh:utils:entity_finder',
33
    description: 'A command to find where an entity is included.'
34
)]
35
class EntityFinderCommand extends Command
36
{
37
    public function __construct(
38
        private readonly ContaoFramework $contaoFramework,
39
        private readonly EventDispatcherInterface $eventDispatcher,
40
        private readonly Connection $connection,
41
        private readonly EntityFinderHelper $entityFinderHelper,
42
        private readonly Finder $finder,
43
    )
44
    {
45
        parent::__construct();
1✔
46
    }
47

48
    protected function configure(): void
49
    {
50
        $this
1✔
51
            ->addArgument('table', InputArgument::REQUIRED, 'The database table')
1✔
52
            ->addArgument('id', InputArgument::REQUIRED, 'The entity id or alias (id is better supported).')
1✔
53
        ;
1✔
54
    }
55

56
    protected function execute(InputInterface $input, OutputInterface $output): int
57
    {
58
        $this->contaoFramework->initialize();
×
59
        $io = new SymfonyStyle($input, $output);
×
60

61
        $io->title('Find entity');
×
62

63
        $table = $input->getArgument('table');
×
64
        $id = $input->getArgument('id');
×
65

66
        $result = $this->loop($table, $id);
×
67
        $this->output($io, [$result]);
×
68
        $io->newLine();
×
69

70
        return Command::SUCCESS;
×
71
    }
72

73
    private function loop(string $table, $id): array
74
    {
75
        $current = [
×
76
            'table' => $table,
×
77
            'id' => $id,
×
78
            'parents' => [],
×
79
        ];
×
80

81
        $parents = [];
×
82

83
        $this->findEntity($table, $id, $parents);
×
84
        $event = $this->runLegacyExtendEntityFinderEvent($table, $id, $parents);
×
85

86
        $this->findInserttags($event);
×
87

88
        $cache = [];
×
89

90
        foreach ($event->getParents() as $parent) {
×
91
            if (!isset($parent['table']) || !isset($parent['id'])) {
×
92
                continue;
×
93
            }
94
            $cacheKey = $parent['table'].'_'.$parent['id'];
×
95

96
            if (\in_array($cacheKey, $cache)) {
×
97
                continue;
×
98
            }
99
            $cache[] = $cacheKey;
×
100
            $current['parents'][] = $this->loop($parent['table'], $parent['id']);
×
101
        }
102

103
        return $current;
×
104
    }
105

106
    private function output(SymfonyStyle $io, array $tree, string $prepend = '', int $depth = 0): void
107
    {
108
        $itemCount = \count($tree);
×
109
        $i = 0;
×
110

111
        foreach ($tree as $item) {
×
112
            ++$i;
×
113

114
            if ($depth > 0) {
×
115
                if ($i === $itemCount) {
×
116
                    $newPrepend = $prepend.'└── ';
×
117
                    $nextPrepend = $prepend.'    ';
×
118
                } else {
119
                    $newPrepend = $prepend.'├── ';
×
120
                    $nextPrepend = $prepend.'│   ';
×
121
                }
122
            } else {
123
                $newPrepend = $prepend;
×
124
                $nextPrepend = $prepend;
×
125
            }
126
            $io->writeln($newPrepend.$this->createText($item['table'], $item['id']));
×
127

128
            if ($item['parents'] ?? false) {
×
129
                $this->output($io, $item['parents'], $nextPrepend, ++$depth);
×
130
            }
131
        }
132
    }
133

134
    private function findEntity(string $table, $id, array &$parents, bool $onlyText = false): ?string
135
    {
136
        Controller::loadLanguageFile('default');
×
137

138
        switch ($table) {
139
            case ContentModel::getTable():
×
140
                $element = ContentModel::findByIdOrAlias($id);
×
141

142
                if ($element) {
×
143
                    $parents[] = ['table' => $element->ptable, 'id' => $element->pid];
×
144

145
                    return 'Content Element: '.($GLOBALS['TL_LANG']['CTE'][$element->type][0] ?? $element->type).' (ID: '.$element->id.', Type: '.$element->type.')';
×
146
                }
147

148
                return 'Content Element not found: ID '.$id;
×
149

150
            case ArticleModel::getTable():
×
151
                $element = ArticleModel::findByPk($id);
×
152

153
                if ($element) {
×
154
                    $parents[] = ['table' => PageModel::getTable(), 'id' => $element->pid];
×
155

156
                    if (!$onlyText) {
×
157
                        foreach ($this->entityFinderHelper->findModulesByInserttag('html', 'html', 'insert_article', $element->id) as $id) {
×
158
                            $parents[] = ['table' => ModuleModel::getTable(), 'id' => $id];
×
159
                        }
160
                        foreach ($this->entityFinderHelper->findContentElementByInserttag('html', 'html', 'insert_article', $element->id) as $id) {
×
161
                            $parents[] = ['table' => ContentModel::getTable(), 'id' => $id];
×
162
                        }
163
                    }
164

165
                    return 'Article: '.$element->title.' (ID: '.$element->id.')';
×
166
                }
167

168
                return 'Article not found: ID '.$id;
×
169

170
            case LayoutModel::getTable():
×
171
                $layout = LayoutModel::findById($id);
×
172

173
                if ($layout) {
×
174
                    $parents[] = ['table' => ThemeModel::getTable(), 'id' => $layout->pid];
×
175

176
                    return 'Layout: '.html_entity_decode($layout->name).' (ID: '.$layout->id.')';
×
177
                }
178

179
                return 'Layout not found: ID '.$id;
×
180

181
            case ThemeModel::getTable():
×
182
                $theme = ThemeModel::findByPk($id);
×
183

184
                if ($theme) {
×
185
                    return '<options=bold>Theme: '.$theme->name.'</> (ID: '.$theme->id.')';
×
186
                }
187

188
                return 'Theme not found: ID '.$id;
×
189

190
            case PageModel::getTable():
×
191
                $page = PageModel::findByPk($id);
×
192

193
                if ($page) {
×
194
                    return '<options=bold>Page: '.$page->title.'</> (ID: '.$page->id.', Type: '.$page->type.', DNS: '.$page->getFrontendUrl().' )';
×
195
                }
196

197
                return 'Page not found: ID '.$id;
×
198
        }
199

200
        $element = $this->finder->find($table, $id);
×
201
        if ($element) {
×
202
            if ($onlyText) {
×
203
                return $element->description;
×
204
            }
205
            if (null === $element->parents) {
×
206
                return null;
×
207
            }
208
            foreach ($element->getParents() as $parent) {
×
209
                $parents[] = ['table' => $parent['table'], 'id' => $parent['id']];
×
210
            }
211
            return null;
×
212
        }
213

214
        return null;
×
215
    }
216

217
    private function createText(string $table, $id): string
218
    {
219
        $parents = [];
×
220

221
        if ($text = $this->findEntity($table, $id, $parents, true)) {
×
222
            return $text;
×
223
        }
224

225
        /** @var ExtendEntityFinderEvent $event */
226
        $event = $this->runLegacyExtendEntityFinderEvent($table, $id, [], true);
×
227

228
        if ($event->getOutput()) {
×
229
            return $event->getOutput();
×
230
        }
231

232
        return 'Unsupported entity: '.$table.' (ID: '.$id.')';
×
233
    }
234

235
    private function runLegacyExtendEntityFinderEvent(string $table, $id, array $parents, bool $onlyText = false): ExtendEntityFinderEvent
236
    {
237

238
        if ($this->eventDispatcher->hasListeners(ExtendEntityFinderEvent::class)) {
×
239
            trigger_deprecation(
×
240
                'heimrichhannot/contao-utils-bundle',
×
241
                '3.7',
×
242
                'Using the ExtendEntityFinderEvent is deprecated. Use EntityFinderFindEvent instead.'
×
243
            );
×
244
        }
245
        $event = $this->eventDispatcher->dispatch(
×
246
            new ExtendEntityFinderEvent($table, $id, $parents, [], $this->entityFinderHelper, $onlyText),
×
247
        );
×
248

249
        return $event;
×
250
    }
251

252
    private function findInserttags(ExtendEntityFinderEvent $event): void
253
    {
254
        $stmt = $this->connection->prepare(
×
255
            "SELECT id FROM tl_module WHERE type='html' AND html LIKE ?");
×
256

257
        foreach ($event->getInserttags() as $inserttag) {
×
258
            $result = $stmt->executeQuery(['%'.$inserttag.'%']);
×
259

260
            foreach ($result->fetchAllAssociative() as $row) {
×
261
                $event->addParent(ModuleModel::getTable(), $row['id']);
×
262
            }
263
        }
264
    }
265
}
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

© 2025 Coveralls, Inc