Move the check for JSON to Doctrine/Version class

This commit is contained in:
Bob den Otter
2020-04-04 12:22:11 +02:00
parent 1135c28881
commit 41dca3dcba
6 changed files with 62 additions and 66 deletions
+4 -5
View File
@@ -55,14 +55,13 @@ HELP
$io->text([$message, '']);
$platform = $this->doctrineVersion->getPlatform();
$connection = ! empty($platform['connection_status']) ? sprintf('- <comment>%s</comment>', $platform['connection_status']) : '';
$tableExists = $this->doctrineVersion->tableContentExists() ? '' : sprintf('- <error>Tables not initialised</error>');
$connection = ! empty($platform['connection_status']) ? sprintf(' - <comment>%s</comment>', $platform['connection_status']) : '';
$tableExists = $this->doctrineVersion->tableContentExists() ? '' : sprintf(' - <error>Tables not initialised</error>');
$withJson = $this->doctrineVersion->hasJson() ? 'with JSON' : 'without JSON';
$io->listing([
sprintf('Install type: <info>%s</info>', Version::installType()),
sprintf('Database: <info>%s %s</info> %s %s', $platform['driver_name'], $platform['server_version'], $connection, $tableExists),
sprintf('Database: <info>%s %s</info>%s%s <info>(%s)</info>', $platform['driver_name'], $platform['server_version'], $connection, $tableExists, $withJson),
sprintf('PHP version: <info>%s</info>', PHP_VERSION),
sprintf('Operating System: <info>%s</info> - <comment>%s</comment>', php_uname('s'), php_uname('r')),
]);
+7 -56
View File
@@ -5,60 +5,26 @@ declare(strict_types=1);
namespace Bolt\Doctrine;
use Bolt\Common\Json;
use Doctrine\DBAL\Driver\PDOConnection;
use Doctrine\DBAL\Platforms\MariaDb1027Platform;
use Doctrine\DBAL\Platforms\MySQL57Platform;
use Doctrine\DBAL\Platforms\MySQL80Platform;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\ORM\QueryBuilder;
use Doctrine\DBAL\Connection;
class JsonHelper
{
/**
* We're g̶u̶e̶s̶s̶i̶n̶g̶ doing empirical research on which versions of SQLite
* support JSON. So far, tests indicate:
* - 3.20.1 - Not OK (Travis PHP 7.2)
* - 3.27.2 - OK (Bob's Raspberry Pi, running PHP 7.3.11 on Raspbian)
* - 3.28.0 - OK (Travis PHP 7.3)
* - 3.29.0 - OK (MacOS Mojave)
* - 3.30.1 - OK (MacOS Catalina)
*/
public const SQLITE_WITH_JSON = '3.27.2';
/**
* Because Mysql 5.6 and Sqlite handle values in JSON differently, we
* use this method to check if we can use JSON functions directly.
*/
public static function useJsonFunction(QueryBuilder $qb): bool
{
$platform = $qb->getEntityManager()->getConnection()->getDatabasePlatform();
if ($platform instanceof SqlitePlatform) {
return self::checkSqliteVersion($qb);
}
// MySQL80Platform is implicitly included with MySQL57Platform
if ($platform instanceof MySQL57Platform || $platform instanceof MariaDb1027Platform) {
return true;
}
return false;
}
/**
* Prepare a given $where and $slug to be used in a query, depending on
* whether or not the current platform supports JSON functions
*
* For example, wrapJsonFunction('foo', 'bar') gives:
*
* Sqlite, Mysql 5.6 -> [ 'foo', '["bar"]' ]
* Mysql 5.7 -> [ "JSON_EXTRACT(foo, '$[0]')", 'bar' ]
* Older SQLite, Mysql 5.6 -> [ 'foo', '["bar"]' ]
* Newer SQLite, Mysql 5.7 -> [ "JSON_EXTRACT(foo, '$[0]')", 'bar' ]
*
* @return string|array
*/
public static function wrapJsonFunction(?string $where = null, ?string $slug = null, QueryBuilder $qb)
public static function wrapJsonFunction(?string $where = null, ?string $slug = null, Connection $connection)
{
if (self::useJsonFunction($qb)) {
$version = new Version($connection);
if ($version->hasJson()) {
$resultWhere = 'JSON_EXTRACT(' . $where . ", '$[0]')";
$resultSlug = $slug;
} else {
@@ -76,19 +42,4 @@ class JsonHelper
return [$resultWhere, $resultSlug];
}
private static function checkSqliteVersion(QueryBuilder $qb): bool
{
/** @var PDOConnection $wrapped */
$wrapped = $qb->getEntityManager()->getConnection()->getWrappedConnection();
// If the wrapper doesn't have `getAttribute`, we bail…
if (! method_exists($wrapped, 'getAttribute')) {
return false;
}
[$client_version] = explode(' - ', $wrapped->getAttribute(\PDO::ATTR_CLIENT_VERSION));
return version_compare($client_version, self::SQLITE_WITH_JSON) > 0;
}
}
+43
View File
@@ -7,9 +7,25 @@ namespace Bolt\Doctrine;
use Bolt\Common\Str;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver\PDOConnection;
use Doctrine\DBAL\Platforms\MariaDb1027Platform;
use Doctrine\DBAL\Platforms\MySQL57Platform;
use Doctrine\DBAL\Platforms\SqlitePlatform;
class Version
{
/**
* We're g̶u̶e̶s̶s̶i̶n̶g̶ doing empirical research on which versions of SQLite
* support JSON. So far, tests indicate:
* - 3.20.1 - Not OK (Travis PHP 7.2)
* - 3.27.2 - OK (Bob's Raspberry Pi, running PHP 7.3.11 on Raspbian)
* - 3.28.0 - OK (Travis PHP 7.3)
* - 3.28.0 - Not OK (Bob's PHP 7.2, installed with Brew)
* - 3.29.0 - OK (MacOS Mojave)
* - 3.30.1 - OK (MacOS Catalina)
*/
public const SQLITE_WITH_JSON = '3.27.0';
public const PHP_WITH_SQLITE = '7.3.0';
/** @var Connection */
private $connection;
@@ -53,4 +69,31 @@ class Version
return true;
}
public function hasJson(): bool
{
$platform = $this->connection->getDatabasePlatform();
if ($platform instanceof SqlitePlatform) {
return $this->checkSqliteVersion();
}
// MySQL80Platform is implicitly included with MySQL57Platform
if ($platform instanceof MySQL57Platform || $platform instanceof MariaDb1027Platform) {
return true;
}
return false;
}
private function checkSqliteVersion(): bool
{
/** @var PDOConnection $wrapped */
$wrapped = $this->connection->getWrappedConnection();
[$client_version] = explode(' - ', $wrapped->getAttribute(\PDO::ATTR_CLIENT_VERSION));
return (version_compare($client_version, self::SQLITE_WITH_JSON) > 0) &&
(version_compare(PHP_VERSION, self::PHP_WITH_SQLITE) > 0);
}
}
+4 -2
View File
@@ -127,8 +127,9 @@ class ContentRepository extends ServiceEntityRepository
public function findOneBySlug(string $slug, ?ContentType $contentType = null): ?Content
{
$qb = $this->getQueryBuilder();
$connection = $qb->getEntityManager()->getConnection();
[$where, $slug] = JsonHelper::wrapJsonFunction('translations.value', $slug, $qb);
[$where, $slug] = JsonHelper::wrapJsonFunction('translations.value', $slug, $connection);
$query = $qb
->innerJoin('content.fields', 'field')
@@ -155,8 +156,9 @@ class ContentRepository extends ServiceEntityRepository
public function findOneByFieldValue(string $fieldName, string $value, ?ContentType $contentType = null): ?Content
{
$qb = $this->getQueryBuilder();
$connection = $qb->getEntityManager()->getConnection();
[$where, $value] = JsonHelper::wrapJsonFunction('translation.value', $value, $qb);
[$where, $value] = JsonHelper::wrapJsonFunction('translation.value', $value, $connection);
$query = $qb
->innerJoin('content.fields', 'field')
+2 -1
View File
@@ -32,8 +32,9 @@ class FieldRepository extends ServiceEntityRepository
public function findOneBySlug(string $slug): ?Field
{
$qb = $this->getQueryBuilder();
$connection = $qb->getEntityManager()->getConnection();
[$where, $slug] = JsonHelper::wrapJsonFunction('translations.value', $slug, $qb);
[$where, $slug] = JsonHelper::wrapJsonFunction('translations.value', $slug, $connection);
return $qb
->innerJoin('field.translations', 'translations')
+2 -2
View File
@@ -352,7 +352,7 @@ class SelectQuery implements QueryInterface
$originalLeftExpression = 'content.' . $key;
$newLeftExpression = JsonHelper::wrapJsonFunction($translationsAlias . '.value', null, $this->qb);
$newLeftExpression = JsonHelper::wrapJsonFunction($translationsAlias . '.value', null, $em->getConnection());
$where = $filter->getExpression();
$where = str_replace($originalLeftExpression, $newLeftExpression, $where);
@@ -377,7 +377,7 @@ class SelectQuery implements QueryInterface
->andWhere($this->qb->expr()->in('content.id', $innerQuery->getDQL()));
foreach ($filter->getParameters() as $key => $value) {
$value = JsonHelper::wrapJsonFunction(null, $value, $this->qb);
$value = JsonHelper::wrapJsonFunction(null, $value, $em->getConnection());
$this->qb->setParameter($key, $value);
}
}