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

heimrichhannot / contao-utils-bundle / 8325220320

18 Mar 2024 10:43AM UTC coverage: 23.14%. Remained the same
8325220320

push

github

koertho
prepare 2.235.2

1291 of 5579 relevant lines covered (23.14%)

1.54 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\CoreBundle\Framework\ContaoFrameworkInterface;
12
use Contao\Database;
13
use Contao\System;
14
use HeimrichHannot\UtilsBundle\Container\ContainerUtil;
15
use HeimrichHannot\UtilsBundle\Model\ModelUtil;
16
use Symfony\Component\Filesystem\Filesystem;
17
use Symfony\Component\HttpFoundation\RequestStack;
18

19
class DatabaseTreeCache
20
{
21
    /**
22
     * Cache tree.
23
     *
24
     * @var array
25
     */
26
    protected static $cache = [];
27

28
    /**
29
     * @var ContaoFrameworkInterface
30
     */
31
    protected $framework;
32

33
    /**
34
     * @var Filesystem
35
     */
36
    protected $filesystem;
37

38
    /**
39
     * @var ModelUtil
40
     */
41
    protected $modelUtil;
42

43
    /**
44
     * @var Database
45
     */
46
    protected $database;
47

48
    /**
49
     * Tree cache directory.
50
     *
51
     * @var string
52
     */
53
    protected $cacheDir;
54
    /**
55
     * @var ContainerUtil
56
     */
57
    protected $containerUtil;
58
    /**
59
     * @var RequestStack
60
     */
61
    protected $requestStack;
62

63
    public function __construct(ContaoFrameworkInterface $framework, Filesystem $filesystem, ModelUtil $modelUtil, ContainerUtil $containerUtil, RequestStack $requestStack)
64
    {
65
        $this->framework = $framework;
×
66
        $this->filesystem = $filesystem;
×
67
        $this->modelUtil = $modelUtil;
×
68
        $this->database = $this->framework->createInstance(Database::class);
×
69
        $this->cacheDir = \Contao\System::getContainer()->getParameter('kernel.cache_dir').'/tree_cache';
×
70
        $this->containerUtil = $containerUtil;
×
71
        $this->requestStack = $requestStack;
×
72
    }
73

74
    /**
75
     * Generate tree cache.
76
     */
77
    public function loadDataContainer($table)
78
    {
79
        if (!$this->database->tableExists($table)) {
×
80
            return;
×
81
        }
82

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

87
        if ($this->containerUtil->isInstall() || !$this->requestStack->getCurrentRequest()) {
×
88
            return;
×
89
        }
90

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

95
        $configurations = $GLOBALS['TL_DCA'][$table]['config']['treeCache'];
×
96

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

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

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

110
        if (null === ($roots = $this->modelUtil->findModelInstancesBy($table, $config['columns'] ?? [], $config['values'] ?? [], $config['options']))) {
×
111
            return;
×
112
        }
113

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

116
        $this->filesystem->dumpFile(
×
117
            $this->cacheDir.'/'.$filename,
×
118
            sprintf("<?php\n\nreturn %s;\n", var_export($tree, true))
×
119
        );
×
120
    }
121

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

140
        foreach ($ids as $i => $id) {
×
141
            if (!isset($tree[$id]) || !\is_array($tree[$id])) {
×
142
                continue;
×
143
            }
144

145
            $children = array_merge($children, $tree[$id]);
×
146

147
            if (1 === $maxLevels) {
×
148
                continue;
×
149
            }
150

151
            if ($maxLevels > 0 && $level > $maxLevels) {
×
152
                return [];
×
153
            }
154

155
            if (!empty($nested = self::getChildRecords($table, $tree[$id], $maxLevels, $key, $children, ++$level))) {
×
156
                $children = $nested;
×
157
            } else {
158
                $depth = 0;
×
159
            }
160
        }
161

162
        return $children;
×
163
    }
164

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

183
        if (isset($tree[$id]) && 0 === $level) {
×
184
            $parents[] = $id;
×
185
        }
186

187
        foreach ($tree as $pid => $ids) {
×
188
            if (!\in_array($id, $ids)) {
×
189
                continue;
×
190
            }
191

192
            $parents[] = $pid;
×
193

194
            if (1 === $maxLevels) {
×
195
                continue;
×
196
            }
197

198
            if ($maxLevels > 0 && $level > $maxLevels) {
×
199
                return [];
×
200
            }
201

202
            if (!empty($nested = self::getParentRecords($table, $pid, $maxLevels, $key, $parents, ++$level))) {
×
203
                $parents = $nested;
×
204
            } else {
205
                $level = 0;
×
206
            }
207
        }
208

209
        return $parents;
×
210
    }
211

212
    /**
213
     * Get the tree cache for a given table and key.
214
     *
215
     * @param string $table The database table
216
     * @param string Custom index key (default: primary key from model)
217
     */
218
    public function getTreeCache($table, $key): ?array
219
    {
220
        $filename = $table.'_'.$key.'.php';
×
221

222
        if (file_exists($this->cacheDir.'/'.$filename)) {
×
223
            self::$cache[$table.'_'.$key] = (include $this->cacheDir.'/'.$filename);
×
224
        }
225

226
        return self::$cache[$table.'_'.$key] ?? null;
×
227
    }
228

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

246
                continue;
×
247
            }
248

249
            while ($children->next()) {
×
250
                $return[$children->pid][$children->{$key}] = $children->{$key};
×
251
                $return = $this->generateCacheTree($table, [$children->{$key}], $key, $config, $return);
×
252
            }
253
        }
254

255
        return $return;
×
256
    }
257

258
    /**
259
     * Generate all cache trees.
260
     *
261
     * @param $cacheDir
262
     */
263
    public function generateAllCacheTree($cacheDir)
264
    {
265
        $this->cacheDir = $cacheDir;
×
266

267
        $tables = $this->database->listTables();
×
268

269
        foreach ($tables as $table) {
×
270
            // trigger loadDataContainer TL_HOOK
271
            System::getContainer()->get('huh.utils.dca')->loadDc($table);
×
272
        }
273
    }
274

275
    /**
276
     * Register a dca to the tree cache.
277
     *
278
     * @param string $table   (The dca table)
279
     * @param array  $columns Parent sql filter columns (e.g. `tl_page.type`)
280
     * @param array  $values  Parent sql filter values (e.g. `root` for `tl_page.type`)
281
     * @param array  $options SQL Options for sorting
282
     * @param string Custom index key (default: primary key from model)
283
     *
284
     * @return bool Acknowledge state if register succeeded
285
     */
286
    public function registerDcaToCacheTree(string $table, array $columns = [], array $values = [], array $options = [], string $key = 'id')
287
    {
288
        System::getContainer()->get('huh.utils.dca')->loadDc($table);
×
289

290
        if (!isset($GLOBALS['TL_DCA'][$table])) {
×
291
            return false;
×
292
        }
293

294
        $GLOBALS['TL_DCA'][$table]['config']['treeCache'][$key] = [
×
295
            'columns' => $columns,
×
296
            'values' => $values,
×
297
            'options' => $options,
×
298
            'key' => $key,
×
299
        ];
×
300

301
        $GLOBALS['TL_DCA'][$table]['config']['ondelete_callback']['huh.utils.cache.database_tree'] = ['huh.utils.cache.database_tree', 'purgeCacheTree'];
×
302
        $GLOBALS['TL_DCA'][$table]['config']['oncut_callback']['huh.utils.cache.database_tree'] = ['huh.utils.cache.database_tree', 'purgeCacheTree'];
×
303
        $GLOBALS['TL_DCA'][$table]['config']['onsubmit_callback']['huh.utils.cache.database_tree'] = ['huh.utils.cache.database_tree', 'purgeCacheTree'];
×
304
        $GLOBALS['TL_DCA'][$table]['config']['onrestore_callback']['huh.utils.cache.database_tree'] = ['huh.utils.cache.database_tree', 'purgeCacheTree'];
×
305

306
        return true;
×
307
    }
308

309
    /**
310
     * Purge the tree cache completely in order to take table relations into consideration.
311
     */
312
    public function purgeCacheTree()
313
    {
314
        $this->filesystem->remove($this->cacheDir);
×
315
    }
316

317
    private function isCompleteInstallation($table)
318
    {
319
        try {
320
            $this->modelUtil->findOneModelInstanceBy($table, [], []);
×
321
        } catch (\Exception $e) {
×
322
            return false;
×
323
        }
324

325
        return true;
×
326
    }
327
}
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