diff --git a/common/class.Collection.php b/common/class.Collection.php index 70706afeb..917f97718 100644 --- a/common/class.Collection.php +++ b/common/class.Collection.php @@ -347,4 +347,4 @@ public function toArray() return (array) $returnValue; } -} \ No newline at end of file +} diff --git a/common/class.Utils.php b/common/class.Utils.php index e357fa9b9..5907cfe27 100644 --- a/common/class.Utils.php +++ b/common/class.Utils.php @@ -1,7 +1,4 @@ params = $params; } - - /** - * Generates a unique, not auto-increment based, primary key. - * - * @return string - */ - public function getUniquePrimaryKey() - { - return strrev(uniqid('', true)); - } } diff --git a/common/persistence/sql/SetupDb.php b/common/persistence/sql/SetupDb.php index 626cd6c9d..ac77b368e 100644 --- a/common/persistence/sql/SetupDb.php +++ b/common/persistence/sql/SetupDb.php @@ -20,16 +20,19 @@ * @license GPLv2 * @package tao */ + namespace oat\generis\persistence\sql; use Doctrine\DBAL\Schema\AbstractSchemaManager; use oat\oatbox\log\LoggerAwareTrait; +use oat\oatbox\service\ConfigurableService; use Psr\Log\LoggerAwareInterface; use Doctrine\DBAL\Schema\Schema; use oat\generis\model\kernel\persistence\smoothsql\install\SmoothRdsModel; use Doctrine\DBAL\Exception\ConnectionException; +use common_persistence_SqlPersistence as Persistence; -class SetupDb implements LoggerAwareInterface +class SetupDb extends ConfigurableService implements LoggerAwareInterface { use LoggerAwareTrait; @@ -38,7 +41,7 @@ class SetupDb implements LoggerAwareInterface * @param \common_persistence_SqlPersistence $p * @throws \common_exception_InconsistentData */ - public function setupDatabase(\common_persistence_SqlPersistence $p) + public function setupDatabase(Persistence $p) { $dbalDriver = $p->getDriver(); if (!$dbalDriver instanceof \common_persistence_sql_dbal_Driver) { @@ -50,10 +53,7 @@ public function setupDatabase(\common_persistence_SqlPersistence $p) $this->setupTables($p); } - /** - * @author "Lionel Lecaque, " - */ - private function verifyDatabase(\common_persistence_SqlPersistence $p, $dbName) + private function verifyDatabase(Persistence $p, $dbName) { $schemaManager = $p->getSchemaManager()->getDbalSchemaManager(); if (!$this->dbExists($schemaManager, $dbName)) { @@ -61,10 +61,7 @@ private function verifyDatabase(\common_persistence_SqlPersistence $p, $dbName) } } - /** - * @author "Lionel Lecaque, " - */ - private function setupTables(\common_persistence_SqlPersistence $p) + private function setupTables(Persistence $p) { $queries = $p->getPlatForm()->schemaToSql($this->getSchema($p)); foreach ($queries as $query){ @@ -76,10 +73,11 @@ private function setupTables(\common_persistence_SqlPersistence $p) * Generate databse schema * @return Schema */ - public function getSchema(\common_persistence_SqlPersistence $p) + public function getSchema(Persistence $p) { $schema = $p->getSchemaManager()->createSchema(); - SmoothRdsModel::addSmoothTables($schema); + $smoothRdsModel = $this->getServiceLocator()->get(SmoothRdsModel::class); + $smoothRdsModel->addSmoothTables($schema); $this->addKeyValueStoreTable($schema); return $schema; } @@ -99,8 +97,9 @@ protected function addKeyValueStoreTable(Schema $schema) } /** - * @author "Lionel Lecaque, " + * @param AbstractSchemaManager $schemaManager * @param string $dbName + * @return bool */ private function dbExists(AbstractSchemaManager $schemaManager, $dbName) { @@ -112,10 +111,7 @@ private function dbExists(AbstractSchemaManager $schemaManager, $dbName) } } - /** - * @author "Lionel Lecaque, " - */ - private function cleanDb(\common_persistence_SqlPersistence $p) + private function cleanDb(Persistence $p) { $schema = $p->getSchemaManager()->createSchema(); $queries = $p->getPlatForm()->toDropSql($schema); diff --git a/composer.json b/composer.json index fb675162a..3c6b8f336 100755 --- a/composer.json +++ b/composer.json @@ -63,11 +63,12 @@ "oat-sa/oatbox-extension-installer": "~1.1||dev-master", "php": "^7.1", "psr/log": "~1.0", - "oat-sa/lib-generis-search": "^2.0.1", + "oat-sa/lib-generis-search": "^2.1.0", "monolog/monolog": "^1.23.0", "fluent/logger": "^1.0.1", "symfony/lock": "^3.4", - "psr/container": "^1.0.0" + "psr/container": "^1.0.0", + "ramsey/uuid": "3.8.0" }, "require-dev": { "mikey179/vfsstream": "1.4.0", diff --git a/core/kernel/api/NewSqlModelFactory.php b/core/kernel/api/NewSqlModelFactory.php new file mode 100644 index 000000000..680525e71 --- /dev/null +++ b/core/kernel/api/NewSqlModelFactory.php @@ -0,0 +1,119 @@ +getPersistence()->insert('models', ['modelid' => $modelId, 'modeluri' => $namespace]) === 0) { + throw new RuntimeException('A problem occurred while creating a new model.'); + } + return $modelId; + } + + /** + * @inheritdoc + */ + public function prepareStatement($modelId, $subject, $predicate, $object, $lang, $author) + { + $date = $this->getPersistence()->getPlatForm()->getNowExpression(); + + return [ + 'id' => $this->getUniquePrimaryKey(), + 'modelid' => $modelId, + 'subject' => $subject, + 'predicate' => $predicate, + 'object' => $object, + 'l_language' => $lang, + 'author' => $author ?? '', + 'epoch' => $date, + ]; + } + + /** + * @inheritdoc + */ + public function buildModelSqlCondition(array $models) + { + $models = array_map( + function ($a) { + return "'" . $a . "'"; + }, + $models + ); + return parent::buildModelSqlCondition($models); + } + + /** + * @inheritdoc + */ + public function getPropertySortingField() + { + return 'epoch'; + } + + /** + * @inheritdoc + */ + public function createModelsTable(Schema $schema) + { + $table = $schema->createTable('models'); + $table->addColumn('modelid', 'string', ['length' => 36, 'notnull' => true]); + $table->addColumn('modeluri', 'string', ['length' => 255]); + $table->setPrimaryKey(['modelid']); + + return $table; + } + + /** + * @inheritdoc + */ + public function createStatementsTable(Schema $schema) + { + $table = $schema->createTable('statements'); + $table->addColumn('id', 'string', ['length' => 36, 'notnull' => true]); + $table->addColumn('modelid', 'string', ['length' => 23, 'notnull' => true]); + $table->addColumn('subject', 'string', ['length' => 255]); + $table->addColumn('predicate', 'string', ['length' => 255]); + $table->addColumn('object', 'text', []); + $table->addColumn('l_language', 'string', ['length' => 255]); + $table->addColumn('author', 'string', ['length' => 255]); + $table->addColumn('epoch', 'string', ['notnull' => true]); + + $table->setPrimaryKey(['id']); + $table->addIndex(['subject', 'predicate'], 'k_sp'); + $table->addIndex(['predicate', 'object'], 'k_po'); + + return $table; + } +} diff --git a/core/kernel/api/RdsModelFactory.php b/core/kernel/api/RdsModelFactory.php new file mode 100644 index 000000000..aea184593 --- /dev/null +++ b/core/kernel/api/RdsModelFactory.php @@ -0,0 +1,108 @@ +getPersistence(); + if ($persistence->insert('models', ['modeluri' => $namespace]) === 0) { + throw new RuntimeException('A problem occurred while creating a new model.'); + } + + // Retrieving the inserted modelid (auto-increment). + $result = $persistence->query('select modelid from models where modeluri = ?', [$namespace]); + $modelId = $result->fetch(); + return $modelId['modelid']; + } + + /** + * @inheritdoc + */ + public function prepareStatement($modelId, $subject, $predicate, $object, $lang, $author) + { + $date = $this->getPersistence()->getPlatForm()->getNowExpression(); + + return [ + 'modelid' => $modelId, + 'subject' => $subject, + 'predicate' => $predicate, + 'object' => $object, + 'l_language' => $lang, + 'author' => $author ?? '', + 'epoch' => $date, + ]; + } + + /** + * @inheritdoc + */ + public function getPropertySortingField() + { + return 'id'; + } + + /** + * @inheritdoc + */ + public function createModelsTable(Schema $schema) + { + // Models table. + $table = $schema->createTable('models'); + $table->addColumn('modelid', 'integer', ['notnull' => true, 'autoincrement' => true]); + $table->addColumn('modeluri', 'string', ['length' => 255, 'default' => null]); + $table->setPrimaryKey(['modelid']); + $table->addOption('engine', 'MyISAM'); + + return $table; + } + + /** + * @inheritdoc + */ + public function createStatementsTable(Schema $schema) + { + $table = $schema->createTable('statements'); + $table->addColumn('id', 'integer', ['notnull' => true, 'autoincrement' => true]); + $table->addColumn('modelid', 'integer', ['notnull' => true, 'default' => 0]); + $table->addColumn('subject', 'string', ['length' => 255, 'default' => null]); + $table->addColumn('predicate', 'string', ['length' => 255, 'default' => null]); + $table->addColumn('object', 'text', ['default' => null, 'notnull' => false]); + $table->addColumn('l_language', 'string', ['length' => 255, 'default' => null, 'notnull' => false]); + $table->addColumn('author', 'string', ['length' => 255, 'default' => null, 'notnull' => false]); + $table->addColumn('epoch', 'string', ['notnull' => null]); + + $table->setPrimaryKey(['id']); + $table->addIndex(['subject', 'predicate'], 'k_sp', [], ['lengths' => [164, 164]]); + $table->addIndex(['predicate', 'object'], 'k_po', [], ['lengths' => [164, 164]]); + $table->addOption('engine', 'MyISAM'); + + return $table; + } +} diff --git a/core/kernel/api/class.ModelFactory.php b/core/kernel/api/class.ModelFactory.php index c47d2dd0d..351b5d583 100644 --- a/core/kernel/api/class.ModelFactory.php +++ b/core/kernel/api/class.ModelFactory.php @@ -16,112 +16,193 @@ * * Copyright (c) 2013 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT); * - * @author "Lionel Lecaque, " + * @author "Lionel Lecaque, " * @license GPLv2 * @package generis - * */ -class core_kernel_api_ModelFactory{ - + +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Table; +use common_persistence_SqlPersistence as Persistence; +use oat\generis\persistence\PersistenceManager; +use oat\oatbox\log\LoggerAwareTrait; +use oat\oatbox\service\ConfigurableService; + +abstract class core_kernel_api_ModelFactory extends ConfigurableService +{ + use LoggerAwareTrait; + const SERVICE_ID = __CLASS__; + const OPTION_PERSISTENCE = 'persistence'; + const DEFAULT_AUTHOR = 'http://www.tao.lu/Ontologies/TAO.rdf#installator'; + /** * @author "Lionel Lecaque, " * @param string $namespace * @return string */ - private function getModelId($namespace){ - $dbWrapper = core_kernel_classes_DbWrapper::singleton(); - + public function getModelId($namespace) + { + if (substr($namespace, -1) !== '#') { + $namespace .= '#'; + } + $query = 'SELECT modelid FROM models WHERE (modeluri = ?)'; - $results = $dbWrapper->query($query, array($namespace)); - - return $results->fetchColumn(0); + $results = $this->getPersistence()->query($query, [$namespace]); + return $results->fetchColumn(0); } - + /** - * @author "Lionel Lecaque, " + * Creates a new model in the ontology. + * * @param string $namespace + * + * @return string|int new added model id + * @throws RuntimeException when a problem occurs when creating the new model in db. */ - private function addNewModel($namespace){ - $dbWrapper = core_kernel_classes_DbWrapper::singleton(); - $results = $dbWrapper->insert('models', array('modeluri' =>$namespace)); - - - } - + abstract public function addNewModel($namespace); + /** - * @author "Lionel Lecaque, " + * Creates a new model. * @param string $namespace * @param string $data xml content + * @return bool Were triples added? */ - public function createModel($namespace, $data){ - + public function createModel($namespace, $data) + { $modelId = $this->getModelId($namespace); - if($modelId === false){ - common_Logger::d('modelId not found, need to add namespace '. $namespace); - $this->addNewModel($namespace); - //TODO bad way, need to find better - $modelId = $this->getModelId($namespace); + if ($modelId === false) { + $this->logInfo('modelId not found, need to add namespace ' . $namespace); + $modelId = $this->addNewModel($namespace); } $modelDefinition = new EasyRdf_Graph($namespace); - if(is_file($data)){ + if (is_file($data)) { $modelDefinition->parseFile($data); - }else { + } else { $modelDefinition->parse($data); } - $graph = $modelDefinition->toRdfPhp(); - $resources = $modelDefinition->resources(); $format = EasyRdf_Format::getFormat('php'); - + $data = $modelDefinition->serialise($format); - - foreach ($data as $subjectUri => $propertiesValues){ - foreach ($propertiesValues as $prop=>$values){ - foreach ($values as $k => $v) { - $this->addStatement($modelId, $subjectUri, $prop, $v['value'], isset($v['lang']) ? $v['lang'] : null); + + $returnValue = false; + + foreach ($data as $subjectUri => $propertiesValues) { + foreach ($propertiesValues as $prop => $values) { + foreach ($values as $v) { + $returnValue |= $this->addStatement($modelId, $subjectUri, $prop, $v['value'], isset($v['lang']) ? $v['lang'] : null); } } } - - return true; + + return $returnValue; } - + /** - * Adds a statement to the ontology if it does not exist yet - * - * @author "Joel Bout, " - * @param int $modelId - * @param string $subject - * @param string $predicate - * @param string $object - * @param string $lang + * Adds a statement to the ontology if it does not exist yet. + * @param string|int $modelId + * @param string $subject + * @param string $predicate + * @param string $object + * @param string $lang + * @param string $author + * @return bool Was a row added? */ - private function addStatement($modelId, $subject, $predicate, $object, $lang = null) { - $result = core_kernel_classes_DbWrapper::singleton()->query( + public function addStatement($modelId, $subject, $predicate, $object, $lang = null, $author = self::DEFAULT_AUTHOR) + { + // Casting values and types. + $object = (string)$object; + if (is_null($lang)) { + $lang = ''; + } + + // TODO: refactor this to use a triple store abstraction. + $result = $this->getPersistence()->query( 'SELECT count(*) FROM statements WHERE modelid = ? AND subject = ? AND predicate = ? AND object = ? AND l_language = ?', - array($modelId, $subject, $predicate, $object, (is_null($lang)) ? '' : $lang) + [$modelId, $subject, $predicate, $object, $lang] ); - - if (intval($result->fetchColumn()) === 0) { - $dbWrapper = core_kernel_classes_DbWrapper::singleton(); - $date = $dbWrapper->getPlatForm()->getNowExpression(); - - $dbWrapper->insert( - 'statements', - array( - 'modelid' => $modelId, - 'subject' =>$subject, - 'predicate'=> $predicate, - 'object' => $object, - 'l_language' => is_null($lang) ? '' : $lang, - 'author' => 'http://www.tao.lu/Ontologies/TAO.rdf#installator', - 'epoch' => $date - ) - ); + + if ((int)$result->fetchColumn() > 0) { + return false; } + + return (bool)$this->getPersistence()->insert( + 'statements', + $this->prepareStatement($modelId, $subject, $predicate, $object, $lang, is_null($author) ? '' : $author) + ); } - -} \ No newline at end of file + /** + * Prepares a statement to be inserted in the ontology. + * @param string|int $modelId + * @param string $subject + * @param string $predicate + * @param string $object + * @param string $lang + * @param string $author + * @return array + */ + abstract public function prepareStatement($modelId, $subject, $predicate, $object, $lang, $author); + + /** + * Creates a query for iterator on selected statements. + * @param $modelIds + * @return string + */ + public function getIteratorQuery($modelIds) + { + $where = ''; + if ($modelIds !== null) { + $where = 'WHERE ' . $this->buildModelSqlCondition($modelIds); + } + return sprintf('SELECT * FROM statements %s ORDER BY %s', $where, $this->getPropertySortingField()); + } + + /** + * Prepares parameters for statement selection. + * @param array $models + * + * @return string + */ + public function buildModelSqlCondition(array $models) + { + return 'modelid IN (' . implode(',', $models) . ')'; + } + + /** + * Returns the property to sort the statements on. + * @return string + */ + abstract public function getPropertySortingField(); + + /** + * Creates table schema for models. + * + * @param Schema $schema + * + * @return Table + */ + abstract public function createModelsTable(Schema $schema); + + /** + * Creates table schema for statements. + * + * @param Schema $schema + * + * @return Table + */ + abstract public function createStatementsTable(Schema $schema); + + /** + * @return Persistence + */ + public function getPersistence() + { + $persistenceId = $this->hasOption(self::OPTION_PERSISTENCE) ? + $this->getOption(self::OPTION_PERSISTENCE) + : 'default'; + return $this->getServiceLocator()->get(PersistenceManager::SERVICE_ID)->getPersistenceById($persistenceId); + } +} diff --git a/core/kernel/impl/class.ApiModelOO.php b/core/kernel/impl/class.ApiModelOO.php index 21996e8e8..57a16f5f3 100644 --- a/core/kernel/impl/class.ApiModelOO.php +++ b/core/kernel/impl/class.ApiModelOO.php @@ -1,548 +1,479 @@ - - - * @package generis - - */ -class core_kernel_impl_ApiModelOO - extends core_kernel_impl_Api - implements core_kernel_api_ApiModel -{ - // --- ASSOCIATIONS --- - - - // --- ATTRIBUTES --- - - /** - * Short description of attribute instance - * - * @access private - * @var ApiModelOO - */ - private static $instance = null; - - // --- OPERATIONS --- - - - /** - * build xml rdf containing rdf:Description of all meta-data the conected - * may get - * - * @deprecated - * @access public - * @author firstname and lastname of author, - * @param array sourceNamespaces - * @return string - */ - public function exportXmlRdf($sourceNamespaces = array()) - { - $modelIds = array(); - foreach($sourceNamespaces as $namespace){ - if(!preg_match("/\#$/", $namespace)){ - $namespace .= "#"; - } - - $result = $dbWrapper->query('SELECT * FROM "models" WHERE "modeluri" = ?', array( - $namespace - )); - if ($row = $result->fetch(PDO::FETCH_ASSOC)){ - $modelIds[] = $row['modelid']; - $result->closeCursor(); - } - } - return $xml = core_kernel_api_ModelExporter::exportXmlRdf($modelIds); - - } - - /** - * import xml rdf files into the knowledge base - * - * @access public - * @author firstname and lastname of author, - * @param string targetNameSpace - * @param string fileLocation - * @return boolean - */ - public function importXmlRdf($targetNameSpace, $fileLocation) - { - $returnValue = (bool) false; - - if(!file_exists($fileLocation) || !is_readable($fileLocation)){ - throw new common_Exception("Unable to load ontology : $fileLocation"); - } - - if(!preg_match("/#$/", $targetNameSpace)){ - $targetNameSpace .= '#'; - } - $modFactory = new core_kernel_api_ModelFactory(); - $returnValue = $modFactory->createModel($targetNameSpace, file_get_contents($fileLocation)); - - - - return (bool) $returnValue; - } - - - - /** - * returns an xml rdf serialization for uriResource with all meta dat found - * inferenced from te knowlege base about this resource - * - * @access public - * @author firstname and lastname of author, - * @param string uriResource - * @return string - */ - public function getResourceDescriptionXML($uriResource) - { - $returnValue = (string) ''; - - - - - - $dbWrapper = core_kernel_classes_DbWrapper::singleton(); - $subject = $dbWrapper->quote($uriResource); - - $baseNs = array( - 'xmlns:rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', - 'xmlns:rdfs' => 'http://www.w3.org/2000/01/rdf-schema#' - ); - - $query = 'SELECT "models"."modelid", "models"."modeluri" FROM "models" INNER JOIN "statements" ON "statements"."modelid" = "models"."modelid" - WHERE "statements"."subject" = ' . $subject; - $query = $dbWrapper->limitStatement($query, 1); - $result = $dbWrapper->query($query); - if($row = $result->fetch()){ - $modelId = $row['modelid']; - $modelUri = $row['modeluri']; - if(!preg_match("/#$/", $modelUri)){ - $modelUri .= '#'; - } - - $result->closeCursor(); - } - $currentNs = array("xmlns:ns{$modelId}" => $modelUri); - $currentNs = array_merge($baseNs, $currentNs); - - - $allModels = array(); - $result = $dbWrapper->query('SELECT * FROM "models"'); - while($row = $result->fetch(PDO::FETCH_ASSOC)){ - $allModels[] = $row; - } - - $allNs = array(); - foreach($allModels as $i => $model){ - if(!preg_match("/#$/", $model['modeluri'])){ - $model['modeluri'] .= '#'; - } - $allNs["xmlns:ns{$model['modelid']}"] = $model['modeluri']; - } - $allNs = array_merge($baseNs, $allNs); - - try{ - - $dom = new DOMDocument(); - $dom->formatOutput = true; - $root = $dom->createElement('rdf:RDF'); - - foreach($currentNs as $namespaceId => $namespaceUri){ - $root->setAttribute($namespaceId, $namespaceUri); - } - $dom->appendChild($root); - - $description = $dom->createElement('rdf:Description'); - $description->setAttribute('rdf:about', $uriResource); - - $result = $dbWrapper->query('SELECT * FROM "statements" WHERE "subject" = ' . $subject); - while($row = $result->fetch()){ - - $predicate = trim($row['predicate']); - $object = trim($row['object']); - $lang = trim($row['l_language']); - - $nodeName = null; - - foreach($allNs as $namespaceId => $namespaceUri){ - if($namespaceId == 'xml:base') { - continue; - } - if(preg_match("/^".preg_quote($namespaceUri, '/')."/", $predicate)){ - if(!array_key_exists($namespaceId, $currentNs)){ - $currentNs[$namespaceId] = $namespaceUri; - $root->setAttribute($namespaceId, $namespaceUri); - } - $nodeName = str_replace('xmlns:', '', $namespaceId).':'.str_replace($namespaceUri, '', $predicate); - break; - } - } - - $resourceValue = false; - foreach($allNs as $namespaceId => $namespaceUri){ - if( preg_match("/^".preg_quote($namespaceUri, '/')."/", $object) || - preg_match("/^http\:\/\/(.*)\#[a-zA-Z1-9]*/", $object)){ - $resourceValue = true; - break; - } - } - if(!is_null($nodeName)){ - try{ - $node = $dom->createElement($nodeName); - if(!empty($lang)){ - $node->setAttribute('xml:lang', $lang); - } - - if($resourceValue){ - $node->setAttribute('rdf:resource', $object); - } - else{ - if(!empty($object) && !is_null($object)){ - - /** - * Replace the CDATA section inside XML fields by a replacement tag: - * to - * @todo check if this behavior is the right - */ - $object = str_replace('', $object); - $object = str_replace(']]>', '', $object); - - $node->appendChild($dom->createCDATASection($object)); - } - } - $description->appendChild($node); - } - catch(DOMException $de){ - //print $de; - } - } - } - $root->appendChild($description); - $returnValue = $dom->saveXml(); - } - catch(DomException $e){ - print $e; - } - - - - - return (string) $returnValue; - } - - /** - * returns metaclasses tat are not subclasses of other metaclasses - * - * @access public - * @author patrick.plichart@tudor.lu - * @return core_kernel_classes_ContainerCollection - */ - public function getMetaClasses() - { - $returnValue = null; - - - $returnValue = new core_kernel_classes_ContainerCollection(new core_kernel_classes_Container(__METHOD__),__METHOD__); - - $classClass = new core_kernel_classes_Class(OntologyRdfs::RDFS_CLASS); - foreach($classClass->getSubClasses(true) as $uri => $subClass){ - $returnValue->add($subClass); - } - - - return $returnValue; - } - - /** - * returns classes that are not subclasses of other classes - * - * @access public - * @author patrick.plichart@tudor.lu - * @return core_kernel_classes_ContainerCollection - */ - public function getRootClasses() - { - $returnValue = null; - - - - $returnValue = new core_kernel_classes_ContainerCollection(new core_kernel_classes_Container(__METHOD__),__METHOD__); - - $dbWrapper = core_kernel_classes_DbWrapper::singleton(); - - $query = "SELECT DISTINCT subject FROM statements WHERE (predicate = ? AND object = ?) - AND subject NOT IN (SELECT subject FROM statements WHERE predicate = ?)"; - $result = $dbWrapper->query($query, array( - OntologyRdf::RDF_TYPE, - OntologyRdfs::RDFS_CLASS, - OntologyRdfs::RDFS_SUBCLASSOF - )); - - while ($row = $result->fetch()) { - $returnValue->add(new core_kernel_classes_Class($row['subject'])); - } - - - - return $returnValue; - } - - /** - * add a new statment to the knowledge base - * - * @access public - * @author patrick.plichart@tudor.lu - * @param string subject - * @param string predicate - * @param string object - * @param string language - * @return boolean - */ - public function setStatement($subject, $predicate, $object, $language) - { - $returnValue = (bool) false; - - - $dbWrapper = core_kernel_classes_DbWrapper::singleton(); - $platform = $dbWrapper->getPlatForm(); - $localNs = common_ext_NamespaceManager::singleton()->getLocalNamespace(); - $mask = 'yyy[admin,administrators,authors]'; //now it's the default right mode - $query = 'INSERT INTO statements (modelid,subject,predicate,object,l_language,author,epoch) - VALUES (?, ?, ?, ?, ?, ? , ?);'; - - try{ - $returnValue = $dbWrapper->exec($query, array( - $localNs->getModelId(), - $subject, - $predicate, - $object, - $language, - \common_session_SessionManager::getSession()->getUserUri(), - $platform->getNowExpression() - - )); - } - catch (DBALException $e){ - if($e->getCode() !== '00000'){ - throw new common_Exception ("Unable to setStatement (SPO) {$subject}, {$predicate}, {$object} : " . $e->getMessage()); - } - } - - - - return (bool) $returnValue; - } - - /** - * Short description of method getAllClasses - * - * @access public - * @author firstname and lastname of author, - * @return core_kernel_classes_ContainerCollection - */ - public function getAllClasses() - { - $returnValue = null; - - - - $returnValue = new core_kernel_classes_ContainerCollection(new core_kernel_classes_Container(__METHOD__),__METHOD__); - - $dbWrapper = core_kernel_classes_DbWrapper::singleton(); - - $query = "SELECT DISTINCT subject FROM statements WHERE (predicate = ? AND object = ?) OR predicate = ?"; - $result = $dbWrapper->query($query, array( - OntologyRdf::RDF_TYPE, - OntologyRdfs::RDFS_CLASS, - OntologyRdfs::RDFS_SUBCLASSOF - )); - - while ($row = $result->fetch()) { - $returnValue->add(new core_kernel_classes_Class($row['subject'])); - } - - - - return $returnValue; - } - - /** - * Short description of method getSubject - * - * @access public - * @author firstname and lastname of author, - * @param string predicate - * @param string object - * @return core_kernel_classes_ContainerCollection - */ - public function getSubject($predicate, $object) - { - $returnValue = null; - - - - $sqlQuery = "SELECT subject FROM statements WHERE predicate = ? AND object= ? "; - $dbWrapper = core_kernel_classes_DbWrapper::singleton(); - $sqlResult = $dbWrapper->query($sqlQuery, array ( - $predicate, - $object - )); - $returnValue = new core_kernel_classes_ContainerCollection(new common_Object(__METHOD__)); - while ($row = $sqlResult->fetch()){ - $container = new core_kernel_classes_Resource($row['subject'], __METHOD__); - $container->debug = __METHOD__ ; - $returnValue->add($container); - } - - - - return $returnValue; - } - - /** - * Short description of method removeStatement - * - * @access public - * @author firstname and lastname of author, - * @param string subject - * @param string predicate - * @param string object - * @param string language - * @return boolean - */ - public function removeStatement($subject, $predicate, $object, $language) - { - $returnValue = (bool) false; - - - $dbWrapper = core_kernel_classes_DbWrapper::singleton(); - - $query = "DELETE FROM statements WHERE subject = ? - AND predicate = ? AND object = ? - AND (l_language = ? OR l_language = '')"; - - $returnValue = $dbWrapper->exec($query, array( - $subject, - $predicate, - $object, - $language - )); - - - - return (bool) $returnValue; - } - - /** - * Short description of method getObject - * - * @access public - * @author firstname and lastname of author, - * @param string subject - * @param string predicate - * @return core_kernel_classes_ContainerCollection - */ - public function getObject($subject, $predicate) - { - $returnValue = null; - - - $sqlQuery = "SELECT object FROM statements WHERE subject = ? AND predicate = ?"; - $dbWrapper = core_kernel_classes_DbWrapper::singleton(); - $sqlResult = $dbWrapper->query($sqlQuery, array ( - $subject, - $predicate - )); - $returnValue = new core_kernel_classes_ContainerCollection(new common_Object(__METHOD__)); - while ($row = $sqlResult->fetch()){ - - $value = $row['object']; - if(!common_Utils::isUri($value)) { - $container = new core_kernel_classes_Literal($value); - } - else{ - $container = new core_kernel_classes_Resource($value); - } - $container->debug = __METHOD__ ; - $returnValue->add($container); - } - - - return $returnValue; - } - - /** - * Short description of method singleton - * - * @access public - * @author firstname and lastname of author, - * @return core_kernel_impl_ApiModelOO - */ - public static function singleton() - { - $returnValue = null; - - - if (!isset(self::$instance)) { - $c = __CLASS__; - self::$instance = new $c(); - } - $returnValue = self::$instance; - - - return $returnValue; - } - - /** - * Short description of method __construct - * - * @access private - * @author firstname and lastname of author, - * @return void - */ - private function __construct() - { - - - } - -} \ No newline at end of file + + * @package generis + + */ +class core_kernel_impl_ApiModelOO + extends core_kernel_impl_Api + implements core_kernel_api_ApiModel +{ + use LoggerAwareTrait; + + /** + * @var core_kernel_impl_ApiModelOO + */ + private static $instance = null; + + /** @var ModelFactory */ + private $modelFactory; + + /** + * @author firstname and lastname of author, + * @return void + */ + private function __construct() + { + $serviceManager = ServiceManager::getServiceManager(); + $this->modelFactory = $serviceManager->get(ModelFactory::SERVICE_ID); + } + + /** + * build xml rdf containing rdf:Description of all meta-data the conected + * may get + * + * @deprecated + * @access public + * @author firstname and lastname of author, + * @param array sourceNamespaces + * @return string + */ + public function exportXmlRdf($sourceNamespaces = array()) + { + $modelIds = array(); + foreach($sourceNamespaces as $namespace){ + if(!preg_match("/\#$/", $namespace)){ + $namespace .= "#"; + } + + $result = $this->getPersistence()->query('SELECT * FROM "models" WHERE "modeluri" = ?', array( + $namespace + )); + if ($row = $result->fetch(PDO::FETCH_ASSOC)){ + $modelIds[] = $row['modelid']; + $result->closeCursor(); + } + } + return $xml = core_kernel_api_ModelExporter::exportXmlRdf($modelIds); + + } + + /** + * import xml rdf files into the knowledge base + * + * @access public + * @author firstname and lastname of author, + * @param string targetNameSpace + * @param string fileLocation + * @return boolean + */ + public function importXmlRdf($targetNameSpace, $fileLocation) + { + if(!file_exists($fileLocation) || !is_readable($fileLocation)){ + throw new common_Exception("Unable to load ontology : $fileLocation"); + } + + if(!preg_match("/#$/", $targetNameSpace)){ + $targetNameSpace .= '#'; + } + + return $this->modelFactory->createModel($targetNameSpace, file_get_contents($fileLocation)); + } + + + + /** + * returns an xml rdf serialization for uriResource with all meta dat found + * inferenced from te knowlege base about this resource + * + * @access public + * @author firstname and lastname of author, + * @param string uriResource + * @return string + */ + public function getResourceDescriptionXML($uriResource) + { + $returnValue = ''; + + $persistence = $this->getPersistence(); + $subject = $persistence->quote($uriResource); + + $baseNs = array( + 'xmlns:rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', + 'xmlns:rdfs' => 'http://www.w3.org/2000/01/rdf-schema#' + ); + + $query = 'SELECT "models"."modelid", "models"."modeluri" FROM "models" INNER JOIN "statements" ON "statements"."modelid" = "models"."modelid" + WHERE "statements"."subject" = ' . $subject; + $query = $persistence->limitStatement($query, 1); + $result = $persistence->query($query); + if($row = $result->fetch()){ + $modelId = $row['modelid']; + $modelUri = $row['modeluri']; + if(!preg_match("/#$/", $modelUri)){ + $modelUri .= '#'; + } + + $result->closeCursor(); + } + $currentNs = array("xmlns:ns{$modelId}" => $modelUri); + $currentNs = array_merge($baseNs, $currentNs); + + + $allModels = array(); + $result = $persistence->query('SELECT * FROM "models"'); + while($row = $result->fetch(PDO::FETCH_ASSOC)){ + $allModels[] = $row; + } + + $allNs = array(); + foreach($allModels as $i => $model){ + if(!preg_match("/#$/", $model['modeluri'])){ + $model['modeluri'] .= '#'; + } + $allNs["xmlns:ns{$model['modelid']}"] = $model['modeluri']; + } + $allNs = array_merge($baseNs, $allNs); + + try{ + + $dom = new DOMDocument(); + $dom->formatOutput = true; + $root = $dom->createElement('rdf:RDF'); + + foreach($currentNs as $namespaceId => $namespaceUri){ + $root->setAttribute($namespaceId, $namespaceUri); + } + $dom->appendChild($root); + + $description = $dom->createElement('rdf:Description'); + $description->setAttribute('rdf:about', $uriResource); + + $result = $persistence->query('SELECT * FROM "statements" WHERE "subject" = ' . $subject); + while($row = $result->fetch()){ + + $predicate = trim($row['predicate']); + $object = trim($row['object']); + $lang = trim($row['l_language']); + + $nodeName = null; + + foreach($allNs as $namespaceId => $namespaceUri){ + if($namespaceId == 'xml:base') { + continue; + } + if(preg_match("/^".preg_quote($namespaceUri, '/')."/", $predicate)){ + if(!array_key_exists($namespaceId, $currentNs)){ + $currentNs[$namespaceId] = $namespaceUri; + $root->setAttribute($namespaceId, $namespaceUri); + } + $nodeName = str_replace('xmlns:', '', $namespaceId).':'.str_replace($namespaceUri, '', $predicate); + break; + } + } + + $resourceValue = false; + foreach($allNs as $namespaceId => $namespaceUri){ + if( preg_match("/^".preg_quote($namespaceUri, '/')."/", $object) || + preg_match("/^http\:\/\/(.*)\#[a-zA-Z1-9]*/", $object)){ + $resourceValue = true; + break; + } + } + if(!is_null($nodeName)){ + try{ + $node = $dom->createElement($nodeName); + if(!empty($lang)){ + $node->setAttribute('xml:lang', $lang); + } + + if($resourceValue){ + $node->setAttribute('rdf:resource', $object); + } + else{ + if(!empty($object) && !is_null($object)){ + + /** + * Replace the CDATA section inside XML fields by a replacement tag: + * to + * @todo check if this behavior is the right + */ + $object = str_replace('', $object); + $object = str_replace(']]>', '', $object); + + $node->appendChild($dom->createCDATASection($object)); + } + } + $description->appendChild($node); + } catch(DOMException $de){ + $this->logError($de); + } + } + } + $root->appendChild($description); + $returnValue = $dom->saveXml(); + } catch(DomException $e){ + $this->logError($e); + } + + return $returnValue; + } + + /** + * returns metaclasses tat are not subclasses of other metaclasses + * + * @access public + * @author patrick.plichart@tudor.lu + * @return core_kernel_classes_ContainerCollection + */ + public function getMetaClasses() + { + $returnValue = new core_kernel_classes_ContainerCollection(new core_kernel_classes_Container(__METHOD__),__METHOD__); + + $classClass = new core_kernel_classes_Class(OntologyRdfs::RDFS_CLASS); + foreach($classClass->getSubClasses(true) as $uri => $subClass){ + $returnValue->add($subClass); + } + + return $returnValue; + } + + /** + * returns classes that are not subclasses of other classes + * + * @access public + * @author patrick.plichart@tudor.lu + * @return core_kernel_classes_ContainerCollection + */ + public function getRootClasses() + { + $returnValue = new core_kernel_classes_ContainerCollection(new core_kernel_classes_Container(__METHOD__),__METHOD__); + + $query = "SELECT DISTINCT subject FROM statements WHERE (predicate = ? AND object = ?) + AND subject NOT IN (SELECT subject FROM statements WHERE predicate = ?)"; + $result = $this->getPersistence()->query($query, array( + OntologyRdf::RDF_TYPE, + OntologyRdfs::RDFS_CLASS, + OntologyRdfs::RDFS_SUBCLASSOF + )); + + while ($row = $result->fetch()) { + $returnValue->add(new core_kernel_classes_Class($row['subject'])); + } + + return $returnValue; + } + + /** + * add a new statment to the knowledge base + * + * @access public + * @author patrick.plichart@tudor.lu + * @param string subject + * @param string predicate + * @param string object + * @param string language + * @return boolean + */ + public function setStatement($subject, $predicate, $object, $language) + { + $localNsManager = common_ext_NamespaceManager::singleton(); + $modelId = $localNsManager->getLocalNamespace()->getModelId(); + $currentUser = common_session_SessionManager::getSession()->getUserUri(); + + return $this->modelFactory->addStatement( + $modelId, + $subject, + $predicate, + $object, + $language, + $currentUser + ); + } + + /** + * Short description of method getAllClasses + * + * @access public + * @author firstname and lastname of author, + * @return core_kernel_classes_ContainerCollection + */ + public function getAllClasses() + { + $returnValue = new core_kernel_classes_ContainerCollection(new core_kernel_classes_Container(__METHOD__),__METHOD__); + + $query = "SELECT DISTINCT subject FROM statements WHERE (predicate = ? AND object = ?) OR predicate = ?"; + $result = $this->getPersistence()->query($query, array( + OntologyRdf::RDF_TYPE, + OntologyRdfs::RDFS_CLASS, + OntologyRdfs::RDFS_SUBCLASSOF + )); + + while ($row = $result->fetch()) { + $returnValue->add(new core_kernel_classes_Class($row['subject'])); + } + + return $returnValue; + } + + /** + * Short description of method getSubject + * + * @access public + * @author firstname and lastname of author, + * @param string predicate + * @param string object + * @return core_kernel_classes_ContainerCollection + */ + public function getSubject($predicate, $object) + { + $returnValue = new core_kernel_classes_ContainerCollection(new common_Object(__METHOD__)); + + $query = "SELECT subject FROM statements WHERE predicate = ? AND object= ? "; + $sqlResult = $this->getPersistence()->query($query, array ( + $predicate, + $object + )); + + while ($row = $sqlResult->fetch()){ + $container = new core_kernel_classes_Resource($row['subject'], __METHOD__); + $container->debug = __METHOD__ ; + $returnValue->add($container); + } + + return $returnValue; + } + + /** + * Short description of method removeStatement + * + * @access public + * @author firstname and lastname of author, + * @param string subject + * @param string predicate + * @param string object + * @param string language + * @return boolean + */ + public function removeStatement($subject, $predicate, $object, $language) + { + $query = "DELETE FROM statements WHERE subject = ? + AND predicate = ? AND object = ? + AND (l_language = ? OR l_language = '')"; + + $returnValue = $this->getPersistence()->exec($query, array( + $subject, + $predicate, + $object, + $language + )); + + return (bool) $returnValue; + } + + /** + * Short description of method getObject + * + * @access public + * @author firstname and lastname of author, + * @param string subject + * @param string predicate + * @return core_kernel_classes_ContainerCollection + */ + public function getObject($subject, $predicate) + { + $returnValue = new core_kernel_classes_ContainerCollection(new common_Object(__METHOD__)); + + $sqlQuery = "SELECT object FROM statements WHERE subject = ? AND predicate = ?"; + $sqlResult = $this->getPersistence()->query($sqlQuery, array ( + $subject, + $predicate + )); + + while ($row = $sqlResult->fetch()){ + + $value = $row['object']; + if(!common_Utils::isUri($value)) { + $container = new core_kernel_classes_Literal($value); + } + else{ + $container = new core_kernel_classes_Resource($value); + } + $container->debug = __METHOD__ ; + $returnValue->add($container); + } + + return $returnValue; + } + + /** + * Short description of method singleton + * + * @access public + * @author firstname and lastname of author, + * @return core_kernel_impl_ApiModelOO + */ + public static function singleton() + { + $returnValue = null; + + if (!isset(self::$instance)) { + $c = __CLASS__; + self::$instance = new $c(); + } + $returnValue = self::$instance; + + return $returnValue; + } + + /** + * @return DbWrapper + * @throws PersistenceException + */ + public function getPersistence() + { + // @TODO: inject dbWrapper as a dependency. + return DbWrapper::singleton(); + } +} diff --git a/core/kernel/persistence/smoothsql/class.Resource.php b/core/kernel/persistence/smoothsql/class.Resource.php index 6e2c6695c..8ffc52cfe 100644 --- a/core/kernel/persistence/smoothsql/class.Resource.php +++ b/core/kernel/persistence/smoothsql/class.Resource.php @@ -20,6 +20,7 @@ * 2017 (update and modification) Open Assessment Technologies SA (under the project TAO-PRODUCT); */ +use core_kernel_api_ModelFactory as ModelFactory; use oat\generis\model\OntologyRdf; use oat\oatbox\session\SessionService; use oat\oatbox\user\UserLanguageServiceInterface; @@ -36,19 +37,39 @@ class core_kernel_persistence_smoothsql_Resource implements core_kernel_persistence_ResourceInterface { + /** @var ModelFactory */ + protected $modelFactory; /** * @var core_kernel_persistence_smoothsql_SmoothModel */ private $model; - + + /** + * core_kernel_persistence_smoothsql_Resource constructor. + * + * @param core_kernel_persistence_smoothsql_SmoothModel $model + */ public function __construct(core_kernel_persistence_smoothsql_SmoothModel $model) { $this->model = $model; } - + + /** + * @return core_kernel_persistence_smoothsql_SmoothModel + */ protected function getModel() { return $this->model; } + + /** + * @return ModelFactory + */ + protected function getModelFactory() { + if ($this->modelFactory === null) { + $this->modelFactory = $this->getServiceLocator()->get(ModelFactory::SERVICE_ID); + } + return $this->modelFactory; + } /** * @return common_persistence_SqlPersistence @@ -58,11 +79,11 @@ protected function getPersistence() { } protected function getModelReadSqlCondition() { - return 'modelid IN ('.implode(',', $this->model->getReadableModels()).')'; + return $this->getModelFactory()->buildModelSqlCondition($this->model->getReadableModels()); } protected function getModelWriteSqlCondition() { - return 'modelid IN ('.implode(',',$this->model->getWritableModels()).')'; + return $this->getModelFactory()->buildModelSqlCondition($this->model->getWritableModels()); } protected function getNewTripleModelId() { @@ -83,7 +104,7 @@ public function getTypes( core_kernel_classes_Resource $resource) { $returnValue = array(); - + // TODO: move sql query to implementation. $sqlQuery = 'SELECT object FROM statements WHERE subject = ? and predicate = ?'; $sth = $this->getPersistence()->query($sqlQuery,array($resource->getUri(), OntologyRdf::RDF_TYPE)); @@ -101,10 +122,11 @@ public function getTypes( core_kernel_classes_Resource $resource) * * @access public * @author Joel Bout, - * @param Resource resource - * @param Property property + * @param core_kernel_classes_Resource resource + * @param core_kernel_classes_Property property * @param array options * @return array + * @throws core_kernel_persistence_Exception */ public function getPropertyValues( core_kernel_classes_Resource $resource, core_kernel_classes_Property $property, $options = array()) { @@ -118,7 +140,6 @@ public function getPropertyValues( core_kernel_classes_Resource $resource, core $platform = $this->getPersistence()->getPlatForm(); // Define language if required - $lang = ''; $defaultLg = ''; if (isset($options['lg'])) { $lang = $options['lg']; @@ -127,7 +148,8 @@ public function getPropertyValues( core_kernel_classes_Resource $resource, core $default = $this->getServiceLocator()->get(UserLanguageServiceInterface::SERVICE_ID)->getDefaultLanguage(); $defaultLg = ' OR l_language = ' . $this->getPersistence()->quote($default); } - + + // TODO: move sql query to implementation. $query = 'SELECT object, l_language FROM statements WHERE subject = ? @@ -137,14 +159,11 @@ public function getPropertyValues( core_kernel_classes_Resource $resource, core if ($one) { - // Select first - $query .= ' ORDER BY id DESC'; + // Select first only + $query .= ' ORDER BY ' . $this->getModelFactory()->getPropertySortingField() . ' DESC'; $query = $platform->limitStatement($query, 1, 0); - $result = $this->getPersistence()->query($query,array($resource->getUri(), $property->getUri(), $lang)); - } else { - // Select All - $result = $this->getPersistence()->query($query,array($resource->getUri(), $property->getUri(), $lang)); } + $result = $this->getPersistence()->query($query,array($resource->getUri(), $property->getUri(), $lang)); // Treat the query result if ($result == true) { @@ -167,8 +186,8 @@ public function getPropertyValues( core_kernel_classes_Resource $resource, core * * @access public * @author Joel Bout, - * @param Resource resource - * @param Property property + * @param core_kernel_classes_Resource resource + * @param core_kernel_classes_Property property * @param string lg * @return core_kernel_classes_ContainerCollection */ @@ -197,8 +216,8 @@ public function getPropertyValuesByLg( core_kernel_classes_Resource $resource, * * @access public * @author Joel Bout, - * @param Resource resource - * @param Property property + * @param core_kernel_classes_Resource resource + * @param core_kernel_classes_Property property * @param string object * @param string lg * @return boolean @@ -207,9 +226,9 @@ public function setPropertyValue( core_kernel_classes_Resource $resource, core_ { $userId = $this->getServiceLocator()->get(SessionService::SERVICE_ID)->getCurrentUser()->getIdentifier(); $object = $object instanceof core_kernel_classes_Resource ? $object->getUri() : (string) $object; - $platform = $this->getPersistence()->getPlatForm(); - $lang = ""; + // Define language if required + $lang = ''; if ($property->isLgDependent()){ if ($lg!=null){ $lang = $lg; @@ -217,23 +236,15 @@ public function setPropertyValue( core_kernel_classes_Resource $resource, core_ $lang = $this->getServiceLocator()->get(SessionService::SERVICE_ID)->getCurrentSession()->getDataLanguage(); } } - - $query = 'INSERT INTO statements (modelid, subject, predicate, object, l_language, author,epoch) - VALUES (?, ?, ?, ?, ?, ? , ?)'; - - $returnValue = $this->getPersistence()->exec($query, array( - $this->getNewTripleModelId(), - $resource->getUri(), - $property->getUri(), - $object, - $lang, - $userId, - $platform->getNowExpression() - )); - - - return (bool) $returnValue; + return $this->getModelFactory()->addStatement( + $this->getNewTripleModelId(), + $resource->getUri(), + $property->getUri(), + $object, + $lang, + $userId + ); } /** @@ -241,20 +252,21 @@ public function setPropertyValue( core_kernel_classes_Resource $resource, core_ * * @access public * @author Joel Bout, - * @param Resource resource + * @param core_kernel_classes_Resource resource * @param array properties * @return boolean */ public function setPropertiesValues( core_kernel_classes_Resource $resource, $properties) { - $returnValue = (bool) false; + $returnValue = false; if (is_array($properties) && count($properties) > 0) { $session = $this->getServiceLocator()->get(SessionService::SERVICE_ID)->getCurrentSession(); - $platform = $this->getPersistence()->getPlatForm(); - $valuesToInsert = []; + $modelId = $this->getNewTripleModelId(); + $subject = $resource->getUri(); + $author = $session->getUser()->getIdentifier(); foreach ($properties as $propertyUri => $value) { @@ -263,6 +275,7 @@ public function setPropertiesValues( core_kernel_classes_Resource $resource, $pr $lang = ($property->isLgDependent() ? $session->getDataLanguage() : ''); $formatedValues = []; + // @TODO: refactor this if ($value instanceof core_kernel_classes_Resource) { $formatedValues[] = $value->getUri(); @@ -275,22 +288,12 @@ public function setPropertiesValues( core_kernel_classes_Resource $resource, $pr } foreach ($formatedValues as $object) { - $valuesToInsert[] = [ - 'modelid' => $this->getNewTripleModelId(), - 'subject' => $resource->getUri(), - 'predicate' => $property->getUri(), - 'object' => $object, - 'l_language' => $lang, - 'author' => $session->getUser()->getIdentifier(), - 'epoch' => $platform->getNowExpression() - ]; + $returnValue |= $this->getModelFactory()->addStatement($modelId, $subject, $property->getUri(), $object, $lang, $author); } } - - $returnValue = $this->getPersistence()->insertMultiple('statements', $valuesToInsert); } - - return (bool) $returnValue; + + return $returnValue; } /** @@ -298,37 +301,24 @@ public function setPropertiesValues( core_kernel_classes_Resource $resource, $pr * * @access public * @author Joel Bout, - * @param Resource resource - * @param Property property + * @param core_kernel_classes_Resource resource + * @param core_kernel_classes_Property property * @param string value * @param string lg * @return boolean */ public function setPropertyValueByLg( core_kernel_classes_Resource $resource, core_kernel_classes_Property $property, $value, $lg) { - $returnValue = (bool) false; - - - - $platform = $this->getPersistence()->getPlatForm(); $userId = $this->getServiceLocator()->get(SessionService::SERVICE_ID)->getCurrentUser()->getIdentifier(); - - $query = 'INSERT INTO statements (modelid,subject,predicate,object,l_language,author,epoch) - VALUES (?, ?, ?, ?, ?, ?, ?)'; - - $returnValue = $this->getPersistence()->exec($query, array( - $this->getNewTripleModelId(), - $resource->getUri(), - $property->getUri(), - $value, - ($property->isLgDependent() ? $lg : ''), - $userId, - $platform->getNowExpression() - )); - - - return (bool) $returnValue; + return $this->getModelFactory()->addStatement( + $this->getNewTripleModelId(), + $resource->getUri(), + $property->getUri(), + $value, + ($property->isLgDependent() ? $lg : ''), + $userId + ); } /** @@ -336,8 +326,8 @@ public function setPropertyValueByLg( core_kernel_classes_Resource $resource, c * * @access public * @author Joel Bout, - * @param Resource resource - * @param Property property + * @param core_kernel_classes_Resource resource + * @param core_kernel_classes_Property property * @param array options * @return boolean */ @@ -348,7 +338,8 @@ public function removePropertyValues( core_kernel_classes_Resource $resource, c // Optional params $pattern = isset($options['pattern']) && !is_null($options['pattern']) ? $options['pattern'] : null; $like = isset($options['like']) && $options['like'] == true ? true : false; - + + // TODO: move sql query to implementation. //build query: $query = 'DELETE FROM statements WHERE subject = ? AND predicate = ?'; $objectType = $this->getPersistence()->getPlatForm()->getObjectTypeCondition(); @@ -407,33 +398,24 @@ public function removePropertyValues( core_kernel_classes_Resource $resource, c * * @access public * @author Joel Bout, - * @param Resource resource - * @param Property property + * @param core_kernel_classes_Resource resource + * @param core_kernel_classes_Property property * @param string lg * @param array options * @return boolean */ public function removePropertyValueByLg( core_kernel_classes_Resource $resource, core_kernel_classes_Property $property, $lg, $options = array()) { - $returnValue = (bool) false; - + // TODO: move sql query to implementation. $sqlQuery = 'DELETE FROM statements WHERE subject = ? and predicate = ? and l_language = ?'; //be sure the property we try to remove is included in an updatable model $sqlQuery .= ' AND '.$this->getModelWriteSqlCondition(); - $returnValue = $this->getPersistence()->exec($sqlQuery, array ( + return (bool) $this->getPersistence()->exec($sqlQuery, array ( $resource->getUri(), $property->getUri(), ($property->isLgDependent() ? $lg : '') )); - - if (!$returnValue){ - $returnValue = false; - } - - - - return (bool) $returnValue; } /** @@ -447,7 +429,8 @@ public function removePropertyValueByLg( core_kernel_classes_Resource $resource, public function getRdfTriples( core_kernel_classes_Resource $resource) { $returnValue = null; - + + // TODO: move sql query to implementation. $query = 'SELECT * FROM statements WHERE subject = ? AND '.$this->getModelReadSqlCondition().' ORDER BY predicate'; $result = $this->getPersistence()->query($query, array($resource->getUri())); @@ -473,16 +456,15 @@ public function getRdfTriples( core_kernel_classes_Resource $resource) * * @access public * @author Joel Bout, - * @param Resource resource - * @param Property property + * @param core_kernel_classes_Resource resource + * @param core_kernel_classes_Property property * @return array */ public function getUsedLanguages( core_kernel_classes_Resource $resource, core_kernel_classes_Property $property) { $returnValue = array(); - - + // TODO: move sql query to implementation. $sqlQuery = 'SELECT l_language FROM statements WHERE subject = ? AND predicate = ? '; $sqlResult = $this->getPersistence()->query($sqlQuery, array ( $resource->getUri(), @@ -504,42 +486,41 @@ public function getUsedLanguages( core_kernel_classes_Resource $resource, core_ * * @access public * @author Joel Bout, - * @param Resource resource + * @param core_kernel_classes_Resource resource * @param array excludedProperties - * @return core_kernel_classes_Resource + * @return core_kernel_classes_Resource|null + * @throws common_exception_Error */ public function duplicate( core_kernel_classes_Resource $resource, $excludedProperties = array()) { - $returnValue = null; $newUri = $this->getServiceLocator()->get(UriProvider::SERVICE_ID)->provide(); $collection = $this->getRdfTriples($resource); - - if ($collection->count() > 0) { - - $platform = $this->getPersistence()->getPlatForm(); + + if ($collection->count() === 0) { + return null; + } + $user = $this->getServiceLocator()->get(SessionService::SERVICE_ID)->getCurrentUser()->getIdentifier(); - $valuesToInsert = []; - + $modelId = $this->getNewTripleModelId(); + $addedRows = false; + foreach ($collection->getIterator() as $triple) { if (!in_array($triple->predicate, $excludedProperties)) { - $valuesToInsert[] = [ - 'modelid' => $this->getNewTripleModelId(), - 'subject' => $newUri, - 'predicate'=> $triple->predicate, - 'object' => ($triple->object == null) ? '' : $triple->object, - 'l_language' => ($triple->lg == null) ? '' : $triple->lg, - 'author' => $user, - 'epoch' => $platform->getNowExpression() - ]; + $addedRows |= $this->getModelFactory()->addStatement( + $modelId, + $newUri, + $triple->predicate, + $triple->object ?? '', + $triple->lg ?? '', + $user + ); } } - - if ($this->getPersistence()->insertMultiple('statements', $valuesToInsert)) { - $returnValue = $this->getModel()->getResource($newUri); - } - } - - return $returnValue; + if ($addedRows) { + return $this->getModel()->getResource($newUri); + } + + return null; } /** @@ -547,14 +528,13 @@ public function duplicate( core_kernel_classes_Resource $resource, $excludedProp * * @access public * @author Joel Bout, - * @param Resource resource + * @param core_kernel_classes_Resource resource * @param boolean deleteReference * @return boolean */ public function delete( core_kernel_classes_Resource $resource, $deleteReference = false) { - $returnValue = (bool) false; - + // TODO: move sql query to implementation. $query = 'DELETE FROM statements WHERE subject = ? AND '.$this->getModelWriteSqlCondition(); $returnValue = $this->getPersistence()->exec($query, array($resource->getUri())); @@ -563,6 +543,7 @@ public function delete( core_kernel_classes_Resource $resource, $deleteReference $returnValue = false; } else if($deleteReference){ + // TODO: move sql query to implementation. $sqlQuery = 'DELETE FROM statements WHERE ' . $this->getPersistence()->getPlatForm()->getObjectTypeCondition() . ' = ? AND '.$this->getModelWriteSqlCondition(); $return = $this->getPersistence()->exec($sqlQuery, array ($resource->getUri())); @@ -580,9 +561,10 @@ public function delete( core_kernel_classes_Resource $resource, $deleteReference * * @access public * @author Joel Bout, - * @param Resource resource + * @param core_kernel_classes_Resource resource * @param array properties * @return array + * @throws common_exception_Error */ public function getPropertiesValues( core_kernel_classes_Resource $resource, $properties) { @@ -611,6 +593,8 @@ public function getPropertiesValues( core_kernel_classes_Resource $resource, $pr $platform = $this->getPersistence()->getPlatForm(); $lang = $this->getServiceLocator()->get(SessionService::SERVICE_ID)->getCurrentSession()->getDataLanguage(); $default = $this->getServiceLocator()->get(UserLanguageServiceInterface::SERVICE_ID)->getDefaultLanguage(); + + // TODO: move sql query to implementation. //the unique sql query $query = 'SELECT predicate, object, l_language FROM statements @@ -641,8 +625,8 @@ public function getPropertiesValues( core_kernel_classes_Resource $resource, $pr * * @access public * @author Joel Bout, - * @param Resource resource - * @param Class class + * @param core_kernel_classes_Resource resource + * @param core_kernel_classes_Class class * @return boolean */ public function setType( core_kernel_classes_Resource $resource, core_kernel_classes_Class $class) @@ -656,16 +640,13 @@ public function setType( core_kernel_classes_Resource $resource, core_kernel_cl * * @access public * @author Joel Bout, - * @param Resource resource - * @param Class class + * @param core_kernel_classes_Resource resource + * @param core_kernel_classes_Class class * @return boolean */ public function removeType( core_kernel_classes_Resource $resource, core_kernel_classes_Class $class) { - $returnValue = (bool) false; - - - + // TODO: move sql query to implementation. $query = 'DELETE FROM statements WHERE subject = ? AND predicate = ? AND '. $this->getPersistence()->getPlatForm()->getObjectTypeCondition() .' = ?'; diff --git a/core/kernel/persistence/smoothsql/class.SmoothIterator.php b/core/kernel/persistence/smoothsql/class.SmoothIterator.php index 872e44146..15e182c0a 100644 --- a/core/kernel/persistence/smoothsql/class.SmoothIterator.php +++ b/core/kernel/persistence/smoothsql/class.SmoothIterator.php @@ -18,6 +18,9 @@ * */ +use core_kernel_api_ModelFactory as ModelFactory; +use oat\oatbox\service\ServiceManager; + /** * Iterator over all triples * @@ -33,10 +36,11 @@ class core_kernel_persistence_smoothsql_SmoothIterator * @param array $modelIds */ public function __construct(common_persistence_SqlPersistence $persistence, $modelIds = null) { - $query = 'SELECT * FROM statements ' - .(is_null($modelIds) ? '' : 'WHERE modelid IN ('.implode(',', $modelIds).') ') - .'ORDER BY id'; - parent::__construct($persistence, $query); + + $serviceManager = ServiceManager::getServiceManager(); + /** @var ModelFactory $modelFactory */ + $modelFactory = $serviceManager->get(ModelFactory::SERVICE_ID); + parent::__construct($persistence, $modelFactory->getIteratorQuery($modelIds)); } /** @@ -46,7 +50,8 @@ public function __construct(common_persistence_SqlPersistence $persistence, $mod */ function current() { $statement = parent::current(); - + + // TODO: create a constructor $triple = new core_kernel_classes_Triple(); $triple->modelid = $statement["modelid"]; $triple->subject = $statement["subject"]; @@ -57,4 +62,4 @@ function current() { $triple->author = $statement["author"]; return $triple; } -} \ No newline at end of file +} diff --git a/core/kernel/persistence/smoothsql/class.SmoothRdf.php b/core/kernel/persistence/smoothsql/class.SmoothRdf.php index 8baa34e07..445a543e5 100644 --- a/core/kernel/persistence/smoothsql/class.SmoothRdf.php +++ b/core/kernel/persistence/smoothsql/class.SmoothRdf.php @@ -18,6 +18,7 @@ * */ +use core_kernel_api_ModelFactory as ModelFactory; use oat\generis\model\data\RdfInterface; use oat\generis\model\OntologyRdf; use oat\generis\model\OntologyRdfs; @@ -27,7 +28,7 @@ /** * Implementation of the RDF interface for the smooth sql driver - * + * * @author joel bout * @package generis */ @@ -38,15 +39,15 @@ class core_kernel_persistence_smoothsql_SmoothRdf * @var core_kernel_persistence_smoothsql_SmoothModel */ private $model; - + public function __construct(core_kernel_persistence_smoothsql_SmoothModel $model) { $this->model = $model; } - + protected function getPersistence() { return $this->model->getPersistence(); } - + /** * (non-PHPdoc) * @see \oat\generis\model\data\RdfInterface::get() @@ -54,7 +55,7 @@ protected function getPersistence() { public function get($subject, $predicate) { throw new \common_Exception('Not implemented'); } - + /** * (non-PHPdoc) * @see \oat\generis\model\data\RdfInterface::add() @@ -63,15 +64,26 @@ public function add(\core_kernel_classes_Triple $triple) { if (!in_array($triple->modelid, $this->model->getReadableModels())) { $this->model->addReadableModel($triple->modelid); } - $query = "INSERT INTO statements ( modelId, subject, predicate, object, l_language, epoch, author) VALUES ( ? , ? , ? , ? , ? , ?, ?);"; - $success = $this->getPersistence()->exec($query, array($triple->modelid, $triple->subject, $triple->predicate, $triple->object, is_null($triple->lg) ? '' : $triple->lg, $this->getPersistence()->getPlatForm()->getNowExpression(), is_null($triple->author) ? '' : $triple->author)); + + /** @var ModelFactory $modelFactory */ + $modelFactory = $this->getServiceManager()->get(ModelFactory::SERVICE_ID); + $returnValue = $modelFactory->addStatement( + $triple->modelid, + $triple->subject, + $triple->predicate, + $triple->object, + is_null($triple->lg) ? '' : $triple->lg, + is_null($triple->author) ? '' : $triple->author + ); + if ($triple->predicate == OntologyRdfs::RDFS_SUBCLASSOF || $triple->predicate == OntologyRdf::RDF_TYPE) { $eventManager = $this->getServiceManager()->get(EventManager::CONFIG_ID); $eventManager->trigger(new ResourceCreated(new core_kernel_classes_Resource($triple->subject))); } - return $success; + + return $returnValue; } - + /** * (non-PHPdoc) * @see \oat\generis\model\data\RdfInterface::remove() @@ -80,7 +92,7 @@ public function remove(\core_kernel_classes_Triple $triple) { $query = "DELETE FROM statements WHERE subject = ? AND predicate = ? AND object = ? AND l_language = ?;"; return $this->getPersistence()->exec($query, array($triple->subject, $triple->predicate, $triple->object, is_null($triple->lg) ? '' : $triple->lg)); } - + /** * (non-PHPdoc) * @see \oat\generis\model\data\RdfInterface::search() @@ -88,13 +100,13 @@ public function remove(\core_kernel_classes_Triple $triple) { public function search($predicate, $object) { throw new \common_Exception('Not implemented'); } - + public function getIterator() { return new core_kernel_persistence_smoothsql_SmoothIterator($this->getPersistence()); } - + public function getServiceManager() { return ServiceManager::getServiceManager(); } -} \ No newline at end of file +} diff --git a/core/kernel/persistence/smoothsql/install/SmoothRdsModel.php b/core/kernel/persistence/smoothsql/install/SmoothRdsModel.php index ba3951414..21b58cd2f 100644 --- a/core/kernel/persistence/smoothsql/install/SmoothRdsModel.php +++ b/core/kernel/persistence/smoothsql/install/SmoothRdsModel.php @@ -17,42 +17,32 @@ * Copyright (c) 2018 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT); * */ -namespace oat\generis\model\kernel\persistence\smoothsql\install; + +namespace oat\generis\model\kernel\persistence\smoothsql\install; use Doctrine\DBAL\Schema\Schema; +use core_kernel_api_ModelFactory as ModelFactory; +use oat\oatbox\service\ConfigurableService; + /** * Helper to setup the required tables for generis smoothsql */ -class SmoothRdsModel { - +class SmoothRdsModel extends ConfigurableService +{ /** - * + * * @param Schema $schema + * * @return \Doctrine\DBAL\Schema\Schema */ - public static function addSmoothTables(Schema $schema) + public function addSmoothTables(Schema $schema) { - $table = $schema->createTable("models"); - $table->addColumn('modelid', "integer",["notnull" => true,"autoincrement" => true]); - $table->addColumn('modeluri', "string", ["length" => 255,"default" => null]); - $table->addOption('engine' , 'MyISAM'); - $table->setPrimaryKey(['modelid']); + /** @var ModelFactory $modelFactory */ + $modelFactory = $this->getServiceLocator()->get(ModelFactory::SERVICE_ID); - $table = $schema->createTable("statements"); - $table->addColumn("modelid", "integer",["notnull" => true,"default" => 0]); - $table->addColumn("subject", "string",["length" => 255,"default" => null]); - $table->addColumn("predicate", "string",["length" => 255,"default" => null]); - $table->addColumn("object", "text", ["default" => null,"notnull" => false]); - - $table->addColumn("l_language", "string",["length" => 255,"default" => null,"notnull" => false]); - $table->addColumn("id", "integer",["notnull" => true,"autoincrement" => true]); - $table->addColumn("author", "string",["length" => 255,"default" => null,"notnull" => false]); - $table->setPrimaryKey(["id"]); - $table->addOption('engine' , 'MyISAM'); - $table->addColumn("epoch", "string" , ["notnull" => null]); + $modelFactory->createModelsTable($schema); + $modelFactory->createStatementsTable($schema); - $table->addIndex(["subject","predicate"],"k_sp", [], ['lengths' => [164,164]]); - $table->addIndex(["predicate","object"],"k_po", [], ['lengths' => [164,164]]); return $schema; } } diff --git a/helpers/UuidPrimaryKeyTrait.php b/helpers/UuidPrimaryKeyTrait.php new file mode 100644 index 000000000..dbb9f69f5 --- /dev/null +++ b/helpers/UuidPrimaryKeyTrait.php @@ -0,0 +1,39 @@ +" + * @license GPLv2 + * @package generis + * + */ +namespace oat\generis\Helper; + +use Ramsey\Uuid\Uuid; + +trait UuidPrimaryKeyTrait +{ + /** + * Generates a unique, not auto-increment based, primary key. + * @return string + * @throws \Exception + */ + public function getUniquePrimaryKey() + { + return Uuid::uuid4(); + } +} diff --git a/test/GenerisTestCase.php b/test/GenerisTestCase.php index a8d2353ce..184a684e4 100755 --- a/test/GenerisTestCase.php +++ b/test/GenerisTestCase.php @@ -19,6 +19,8 @@ */ namespace oat\generis\test; +use core_kernel_api_ModelFactory as ModelFactory; +use oat\generis\model\kernel\api\RdsModelFactory; use oat\generis\model\kernel\persistence\smoothsql\install\SmoothRdsModel; use oat\oatbox\user\UserLanguageServiceInterface; use oat\oatbox\session\SessionService; @@ -42,11 +44,7 @@ protected function getOntologyMock() $pm = $this->getSqlMock('mockSql'); $rds = $pm->getPersistenceById('mockSql'); $schema = $rds->getSchemaManager()->createSchema(); - $schema = SmoothRdsModel::addSmoothTables($schema); - $queries = $rds->getPlatform()->schemaToSql($schema); - foreach ($queries as $query){ - $rds->query($query); - } + $smoothRdsModel = new SmoothRdsModel(); $session = new \common_session_AnonymousSession(); $sl = $this->getServiceLocatorMock([ @@ -56,6 +54,7 @@ protected function getOntologyMock() EventManager::SERVICE_ID => new EventManager(), LoggerService::SERVICE_ID => $this->prophesize(LoggerInterface::class)->reveal(), UriProvider::SERVICE_ID => new Bin2HexUriProvider([Bin2HexUriProvider::OPTION_NAMESPACE => 'http://ontology.mock/bin2hex#']), + ModelFactory::SERVICE_ID => new RdsModelFactory(), 'smoothcache' => new \common_cache_NoCache() ]); $session->setServiceLocator($sl); @@ -67,6 +66,13 @@ protected function getOntologyMock() 'cache' => 'smoothcache' ]); $model->setServiceLocator($sl); + $smoothRdsModel->setServiceLocator($sl); + + $schema = $smoothRdsModel->addSmoothTables($schema); + $queries = $rds->getPlatform()->schemaToSql($schema); + foreach ($queries as $query){ + $rds->query($query); + } return $model; } diff --git a/test/unit/model/persistence/smoothsql/SmoothModelIteratorTest.php b/test/integration/model/persistence/smoothsql/SmoothModelIteratorTest.php similarity index 98% rename from test/unit/model/persistence/smoothsql/SmoothModelIteratorTest.php rename to test/integration/model/persistence/smoothsql/SmoothModelIteratorTest.php index ae50e901b..df96ac66d 100644 --- a/test/unit/model/persistence/smoothsql/SmoothModelIteratorTest.php +++ b/test/integration/model/persistence/smoothsql/SmoothModelIteratorTest.php @@ -17,7 +17,7 @@ * Copyright (c) (original work) 2015 Open Assessment Technologies SA * */ -namespace oat\generis\test\unit\model\persistence\smoothsql; +namespace oat\generis\test\integration\model\persistence\smoothsql; use Prophecy\Promise\ReturnPromise; use Prophecy\Argument; diff --git a/test/unit/model/persistence/smoothsql/SmoothRdfTest.php b/test/integration/model/persistence/smoothsql/SmoothRdfTest.php similarity index 98% rename from test/unit/model/persistence/smoothsql/SmoothRdfTest.php rename to test/integration/model/persistence/smoothsql/SmoothRdfTest.php index b2b5128bf..1d8cdda1e 100644 --- a/test/unit/model/persistence/smoothsql/SmoothRdfTest.php +++ b/test/integration/model/persistence/smoothsql/SmoothRdfTest.php @@ -17,7 +17,7 @@ * Copyright (c) (original work) 2015 Open Assessment Technologies SA * */ -namespace oat\generis\test\unit\model\persistence\smoothsql; +namespace oat\generis\test\integration\model\persistence\smoothsql; use \core_kernel_persistence_smoothsql_SmoothRdf; use Prophecy\Prophet; @@ -171,4 +171,4 @@ public function testRemove() } } -?> \ No newline at end of file +?> diff --git a/test/unit/OntologyMockTest.php b/test/unit/OntologyMockTest.php index f9b023b7e..4b1dbefee 100755 --- a/test/unit/OntologyMockTest.php +++ b/test/unit/OntologyMockTest.php @@ -22,24 +22,28 @@ use oat\generis\model\data\Model; use oat\generis\test\GenerisTestCase; use oat\generis\model\data\Ontology; +use core_kernel_persistence_smoothsql_SmoothModel as SmoothModel; /** * */ class OntologyMockTest extends GenerisTestCase { - public function testModel() + /** @var SmoothModel */ + private $subject; + + public function setUp() { - $model = $this->getOntologyMock(); - $this->assertInstanceOf(Model::class, $model); - return $model; + $this->subject = $this->getOntologyMock(); } - /** - * @depends testModel - */ - public function testSetLabel($model) + public function testOntologyMockClass() { - $resource = $model->getResource('http://testing'); + $this->assertInstanceOf(Model::class, $this->subject); + } + + public function testSetLabel() + { + $resource = $this->subject->getResource('http://testing'); $this->assertInstanceOf(\core_kernel_classes_Resource::class, $resource); $label = $resource->getLabel(); $this->assertEquals('', $label); @@ -48,12 +52,9 @@ public function testSetLabel($model) $this->assertEquals('magic', $label); } - /** - * @depends testModel - */ - public function testCreateInstance($model) + public function testCreateInstance() { - $class = $model->getClass('http://testing#class'); + $class = $this->subject->getClass('http://testing#class'); $this->assertInstanceOf(\core_kernel_classes_Class::class, $class); // with URI $resource = $class->createInstance('sample', 'comment', 'http://testing#resource'); @@ -61,15 +62,11 @@ public function testCreateInstance($model) // without URI $resource = $class->createInstance('sample'); $this->assertInstanceOf(\core_kernel_classes_Resource::class, $resource); - return $resource; } - /** - * @depends testModel - */ - public function testDuplicateInstance(Ontology $model) + public function testDuplicateInstance() { - $class = $model->getClass('http://testing#class'); + $class = $this->subject->getClass('http://testing#class'); $this->assertInstanceOf(\core_kernel_classes_Class::class, $class); $resource = $class->createInstance('original'); $this->assertInstanceOf(\core_kernel_classes_Resource::class, $resource); @@ -79,11 +76,11 @@ public function testDuplicateInstance(Ontology $model) $this->assertNotEquals($resource, $resourceClone); } - /** - * @depends testCreateInstance - */ - public function testDeleteInstance(\core_kernel_classes_Resource $resource) + public function testDeleteInstance() { + $class = $this->subject->getClass('http://testing#class'); + $resource = $class->createInstance('sample'); + $this->assertTrue($resource->exists()); $resource->delete(); $this->assertFalse($resource->exists());