summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableRowMapper.php
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableRowMapper.php')
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableRowMapper.php310
1 files changed, 310 insertions, 0 deletions
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableRowMapper.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableRowMapper.php
new file mode 100644
index 00000000..b12554ca
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableRowMapper.php
@@ -0,0 +1,310 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use RuntimeException;
+use SMW\Exception\PredefinedPropertyLabelMismatchException;
+use SMW\SemanticData;
+use SMW\SQLStore\ChangeOp\ChangeOp;
+use SMW\Store;
+use SMWDataItem as DataItem;
+use SMWDIError as DIError;
+
+/**
+ * Builds a table row representation for a SemanticData object.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PropertyTableRowMapper {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @since 2.3
+ *
+ * @param Store $store
+ */
+ public function __construct( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $id
+ * @param SemanticData $semanticData
+ *
+ * @return ChangeOp
+ */
+ public function newChangeOp( $id, SemanticData $semanticData ) {
+
+ list( $dataArray, $textItems, $propertyList, $fixedPropertyList ) = $this->mapToRows(
+ $id,
+ $semanticData
+ );
+
+ $subject = $semanticData->getSubject();
+ $changeOp = new ChangeOp( $subject );
+
+ foreach ( $fixedPropertyList as $key => $record ) {
+ $changeOp->addFixedPropertyRecord( $key, $record );
+ }
+
+ $changeOp->addPropertyList( $propertyList );
+
+ $changeOp->addDataOp(
+ $subject->getHash(),
+ $dataArray
+ );
+
+ return $changeOp;
+ }
+
+ /**
+ * Create an array of rows to insert into property tables in order to
+ * store the given SemanticData. The given $sid (subject page id) is
+ * used directly and must belong to the subject of the data container.
+ * Sortkeys are ignored since they are not stored in a property table
+ * but in the ID table.
+ *
+ * The returned array uses property table names as keys and arrays of
+ * table rows as values. Each table row is an array mapping column
+ * names to values.
+ *
+ * @note Property tables that do not use ids as subjects are ignored.
+ * This just excludes redirects that are handled differently anyway;
+ * it would not make a difference to include them here.
+ *
+ * @since 3.0
+ *
+ * @param integer $sid
+ * @param SemanticData $semanticData
+ *
+ * @return array
+ */
+ public function mapToRows( $sid, SemanticData $semanticData ) {
+
+ list( $rows, $textItems, $propertyList, $fixedPropertyList ) = $this->mapData(
+ $sid,
+ $semanticData
+ );
+
+ return [ $rows, $textItems, $propertyList, $fixedPropertyList ];
+ }
+
+ /**
+ * Create a string key for hashing an array of values that represents a
+ * row in the database. Used to eliminate duplicates and to support
+ * diff computation. This is not stored in the database, so it can be
+ * changed without causing any problems with legacy data.
+ *
+ * @since 3.0
+ *
+ * @param array $fieldArray
+ *
+ * @return string
+ */
+ public function makeHash( array $array ) {
+ return md5( implode( '#', $array ) );;
+ }
+
+ /**
+ * Create an array of rows to insert into property tables in order to
+ * store the given SMWSemanticData. The given $sid (subject page id) is
+ * used directly and must belong to the subject of the data container.
+ * Sortkeys are ignored since they are not stored in a property table
+ * but in the ID table.
+ *
+ * The returned array uses property table names as keys and arrays of
+ * table rows as values. Each table row is an array mapping column
+ * names to values.
+ *
+ * @note Property tables that do not use ids as subjects are ignored.
+ * This just excludes redirects that are handled differently anyway;
+ * it would not make a difference to include them here.
+ *
+ * @since 1.8
+ *
+ * @param integer $sid
+ * @param SemanticData $semanticData
+ *
+ * @return array
+ */
+ private function mapData( $sid, SemanticData $semanticData ) {
+
+ $subject = $semanticData->getSubject();
+ $propertyTables = $this->store->getPropertyTables();
+
+ $rows = [];
+
+ // Keep the list for the Diff to avoid having to lookup any property ID
+ // reference during a post processing
+ $propertyList = [];
+ $fixedPropertyList = [];
+ $textItems = [];
+
+ foreach ( $semanticData->getProperties() as $property ) {
+
+ $tableId = $this->store->findPropertyTableID( $property );
+
+ // not stored in a property table, e.g., sortkeys
+ if ( $tableId === null ) {
+ continue;
+ }
+
+ // "Notice: Undefined index"
+ if ( !isset( $propertyTables[$tableId] ) ) {
+ throw new RuntimeException( "Unable to find a property table for " . $property->getKey() );
+ }
+
+ $propertyTable = $propertyTables[$tableId];
+
+ // not using subject ids, e.g., redirects
+ if ( !$propertyTable->usesIdSubject() ) {
+ continue;
+ }
+
+ $insertValues = [ 's_id' => $sid ];
+ $p_type = $property->findPropertyValueType();
+
+ if ( !$propertyTable->isFixedPropertyTable() ) {
+ $insertValues['p_id'] = $this->store->getObjectIds()->makeSMWPropertyID(
+ $property
+ );
+
+ $propertyList[$property->getKey()] = [ '_id' => $insertValues['p_id'], '_type' => $p_type ];
+ } else {
+ $pid = $this->store->getObjectIds()->makeSMWPropertyID(
+ $property
+ );
+
+ $fixedPropertyList[$tableId] = [
+ 'key' => $property->getKey(),
+ 'p_id' => $pid,
+ ];
+
+ $propertyList[$property->getKey()] = [ '_id' => $pid, '_type' => $p_type ];
+ }
+
+ $pid = $propertyList[$property->getKey()]['_id'];
+
+ if ( !isset( $textItems[$pid] ) ) {
+ $textItems[$pid] = [];
+ }
+
+ // Avoid issues when an expected predefined property is no longer
+ // available (i.e. an extension that defined that property was disabled)
+ try {
+ $propertyValues = $semanticData->getPropertyValues( $property );
+ } catch( PredefinedPropertyLabelMismatchException $e ) {
+ continue;
+ }
+
+ foreach ( $propertyValues as $dataItem ) {
+
+ if ( $dataItem instanceof DIError ) { // ignore error values
+ continue;
+ }
+
+ $tableName = $propertyTable->getName();
+
+ if ( !array_key_exists( $tableName, $rows ) ) {
+ $rows[$tableName] = [];
+ }
+
+ if ( $dataItem->getDIType() === DataItem::TYPE_BLOB ) {
+ $textItems[$pid][] = $dataItem->getString();
+ } elseif ( $dataItem->getDIType() === DataItem::TYPE_URI ) {
+ $textItems[$pid][] = $dataItem->getSortKey();
+ } elseif ( $dataItem->getDIType() === DataItem::TYPE_WIKIPAGE ) {
+ $textItems[$pid][] = $dataItem->getSortKey();
+ }
+
+ $dataItemValues = $this->store->getDataItemHandlerForDIType( $dataItem->getDIType() )->getInsertValues( $dataItem );
+
+ // Ensure that the sortkey is a string
+ if ( isset( $dataItemValues['o_sortkey'] ) ) {
+ $dataItemValues['o_sortkey'] = (string)$dataItemValues['o_sortkey'];
+ }
+
+ $insertValues = array_merge( $insertValues, $dataItemValues );
+
+ // Make sure to build a unique set without duplicates which could happen
+ // if an annotation is made to a property that has a redirect pointing
+ // to the same p_id
+ $hash = $this->makeHash(
+ $insertValues
+ );
+
+ $rows[$tableName][$hash] = $insertValues;
+ }
+
+ // Unused
+ if ( $textItems[$pid] === [] ) {
+ unset( $textItems[$pid] );
+ }
+ }
+
+ // Special handling of Concepts
+ if ( $subject->getNamespace() === SMW_NS_CONCEPT && $subject->getSubobjectName() == '' ) {
+ $this->mapConceptTable( $sid, $rows );
+ }
+
+ return [ $rows, $textItems, $propertyList, $fixedPropertyList ];
+ }
+
+ /**
+ * Add cache information to concept data and make sure that there is
+ * exactly one value for the concept table.
+ *
+ * @note This code will vanish when concepts have a more standard
+ * handling. So not point in optimizing this much now.
+ *
+ * @since 1.8
+ * @param integer $sid
+ * @param &array $insertData
+ */
+ private function mapConceptTable( $sid, &$insertData ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ // Make sure that there is exactly one row to be written:
+ if ( array_key_exists( 'smw_fpt_conc', $insertData ) && !empty( $insertData['smw_fpt_conc'] ) ) {
+ $insertValues = end( $insertData['smw_fpt_conc'] );
+ } else {
+ $insertValues = [
+ 's_id' => $sid,
+ 'concept_txt' => '',
+ 'concept_docu' => '',
+ 'concept_features' => 0,
+ 'concept_size' => -1,
+ 'concept_depth' => -1
+ ];
+ }
+
+ // Add existing cache status data to this row:
+ $row = $connection->selectRow(
+ 'smw_fpt_conc',
+ [ 'cache_date', 'cache_count' ],
+ [ 's_id' => $sid ],
+ __METHOD__
+ );
+
+ if ( $row === false ) {
+ $insertValues['cache_date'] = null;
+ $insertValues['cache_count'] = null;
+ } else {
+ $insertValues['cache_date'] = $row->cache_date;
+ $insertValues['cache_count'] = $row->cache_count;
+ }
+
+ $insertData['smw_fpt_conc'] = [ $insertValues ];
+ }
+
+}