Completed
Push — master ( 94ee7f...8c2cbc )
by Craig
10:45
created

BundlesSchemaHelper::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 3
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Zikula package.
7
 *
8
 * Copyright Zikula Foundation - https://ziku.la/
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Zikula\Bundle\CoreBundle\Bundle\Helper;
15
16
use Doctrine\DBAL\Connection;
17
use Doctrine\DBAL\Schema\Table;
18
use InvalidArgumentException;
19
use Zikula\Bundle\CoreBundle\Bundle\MetaData;
20
use Zikula\Bundle\CoreBundle\Bundle\Scanner;
21
use Zikula\Bundle\CoreBundle\CacheClearer;
22
use Zikula\Common\Translator\TranslatorInterface;
23
use Zikula\Core\AbstractBundle;
24
25
class BundlesSchemaHelper
26
{
27
    /**
28
     * @var Connection
29
     */
30
    private $conn;
31
32
    /**
33
     * @var TranslatorInterface
34
     */
35
    private $translator;
36
37
    /**
38
     * @var CacheClearer
39
     */
40
    private $cacheClearer;
41
42
    public function __construct(Connection $conn, TranslatorInterface $translator, CacheClearer $cacheClearer)
43
    {
44
        $this->conn = $conn;
45
        $this->translator = $translator;
46
        $this->cacheClearer = $cacheClearer;
47
    }
48
49
    public function load(): void
50
    {
51
        $this->verifySchema();
52
        $scanner = new Scanner();
53
        $scanner->setTranslator($this->translator);
54
        $scanner->scan(['modules', 'themes']);
55
        $array = array_merge($scanner->getModulesMetaData(), $scanner->getThemesMetaData());
56
        $this->sync($array);
57
    }
58
59
    /**
60
     * Sync the filesystem scan and the bundles table.
61
     * This is a 'dumb' scan - there is no state management here.
62
     * State management occurs in the module and theme management and is checked in Bundle/PersistedBundleHandler.
63
     */
64
    private function sync(array $fileExtensions = []): void
65
    {
66
        // add what is in array but missing from db
67
        /** @var $metadata MetaData */
68
        foreach ($fileExtensions as $name => $metadata) {
69
            $qb = $this->conn->createQueryBuilder();
70
            $qb->select('b.id', 'b.bundlename', 'b.bundleclass', 'b.autoload', 'b.bundletype', 'b.bundlestate')
71
                ->from('bundles', 'b')
72
                ->where('b.bundlename = :name')
73
                ->setParameter('name', $name);
74
            $result = $qb->execute();
75
            $row = $result->fetch();
76
            if (!$row) {
77
                // bundle doesn't exist
78
                $this->insert($metadata);
79
            } elseif (($metadata->getClass() !== $row['bundleclass']) || (serialize($metadata->getAutoload()) !== $row['autoload'])) {
80
                // bundle json has been updated
81
                $updatedMeta = [
82
                    'bundleclass' => $metadata->getClass(),
83
                    'autoload' => serialize($metadata->getAutoload())
84
                ];
85
                $this->conn->update('bundles', $updatedMeta, ['id' => $row['id']]);
86
            }
87
        }
88
89
        // remove what is in db but missing from array
90
        $qb = $this->conn->createQueryBuilder();
91
        $qb->select('b.id', 'b.bundlename', 'b.bundleclass', 'b.autoload', 'b.bundletype', 'b.bundlestate')
92
            ->from('bundles', 'b');
93
        $res = $qb->execute();
94
        foreach ($res->fetchAll() as $row) {
95
            if (!array_key_exists($row['bundlename'], $fileExtensions)) {
96
                $this->removeById((int)$row['id']);
97
            }
98
        }
99
100
        // clear the cache
101
        $this->cacheClearer->clear('symfony.config');
102
    }
103
104
    private function removeById(int $id): void
105
    {
106
        $this->conn->delete('bundles', ['id' => $id]);
107
    }
108
109
    private function insert(MetaData $metadata): void
110
    {
111
        $name = $metadata->getName();
112
        $autoload = serialize($metadata->getAutoload());
113
        $class = $metadata->getClass();
114
        switch ($metadata->getType()) {
115
            case 'zikula-module':
116
                $type = 'M';
117
                break;
118
            case 'zikula-theme':
119
                $type = 'T';
120
                break;
121
            default:
122
                throw new InvalidArgumentException(sprintf('Unknown type %s', $metadata->getType()));
123
        }
124
125
        $this->conn->insert('bundles', [
126
            'bundlename'  => $name,
127
            'autoload'    => $autoload,
128
            'bundleclass' => $class,
129
            'bundletype'  => $type,
130
            'bundlestate' => AbstractBundle::STATE_ACTIVE,
131
        ]);
132
    }
133
134
    private function verifySchema(): void
135
    {
136
        $schemaManager = $this->conn->getSchemaManager();
137
        if ($schemaManager->tablesExist(array('bundles')) !== true) {
138
            $this->createSchema();
139
        }
140
    }
141
142
    private function createSchema(): void
143
    {
144
        $schema = $this->conn->getSchemaManager();
145
        $table = new Table('bundles');
146
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
147
        $table->addColumn('bundlename', 'string', ['length' => 100]);
148
        $table->addColumn('autoload', 'string', ['length' => 384]);
149
        $table->addColumn('bundleclass', 'string', ['length' => 100]);
150
        $table->addColumn('bundletype', 'string', ['length' => 2]);
151
        $table->addColumn('bundlestate', 'integer', ['length' => 1]);
152
        $table->setPrimaryKey(['id']);
153
        $table->addUniqueIndex(['bundlename']);
154
        $schema->createTable($table);
155
    }
156
}
157