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

heimrichhannot / contao-utils-bundle / 12155845055

04 Dec 2024 08:30AM UTC coverage: 24.058% (-0.03%) from 24.087%
12155845055

push

github

koertho
fixed enable_generate_database_tree_cache not working, removed some deprecations

0 of 17 new or added lines in 1 file covered. (0.0%)

1 existing line in 1 file now uncovered.

1379 of 5732 relevant lines covered (24.06%)

1.56 hits per line

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

0.0
/src/Cache/DatabaseTreeCache.php
1
<?php
2

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

9
namespace HeimrichHannot\UtilsBundle\Cache;
10

11
use Contao\Controller;
12
use Contao\CoreBundle\Framework\ContaoFramework;
13
use Contao\CoreBundle\Framework\ContaoFrameworkInterface;
14
use Contao\Database;
15
use Contao\System;
16
use HeimrichHannot\UtilsBundle\Container\ContainerUtil;
17
use HeimrichHannot\UtilsBundle\Model\ModelUtil;
18
use HeimrichHannot\UtilsBundle\Util\Utils;
19
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
20
use Symfony\Component\Filesystem\Filesystem;
21
use Symfony\Component\HttpFoundation\RequestStack;
22

23
class DatabaseTreeCache
24
{
25
    protected static array $cache = [];
26

27
    protected ContaoFramework $framework;
28

29
    /**
30
     * @var Filesystem
31
     */
32
    protected Filesystem $filesystem;
33

34
    /**
35
     * @var Database
36
     */
37
    protected $database;
38

39
    /**
40
     * Tree cache directory.
41
     *
42
     * @var string
43
     */
44
    protected string $cacheDir;
45
    /**
46
     * @var RequestStack
47
     */
48
    protected RequestStack $requestStack;
49
    private ParameterBagInterface $parameterBag;
50
    private Utils $utils;
51

52
    public function __construct(
53
        ContaoFramework       $framework,
54
        Filesystem            $filesystem,
55
        RequestStack          $requestStack,
56
        ParameterBagInterface $parameterBag,
57
        Utils                 $utils
58
    )
59
    {
60
        $this->framework = $framework;
×
61
        $this->filesystem = $filesystem;
×
62
        $this->database = $this->framework->createInstance(Database::class);
×
63
        $this->requestStack = $requestStack;
×
NEW
64
        $this->parameterBag = $parameterBag;
×
NEW
65
        $this->utils = $utils;
×
66

NEW
67
        $this->cacheDir = $this->parameterBag->get('kernel.cache_dir').'/tree_cache';
×
68
    }
69

70
    /**
71
     * Generate tree cache.
72
     */
73
    public function loadDataContainer($table): void
74
    {
NEW
75
        if (!($this->parameterBag->get('huh_utils')['cache']['enable_generate_database_tree_cache'] ?? false)) {
×
NEW
76
           return;
×
77
        }
78

NEW
79
        $this->framework->initialize();
×
80

81
        if (!$this->database->tableExists($table)) {
×
82
            return;
×
83
        }
84

85
        if (!isset($GLOBALS['TL_DCA'][$table]) || !isset($GLOBALS['TL_DCA'][$table]['config']['treeCache']) || !\is_array($GLOBALS['TL_DCA'][$table]['config']['treeCache'])) {
×
86
            return;
×
87
        }
88

NEW
89
        if ($this->utils->container()->isInstall() || !$this->requestStack->getCurrentRequest()) {
×
90
            return;
×
91
        }
92

93
        if (!$this->isCompleteInstallation($table)) {
×
94
            return;
×
95
        }
96

97
        $configurations = $GLOBALS['TL_DCA'][$table]['config']['treeCache'];
×
98

99
        foreach ($configurations as $key => $config) {
×
100
            $this->addConfigToTreeCache($table, $key, $config);
×
101
        }
102
    }
103

104
    public function addConfigToTreeCache(string $table, string $key, array $config = [])
105
    {
106
        $filename = $table.'_'.$key.'.php';
×
107

NEW
108
        if (file_exists($this->cacheDir . '/' . $filename)) {
×
109
            return;
×
110
        }
111

NEW
112
        if (null === ($roots = $this->utils->model()->findModelInstancesBy(
×
NEW
113
                $table,
×
NEW
114
                $config['columns'] ?? [],
×
NEW
115
                $config['values'] ?? [],
×
NEW
116
                $config['options'])
×
117
            )) {
UNCOV
118
            return;
×
119
        }
120

121
        $tree = $this->generateCacheTree($table, $roots->fetchEach($key), $key, $config);
×
122

123
        $this->filesystem->dumpFile(
×
124
            $this->cacheDir.'/'.$filename,
×
125
            sprintf("<?php\n\nreturn %s;\n", var_export($tree, true))
×
126
        );
×
127
    }
128

129
    /**
130
     * Get all child records for given parent entities.
131
     *
132
     * @param string $table     The database table
133
     * @param array  $ids       The parent entity ids
134
     * @param int    $maxLevels The max stop level
135
     * @param string Custom index key (default: primary key from model)
136
     * @param array $children Internal children return array
137
     * @param int   $level    Internal depth attribute
138
     *
139
     * @return array An array containing all children for given parent entities
140
     */
141
    public function getChildRecords(string $table, array $ids = [], $maxLevels = null, string $key = 'id', array $children = [], int $level = 0): array
142
    {
143
        if (null === ($tree = $this->getTreeCache($table, $key))) {
×
144
            return $this->database->getChildRecords($ids, $table);
×
145
        }
146

147
        foreach ($ids as $i => $id) {
×
148
            if (!isset($tree[$id]) || !\is_array($tree[$id])) {
×
149
                continue;
×
150
            }
151

152
            $children = array_merge($children, $tree[$id]);
×
153

154
            if (1 === $maxLevels) {
×
155
                continue;
×
156
            }
157

158
            if ($maxLevels > 0 && $level > $maxLevels) {
×
159
                return [];
×
160
            }
161

162
            if (!empty($nested = self::getChildRecords($table, $tree[$id], $maxLevels, $key, $children, ++$level))) {
×
163
                $children = $nested;
×
164
            } else {
165
                $depth = 0;
×
166
            }
167
        }
168

169
        return $children;
×
170
    }
171

172
    /**
173
     * Get all parent records for given child entity.
174
     *
175
     * @param string $table     The database table
176
     * @param int    $id        The current entity id
177
     * @param int    $maxLevels The max stop level
178
     * @param string Custom index key (default: primary key from model)
179
     * @param array $parents Internal children return array
180
     * @param int   $level   Internal depth attribute
181
     *
182
     * @return array An array containing all children for given parent entities
183
     */
184
    public function getParentRecords(string $table, int $id, $maxLevels = null, string $key = 'id', array $parents = [], int $level = 0): array
185
    {
186
        if (null === ($tree = $this->getTreeCache($table, $key))) {
×
187
            return $this->database->getParentRecords($id, $table);
×
188
        }
189

190
        if (isset($tree[$id]) && 0 === $level) {
×
191
            $parents[] = $id;
×
192
        }
193

194
        foreach ($tree as $pid => $ids) {
×
195
            if (!\in_array($id, $ids)) {
×
196
                continue;
×
197
            }
198

199
            $parents[] = $pid;
×
200

201
            if (1 === $maxLevels) {
×
202
                continue;
×
203
            }
204

205
            if ($maxLevels > 0 && $level > $maxLevels) {
×
206
                return [];
×
207
            }
208

209
            if (!empty($nested = self::getParentRecords($table, $pid, $maxLevels, $key, $parents, ++$level))) {
×
210
                $parents = $nested;
×
211
            } else {
212
                $level = 0;
×
213
            }
214
        }
215

216
        return $parents;
×
217
    }
218

219
    /**
220
     * Get the tree cache for a given table and key.
221
     *
222
     * @param string $table The database table
223
     * @param string Custom index key (default: primary key from model)
224
     */
225
    public function getTreeCache($table, $key): ?array
226
    {
227
        $filename = $table.'_'.$key.'.php';
×
228

229
        if (file_exists($this->cacheDir.'/'.$filename)) {
×
230
            self::$cache[$table.'_'.$key] = (include $this->cacheDir.'/'.$filename);
×
231
        }
232

233
        return self::$cache[$table.'_'.$key] ?? null;
×
234
    }
235

236
    /**
237
     * Generate the flat cache tree.
238
     *
239
     * @param string $table  The database table
240
     * @param string $key    Custom index key (default: primary key from model)
241
     * @param array  $ids    Root identifiers (parent ids)
242
     * @param array  $config Tree config
243
     * @param array  $return Internal return array
244
     *
245
     * @return array The flat cache tree
246
     */
247
    public function generateCacheTree(string $table, array $ids = [], string $key = 'id', array $config = [], $return = []): array
248
    {
249
        foreach ($ids as $id) {
×
NEW
250
            if (null === ($children = $this->utils->model()->findModelInstancesBy($table, [$table.'.pid = ?'], $id, $config['options']))) {
×
251
                $return[$id] = [];
×
252

253
                continue;
×
254
            }
255

256
            while ($children->next()) {
×
257
                $return[$children->pid][$children->{$key}] = $children->{$key};
×
258
                $return = $this->generateCacheTree($table, [$children->{$key}], $key, $config, $return);
×
259
            }
260
        }
261

262
        return $return;
×
263
    }
264

265
    /**
266
     * Generate all cache trees.
267
     *
268
     * @param $cacheDir
269
     */
270
    public function generateAllCacheTree($cacheDir)
271
    {
272
        $this->cacheDir = $cacheDir;
×
273

274
        $tables = $this->database->listTables();
×
275

276
        foreach ($tables as $table) {
×
277
            // trigger loadDataContainer TL_HOOK
NEW
278
            Controller::loadDataContainer($table);
×
279
        }
280
    }
281

282
    /**
283
     * Register a dca to the tree cache.
284
     *
285
     * @param string $table   (The dca table)
286
     * @param array  $columns Parent sql filter columns (e.g. `tl_page.type`)
287
     * @param array  $values  Parent sql filter values (e.g. `root` for `tl_page.type`)
288
     * @param array  $options SQL Options for sorting
289
     * @param string Custom index key (default: primary key from model)
290
     *
291
     * @return bool Acknowledge state if register succeeded
292
     */
293
    public function registerDcaToCacheTree(string $table, array $columns = [], array $values = [], array $options = [], string $key = 'id')
294
    {
NEW
295
        Controller::loadDataContainer($table);
×
296

297
        if (!isset($GLOBALS['TL_DCA'][$table])) {
×
298
            return false;
×
299
        }
300

301
        $GLOBALS['TL_DCA'][$table]['config']['treeCache'][$key] = [
×
302
            'columns' => $columns,
×
303
            'values' => $values,
×
304
            'options' => $options,
×
305
            'key' => $key,
×
306
        ];
×
307

308
        $GLOBALS['TL_DCA'][$table]['config']['ondelete_callback']['huh.utils.cache.database_tree'] = ['huh.utils.cache.database_tree', 'purgeCacheTree'];
×
309
        $GLOBALS['TL_DCA'][$table]['config']['oncut_callback']['huh.utils.cache.database_tree'] = ['huh.utils.cache.database_tree', 'purgeCacheTree'];
×
310
        $GLOBALS['TL_DCA'][$table]['config']['onsubmit_callback']['huh.utils.cache.database_tree'] = ['huh.utils.cache.database_tree', 'purgeCacheTree'];
×
311
        $GLOBALS['TL_DCA'][$table]['config']['onrestore_callback']['huh.utils.cache.database_tree'] = ['huh.utils.cache.database_tree', 'purgeCacheTree'];
×
312

313
        return true;
×
314
    }
315

316
    /**
317
     * Purge the tree cache completely in order to take table relations into consideration.
318
     */
319
    public function purgeCacheTree()
320
    {
321
        $this->filesystem->remove($this->cacheDir);
×
322
    }
323

324
    private function isCompleteInstallation($table)
325
    {
326
        try {
NEW
327
            $this->utils->model()->findOneModelInstanceBy($table, [], []);
×
328
        } catch (\Exception $e) {
×
329
            return false;
×
330
        }
331

332
        return true;
×
333
    }
334
}
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