diff --git a/common/persistence/class.SqlPersistence.php b/common/persistence/class.SqlPersistence.php index db1d1fa0f..5519fc18c 100755 --- a/common/persistence/class.SqlPersistence.php +++ b/common/persistence/class.SqlPersistence.php @@ -21,6 +21,7 @@ * @author */ +use Doctrine\DBAL\DBALException; /** * Persistence base on SQL */ @@ -32,6 +33,7 @@ class common_persistence_SqlPersistence extends common_persistence_Persistence * @param array $params * @param array $types * @return int number of updated rows + * @throws DBALException */ public function exec($statement, array $params = [], array $types = []) { @@ -60,6 +62,7 @@ public function getPlatForm(){ * @param array $data * @param array $types * @return int number of updated rows + * @throws DBALException */ public function insert($tableName, array $data, array $types = []) { @@ -96,6 +99,7 @@ public function updateMultiple($table, array $data) * @param string $statement * @param array $params * @return \Doctrine\DBAL\Driver\Statement + * @throws DBALException */ public function query($statement, $params = [], array $types = []) { diff --git a/common/persistence/sql/SetupDb.php b/common/persistence/sql/SetupDb.php index 626cd6c9d..179150a06 100644 --- a/common/persistence/sql/SetupDb.php +++ b/common/persistence/sql/SetupDb.php @@ -29,7 +29,7 @@ use oat\generis\model\kernel\persistence\smoothsql\install\SmoothRdsModel; use Doctrine\DBAL\Exception\ConnectionException; -class SetupDb implements LoggerAwareInterface +class SetupDb extends ConfigurableService implements LoggerAwareInterface { use LoggerAwareTrait; @@ -78,8 +78,9 @@ private function setupTables(\common_persistence_SqlPersistence $p) */ public function getSchema(\common_persistence_SqlPersistence $p) { + $smoothRdsModel = new SmoothRdsModel(); $schema = $p->getSchemaManager()->createSchema(); - SmoothRdsModel::addSmoothTables($schema); + $smoothRdsModel->addSmoothTables($schema); $this->addKeyValueStoreTable($schema); return $schema; } diff --git a/common/persistence/sql/dbal/class.Driver.php b/common/persistence/sql/dbal/class.Driver.php index a20ca164b..2107fc099 100755 --- a/common/persistence/sql/dbal/class.Driver.php +++ b/common/persistence/sql/dbal/class.Driver.php @@ -17,6 +17,8 @@ * Copyright (c) 2013 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT); */ +use Doctrine\DBAL\DBALException; + /** * Dbal Driver * @@ -45,7 +47,7 @@ class common_persistence_sql_dbal_Driver implements common_persistence_sql_Drive * @param string $id * @param array $params * @return common_persistence_Persistence|common_persistence_SqlPersistence - * @throws \Doctrine\DBAL\DBALException + * @throws DBALException */ public function connect($id, array $params) { @@ -71,7 +73,7 @@ public function connect($id, array $params) * Endless connection * * @param $connectionParams - * @throws \Doctrine\DBAL\DBALException + * @throws DBALException */ protected function persistentConnect($connectionParams) { @@ -106,7 +108,7 @@ protected function persistentConnect($connectionParams) * @param $config * @return \Doctrine\DBAL\Connection * - * @throws \Doctrine\DBAL\DBALException + * @throws DBALException * */ private function getConnection($params, $config) @@ -157,6 +159,7 @@ public function getSchemaManager() * @param array $params * @param array $types * @return integer number of affected row + * @throws DBALException; */ public function exec($statement, $params = [], array $types = []) { @@ -170,6 +173,7 @@ public function exec($statement, $params = [], array $types = []) * @param array $params * @param array $types * @return \Doctrine\DBAL\Driver\Statement + * @throws DBALException; */ public function query($statement, $params = [], array $types = []) { @@ -189,7 +193,9 @@ public function quote($parameter, $parameter_type = PDO::PARAM_STR){ } /** - * @inheritdoc + * (non-PHPdoc) + * @see common_persistence_sql_Driver::insert() + * @throws DBALException; */ public function insert($tableName, array $data, array $types = []) { diff --git a/common/persistence/sql/interface.Driver.php b/common/persistence/sql/interface.Driver.php index a87e2d1b3..4791c1dc0 100644 --- a/common/persistence/sql/interface.Driver.php +++ b/common/persistence/sql/interface.Driver.php @@ -21,11 +21,21 @@ * @package generis * */ + +use Doctrine\DBAL\DBALException; + interface common_persistence_sql_Driver extends common_persistence_Driver { - + /** + * @inheritdoc + * @throws DBALException; + */ public function query($statement,$params, array $types = []); - + + /** + * @inheritdoc + * @throws DBALException; + */ public function exec($statement,$params, array $types = []); /** @@ -35,7 +45,8 @@ public function exec($statement,$params, array $types = []); * * @param string $tableName name of the table * @param array $data An associative array containing column-value pairs. - * @return integer The number of affected rows. + * @return integer The number of affected rows. + * @throws DBALException */ public function insert($tableName, array $data, array $types = []); diff --git a/core/kernel/api/NewSqlModelFactory.php b/core/kernel/api/NewSqlModelFactory.php new file mode 100644 index 000000000..1a83af04c --- /dev/null +++ b/core/kernel/api/NewSqlModelFactory.php @@ -0,0 +1,120 @@ +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) + { + return [ + 'id' => $this->getUniquePrimaryKey(), + 'modelid' => $modelId, + 'subject' => $subject, + 'predicate' => $predicate, + 'object' => $object, + 'l_language' => $lang, + 'author' => $author ?? '', + 'epoch' => $this->getPersistence()->getPlatForm()->getNowExpression(), + ]; + } + + /** + * @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..f649fdadc --- /dev/null +++ b/core/kernel/api/RdsModelFactory.php @@ -0,0 +1,109 @@ +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) + { + return [ + 'modelid' => $modelId, + 'subject' => $subject, + 'predicate' => $predicate, + 'object' => $object, + 'l_language' => $lang, + 'author' => $author ?? '', + 'epoch' => $this->getPersistence()->getPlatForm()->getNowExpression(), + ]; + } + + /** + * @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..0c3ba99a3 100644 --- a/core/kernel/api/class.ModelFactory.php +++ b/core/kernel/api/class.ModelFactory.php @@ -22,48 +22,61 @@ * */ -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)); + $results = $this->getPersistence()->query($query, array($namespace)); return $results->fetchColumn(0); } /** + * Creates a new model in the ontology. * @author "Lionel Lecaque, " * @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); /** + * Creates a new model. * @author "Lionel Lecaque, " * @param string $namespace * @param string $data xml content + * @return bool Were triples added? */ 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); + $this->logInfo('modelId not found, need to add namespace '. $namespace); + $modelId = $this->addNewModel($namespace); } $modelDefinition = new EasyRdf_Graph($namespace); if(is_file($data)){ @@ -77,51 +90,119 @@ public function createModel($namespace, $data){ $data = $modelDefinition->serialise($format); + $returnValue = false; 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 |= $this->addStatement($modelId, $subjectUri, $prop, $v['value'], $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|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) + array($modelId, $subject, $predicate, $object, $lang) ); - if (intval($result->fetchColumn()) === 0) { - $dbWrapper = core_kernel_classes_DbWrapper::singleton(); - $date = $dbWrapper->getPlatForm()->getNowExpression(); + if ((int)$result->fetchColumn() > 0) { + return false; + } + + return (bool)$this->getPersistence()->insert( + 'statements', + $this->prepareStatement($modelId, $subject, $predicate, $object, $lang, is_null($author) ? '' : $author) + ); + } + + /** + * 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); - $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 - ) - ); + /** + * 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()); } - -} \ No newline at end of file + /** + * 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..ed913c023 100644 --- a/core/kernel/impl/class.ApiModelOO.php +++ b/core/kernel/impl/class.ApiModelOO.php @@ -22,9 +22,13 @@ ?> query('SELECT * FROM "models" WHERE "modeluri" = ?', array( + $result = $this->getPersistence()->query('SELECT * FROM "models" WHERE "modeluri" = ?', array( $namespace )); if ($row = $result->fetch(PDO::FETCH_ASSOC)){ @@ -104,8 +113,6 @@ public function exportXmlRdf($sourceNamespaces = array()) */ public function importXmlRdf($targetNameSpace, $fileLocation) { - $returnValue = (bool) false; - if(!file_exists($fileLocation) || !is_readable($fileLocation)){ throw new common_Exception("Unable to load ontology : $fileLocation"); } @@ -113,12 +120,7 @@ public function importXmlRdf($targetNameSpace, $fileLocation) if(!preg_match("/#$/", $targetNameSpace)){ $targetNameSpace .= '#'; } - $modFactory = new core_kernel_api_ModelFactory(); - $returnValue = $modFactory->createModel($targetNameSpace, file_get_contents($fileLocation)); - - - - return (bool) $returnValue; + return $this->modelFactory->createModel($targetNameSpace, file_get_contents($fileLocation)); } @@ -134,14 +136,11 @@ public function importXmlRdf($targetNameSpace, $fileLocation) */ public function getResourceDescriptionXML($uriResource) { - $returnValue = (string) ''; + $returnValue = ''; - - - - $dbWrapper = core_kernel_classes_DbWrapper::singleton(); - $subject = $dbWrapper->quote($uriResource); + $persistence = $this->getPersistence(); + $subject = $persistence->quote($uriResource); $baseNs = array( 'xmlns:rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', @@ -150,8 +149,8 @@ public function getResourceDescriptionXML($uriResource) $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); + $query = $persistence->limitStatement($query, 1); + $result = $persistence->query($query); if($row = $result->fetch()){ $modelId = $row['modelid']; $modelUri = $row['modeluri']; @@ -166,7 +165,7 @@ public function getResourceDescriptionXML($uriResource) $allModels = array(); - $result = $dbWrapper->query('SELECT * FROM "models"'); + $result = $persistence->query('SELECT * FROM "models"'); while($row = $result->fetch(PDO::FETCH_ASSOC)){ $allModels[] = $row; } @@ -194,7 +193,7 @@ public function getResourceDescriptionXML($uriResource) $description = $dom->createElement('rdf:Description'); $description->setAttribute('rdf:about', $uriResource); - $result = $dbWrapper->query('SELECT * FROM "statements" WHERE "subject" = ' . $subject); + $result = $persistence->query('SELECT * FROM "statements" WHERE "subject" = ' . $subject); while($row = $result->fetch()){ $predicate = trim($row['predicate']); @@ -252,7 +251,7 @@ public function getResourceDescriptionXML($uriResource) $description->appendChild($node); } catch(DOMException $de){ - //print $de; + $this->logError($de); } } } @@ -260,13 +259,13 @@ public function getResourceDescriptionXML($uriResource) $returnValue = $dom->saveXml(); } catch(DomException $e){ - print $e; + $this->logError($e); } - return (string) $returnValue; + return $returnValue; } /** @@ -278,9 +277,6 @@ public function getResourceDescriptionXML($uriResource) */ 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); @@ -301,17 +297,11 @@ public function getMetaClasses() */ 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( + $result = $this->getPersistence()->query($query, array( OntologyRdf::RDF_TYPE, OntologyRdfs::RDFS_CLASS, OntologyRdfs::RDFS_SUBCLASSOF @@ -339,37 +329,16 @@ public function getRootClasses() */ 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; + $modelId = common_ext_NamespaceManager::singleton()->getLocalNamespace()->getModelId(); + $currentUser = common_session_SessionManager::getSession()->getUserUri(); + return $this->modelFactory->addStatement( + $modelId, + $subject, + $predicate, + $object, + $language, + $currentUser + ); } /** @@ -381,16 +350,10 @@ public function setStatement($subject, $predicate, $object, $language) */ 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( + $result = $this->getPersistence()->query($query, array( OntologyRdf::RDF_TYPE, OntologyRdfs::RDFS_CLASS, OntologyRdfs::RDFS_SUBCLASSOF @@ -416,17 +379,14 @@ public function getAllClasses() */ public function getSubject($predicate, $object) { - $returnValue = null; - + $returnValue = new core_kernel_classes_ContainerCollection(new common_Object(__METHOD__)); $sqlQuery = "SELECT subject FROM statements WHERE predicate = ? AND object= ? "; - $dbWrapper = core_kernel_classes_DbWrapper::singleton(); - $sqlResult = $dbWrapper->query($sqlQuery, array ( + $sqlResult = $this->getPersistence()->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__ ; @@ -451,16 +411,11 @@ public function getSubject($predicate, $object) */ 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( + $returnValue = $this->getPersistence()->exec($query, array( $subject, $predicate, $object, @@ -483,16 +438,14 @@ public function removeStatement($subject, $predicate, $object, $language) */ public function getObject($subject, $predicate) { - $returnValue = null; - + $returnValue = new core_kernel_classes_ContainerCollection(new common_Object(__METHOD__)); $sqlQuery = "SELECT object FROM statements WHERE subject = ? AND predicate = ?"; - $dbWrapper = core_kernel_classes_DbWrapper::singleton(); - $sqlResult = $dbWrapper->query($sqlQuery, array ( + $sqlResult = $this->getPersistence()->query($sqlQuery, array ( $subject, $predicate )); - $returnValue = new core_kernel_classes_ContainerCollection(new common_Object(__METHOD__)); + while ($row = $sqlResult->fetch()){ $value = $row['object']; @@ -541,8 +494,17 @@ public static function singleton() */ private function __construct() { - - + $serviceManager = ServiceManager::getServiceManager(); + $this->modelFactory = $serviceManager->get(ModelFactory::SERVICE_ID); } -} \ No newline at end of file + /** + * @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..2d624073d 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() { @@ -105,6 +126,7 @@ public function getTypes( core_kernel_classes_Resource $resource) * @param 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']; @@ -137,14 +158,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) { @@ -207,33 +225,21 @@ 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; - } else { - $lang = $this->getServiceLocator()->get(SessionService::SERVICE_ID)->getCurrentSession()->getDataLanguage(); - } + $lang = $lg ?? $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( + return $this->getModelFactory()->addStatement( $this->getNewTripleModelId(), $resource->getUri(), $property->getUri(), $object, $lang, - $userId, - $platform->getNowExpression() - )); - - - - return (bool) $returnValue; + $userId + ); } /** @@ -247,52 +253,54 @@ public function setPropertyValue( core_kernel_classes_Resource $resource, core_ */ 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) { $property = $this->getModel()->getProperty($propertyUri); $lang = ($property->isLgDependent() ? $session->getDataLanguage() : ''); - $formatedValues = []; - if ($value instanceof core_kernel_classes_Resource) { - $formatedValues[] = $value->getUri(); - - } elseif (is_array($value)) { - foreach($value as $val){ - $formatedValues[] = ($val instanceof core_kernel_classes_Resource) ? $val->getUri() : $val; - } - } else { - $formatedValues[] = ($value == null) ? '' : $value; - } + $formattedValues = $this->formatValue($value); - 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() - ]; + foreach ($formattedValues as $object) { + $returnValue |= $this->getModelFactory()->addStatement($modelId, $subject, $property->getUri(), $object, $lang, $author); } } - - $returnValue = $this->getPersistence()->insertMultiple('statements', $valuesToInsert); } - return (bool) $returnValue; + return $returnValue; } + /** + * Formats single or multiple value + * @param mixed $value + * @return array + */ + private function formatValue($value) + { + if (!is_array($value)) { + $value = [$value]; + } + + $formattedValues = []; + foreach ($value as $val) { + $formattedValues[] = $val instanceof core_kernel_classes_Resource + ? $val->getUri() + : ($val ?? ''); + } + + return $formattedValues; + } + /** * Short description of method setPropertyValueByLg * @@ -306,29 +314,16 @@ public function setPropertiesValues( core_kernel_classes_Resource $resource, $pr */ 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 + ); } /** @@ -343,8 +338,6 @@ public function setPropertyValueByLg( core_kernel_classes_Resource $resource, c */ public function removePropertyValues( core_kernel_classes_Resource $resource, core_kernel_classes_Property $property, $options = array()) { - $returnValue = (bool) false; - // Optional params $pattern = isset($options['pattern']) && !is_null($options['pattern']) ? $options['pattern'] : null; $like = isset($options['like']) && $options['like'] == true ? true : false; @@ -415,25 +408,15 @@ public function removePropertyValues( core_kernel_classes_Resource $resource, c */ public function removePropertyValueByLg( core_kernel_classes_Resource $resource, core_kernel_classes_Property $property, $lg, $options = array()) { - $returnValue = (bool) false; - $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; } /** @@ -507,39 +490,39 @@ public function getUsedLanguages( core_kernel_classes_Resource $resource, core_ * @param Resource resource * @param array excludedProperties * @return core_kernel_classes_Resource + * @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(); - $user = $this->getServiceLocator()->get(SessionService::SERVICE_ID)->getCurrentUser()->getIdentifier(); - $valuesToInsert = []; + if ($collection->count() === 0) { + return null; + } + + $newUri = $this->getServiceLocator()->get(UriProvider::SERVICE_ID)->provide(); + $user = $this->getServiceLocator()->get(SessionService::SERVICE_ID)->getCurrentUser()->getIdentifier(); + $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() - ]; - } - } - - if ($this->getPersistence()->insertMultiple('statements', $valuesToInsert)) { - $returnValue = $this->getModel()->getResource($newUri); - } - } + foreach ($collection->getIterator() as $triple) { + if (!in_array($triple->predicate, $excludedProperties)) { + $addedRows |= $this->getModelFactory()->addStatement( + $modelId, + $newUri, + $triple->predicate, + $triple->object ?? '', + $triple->lg ?? '', + $user + ); + } + } - return $returnValue; + if ($addedRows) { + return $this->getModel()->getResource($newUri); + } + + return null; } /** @@ -553,8 +536,6 @@ public function duplicate( core_kernel_classes_Resource $resource, $excludedProp */ public function delete( core_kernel_classes_Resource $resource, $deleteReference = false) { - $returnValue = (bool) false; - $query = 'DELETE FROM statements WHERE subject = ? AND '.$this->getModelWriteSqlCondition(); $returnValue = $this->getPersistence()->exec($query, array($resource->getUri())); @@ -662,10 +643,6 @@ public function setType( core_kernel_classes_Resource $resource, core_kernel_cl */ public function removeType( core_kernel_classes_Resource $resource, core_kernel_classes_Class $class) { - $returnValue = (bool) false; - - - $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..d0a641e26 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 Zend\ServiceManager\ServiceLocatorAwareTrait; + /** * Iterator over all triples * @@ -27,16 +30,18 @@ class core_kernel_persistence_smoothsql_SmoothIterator extends common_persistence_sql_QueryIterator { + use ServiceLocatorAwareTrait; + /** * Constructor of the iterator expecting the model ids * + * @param common_persistence_SqlPersistence $persistence * @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); + /** @var ModelFactory $modelFactory */ + $modelFactory = $this->getServiceLocator()->get(ModelFactory::SERVICE_ID); + parent::__construct($persistence, $modelFactory->getIteratorQuery($modelIds)); } /** @@ -46,7 +51,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 +63,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..ef5147200 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; @@ -63,8 +64,18 @@ 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); + $success = $modelFactory->addStatement( + $triple->modelid, + $triple->subject, + $triple->predicate, + $triple->object, + $triple->lg ?? '', + $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))); @@ -97,4 +108,4 @@ 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..6d051254f 100644 --- a/core/kernel/persistence/smoothsql/install/SmoothRdsModel.php +++ b/core/kernel/persistence/smoothsql/install/SmoothRdsModel.php @@ -19,40 +19,26 @@ */ namespace oat\generis\model\kernel\persistence\smoothsql\install; +use core_kernel_api_ModelFactory as ModelFactory; use Doctrine\DBAL\Schema\Schema; +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']); - - $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]); - - $table->addIndex(["subject","predicate"],"k_sp", [], ['lengths' => [164,164]]); - $table->addIndex(["predicate","object"],"k_po", [], ['lengths' => [164,164]]); + /** @var ModelFactory $modelFactory */ + $modelFactory = $this->getServiceLocator()->get(ModelFactory::SERVICE_ID); + $modelFactory->createModelsTable($schema); + $modelFactory->createStatementsTable($schema); return $schema; } } diff --git a/test/GenerisTestCase.php b/test/GenerisTestCase.php index a8d2353ce..3f6e5659b 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; @@ -39,14 +41,11 @@ class GenerisTestCase extends TestCase */ protected function getOntologyMock() { + $smoothRdsModel = new SmoothRdsModel(); $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); - } + $schema = $smoothRdsModel->addSmoothTables($schema); $session = new \common_session_AnonymousSession(); $sl = $this->getServiceLocatorMock([ @@ -56,6 +55,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,7 +67,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; }