<?php
/*
* This file is part of Sulu.
*
* (c) Sulu GmbH
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Sulu\Component\Content\Query;
use Sulu\Component\Content\Compat\PropertyInterface;
use Sulu\Component\Content\Compat\Structure;
use Sulu\Component\Content\Compat\StructureInterface;
use Sulu\Component\Content\Compat\StructureManagerInterface;
use Sulu\Component\Content\Extension\ExtensionManagerInterface;
use Sulu\Component\Content\Mapper\Translation\MultipleTranslatedProperties;
use Sulu\Component\Content\Mapper\Translation\TranslatedProperty;
/**
* Basic class for content query builder.
*/
abstract class ContentQueryBuilder implements ContentQueryBuilderInterface
{
/**
* @var StructureManagerInterface
*/
protected $structureManager;
/**
* @var ExtensionManagerInterface
*/
protected $extensionManager;
/**
* @var string
*/
protected $languageNamespace;
/**
* @var MultipleTranslatedProperties
*/
private $translatedProperties;
/**
* @var string[]
*/
private $defaultProperties = [
'template',
'changed',
'changer',
'creator',
'created',
'nodeType',
'state',
'shadow-on',
];
/**
* @var string[]
*/
protected $properties = [];
/**
* Only published content.
*
* @var bool
*/
protected $published = true;
/**
* Load Excerpt data.
*
* @var bool
*/
protected $excerpt = true;
protected static $mixinTypes = ['sulu:page', 'sulu:home'];
public function __construct(
StructureManagerInterface $structureManager,
ExtensionManagerInterface $extensionManager,
$languageNamespace
) {
$this->structureManager = $structureManager;
$this->extensionManager = $extensionManager;
$this->languageNamespace = $languageNamespace;
$properties = \array_unique(\array_merge($this->defaultProperties, $this->properties));
$this->translatedProperties = new MultipleTranslatedProperties($properties, $this->languageNamespace);
}
/**
* Returns translated property name.
*/
protected function getPropertyName($property)
{
return $this->translatedProperties->getName($property);
}
/**
* Configures translated properties to given locale.
*
* @param string $locale
*/
protected function setLocale($locale)
{
$this->translatedProperties->setLanguage($locale);
}
public function build($webspaceKey, $locales)
{
$additionalFields = [];
$where = '';
$select = ['page.*'];
$order = [];
foreach ($locales as $locale) {
$this->setLocale($locale);
$additionalFields[$locale] = [];
if ($this->excerpt) {
$this->buildSelectorForExcerpt($locale, $additionalFields);
}
$customSelect = $this->buildSelect($webspaceKey, $locale, $additionalFields);
if ('' !== $customSelect) {
$select[] = $customSelect;
}
if ($this->published) {
$where .= \sprintf(
'%s ((page.[%s] = %s OR page.[%s] = %s)',
'' !== $where ? 'OR ' : '',
$this->getPropertyName('state'),
Structure::STATE_PUBLISHED,
$this->getPropertyName('shadow-on'),
'true'
);
}
$customWhere = $this->buildWhere($webspaceKey, $locale);
if (null !== $customWhere && '' !== $customWhere) {
$where = $where . ('' !== $where ? ' AND ' : '') . $customWhere;
}
if ($this->published) {
$where .= ')';
}
$customOrder = $this->buildOrder($webspaceKey, $locale);
if (!empty($customOrder)) {
$order[] = $customOrder;
} else {
$order = ['[jcr:path] ASC'];
}
}
$mixinTypeWhere = \implode(' OR ', \array_map(function($mixinType) {
return 'page.[jcr:mixinTypes] = "' . $mixinType . '"';
}, static::$mixinTypes));
$sql2 = \sprintf(
'SELECT %s
FROM [nt:unstructured] AS page
WHERE (%s)
AND (%s)
%s %s',
\implode(', ', $select),
$mixinTypeWhere,
$where,
\count($order) > 0 ? 'ORDER BY' : '',
\implode(', ', $order)
);
return [$sql2, $additionalFields];
}
public function getPublished()
{
return $this->published;
}
/**
* Returns custom select statement.
*/
abstract protected function buildWhere($webspaceKey, $locale);
/**
* Returns custom where statement.
*/
abstract protected function buildSelect($webspaceKey, $locale, &$additionalFields);
/**
* Returns custom order statement.
*/
protected function buildOrder($webspaceKey, $locale)
{
return '';
}
/**
* Returns select statement with all url and title properties.
*/
private function buildSelectForStructures($locale, $structures, &$names)
{
$result = '';
// add node name and url to selector
/** @var StructureInterface $structure */
foreach ($structures as $structure) {
$result .= $this->buildSelectForStructure($locale, $structure, $names);
}
return $result;
}
/**
* Returns select of a single structure with title and url selector.
*/
private function buildSelectForStructure($locale, StructureInterface $structure, &$names)
{
$nodeNameProperty = $structure->getProperty('title');
$result = '';
$name = $this->getTranslatedProperty($nodeNameProperty, $locale)->getName();
if (!\in_array($name, $names)) {
$names[] = $name;
$result .= ', ' . $this->buildSelector($name);
}
if ($structure->hasTag('sulu.rlp')) {
$urlProperty = $structure->getPropertyByTagName('sulu.rlp');
$name = $this->getTranslatedProperty($urlProperty, $locale)->getName();
if ('resource_locator' !== $urlProperty->getContentTypeName() && !\in_array($name, $names)) {
$names[] = $name;
$result .= ', ' . $this->buildSelector($name);
}
}
return $result;
}
/**
* Returns a select statement for excerpt data.
*/
private function buildSelectorForExcerpt($locale, &$additionalFields)
{
$excerptStructure = $this->structureManager->getStructure('excerpt');
$extension = $this->extensionManager->getExtension('', 'excerpt');
foreach ($excerptStructure->getProperties(true) as $property) {
$additionalFields[$locale][] = [
'extension' => $extension,
'target' => 'excerpt',
'property' => $property->getName(),
'name' => $property->getName(),
];
}
}
/**
* Returns single select statement.
*/
protected function buildSelector($name)
{
return \sprintf('page.[%s]', $name);
}
/**
* Returns a translated property.
*/
protected function getTranslatedProperty(PropertyInterface $property, $locale)
{
return new TranslatedProperty($property, $locale, $this->languageNamespace);
}
}