[FEATURE] Allow multiple category fields per table 72/23772/20
authorSebastian Michaelsen <sebastian.michaelsen@t3seo.de>
Wed, 11 Sep 2013 15:07:13 +0000 (17:07 +0200)
committerMarkus Klein <klein.t3@mfc-linz.at>
Tue, 17 Sep 2013 12:48:48 +0000 (14:48 +0200)
It is only possible to do one makeCategorizable() call per
table, because if you do more the later calls would overwrite the
previous category field declaration. That's because of the way
the registry stores the category fields - it can only store one
field per table. This change improves that and allow multiple
fields pers table.

Additionally, a custom label can be set in the options array
for each category field.

Resolves: #51921
Releases: 6.2
Change-Id: I96d1235176109395339e9841564edadc0c71147d
Reviewed-on: https://review.typo3.org/23772
Reviewed-by: Helmut Hummel
Tested-by: Helmut Hummel
Reviewed-by: Markus Klein
Tested-by: Markus Klein
NEWS.txt
typo3/sysext/core/Classes/Category/CategoryRegistry.php
typo3/sysext/core/Tests/Unit/Category/CategoryRegistryTest.php
typo3/sysext/core/ext_tables.sql

index ac01349..7191a4e 100644 (file)
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -45,6 +45,13 @@ PHP Framework Interop Group: http://www.php-fig.org/psr/3/
 Backend
 -------------------------------------------------------------------------------
 
+* Categorization API improvements
+
+\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::makeCategorizable() can now
+be used multiple times on the same table to add more than one category field.
+The options array (the fourth parameter) now can contain a 'label' to set a
+custom label for each category field.
+
 -------------------------------------------------------------------------------
 Frontend
 -------------------------------------------------------------------------------
index c8ef04a..3cd4f83 100644 (file)
@@ -40,6 +40,11 @@ class CategoryRegistry implements \TYPO3\CMS\Core\SingletonInterface {
        protected $registry = array();
 
        /**
+        * @var array
+        */
+       protected $addedCategoryTabs = array();
+
+       /**
         * @var string
         */
        protected $template = '';
@@ -70,23 +75,25 @@ class CategoryRegistry implements \TYPO3\CMS\Core\SingletonInterface {
         *              + fieldList: field configuration to be added to showitems
         *              + typesList: list of types that shall visualize the categories field
         *              + position: insert position of the categories field
+        *              + label: backend label of the categories field
         *              + fieldConfiguration: TCA field config array to override defaults
         * @return boolean
-        * @throws \RuntimeException
+        * @throws \InvalidArgumentException
         */
        public function add($extensionKey, $tableName, $fieldName = 'categories', $options = array()) {
                $result = FALSE;
 
                if ($tableName === '') {
-                       throw new \RuntimeException('TYPO3\\CMS\\Core\\Category\\CategoryRegistry No tableName given.', 1369122038);
+                       throw new \InvalidArgumentException('TYPO3\\CMS\\Core\\Category\\CategoryRegistry No tableName given.', 1369122038);
                }
 
-                       // Makes sure there is an existing table configuration and nothing registered yet:
-               if (!$this->isRegistered($tableName, $fieldName)) {
-                       $this->registry[$extensionKey][$tableName] = array (
-                               'fieldName' => $fieldName,
-                               'options' => $options,
-                       );
+               if (!is_array($options)) {
+                       throw new \InvalidArgumentException('TYPO3\\CMS\\Core\\Category\\CategoryRegistry options parameter must be an array', 1378976970);
+               }
+
+               // Makes sure there is an existing table configuration and nothing registered yet:
+               if (isset($GLOBALS['TCA'][$tableName]) && !$this->isRegistered($tableName, $fieldName)) {
+                       $this->registry[$extensionKey][$tableName][$fieldName] = $options;
                        $result = TRUE;
                }
                return $result;
@@ -95,9 +102,11 @@ class CategoryRegistry implements \TYPO3\CMS\Core\SingletonInterface {
        /**
         * Gets the registered category configurations.
         *
+        * @deprecated since 6.2 will be removed two versions later - Use ->isRegistered to get information about registered category fields.
         * @return array
         */
        public function get() {
+               \TYPO3\CMS\Core\Utility\GeneralUtility::logDeprecatedFunction();
                return $this->registry;
        }
 
@@ -135,7 +144,7 @@ class CategoryRegistry implements \TYPO3\CMS\Core\SingletonInterface {
        public function isRegistered($tableName, $fieldName = 'categories') {
                $isRegistered = FALSE;
                foreach ($this->registry as $configuration) {
-                       if (!empty($configuration[$tableName]['fieldName']) && $configuration[$tableName]['fieldName'] === $fieldName) {
+                       if (isset($configuration[$tableName][$fieldName])) {
                                $isRegistered = TRUE;
                                break;
                        }
@@ -168,8 +177,10 @@ class CategoryRegistry implements \TYPO3\CMS\Core\SingletonInterface {
                }
                $sql = '';
 
-               foreach ($this->registry[$extensionKey] as $tableName => $tableInfo) {
-                       $sql .= sprintf($this->template, $tableName, $tableInfo['fieldName']);
+               foreach ($this->registry[$extensionKey] as $tableName => $fields) {
+                       foreach (array_keys($fields) as $fieldName) {
+                               $sql .= sprintf($this->template, $tableName, $fieldName);
+                       }
                }
                return $sql;
        }
@@ -184,9 +195,11 @@ class CategoryRegistry implements \TYPO3\CMS\Core\SingletonInterface {
                $this->registerDefaultCategorizedTables();
 
                foreach ($this->registry as $registry) {
-                       foreach ($registry as $tableName => $tableInfo) {
-                               $this->addTcaColumn($tableName, $tableInfo['fieldName'], $tableInfo['options']);
-                               $this->addToAllTCAtypes($tableName, $tableInfo['fieldName'], $tableInfo['options']);
+                       foreach ($registry as $tableName => $fields) {
+                               foreach ($fields as $fieldName => $options) {
+                                       $this->addTcaColumn($tableName, $fieldName, $options);
+                                       $this->addToAllTCAtypes($tableName, $fieldName, $options);
+                               }
                        }
                }
        }
@@ -225,8 +238,9 @@ class CategoryRegistry implements \TYPO3\CMS\Core\SingletonInterface {
                // Makes sure to add more TCA to an existing structure
                if (isset($GLOBALS['TCA'][$tableName]['columns'])) {
 
-                       $fieldList = '--div--;LLL:EXT:lang/locallang_tca.xlf:sys_category.tabs.category, ' . $fieldName;
-                       if (!empty($options['fieldList'])) {
+                       if (empty($options['fieldList'])) {
+                               $fieldList = $this->addCategoryTab($tableName, $fieldName);
+                       } else {
                                $fieldList = $options['fieldList'];
                        }
 
@@ -247,12 +261,31 @@ class CategoryRegistry implements \TYPO3\CMS\Core\SingletonInterface {
        }
 
        /**
+        * Creates the 'fieldList' string for $fieldName which includes a categories tab.
+        * But only one categories tab is added per table.
+        *
+        * @param string $tableName
+        * @param string $fieldName
+        * @return string
+        */
+       protected function addCategoryTab($tableName, $fieldName) {
+               $fieldList = '';
+               if (!in_array($tableName, $this->addedCategoryTabs)) {
+                       $fieldList .= '--div--;LLL:EXT:lang/locallang_tca.xlf:sys_category.tabs.category, ';
+                       $this->addedCategoryTabs[] = $tableName;
+               }
+               $fieldList .= $fieldName;
+               return $fieldList;
+       }
+
+       /**
         * Add a new TCA Column
         *
         * @param string $tableName Name of the table to be categorized
         * @param string $fieldName Name of the field to be used to store categories
         * @param array $options Additional configuration options
         *              + fieldConfiguration: TCA field config array to override defaults
+        *              + label: backend label of the categories field
         * @return void
         */
        protected function addTcaColumn($tableName, $fieldName, $options) {
@@ -267,7 +300,10 @@ class CategoryRegistry implements \TYPO3\CMS\Core\SingletonInterface {
                                'foreign_table_where' => ' AND sys_category.sys_language_uid IN (-1, 0) ORDER BY sys_category.title ASC',
                                'MM' => 'sys_category_record_mm',
                                'MM_opposite_field' => 'items',
-                               'MM_match_fields' => array('tablenames' => $tableName),
+                               'MM_match_fields' => array(
+                                       'tablenames' => $tableName,
+                                       'fieldname' => $fieldName,
+                               ),
                                'size' => 10,
                                'autoSizeMax' => 50,
                                'maxitems' => 9999,
@@ -311,10 +347,15 @@ class CategoryRegistry implements \TYPO3\CMS\Core\SingletonInterface {
                                );
                        }
 
+                       $label = 'LLL:EXT:lang/locallang_tca.xlf:sys_category.categories';
+                       if (!empty($options['label'])) {
+                               $label = $options['label'];
+                       }
+
                        $columns = array(
                                $fieldName => array(
                                        'exclude' => 0,
-                                       'label' => 'LLL:EXT:lang/locallang_tca.xlf:sys_category.categories',
+                                       'label' => $label,
                                        'config' => $fieldConfiguration,
                                ),
                        );
index 08a6423..1f74533 100644 (file)
@@ -52,7 +52,13 @@ class CategoryRegistryTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                        'second' => uniqid('second')
                );
                foreach ($this->tables as $tableName) {
-                       $GLOBALS['TCA'][$tableName] = array('ctrl' => array(), 'columns' => array());
+                       $GLOBALS['TCA'][$tableName] = array(
+                               'ctrl' => array(),
+                               'columns' => array(),
+                               'types' => array(
+                                       '1' => array()
+                               ),
+                       );
                }
        }
 
@@ -67,13 +73,6 @@ class CategoryRegistryTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        /**
         * @test
         */
-       public function isRegistryEmptyByDefault() {
-               $this->assertEquals(array(), $this->fixture->get());
-       }
-
-       /**
-        * @test
-        */
        public function doesAddReturnTrueOnDefinedTable() {
                $this->assertTrue($this->fixture->add('test_extension_a', $this->tables['first'], 'categories'));
        }
@@ -82,40 +81,37 @@ class CategoryRegistryTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
         * @test
         */
        public function doesAddReturnFalseOnUndefinedTable() {
-               $this->fixture->add('test_extension_a', $this->tables['first'], 'categories');
-
-               $this->assertFalse(
-                       $this->fixture->add('test_extension_a', $this->tables['first'], 'categories')
-               );
+               $this->assertFalse($this->fixture->add('test_extension_a', 'undefined_table', 'categories'));
        }
 
        /**
         * @test
-        * @expectedException \RuntimeException
+        * @expectedException \InvalidArgumentException
         * @expectedExceptionCode 1369122038
         */
-       public function doesAddThrowExceptionOnEmptyAdds() {
+       public function doesAddThrowExceptionOnEmptyTablename() {
                $this->fixture->add('test_extension_a', '', 'categories');
        }
 
        /**
         * @test
+        * @expectedException \InvalidArgumentException
+        * @expectedExceptionCode 1378976970
+        */
+       public function doesAddThrowExceptionIfOptionsIsNotAnArray() {
+               $this->fixture->add('test_extension_a', $this->tables['first'], 'categories', 'not_an_array');
+       }
+
+       /**
+        * @test
         */
        public function areMultipleElementsOfSameExtensionRegistered() {
                $this->fixture->add('test_extension_a', $this->tables['first'], 'categories');
-               $this->fixture->add('test_extension_b', $this->tables['second'], 'categories');
-               $registry = $this->fixture->get();
-               ob_flush();
-
-               $this->assertEquals(
-                       'categories',
-                       $registry['test_extension_a'][$this->tables['first']]['fieldName']
-               );
+               $this->fixture->add('test_extension_a', $this->tables['second'], 'categories');
+               $this->fixture->applyTca();
 
-               $this->assertEquals(
-                       'categories',
-                       $registry['test_extension_b'][$this->tables['second']]['fieldName']
-               );
+               $this->assertArrayHasKey('categories', $GLOBALS['TCA'][$this->tables['first']]['columns']);
+               $this->assertArrayHasKey('categories', $GLOBALS['TCA'][$this->tables['second']]['columns']);
        }
 
        /**
@@ -124,31 +120,34 @@ class CategoryRegistryTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        public function areElementsOfDifferentExtensionsRegistered() {
                $this->fixture->add('test_extension_a', $this->tables['first'], 'categories');
                $this->fixture->add('test_extension_b', $this->tables['second'], 'categories');
-               $registry = $this->fixture->get();
+               $this->fixture->applyTca();
 
-               $this->assertEquals(
-                       'categories',
-                       $registry['test_extension_a'][$this->tables['first']]['fieldName']
-               );
+               $this->assertArrayHasKey('categories', $GLOBALS['TCA'][$this->tables['first']]['columns']);
+               $this->assertArrayHasKey('categories', $GLOBALS['TCA'][$this->tables['second']]['columns']);
+       }
 
-               $this->assertEquals(
-                       'categories',
-                       $registry['test_extension_b'][$this->tables['second']]['fieldName']
-               );
+       /**
+        * @test
+        */
+       public function areElementsOfDifferentExtensionsOnSameTableRegistered() {
+               $this->fixture->add('test_extension_a', $this->tables['first'], 'categories1');
+               $this->fixture->add('test_extension_b', $this->tables['first'], 'categories2');
+               $this->fixture->applyTca();
+
+               $this->assertArrayHasKey('categories1', $GLOBALS['TCA'][$this->tables['first']]['columns']);
+               $this->assertArrayHasKey('categories2', $GLOBALS['TCA'][$this->tables['first']]['columns']);
        }
 
        /**
         * @test
         */
-       public function areElementsOnSameTableOverridden() {
-               $this->fixture->add('test_extension_a', $this->tables['first'], $this->tables['first']);
-               $this->fixture->add('test_extension_b', $this->tables['second'], $this->tables['second']);
-               $registry = $this->fixture->get();
+       public function areElementsOfSameExtensionOnSameTableRegistered() {
+               $this->fixture->add('test_extension_a', $this->tables['first'], 'categories1');
+               $this->fixture->add('test_extension_a', $this->tables['first'], 'categories2');
+               $this->fixture->applyTca();
 
-               $this->assertEquals(
-                       $this->tables['first'],
-                       $registry['test_extension_a'][$this->tables['first']]['fieldName']
-               );
+               $this->assertArrayHasKey('categories1', $GLOBALS['TCA'][$this->tables['first']]['columns']);
+               $this->assertArrayHasKey('categories2', $GLOBALS['TCA'][$this->tables['first']]['columns']);
        }
 
        /**
@@ -189,44 +188,79 @@ class CategoryRegistryTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                $GLOBALS['TYPO3_CONF_VARS']['SYS']['defaultCategorizedTables'] = $this->tables['first'] . ',' . $this->tables['second'];
                $this->fixture->applyTca();
 
-               $registry = $this->fixture->get();
-               $this->assertCount(2, $registry['core']);
-               $this->assertSame(key($registry['core']), $this->tables['first']);
+               $this->assertArrayHasKey('categories', $GLOBALS['TCA'][$this->tables['first']]['columns']);
+               $this->assertArrayHasKey('categories', $GLOBALS['TCA'][$this->tables['second']]['columns']);
        }
 
        /**
         * @test
         */
-       public function areEmptyStringDefaultCategorizedTablesLoaded() {
-               $GLOBALS['TYPO3_CONF_VARS']['SYS']['defaultCategorizedTables'] = '';
+       public function canApplyTca() {
+               $this->fixture->add('test_extension_a', $this->tables['first'], 'categories');
+               $this->fixture->add('test_extension_b', $this->tables['second'], 'categories');
                $this->fixture->applyTca();
 
-               $registry = $this->fixture->get();
-               $this->assertNull($registry['core']);
+               $this->assertNotEmpty($GLOBALS['TCA'][$this->tables['first']]['columns']['categories']);
+               $this->assertNotEmpty($GLOBALS['TCA'][$this->tables['second']]['columns']['categories']);
+       }
+
+       /**
+        * @test
+        */
+       public function isRegisteredReturnsTrueIfElementIsAlreadyRegistered() {
+               $this->fixture->add('test_extension_a', $this->tables['first'], 'categories');
+               $this->assertTrue($this->fixture->isRegistered($this->tables['first'], 'categories'));
        }
 
        /**
         * @test
         */
-       public function areNullDefaultCategorizedTablesLoaded() {
-               $GLOBALS['TYPO3_CONF_VARS']['SYS']['defaultCategorizedTables'] = NULL;
+       public function isRegisteredReturnsFalseIfElementIsNotRegistered() {
+               $this->fixture->add('test_extension_a', $this->tables['first'], 'categories');
+               $this->assertFalse($this->fixture->isRegistered($this->tables['first'], '_not_registered'));
+               $this->assertFalse($this->fixture->isRegistered($this->tables['second'], 'categories'));
+       }
+
+       /**
+        * @test
+        */
+       public function tabIsAddedForElement() {
+               $this->fixture->add('text_extension_a', $this->tables['first']);
                $this->fixture->applyTca();
 
-               $registry = $this->fixture->get();
-               $this->assertNull($registry['core']);
+               foreach($GLOBALS['TCA'][$this->tables['first']]['types'] as $typeConfig) {
+                       $this->assertContains('--div--;LLL:EXT:lang/locallang_tca.xlf:sys_category.tabs.category', $typeConfig['showitem']);
+               }
        }
 
        /**
         * @test
         */
-       public function canApplyTca() {
-               $this->fixture->add('test_extension_a', $this->tables['first'], 'categories');
-               $this->fixture->add('test_extension_b', $this->tables['second'], 'categories');
+       public function tabIsNotAddedForElementIfFieldListIsSpecified() {
+               $this->fixture->add('text_extension_a', $this->tables['first'], 'categories', array('fieldList' => 'categories'));
+               $this->fixture->applyTca();
+
+               foreach($GLOBALS['TCA'][$this->tables['first']]['types'] as $typeConfig) {
+                       $this->assertNotContains('--div--;LLL:EXT:lang/locallang_tca.xlf:sys_category.tabs.category', $typeConfig['showitem']);
+               }
+       }
+
+       /**
+        * @test
+        */
+       public function tabIsAddedOnlyOncePerTable() {
+               $this->fixture->add('text_extension_a', $this->tables['first'], 'categories1');
+               $this->fixture->add('text_extension_a', $this->tables['first'], 'categories2');
                $this->fixture->applyTca();
-               foreach ($this->tables as $table) {
-                       $this->assertNotEmpty($GLOBALS['TCA'][$table]['columns']['categories']);
+
+               foreach($GLOBALS['TCA'][$this->tables['first']]['types'] as $typeConfig) {
+                       $this->assertSame(
+                               1, substr_count($typeConfig['showitem'], '--div--;LLL:EXT:lang/locallang_tca.xlf:sys_category.tabs.category')
+                       );
                }
+
        }
+
 }
 
 ?>
\ No newline at end of file
index 1888971..09d2dbd 100644 (file)
@@ -652,6 +652,7 @@ CREATE TABLE sys_category_record_mm (
        uid_local int(11) DEFAULT '0' NOT NULL,
        uid_foreign int(11) DEFAULT '0' NOT NULL,
        tablenames varchar(255) DEFAULT '' NOT NULL,
+       fieldname varchar(255) DEFAULT '' NOT NULL,
        sorting int(11) DEFAULT '0' NOT NULL,
        sorting_foreign int(11) DEFAULT '0' NOT NULL,