[TASK] Enhance SQL query reduction in page tree in workspaces 44/42944/7
authorOliver Hader <oliver@typo3.org>
Mon, 31 Aug 2015 14:45:43 +0000 (16:45 +0200)
committerBenni Mack <benni@typo3.org>
Thu, 21 Apr 2016 09:55:17 +0000 (11:55 +0200)
This changeset is a follow-up to the changes for issue #50349 and
it partly reverts the changes that have been introduced back then.

The most important changes are:

* Allow early return on first found record in hasPageVersions.
  The previous implementation performed SQL queries on all tables
  that are defined in the global TCA array. Now the first version
  occurrence is enough to infer that a page has any versions.

* Integrate hooks to modify the determined results. This way, the
  meaning of having versions can be further modified by hooks.
  For instance, the default behavior of the TYPO3 core is to create
  a workspace version record on persisting the same record in the
  backend - without any actual changes to the data model.

Resolves: #69439
Releases: master
Change-Id: I119a79d8fad82b0dc5891861af45ecfdbc681820
Reviewed-on: https://review.typo3.org/42944
Reviewed-by: Andreas Wolf <andreas.wolf@typo3.org>
Reviewed-by: Susanne Moog <typo3@susannemoog.de>
Tested-by: Susanne Moog <typo3@susannemoog.de>
Reviewed-by: Benni Mack <benni@typo3.org>
Tested-by: Benni Mack <benni@typo3.org>
typo3/sysext/core/Documentation/Changelog/master/Feature-69439-EnhanceSQLQueryReductionInPageTreeInWorkspaces.rst [new file with mode: 0644]
typo3/sysext/workspaces/Classes/Service/WorkspaceService.php

diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-69439-EnhanceSQLQueryReductionInPageTreeInWorkspaces.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-69439-EnhanceSQLQueryReductionInPageTreeInWorkspaces.rst
new file mode 100644 (file)
index 0000000..4f87392
--- /dev/null
@@ -0,0 +1,28 @@
+========================================================================
+Feature: #69439 - Enhance SQL query reduction in page tree in workspaces
+========================================================================
+
+Description
+===========
+
+The process of determining whether a page has workspace versions can be
+extended by custom application code utilizing hooks. This way, the meaning
+of having versions can be modified by hooks further. For instance the
+default behavior of the TYPO3 core is to create a workspace version
+record on persisting the same record in the backend - without any
+actual changes to the data model.
+
++ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\\CMS\\Workspaces\\Service\\WorkspaceService']['hasPageRecordVersions']
+  + $parameters['workspaceId']: The submitted workspace ID
+  + $parameters['pageId']: The submitted page ID
+  + $parameters['versionsOnPageCache']: Reference to the state array
++ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\\CMS\\Workspaces\\Service\\WorkspaceService']['fetchPagesWithVersionsInTable']
+  + $parameters['workspaceId']: The submitted workspace ID
+  + $parameters['pageId']: The submitted page ID
+  + $parameters['pagesWithVersionsInTable']: Reference to the state array
+
+
+Impact
+======
+
+The hooks introduce the possibility to modify the determined results - only if those hooks are used.
\ No newline at end of file
index 812cf17..204abfd 100644 (file)
@@ -35,6 +35,11 @@ class WorkspaceService implements SingletonInterface
      */
     protected $versionsOnPageCache = array();
 
+    /**
+     * @var array
+     */
+    protected $pagesWithVersionsInTable = array();
+
     const TABLE_WORKSPACE = 'sys_workspace';
     const SELECT_ALL_WORKSPACES = -98;
     const LIVE_WORKSPACE_ID = 0;
@@ -719,56 +724,145 @@ class WorkspaceService implements SingletonInterface
     }
 
     /**
-     * Checks if a page has record versions according to a given workspace
+     * Determines whether a page has workspace versions.
      *
-     * @param int $workspace
+     * @param int $workspaceId
      * @param int $pageId
      * @return bool
      */
-    public function hasPageRecordVersions($workspace, $pageId)
+    public function hasPageRecordVersions($workspaceId, $pageId)
     {
-        $workspace = (int)$workspace;
-        $pageId = (int)$pageId;
-        if ($workspace === 0) {
+        if ((int)$workspaceId === 0 || (int)$pageId === 0) {
             return false;
         }
 
-        if (isset($this->versionsOnPageCache[$pageId][$workspace])) {
-            return $this->versionsOnPageCache[$pageId][$workspace];
+        if (isset($this->versionsOnPageCache[$workspaceId][$pageId])) {
+            return $this->versionsOnPageCache[$workspaceId][$pageId];
         }
 
-        if (!empty($this->versionsOnPageCache)) {
-            return false;
+        $this->versionsOnPageCache[$workspaceId][$pageId] = false;
+
+        foreach ($GLOBALS['TCA'] as $tableName => $tableConfiguration) {
+            if ($tableName === 'pages' || empty($tableConfiguration['ctrl']['versioningWS'])) {
+                continue;
+            }
+
+            $pages = $this->fetchPagesWithVersionsInTable($workspaceId, $tableName);
+            // Early break on first match
+            if (!empty($pages[(string)$pageId])) {
+                $this->versionsOnPageCache[$workspaceId][$pageId] = true;
+                break;
+            }
+        }
+
+        if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\\CMS\\Workspaces\\Service\\WorkspaceService']['hasPageRecordVersions'])
+            && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\\CMS\\Workspaces\\Service\\WorkspaceService']['hasPageRecordVersions'])) {
+            $parameters = array(
+                'workspaceId' => $workspaceId,
+                'pageId' => $pageId,
+                'versionsOnPageCache' => &$this->versionsOnPageCache,
+            );
+            foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\\CMS\\Workspaces\\Service\\WorkspaceService']['hasPageRecordVersions'] as $hookFunction) {
+                GeneralUtility::callUserFunction($hookFunction, $parameters, $this);
+            }
         }
 
-        $this->versionsOnPageCache[$pageId][$workspace] = false;
+        return $this->versionsOnPageCache[$workspaceId][$pageId];
+    }
+
+    /**
+     * Gets all pages that have workspace versions per table.
+     *
+     * Result:
+     * [
+     *   'tt_content' => [
+     *     1 => 1,
+     *     11 => 11,
+     *     13 => 13,
+     *     15 => 15
+     *   ],
+     *   'tx_something => [
+     *     15 => 15,
+     *     11 => 11,
+     *     21 => 21
+     *   ],
+     * ]
+     *
+     * @param int $workspaceId
+     * @return array
+     */
+    public function getPagesWithVersionsInTable($workspaceId)
+    {
         foreach ($GLOBALS['TCA'] as $tableName => $tableConfiguration) {
             if ($tableName === 'pages' || empty($tableConfiguration['ctrl']['versioningWS'])) {
                 continue;
             }
+
+            $this->fetchPagesWithVersionsInTable($workspaceId, $tableName);
+        }
+
+        return $this->pagesWithVersionsInTable[$workspaceId];
+    }
+
+    /**
+     * Gets all pages that have workspace versions in a particular table.
+     *
+     * Result:
+     * [
+     *   1 => 1,
+     *   11 => 11,
+     *   13 => 13,
+     *   15 => 15
+     * ],
+     *
+     * @param int $workspaceId
+     * @param string $tableName
+     * @return array
+     */
+    protected function fetchPagesWithVersionsInTable($workspaceId, $tableName)
+    {
+        if ((int)$workspaceId === 0) {
+            return array();
+        }
+
+        if (!isset($this->pagesWithVersionsInTable[$workspaceId])) {
+            $this->pagesWithVersionsInTable[$workspaceId] = array();
+        }
+
+        if (!isset($this->pagesWithVersionsInTable[$workspaceId][$tableName])) {
+            $this->pagesWithVersionsInTable[$workspaceId][$tableName] = array();
+
             // Consider records that are moved to a different page
             $movePointer = new VersionState(VersionState::MOVE_POINTER);
             $joinStatement = '(A.t3ver_oid=B.uid AND A.t3ver_state<>' . $movePointer
                 . ' OR A.t3ver_oid=B.t3ver_move_id AND A.t3ver_state=' . $movePointer . ')';
 
-            // Select all records from this table in the database from the workspace
-            // This joins the online version with the offline version as tables A and B
-            $records = $this->getDatabaseConnection()->exec_SELECTgetRows(
-                'B.uid as live_uid, B.pid as live_pid, A.uid as offline_uid',
+
+            $pageIds = $this->getDatabaseConnection()->exec_SELECTgetRows(
+                'B.pid AS pageId',
                 $tableName . ' A,' . $tableName . ' B',
-                'A.pid=-1 AND A.t3ver_wsid=' . $workspace . ' AND ' . $joinStatement .
-                BackendUtility::deleteClause($tableName, 'A') . BackendUtility::deleteClause($tableName, 'B'),
-                'live_pid'
+                'A.pid=-1 AND A.t3ver_wsid=' . (int)$workspaceId . ' AND ' . $joinStatement
+                    . BackendUtility::deleteClause($tableName, 'A') . BackendUtility::deleteClause($tableName, 'B'),
+                'pageId', '', '',
+                'pageId'
             );
 
-            if (!empty($records)) {
-                foreach ($records as $record) {
-                    $this->versionsOnPageCache[$record['live_pid']][$workspace] = true;
+            $this->pagesWithVersionsInTable[$workspaceId][$tableName] = $pageIds;
+
+            if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\\CMS\\Workspaces\\Service\\WorkspaceService']['fetchPagesWithVersionsInTable'])
+                && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\\CMS\\Workspaces\\Service\\WorkspaceService']['fetchPagesWithVersionsInTable'])) {
+                $parameters = array(
+                    'workspaceId' => $workspaceId,
+                    'tableName' => $tableName,
+                    'pagesWithVersionsInTable' => &$this->pagesWithVersionsInTable,
+                );
+                foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\\CMS\\Workspaces\\Service\\WorkspaceService']['fetchPagesWithVersionsInTable'] as $hookFunction) {
+                    GeneralUtility::callUserFunction($hookFunction, $parameters, $this);
                 }
             }
         }
 
-        return $this->versionsOnPageCache[$pageId][$workspace];
+        return $this->pagesWithVersionsInTable[$workspaceId][$tableName];
     }
 
     /**