[TASK] Introduce caching for record titles 59/43259/7
authorNicole Cordes <typo3@cordes.co>
Sun, 13 Sep 2015 09:13:26 +0000 (11:13 +0200)
committerNicole Cordes <typo3@cordes.co>
Sun, 13 Sep 2015 15:44:50 +0000 (17:44 +0200)
The title of a record is fetched multiple times inside the core.
The patch introduces an internal caching according to record and
method parameters.

Resolves: #69749
Related: #69721
Releases: master
Change-Id: I7c0b227872f9ce552d3bf4cce7d6460f4f0b3db2
Reviewed-on: http://review.typo3.org/43259
Reviewed-by: Stephan Großberndt <stephan@grossberndt.de>
Tested-by: Stephan Großberndt <stephan@grossberndt.de>
Reviewed-by: Jan Helke <typo3@helke.de>
Reviewed-by: Mathias Brodala <mbrodala@pagemachine.de>
Tested-by: Mathias Brodala <mbrodala@pagemachine.de>
Reviewed-by: Nicole Cordes <typo3@cordes.co>
Tested-by: Nicole Cordes <typo3@cordes.co>
typo3/sysext/backend/Classes/Utility/BackendUtility.php
typo3/sysext/backend/Tests/Unit/Utility/BackendUtilityTest.php

index 791e4c3..459ad57 100755 (executable)
@@ -58,10 +58,18 @@ class BackendUtility {
         * Cache the TCA configuration of tables with their types during runtime
         *
         * @var array
-        * @see getTCAtypes()
+        * @see self::getTCAtypes()
         */
        static protected $tcaTableTypeConfigurationCache = array();
 
+       /**
+        * Cache the processed titles of records during runtime
+        *
+        * @var array
+        * @see self::getRecordTitle()
+        */
+       static protected $recordTitleCache = array();
+
        /*******************************************
         *
         * SQL-related, selecting records, searching
@@ -2097,6 +2105,12 @@ class BackendUtility {
         * @return string
         */
        static public function getRecordTitle($table, $row, $prep = FALSE, $forceResult = TRUE) {
+               $cacheIdentifier = $table . '-' . $row['uid'] . '-prep-' . (int)$prep . '-forceResult-' . (int)$forceResult;
+               if (isset(self::$recordTitleCache[$cacheIdentifier])) {
+                       return self::$recordTitleCache[$cacheIdentifier];
+               }
+
+               $recordTitle = '';
                if (is_array($GLOBALS['TCA'][$table])) {
                        // If configured, call userFunc
                        if ($GLOBALS['TCA'][$table]['ctrl']['label_userFunc']) {
@@ -2108,47 +2122,48 @@ class BackendUtility {
                                // Create NULL-reference
                                $null = NULL;
                                GeneralUtility::callUserFunction($GLOBALS['TCA'][$table]['ctrl']['label_userFunc'], $params, $null);
-                               $t = $params['title'];
+                               $recordTitle = $params['title'];
                        } else {
                                if (is_array($row)) {
                                        $row = self::replaceL10nModeFields($table, $row);
                                }
 
                                // No userFunc: Build label
-                               $t = self::getProcessedValue($table, $GLOBALS['TCA'][$table]['ctrl']['label'], $row[$GLOBALS['TCA'][$table]['ctrl']['label']], 0, 0, FALSE, $row['uid'], $forceResult);
-                               if ($GLOBALS['TCA'][$table]['ctrl']['label_alt'] && ($GLOBALS['TCA'][$table]['ctrl']['label_alt_force'] || (string)$t === '')) {
+                               $recordTitle = self::getProcessedValue($table, $GLOBALS['TCA'][$table]['ctrl']['label'], $row[$GLOBALS['TCA'][$table]['ctrl']['label']], 0, 0, FALSE, $row['uid'], $forceResult);
+                               if ($GLOBALS['TCA'][$table]['ctrl']['label_alt'] && ($GLOBALS['TCA'][$table]['ctrl']['label_alt_force'] || (string)$recordTitle === '')) {
                                        $altFields = GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$table]['ctrl']['label_alt'], TRUE);
                                        $tA = array();
-                                       if (!empty($t)) {
-                                               $tA[] = $t;
+                                       if (!empty($recordTitle)) {
+                                               $tA[] = $recordTitle;
                                        }
                                        foreach ($altFields as $fN) {
-                                               $t = trim(strip_tags($row[$fN]));
-                                               if ((string)$t !== '') {
-                                                       $t = self::getProcessedValue($table, $fN, $t, 0, 0, FALSE, $row['uid']);
+                                               $recordTitle = trim(strip_tags($row[$fN]));
+                                               if ((string)$recordTitle !== '') {
+                                                       $recordTitle = self::getProcessedValue($table, $fN, $recordTitle, 0, 0, FALSE, $row['uid']);
                                                        if (!$GLOBALS['TCA'][$table]['ctrl']['label_alt_force']) {
                                                                break;
                                                        }
-                                                       $tA[] = $t;
+                                                       $tA[] = $recordTitle;
                                                }
                                        }
                                        if ($GLOBALS['TCA'][$table]['ctrl']['label_alt_force']) {
-                                               $t = implode(', ', $tA);
+                                               $recordTitle = implode(', ', $tA);
                                        }
                                }
                        }
                        // If the current result is empty, set it to '[No title]' (localized) and prepare for output if requested
                        if ($prep || $forceResult) {
                                if ($prep) {
-                                       $t = self::getRecordTitlePrep($t);
+                                       $recordTitle = self::getRecordTitlePrep($recordTitle);
                                }
-                               if (trim($t) === '') {
-                                       $t = self::getNoRecordTitle($prep);
+                               if (trim($recordTitle) === '') {
+                                       $recordTitle = self::getNoRecordTitle($prep);
                                }
                        }
-                       return $t;
                }
-               return '';
+               self::$recordTitleCache[$cacheIdentifier] = $recordTitle;
+
+               return $recordTitle;
        }
 
        /**
index 0b69664..5836c9c 100644 (file)
@@ -1025,6 +1025,27 @@ class BackendUtilityTest extends UnitTestCase {
        /**
         * @test
         */
+       public function getRecordTitleHavingLabelUserFuncCachesValue() {
+               $table = 'tx_mytable';
+               $row = ['uid' => 1];
+
+               $mock = $this->getMock('stdClass', ['labelUserFunc']);
+               $mock->expects($this->once())->method('labelUserFunc')->willReturn('Test');
+
+               // Use wrapping closure for GeneralUtility::callUserFunction()
+               $GLOBALS['TCA'][$table]['ctrl']['label_userFunc'] = function(&$parameters) use ($mock) {
+                       $parameters['title'] = $mock->labelUserFunc();
+               };
+
+               $this->assertEquals('Test', BackendUtility::getRecordTitle($table, $row));
+
+               // Call a second time to make sure labelUserFunc is not called again ($this->once())
+               $this->assertEquals('Test', BackendUtility::getRecordTitle($table, $row));
+       }
+
+       /**
+        * @test
+        */
        public function getSpecConfPartsSplitsDefaultExtras() {
                $defaultExtras = 'nowrap:wizards[foo|bar]:anotherDefaultExtras:some[other|setting|with|parameters]';
                $expected = array(