Completed
Push — ezp-31088-refactor-content-mod... ( ab3ba3 )
by
unknown
13:24 queued 33s
created

DoctrineDatabase::setLanguageQueryParameters()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
5
 * @license For full copyright and license information view LICENSE file distributed with this source code.
6
 */
7
declare(strict_types=1);
8
9
namespace eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway;
10
11
use Doctrine\DBAL\Connection;
12
use Doctrine\DBAL\ParameterType;
13
use Doctrine\DBAL\Query\QueryBuilder;
14
use eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway;
15
use eZ\Publish\SPI\Persistence\Content\Language;
16
use RuntimeException;
17
18
/**
19
 * Doctrine database based Language Gateway.
20
 *
21
 * @internal Gateway implementation is considered internal. Use Persistence Language Handler instead.
22
 *
23
 * @see \eZ\Publish\SPI\Persistence\Content\Language\Handler
24
 */
25
final class DoctrineDatabase extends Gateway
26
{
27
    /**
28
     * The native Doctrine connection.
29
     *
30
     * @var \Doctrine\DBAL\Connection
31
     */
32
    private $connection;
33
34
    /** @var \Doctrine\DBAL\Platforms\AbstractPlatform */
35
    private $dbPlatform;
36
37
    /**
38
     * @throws \Doctrine\DBAL\DBALException
39
     */
40
    public function __construct(Connection $connection)
41
    {
42
        $this->connection = $connection;
43
        $this->dbPlatform = $this->connection->getDatabasePlatform();
44
    }
45
46
    public function insertLanguage(Language $language): int
47
    {
48
        $query = $this->connection->createQueryBuilder();
49
        $query
50
            ->select(
51
                $this->dbPlatform->getMaxExpression('id')
52
            )
53
            ->from(self::CONTENT_LANGUAGE_TABLE);
54
55
        $statement = $query->execute();
56
57
        $lastId = (int)$statement->fetchColumn();
58
59
        // Legacy only supports 8 * PHP_INT_SIZE - 2 languages:
60
        // One bit cannot be used because PHP uses signed integers and a second one is reserved for the
61
        // "always available flag".
62
        if ($lastId == (2 ** (8 * PHP_INT_SIZE - 2))) {
63
            throw new RuntimeException('Maximum number of languages reached.');
64
        }
65
        // Next power of 2 for bit masks
66
        $nextId = ($lastId !== 0 ? $lastId << 1 : 2);
67
68
        $query = $this->connection->createQueryBuilder();
69
        $query
70
            ->insert(self::CONTENT_LANGUAGE_TABLE)
71
            ->values(
72
                [
73
                    'id' => ':id',
74
                    'locale' => ':language_code',
75
                    'name' => ':name',
76
                    'disabled' => ':disabled',
77
                ]
78
            )
79
            ->setParameter('id', $nextId, ParameterType::INTEGER);
80
81
        $this->setLanguageQueryParameters($query, $language);
82
83
        $query->execute();
84
85
        return $nextId;
86
    }
87
88
    /**
89
     * Set columns for $query based on $language.
90
     */
91
    private function setLanguageQueryParameters(QueryBuilder $query, Language $language): void
92
    {
93
        $query
94
            ->setParameter('language_code', $language->languageCode, ParameterType::STRING)
95
            ->setParameter('name', $language->name, ParameterType::STRING)
96
            ->setParameter('disabled', (int)!$language->isEnabled, ParameterType::INTEGER);
97
    }
98
99
    public function updateLanguage(Language $language): void
100
    {
101
        $query = $this->connection->createQueryBuilder();
102
        $query
103
            ->update(self::CONTENT_LANGUAGE_TABLE)
104
            ->set('locale', ':language_code')
105
            ->set('name', ':name')
106
            ->set('disabled', ':disabled');
107
108
        $this->setLanguageQueryParameters($query, $language);
109
110
        $query->where(
111
            $query->expr()->eq(
112
                'id',
113
                $query->createNamedParameter($language->id, ParameterType::INTEGER, ':id')
114
            )
115
        );
116
117
        $query->execute();
118
    }
119
120
    public function loadLanguageListData(array $ids): iterable
121
    {
122
        $query = $this->createFindQuery();
123
        $query
124
            ->where('id IN (:ids)')
125
            ->setParameter('ids', $ids, Connection::PARAM_INT_ARRAY);
126
127
        return $query->execute()->fetchAll();
128
    }
129
130
    public function loadLanguageListDataByLanguageCode(array $languageCodes): iterable
131
    {
132
        $query = $this->createFindQuery();
133
        $query
134
            ->where('locale IN (:locale)')
135
            ->setParameter('locale', $languageCodes, Connection::PARAM_STR_ARRAY);
136
137
        return $query->execute()->fetchAll();
138
    }
139
140
    /**
141
     * Build a Language find (fetch) query.
142
     */
143
    private function createFindQuery(): QueryBuilder
144
    {
145
        $query = $this->connection->createQueryBuilder();
146
        $query
147
            ->select('id', 'locale', 'name', 'disabled')
148
            ->from(self::CONTENT_LANGUAGE_TABLE);
149
150
        return $query;
151
    }
152
153
    public function loadAllLanguagesData(): array
154
    {
155
        return $this->createFindQuery()->execute()->fetchAll();
156
    }
157
158
    public function deleteLanguage(int $id): void
159
    {
160
        $query = $this->connection->createQueryBuilder();
161
        $query
162
            ->delete(self::CONTENT_LANGUAGE_TABLE)
163
            ->where(
164
                $query->expr()->eq(
165
                    'id',
166
                    $query->createPositionalParameter($id, ParameterType::INTEGER)
167
                )
168
            );
169
170
        $query->execute();
171
    }
172
173
    public function canDeleteLanguage(int $id): bool
174
    {
175
        // note: at some point this should be delegated to specific gateways
176
        foreach (self::MULTILINGUAL_TABLES_COLUMNS as $tableName => $columns) {
177
            $languageMaskColumn = $columns[0];
178
            $languageIdColumn = $columns[1] ?? null;
179
            if (
180
                $this->countTableData($id, $tableName, $languageMaskColumn, $languageIdColumn) > 0
181
            ) {
182
                return false;
183
            }
184
        }
185
186
        return true;
187
    }
188
189
    /**
190
     * Count table data rows related to the given language.
191
     *
192
     * @param string|null $languageIdColumn optional column name containing explicit language id
193
     */
194
    private function countTableData(
195
        int $languageId,
196
        string $tableName,
197
        string $languageMaskColumn,
198
        ?string $languageIdColumn = null
199
    ): int {
200
        $query = $this->connection->createQueryBuilder();
201
        $query
202
            // avoiding using "*" as count argument, but don't specify column name because it varies
203
            ->select($this->dbPlatform->getCountExpression(1))
204
            ->from($tableName)
205
            ->where(
206
                $query->expr()->gt(
207
                    $this->dbPlatform->getBitAndComparisonExpression(
208
                        $languageMaskColumn,
209
                        $query->createPositionalParameter($languageId, ParameterType::INTEGER)
210
                    ),
211
                    0
212
                )
213
            );
214
        if (null !== $languageIdColumn) {
215
            $query
216
                ->orWhere(
217
                    $query->expr()->eq(
218
                        $languageIdColumn,
219
                        $query->createPositionalParameter($languageId, ParameterType::INTEGER)
220
                    )
221
                );
222
        }
223
224
        return (int)$query->execute()->fetchColumn();
225
    }
226
}
227