(WIP) Refactor DBAL to ORM

Very rough early variant with some assumptions
This commit is contained in:
Xiao Hu Tai
2018-11-01 09:32:32 +01:00
parent 94ae4e0604
commit e76b25994c
16 changed files with 150 additions and 142 deletions
+1 -1
View File
@@ -138,7 +138,7 @@ entries:
listing_template: listing.twig
listing_records: 10
default_status: published
sort: -datepublish
sort: -publishedAt
recordsperpage: 20
icon_many: "fa:file-alt"
icon_one: "fa:file-alt"
+23 -2
View File
@@ -41,6 +41,27 @@ services:
arguments: ["@knp_menu.factory"]
tags:
- { name: knp_menu.menu_builder, method: createSidebarMenu, alias: sidebar } # The alias is what is used to retrieve the menu
# config/services.yaml
Twig\Extension\StringLoaderExtension:
Twig\Extension\StringLoaderExtension:
# Needed for SetContent from bolt/bolt
Bolt\Storage\Query\Query:
calls:
-
method: addScope
arguments: [ 'frontend', '@Bolt\Storage\Query\FrontendQueryScope' ]
Bolt\Storage\Query\ContentQueryParser:
calls:
-
method: addService
arguments: [ 'select', '@Bolt\Storage\Query\SelectQuery' ]
-
method: addService
arguments: [ 'search', '@Bolt\Storage\Query\SearchQuery' ]
-
method: addService
arguments: [ 'search_weighter', '@Bolt\Storage\Query\SearchWeighter' ]
-
method: addService
arguments: [ 'search_config', '@Bolt\Storage\Query\SearchConfig' ]
Doctrine\ORM\Query\Expr:
+1 -1
View File
@@ -26,7 +26,7 @@ class ContentRepository extends ServiceEntityRepository
parent::__construct($registry, Content::class);
}
private function getQueryBuilder(): QueryBuilder
public function getQueryBuilder(): QueryBuilder
{
return $this->createQueryBuilder('content');
}
+20 -16
View File
@@ -4,8 +4,9 @@ namespace Bolt\Storage\Query;
use Bolt\Events\QueryEvent;
use Bolt\Events\QueryEvents;
use Bolt\Repository\ContentRepository;
use Bolt\Storage\Entity\Content;
use Bolt\Storage\EntityManager;
//use Bolt\Storage\EntityManager;
use Bolt\Storage\Query\Directive\GetQueryDirective;
use Bolt\Storage\Query\Directive\HydrateDirective;
use Bolt\Storage\Query\Directive\LimitDirective;
@@ -27,11 +28,12 @@ use Bolt\Storage\Query\Handler\SelectQueryHandler;
* object representation.
*
* @author Ross Riley <riley.ross@gmail.com>
* @author Xiao-Hu Tai <xiao@twokings.nl>
*/
class ContentQueryParser
{
/** @var EntityManager */
protected $em;
/** @var ContentRepository */
protected $repo;
/** @var string */
protected $query;
/** @var array */
@@ -58,12 +60,12 @@ class ContentQueryParser
/**
* Constructor.
*
* @param EntityManager $em
* @param QueryInterface $queryHandler
* @param ContentRepository $repo
* @param QueryInterface $queryHandler
*/
public function __construct(EntityManager $em, QueryInterface $queryHandler = null)
public function __construct(ContentRepository $repo, QueryInterface $queryHandler = null)
{
$this->em = $em;
$this->repo = $repo;
if ($queryHandler !== null) {
$this->addService('select', $queryHandler);
@@ -250,13 +252,13 @@ class ContentQueryParser
}
/**
* Gets the object EntityManager.
* Gets the content repository.
*
* @return EntityManager
* @return ContentRepository
*/
public function getEntityManager()
public function getContentRepository()
{
return $this->em;
return $this->repo;
}
/**
@@ -442,12 +444,14 @@ class ContentQueryParser
public function fetch()
{
$this->parse();
$parseEvent = new QueryEvent($this);
$this->getEntityManager()->getEventManager()->dispatch(QueryEvents::PARSE, $parseEvent);
// $parseEvent = new QueryEvent($this);
// $this->getEntityManager()->getEventManager()->dispatch(QueryEvents::PARSE, $parseEvent);
if (! empty($this->getOperation))
$result = call_user_func($this->handlers[$this->getOperation()], $this);
$executeEvent = new QueryEvent($this, $result);
$this->getEntityManager()->getEventManager()->dispatch(QueryEvents::EXECUTE, $executeEvent);
else
$result = call_user_func($this->handlers['select'], $this);;
// $executeEvent = new QueryEvent($this, $result);
// $this->getEntityManager()->getEventManager()->dispatch(QueryEvents::EXECUTE, $executeEvent);
return $result;
}
@@ -7,7 +7,7 @@ use Bolt\Storage\Query\QueryInterface;
/**
* Directive to alter query based on 'order' parameter.
*
* eg: 'pages', ['order'=>'-datepublish']
* eg: 'pages', ['order'=>'-publishedAt']
*/
class OrderDirective
{
@@ -22,7 +22,7 @@ class OrderDirective
}
// remove default order
$query->getQueryBuilder()->resetQueryPart('orderBy');
$query->getQueryBuilder()->resetDQLPart('orderBy');
$separatedOrders = $this->getOrderBys($order);
foreach ($separatedOrders as $order) {
@@ -36,7 +36,7 @@ class OrderDirective
} else {
$direction = null;
}
$query->getQueryBuilder()->addOrderBy($order, $direction);
$query->getQueryBuilder()->addOrderBy('content.' . $order, $direction);
}
}
+5 -5
View File
@@ -2,7 +2,7 @@
namespace Bolt\Storage\Query;
use Doctrine\DBAL\Query\Expression\CompositeExpression;
use Doctrine\ORM\Query\Expr\Composite;
/**
* This class represents a single filter that converts to an expression along
@@ -14,7 +14,7 @@ use Doctrine\DBAL\Query\Expression\CompositeExpression;
class Filter
{
protected $key;
/** @var CompositeExpression */
/** @var Composite */
protected $expression;
/** @var array */
protected $parameters = [];
@@ -53,9 +53,9 @@ class Filter
/**
* Allows replacing the expression object with a modified one.
*
* @param CompositeExpression $expression
* @param Composite $expression
*/
public function setExpression(CompositeExpression $expression)
public function setExpression(Composite $expression)
{
$this->expression = $expression;
}
@@ -65,7 +65,7 @@ class Filter
* only needed for on the fly modification, to get the compiled
* expression use getExpression().
*
* @return CompositeExpression
* @return Composite
*/
public function getExpressionObject()
{
+4 -4
View File
@@ -2,7 +2,7 @@
namespace Bolt\Storage\Query;
use Bolt\Config;
use Bolt\Configuration\Config;
use Bolt\Storage\Query\Directive\OrderDirective;
/**
@@ -52,7 +52,7 @@ class FrontendQueryScope implements QueryScopeInterface
{
$contentTypes = $this->config->get('contenttypes');
foreach ($contentTypes as $type => $values) {
$sort = $values['sort'] ?: '-datepublish';
$sort = $values['sort'] ?: '-publishedAt';
$this->orderBys[$type] = $sort;
if (isset($values['singular_slug'])) {
$this->orderBys[$values['singular_slug']] = $sort;
@@ -68,7 +68,7 @@ class FrontendQueryScope implements QueryScopeInterface
$ct = $query->getContentType();
// Setup default ordering of queries on a per-contenttype basis
if (empty($query->getQueryBuilder()->getQueryPart('orderBy')) && isset($this->orderBys[$ct])) {
if (empty($query->getQueryBuilder()->getParameter('orderBy')) && isset($this->orderBys[$ct])) {
$handler = new OrderDirective();
$handler($query, $this->orderBys[$ct]);
}
@@ -76,7 +76,7 @@ class FrontendQueryScope implements QueryScopeInterface
// Setup status to only published unless otherwise specified
$status = $query->getParameter('status');
if (!$status) {
$query->setParameter('status', 'published');
$query->setParameter('status', 'published'); // no work??
}
}
}
@@ -27,9 +27,19 @@ class SelectQueryHandler
foreach ($contentQuery->getContentTypes() as $contentType) {
$contentType = str_replace('-', '_', $contentType);
$repo = $contentQuery->getEntityManager()->getRepository($contentType);
$query->setQueryBuilder($repo->createQueryBuilder('_' . $contentType));
$query->setContentType($contentType);
$repo = $contentQuery->getContentRepository();
$query->setQueryBuilder($repo
->getQueryBuilder()
// ->where('content.contentType = :ct')
// ->setParameter('ct', $contentType)
);
// $query->setContentType($contentType);
$query->setContentType('content');
// $repo = $contentQuery->getEntityManager()->getRepository($contentType);
// $query->setQueryBuilder($repo->createQueryBuilder('_' . $contentType));
// $query->setContentType($contentType);
/** Run the parameters through the whitelister. If we get a false back from this method it's because there
* is no need to continue with the query.
@@ -38,13 +48,22 @@ class SelectQueryHandler
if (!$params && count($contentQuery->getParameters())) {
continue;
}
//$params['contentType'] = $contentType;
/** Continue and run the query add the results to the set */
$query->setParameters($params);
$contentQuery->runScopes($query);
$contentQuery->runDirectives($query);
// dd($query->build());
// $query is of Bolt\Storage\Query\SelectQuery
// dd($query->getQueryBuilder()->getQuery());
$test = $query->build()
->andWhere('content.contentType = :ct')
->setParameter('ct', $contentType)
->getQuery()
;
$result = $test->getResult();
$result = $repo->queryWith($query);
if ($result) {
$set->setOriginalQuery($contentType, $query->getQueryBuilder());
$set->add($result, $contentType);
@@ -73,10 +92,27 @@ class SelectQueryHandler
*
* @return bool|array $cleanParams
*/
public function whitelistParameters(array $queryParams, Repository $repo)
public function whitelistParameters(array $queryParams, $repo)
{
$metadata = $repo->getClassMetadata();
$allowedParams = array_keys($metadata->getFieldMappings());
// $metadata = $repo->getClassMetadata();
// $allowedParams = array_keys($metadata->getFieldMappings());
$allowedParams = [
0 => "id",
1 => "slug",
2 => "datecreated",
3 => "datechanged",
4 => "datepublish",
5 => "datedepublish",
6 => "ownerid",
7 => "status",
8 => "templatefields",
9 => "title",
10 => "image",
11 => "teaser",
12 => "content",
13 => "contentlink",
14 => "incomingrelation",
];
$cleanParams = [];
foreach ($queryParams as $fieldSelect => $valueSelect) {
$stack = [];
+1
View File
@@ -24,6 +24,7 @@ class Query
{
$this->parser = $parser;
$this->recordsView = $recordsView;
$this->scopes = [];
}
/**
+6 -20
View File
@@ -2,8 +2,7 @@
namespace Bolt\Storage\Query;
use Bolt\Exception\QueryParseException;
use Doctrine\DBAL\Query\Expression\ExpressionBuilder;
use Doctrine\ORM\Query\Expr;
/**
* Handler class to convert the DSL for content query parameters
@@ -32,7 +31,7 @@ class QueryParameterParser
*
* @param ExpressionBuilder $expr
*/
public function __construct(ExpressionBuilder $expr = null)
public function __construct(Expr $expr)
{
$this->expr = $expr;
$this->setupDefaults();
@@ -77,15 +76,12 @@ class QueryParameterParser
* @param string $key
* @param mixed $value
*
* @throws QueryParseException
* @throws QueryParseException
*
* @return Filter|null
*/
public function getFilter($key, $value = null)
{
if (!$this->expr instanceof ExpressionBuilder) {
throw new QueryParseException('Cannot call method without an Expression Builder parameter set', 1);
if (!$this->expr instanceof Expr) {
throw new \Exception('Cannot call method without an Expression Builder parameter set', 1);
}
/** @var callable $callback */
@@ -106,8 +102,6 @@ class QueryParameterParser
* @param string $value
* @param ExpressionBuilder $expr
*
* @throws QueryParseException
*
* @return null
*/
public function incorrectQueryHandler($key, $value, $expr)
@@ -116,7 +110,7 @@ class QueryParameterParser
return null;
}
if (strpos($value, '&&') && strpos($value, '||')) {
throw new QueryParseException('Mixed && and || operators are not supported', 1);
throw new \Exception('Mixed && and || operators are not supported', 1);
}
}
@@ -129,8 +123,6 @@ class QueryParameterParser
* @param string $value
* @param ExpressionBuilder $expr
*
* @throws QueryParseException
*
* @return Filter|null
*/
public function multipleKeyAndValueHandler($key, $value, $expr)
@@ -185,8 +177,6 @@ class QueryParameterParser
* @param string $value
* @param ExpressionBuilder $expr
*
* @throws QueryParseException
*
* @return Filter|null
*/
public function multipleValueHandler($key, $value, $expr)
@@ -237,8 +227,6 @@ class QueryParameterParser
* @param string|array $value
* @param ExpressionBuilder $expr
*
* @throws QueryParseException
*
* @return Filter
*/
public function defaultFilterHandler($key, $value, $expr)
@@ -287,8 +275,6 @@ class QueryParameterParser
*
* @param string $value Value to process
*
* @throws QueryParseException
*
* @return array Parsed values
*/
public function parseValue($value)
@@ -309,7 +295,7 @@ class QueryParameterParser
}
}
throw new QueryParseException(sprintf('No matching value found for "%s"', $value));
throw new \Exception(sprintf('No matching value found for "%s"', $value));
}
/**
+1 -3
View File
@@ -2,7 +2,7 @@
namespace Bolt\Storage\Query;
use Bolt\Config;
use Bolt\Configuration\Config;
/**
* This class takes an overall config array as input and parses into values
@@ -17,10 +17,8 @@ class SearchConfig
protected $config = [];
/** @var array */
protected $searchableTypes = [];
/** @var array */
protected $invisibleTypes = [];
/** @var array */
protected $joins = [];
+1 -1
View File
@@ -32,7 +32,7 @@ class SearchQuery extends SelectQuery
* @param QueryParameterParser $parser
* @param SearchConfig $config
*/
public function __construct(QueryBuilder $qb, QueryParameterParser $parser, SearchConfig $config)
public function __construct(QueryBuilder $qb = null, QueryParameterParser $parser, SearchConfig $config)
{
parent::__construct($qb, $parser);
$this->config = $config;
+9 -7
View File
@@ -2,9 +2,9 @@
namespace Bolt\Storage\Query;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Query\Expression\CompositeExpression;
use Doctrine\DBAL\Query\QueryBuilder;
use Doctrine\ORM\Connection;
use Doctrine\ORM\Query\Expression\CompositeExpression;
use Doctrine\ORM\Query\QueryBuilder;
/**
* This query class coordinates a select query build from Bolt's
@@ -29,6 +29,7 @@ class SelectQuery implements ContentQueryInterface
protected $params;
/** @var Filter[] */
protected $filters = [];
/** @var array */
protected $replacements = [];
/** @var bool */
protected $singleFetchMode = false;
@@ -39,7 +40,7 @@ class SelectQuery implements ContentQueryInterface
* @param QueryBuilder $qb
* @param QueryParameterParser $parser
*/
public function __construct(QueryBuilder $qb, QueryParameterParser $parser)
public function __construct(QueryBuilder $qb = null, QueryParameterParser $parser)
{
$this->qb = $qb;
$this->parser = $parser;
@@ -115,7 +116,6 @@ class SelectQuery implements ContentQueryInterface
if (!count($this->filters)) {
return null;
}
$expr = $this->qb->expr()->andX();
foreach ($this->filters as $filter) {
$expr = $expr->add($filter->getExpression());
@@ -223,7 +223,7 @@ class SelectQuery implements ContentQueryInterface
*
* @param QueryBuilder $qb
*/
public function setQueryBuilder(QueryBuilder $qb)
public function setQueryBuilder($qb)
{
$this->qb = $qb;
}
@@ -269,7 +269,9 @@ class SelectQuery implements ContentQueryInterface
{
$this->filters = [];
foreach ($this->params as $key => $value) {
$this->parser->setAlias('_' . $this->contentType);
// $this->parser // Bolt\Storage\Query\QueryParameterParser
// $this->parser->setAlias('_' . $this->contentType);
$this->parser->setAlias('content');
$filter = $this->parser->getFilter($key, $value);
if ($filter) {
$this->addFilter($filter);
+1 -1
View File
@@ -48,7 +48,7 @@ class SetcontentNode extends Node
->write("\$context['")
->raw($this->getAttribute('name'))
->raw("'] = ")
->raw("\$this->env->getRuntime('" . SetcontentRuntime::class . "')->getContent(")
->raw("\$this->env->getRuntime('" . SetcontentRuntime::class . "')->getQueryEngine()->getContentForTwig(")
->subcompile($this->getAttribute('contenttype'))
->raw(', ')
->subcompile($arguments)
+13 -54
View File
@@ -4,71 +4,30 @@ declare(strict_types=1);
namespace Bolt\Twig\Runtime;
use Bolt\Configuration\Config;
use Bolt\Repository\ContentRepository;
use Bolt\Storage\Query\Query;
class SetcontentRuntime
{
/** @var Config $config */
private $config;
/** @var ContentRepository $repo */
private $repo;
/** @var Query $queryEngine */
private $queryEngine;
/** @var MetadataDriver $metadataDriver */
private $metadataDriver;
/**
* @param Config $config
* @param ContentRepository $repo
* @param Query $queryEngine
* @param MetadataDriver $metadataDriver
*/
public function __construct(Config $config, ContentRepository $repo)
public function __construct(Query $queryEngine, MetadataDriver $metadataDriver = null)
{
$this->config = $config;
$this->repo = $repo;
$this->queryEngine = $queryEngine;
$this->metadataDriver = $metadataDriver; // still needed?
}
/**
* @return ContentRepository
* @return Query
*/
public function getContentRepository()
public function getQueryEngine()
{
return $this->repo;
}
/**
* @param string $textQuery
* @param array $parameters
*/
public function getContent($textQuery, array $parameters = [])
{
// fix BC break
if (func_num_args() === 3) {
$whereparameters = func_get_arg(2);
if (is_array($whereparameters) && !empty($whereparameters)) {
$parameters = array_merge($parameters, $whereparameters);
}
}
$qb = $this->repo->createQueryBuilder('content')
->addSelect('a')
->innerJoin('content.author', 'a')
->orderBy('content.modifiedAt', 'DESC')
;
$contentType = explode('/', $textQuery)[0];
if ($contentType) {
$qb
->where('content.contentType = :ct')
->setParameter('ct', $contentType)
;
}
return $qb
->getQuery()
->getResult()
;
// return $this->createPaginator($qb->getQuery(), $page);
// return $this->recordsView->createView(
// $this->getContentByScope('frontend', $textQuery, $parameters)
// );
return $this->queryEngine;
}
}
+17 -16
View File
@@ -2,11 +2,11 @@
namespace Bolt\Twig;
use Bolt\Storage\Entity\Content;
use Bolt\Storage\Field\Collection\FieldCollection;
// use Bolt\Storage\Entity\Content;
// use Bolt\Storage\Field\Collection\FieldCollection;
use Bolt\Storage\Mapping\MetadataDriver;
use Bolt\Storage\Query\QueryResultset;
use ParsedownExtra;
// use ParsedownExtra;
use Twig\Markup;
/**
@@ -14,6 +14,7 @@ use Twig\Markup;
* so appropriate transformation can occur.
*
* @author Ross Riley <riley.ross@gmail.com>
* @author Xiao-Hu Tai <xiao@twokings.nl>
*/
class TwigRecordsView
{
@@ -27,7 +28,7 @@ class TwigRecordsView
*
* @param MetadataDriver $metadata
*/
public function __construct(MetadataDriver $metadata)
public function __construct(MetadataDriver $metadata = null)
{
$this->metadata = $metadata;
$this->setupDefaults();
@@ -55,20 +56,20 @@ class TwigRecordsView
return new Markup($value, 'UTF-8');
});
$this->addTransformer('repeater', function ($value) {
/** @var FieldCollection $collection */
foreach ($value as $collection) {
foreach ($collection as $field) {
$field->setValue($this->transform($field->getValue(), $field->getFieldType()));
}
}
// $this->addTransformer('repeater', function ($value) {
// /** @var FieldCollection $collection */
// foreach ($value as $collection) {
// foreach ($collection as $field) {
// $field->setValue($this->transform($field->getValue(), $field->getFieldType()));
// }
// }
return $value;
});
// return $value;
// });
$this->addTransformer('block', function ($value) {
return $this->transform($value, 'repeater');
});
// $this->addTransformer('block', function ($value) {
// return $this->transform($value, 'repeater');
// });
}
/**