386 lines
12 KiB
PHP
386 lines
12 KiB
PHP
|
<?php
|
||
|
|
||
|
/**
|
||
|
* This program is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU General Public License
|
||
|
* as published by the Free Software Foundation; under version 2
|
||
|
* of the License (non-upgradable).
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
|
*
|
||
|
* Copyright (c) 2015 (original work) Open Assessment Technologies SA;
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
namespace oat\taoRevision\model\storage;
|
||
|
|
||
|
use common_ext_Namespace;
|
||
|
use common_ext_NamespaceManager;
|
||
|
use common_Object;
|
||
|
use common_persistence_SqlPersistence;
|
||
|
use core_kernel_classes_ContainerCollection as TriplesCollection;
|
||
|
use core_kernel_classes_Triple as Triple;
|
||
|
use Doctrine\DBAL\Driver\Statement;
|
||
|
use Doctrine\DBAL\Query\QueryBuilder;
|
||
|
use Doctrine\DBAL\Schema\Schema;
|
||
|
use oat\generis\model\kernel\persistence\smoothsql\search\driver\TaoSearchDriver;
|
||
|
use oat\generis\model\OntologyRdfs;
|
||
|
use oat\generis\persistence\PersistenceManager;
|
||
|
use oat\generis\persistence\sql\SchemaCollection;
|
||
|
use oat\generis\persistence\sql\SchemaProviderInterface;
|
||
|
use oat\oatbox\service\ConfigurableService;
|
||
|
use oat\taoRevision\model\Revision;
|
||
|
use oat\taoRevision\model\RevisionNotFoundException;
|
||
|
use oat\taoRevision\model\RevisionStorageInterface;
|
||
|
|
||
|
/**
|
||
|
* Storage class for the revision data
|
||
|
*
|
||
|
* @author Joel Bout <joel@taotesting.com>
|
||
|
*/
|
||
|
class RdsStorage extends ConfigurableService implements RevisionStorageInterface, SchemaProviderInterface
|
||
|
{
|
||
|
public const REVISION_TABLE_NAME = 'revision';
|
||
|
|
||
|
public const REVISION_RESOURCE = 'resource';
|
||
|
public const REVISION_VERSION = 'version';
|
||
|
public const REVISION_USER = 'user';
|
||
|
public const REVISION_CREATED = 'created';
|
||
|
public const REVISION_MESSAGE = 'message';
|
||
|
|
||
|
public const DATA_TABLE_NAME = 'revision_data';
|
||
|
|
||
|
public const DATA_RESOURCE = 'resource';
|
||
|
public const DATA_VERSION = 'version';
|
||
|
public const DATA_SUBJECT = 'subject';
|
||
|
public const DATA_PREDICATE = 'predicate';
|
||
|
public const DATA_OBJECT = 'object';
|
||
|
public const DATA_LANGUAGE = 'language';
|
||
|
|
||
|
/** @var common_persistence_SqlPersistence */
|
||
|
private $persistence;
|
||
|
|
||
|
/**
|
||
|
* @return common_persistence_SqlPersistence
|
||
|
*/
|
||
|
protected function getPersistence()
|
||
|
{
|
||
|
if ($this->persistence === null) {
|
||
|
$this->persistence = $this->getServiceLocator()
|
||
|
->get(PersistenceManager::SERVICE_ID)
|
||
|
->getPersistenceById($this->getPersistenceId());
|
||
|
}
|
||
|
|
||
|
return $this->persistence;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param Revision $revision
|
||
|
* @param Triple[] $data
|
||
|
*
|
||
|
* @return Revision
|
||
|
*/
|
||
|
public function addRevision(Revision $revision, array $data)
|
||
|
{
|
||
|
$this->getPersistence()->insert(
|
||
|
self::REVISION_TABLE_NAME,
|
||
|
[
|
||
|
self::REVISION_RESOURCE => $revision->getResourceId(),
|
||
|
self::REVISION_VERSION => $revision->getVersion(),
|
||
|
self::REVISION_USER => $revision->getAuthorId(),
|
||
|
self::REVISION_MESSAGE => $revision->getMessage(),
|
||
|
self::REVISION_CREATED => $revision->getDateCreated(),
|
||
|
]
|
||
|
);
|
||
|
|
||
|
if (!empty($data)) {
|
||
|
$this->saveData($revision, $data);
|
||
|
}
|
||
|
|
||
|
return $revision;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @param string $resourceId
|
||
|
* @param int $version
|
||
|
*
|
||
|
* @return Revision
|
||
|
* @throws RevisionNotFoundException
|
||
|
*/
|
||
|
public function getRevision(string $resourceId, int $version)
|
||
|
{
|
||
|
$queryBuilder = $this->getQueryBuilder()
|
||
|
->select('*')
|
||
|
->from(self::REVISION_TABLE_NAME)
|
||
|
->where(sprintf('%s = ?', self::REVISION_RESOURCE))
|
||
|
->andWhere(sprintf('%s = ?', self::REVISION_VERSION));
|
||
|
|
||
|
$variables = $this->getPersistence()
|
||
|
->query($queryBuilder->getSQL(), [$resourceId, $version])
|
||
|
->fetchAll();
|
||
|
|
||
|
if (count($variables) !== 1) {
|
||
|
throw new RevisionNotFoundException($resourceId, $version);
|
||
|
}
|
||
|
|
||
|
$variable = reset($variables);
|
||
|
|
||
|
return new Revision(
|
||
|
$variable[self::REVISION_RESOURCE],
|
||
|
$variable[self::REVISION_VERSION],
|
||
|
$variable[self::REVISION_CREATED],
|
||
|
$variable[self::REVISION_USER],
|
||
|
$variable[self::REVISION_MESSAGE]
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $resourceId
|
||
|
*
|
||
|
* @return Revision[]
|
||
|
*/
|
||
|
public function getAllRevisions(string $resourceId)
|
||
|
{
|
||
|
$queryBuilder = $this->getQueryBuilder()
|
||
|
->select('*')
|
||
|
->from(self::REVISION_TABLE_NAME)
|
||
|
->where(sprintf('%s = ?', self::REVISION_RESOURCE));
|
||
|
|
||
|
$variables = $this->getPersistence()
|
||
|
->query($queryBuilder->getSQL(), [$resourceId])
|
||
|
->fetchAll();
|
||
|
|
||
|
return $this->buildRevisionCollection($variables);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param array $variables
|
||
|
* @return Revision[]
|
||
|
*/
|
||
|
public function buildRevisionCollection(array $variables)
|
||
|
{
|
||
|
$revisions = [];
|
||
|
foreach ($variables as $variable) {
|
||
|
$revisions[] = new Revision(
|
||
|
$variable[self::REVISION_RESOURCE],
|
||
|
$variable[self::REVISION_VERSION],
|
||
|
$variable[self::REVISION_CREATED],
|
||
|
$variable[self::REVISION_USER],
|
||
|
$variable[self::REVISION_MESSAGE]
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return $revisions;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param Revision $revision
|
||
|
*
|
||
|
* @return TriplesCollection
|
||
|
*/
|
||
|
public function getData(Revision $revision)
|
||
|
{
|
||
|
$queryBuilder = $this->getQueryBuilder()
|
||
|
->select('*')
|
||
|
->from(self::DATA_TABLE_NAME)
|
||
|
->where(sprintf('%s = ?', self::DATA_RESOURCE))
|
||
|
->andWhere(sprintf('%s = ?', self::DATA_VERSION));
|
||
|
|
||
|
$result = $this->getPersistence()
|
||
|
->query($queryBuilder->getSQL(), [$revision->getResourceId(), $revision->getVersion()]);
|
||
|
|
||
|
$triples = new TriplesCollection(new common_Object());
|
||
|
while ($statement = $result->fetch()) {
|
||
|
$triples->add($this->prepareDataObject($statement, $this->getLocalModel()->getModelId()));
|
||
|
}
|
||
|
|
||
|
return $triples;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @param Revision $revision
|
||
|
* @param Triple[] $data
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
protected function saveData(Revision $revision, array $data)
|
||
|
{
|
||
|
$dataToSave = [];
|
||
|
|
||
|
foreach ($data as $triple) {
|
||
|
$dataToSave[] = [
|
||
|
self::DATA_RESOURCE => $revision->getResourceId(),
|
||
|
self::DATA_VERSION => $revision->getVersion(),
|
||
|
self::DATA_SUBJECT => $triple->subject,
|
||
|
self::DATA_PREDICATE => $triple->predicate,
|
||
|
self::DATA_OBJECT => $triple->object,
|
||
|
self::DATA_LANGUAGE => $triple->lg,
|
||
|
];
|
||
|
}
|
||
|
|
||
|
return $this->getPersistence()->insertMultiple(self::DATA_TABLE_NAME, $dataToSave);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @deprecated
|
||
|
* @see getResourcesDataByQuery
|
||
|
* @param string $query
|
||
|
* @param array $options
|
||
|
* @param string $predicate
|
||
|
* @return array
|
||
|
*/
|
||
|
public function getResourcesUriByQuery(string $query, array $options = [], string $predicate = OntologyRdfs::RDFS_LABEL)
|
||
|
{
|
||
|
$result = $this->getSelectedResourcesDataByQuery(
|
||
|
[self::DATA_RESOURCE],
|
||
|
$query,
|
||
|
$options,
|
||
|
$predicate
|
||
|
);
|
||
|
|
||
|
$resourcesUri = [];
|
||
|
while ($statement = $result->fetch()) {
|
||
|
$resourcesUri[] = $statement[self::DATA_RESOURCE];
|
||
|
}
|
||
|
return $resourcesUri;
|
||
|
}
|
||
|
|
||
|
public function getResourcesDataByQuery(string $query, array $options = [], string $predicate = OntologyRdfs::RDFS_LABEL): array
|
||
|
{
|
||
|
$result = $this->getSelectedResourcesDataByQuery(
|
||
|
[self::DATA_RESOURCE, self::DATA_OBJECT],
|
||
|
$query,
|
||
|
$options,
|
||
|
$predicate
|
||
|
);
|
||
|
|
||
|
$resourcesData= [];
|
||
|
/** @var Revision $statement */
|
||
|
while ($statement = $result->fetch()) {
|
||
|
$resourcesData[] = [
|
||
|
'id' => $statement[self::DATA_RESOURCE],
|
||
|
'label' => $statement[self::DATA_OBJECT],
|
||
|
];
|
||
|
}
|
||
|
return $resourcesData;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string[] $selectedFields
|
||
|
*/
|
||
|
private function getSelectedResourcesDataByQuery(
|
||
|
array $selectedFields,
|
||
|
string $query,
|
||
|
array $options,
|
||
|
string $predicate
|
||
|
): Statement
|
||
|
{
|
||
|
$queryBuilder = $this->getQueryBuilder();
|
||
|
foreach ($selectedFields as $selectedField) {
|
||
|
$queryBuilder->addSelect('rd.'.$selectedField);
|
||
|
}
|
||
|
$queryBuilder->from(self::DATA_TABLE_NAME, 'rd');
|
||
|
|
||
|
$queryBuilder->join('rd', 'statements', 'st',
|
||
|
'st.subject = rd.'.self::DATA_RESOURCE);
|
||
|
|
||
|
$fieldName = self::DATA_OBJECT;
|
||
|
$condition = "rd.$fieldName {$this->getLike()} '%$query%'";
|
||
|
$queryBuilder->where($condition);
|
||
|
$queryBuilder->andWhere(sprintf('rd.%s = \'%s\'', self::DATA_PREDICATE, $predicate));
|
||
|
|
||
|
if (isset($options['limit'])) {
|
||
|
$queryBuilder->setMaxResults((int)$options['limit']);
|
||
|
}
|
||
|
|
||
|
if (isset($options['offset'])) {
|
||
|
$queryBuilder->setFirstResult((int)$options['offset']);
|
||
|
}
|
||
|
|
||
|
$sort = isset($options['sort']) ? $options['sort'] : self::DATA_RESOURCE;
|
||
|
$order = isset($options['order']) ? strtoupper($options['order']) : ' ASC';
|
||
|
|
||
|
$queryBuilder->addOrderBy($sort, $order);
|
||
|
foreach ($selectedFields as $selectedField) {
|
||
|
$queryBuilder->addGroupBy('rd.'.$selectedField);
|
||
|
}
|
||
|
|
||
|
return $this->getPersistence()->query($queryBuilder->getSQL());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param array $statement
|
||
|
* @param string $modelId
|
||
|
*
|
||
|
* @return Triple
|
||
|
*/
|
||
|
private function prepareDataObject(array $statement, string $modelId)
|
||
|
{
|
||
|
$triple = new Triple();
|
||
|
$triple->modelid = $modelId;
|
||
|
$triple->subject = $statement[self::DATA_SUBJECT];
|
||
|
$triple->predicate = $statement[self::DATA_PREDICATE];
|
||
|
$triple->object = $statement[self::DATA_OBJECT];
|
||
|
$triple->lg = $statement[self::DATA_LANGUAGE];
|
||
|
|
||
|
return $triple;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return QueryBuilder
|
||
|
*/
|
||
|
protected function getQueryBuilder()
|
||
|
{
|
||
|
return $this->getPersistence()->getPlatForm()->getQueryBuilder();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return common_ext_Namespace
|
||
|
*/
|
||
|
protected function getLocalModel()
|
||
|
{
|
||
|
return common_ext_NamespaceManager::singleton()->getLocalNamespace();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return string
|
||
|
*/
|
||
|
protected function getLike()
|
||
|
{
|
||
|
return (new TaoSearchDriver())->like();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @inheritDoc
|
||
|
*/
|
||
|
public function getSchema(Schema $schema)
|
||
|
{
|
||
|
return $this->getServiceLocator()->get(RdsSqlSchema::class)->getSchema($schema);
|
||
|
}
|
||
|
|
||
|
public function getPersistenceId()
|
||
|
{
|
||
|
return $this->getOption(self::OPTION_PERSISTENCE);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritDoc}
|
||
|
* @see \oat\generis\persistence\sql\SchemaProviderInterface::provideSchema()
|
||
|
*/
|
||
|
public function provideSchema(SchemaCollection $schemaCollection)
|
||
|
{
|
||
|
$schema = $schemaCollection->getSchema($this->getPersistenceId());
|
||
|
$this->getSchema($schema);
|
||
|
}
|
||
|
|
||
|
}
|