[BUGFIX] CategoryCollection must handle multiple relations 66/24066/6
authorFrancois Suter <francois@typo3.org>
Thu, 26 Sep 2013 14:00:31 +0000 (16:00 +0200)
committerFrancois Suter <francois@typo3.org>
Thu, 17 Oct 2013 06:50:26 +0000 (08:50 +0200)
Since #51921 it is possible to have multiple fields for defining
relations to categories. The CategoryCollection and the
categories-based page menu are now adapted to handle this correctly,
instead of retrieving records for relations defined in all fields.

Resolves: #52285
Documentation: #52302
Releases: 6.2
Change-Id: I156528669704004a86243e03f34d3a1af07cdb21
Reviewed-on: https://review.typo3.org/24066
Reviewed-by: Markus Klein
Tested-by: Markus Klein
Reviewed-by: Fabien Udriot
Tested-by: Fabien Udriot
Reviewed-by: Francois Suter
Tested-by: Francois Suter
12 files changed:
typo3/sysext/cms/locallang_ttc.xlf
typo3/sysext/core/Classes/Category/CategoryRegistry.php
typo3/sysext/core/Classes/Category/Collection/CategoryCollection.php
typo3/sysext/css_styled_content/static/setup.txt
typo3/sysext/css_styled_content/static/v4.5/setup.txt
typo3/sysext/css_styled_content/static/v4.6/setup.txt
typo3/sysext/css_styled_content/static/v4.7/setup.txt
typo3/sysext/css_styled_content/static/v6.0/setup.txt
typo3/sysext/css_styled_content/static/v6.1/setup.txt
typo3/sysext/frontend/Classes/ContentObject/Menu/CategoryMenuUtility.php
typo3/sysext/frontend/Configuration/TCA/tt_content.php
typo3/sysext/frontend/ext_tables.sql

index 68acf9d..1af2f98 100644 (file)
                        <trans-unit id="selected_categories" xml:space="preserve">
                                <source>Selected categories</source>
                        </trans-unit>
+                       <trans-unit id="category_field" xml:space="preserve">
+                               <source>Category field</source>
+                       </trans-unit>
                        <trans-unit id="ALT.imgOptions" xml:space="preserve">
                                <source>Image Options</source>
                        </trans-unit>
index 8eccde0..fd1b4c5 100644 (file)
@@ -62,7 +62,8 @@ class CategoryRegistry implements \TYPO3\CMS\Core\SingletonInterface {
         * Creates this object.
         */
        public function __construct() {
-               $this->template = str_repeat(PHP_EOL, 3) . 'CREATE TABLE %s (' . PHP_EOL . '  %s int(11) DEFAULT \'0\' NOT NULL' . PHP_EOL . ');' . str_repeat(PHP_EOL, 3);
+               $this->template = str_repeat(PHP_EOL, 3) . 'CREATE TABLE %s (' . PHP_EOL
+                       . '  %s int(11) DEFAULT \'0\' NOT NULL' . PHP_EOL . ');' . str_repeat(PHP_EOL, 3);
        }
 
        /**
@@ -80,7 +81,7 @@ class CategoryRegistry implements \TYPO3\CMS\Core\SingletonInterface {
         * @return boolean
         * @throws \InvalidArgumentException
         */
-       public function add($extensionKey, $tableName, $fieldName = 'categories', $options = array()) {
+       public function add($extensionKey, $tableName, $fieldName = 'categories', array $options = array()) {
                $result = FALSE;
 
                if ($tableName === '') {
@@ -135,6 +136,38 @@ class CategoryRegistry implements \TYPO3\CMS\Core\SingletonInterface {
        }
 
        /**
+        * Returns a list of category fields for a given table for populating selector "category_field"
+        * in tt_content table (called as itemsProcFunc).
+        *
+        * @param array $configuration Current field configuration
+        * @param \TYPO3\CMS\Backend\Form\FormEngine $formObject Back-reference to the calling object
+        * @throws \UnexpectedValueException
+        * @return void
+        */
+       public function getCategoryFieldsForTable(array &$configuration, \TYPO3\CMS\Backend\Form\FormEngine $formObject) {
+               $table = '';
+               // Define the table being looked up from the type of menu
+               if ($configuration['row']['menu_type'] == 9) {
+                       $table = 'pages';
+               }
+               // Return early if no table is defined
+               if (empty($table)) {
+                       throw new \UnexpectedValueException('The given menu_type is not supported.', 1381823570);
+               }
+               // Loop on all registries and find entries for the correct table
+               foreach ($this->registry as $registry) {
+                       foreach ($registry as $tableName => $fields) {
+                               if ($tableName === $table) {
+                                       foreach ($fields as $fieldName => $options) {
+                                               $fieldLabel = $GLOBALS['LANG']->sL($GLOBALS['TCA'][$tableName]['columns'][$fieldName]['label']);
+                                               $configuration['items'][] = array($fieldLabel, $fieldName);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /**
         * Tells whether a table has a category configuration in the registry.
         *
         * @param string $tableName Name of the table to be looked up
@@ -233,7 +266,7 @@ class CategoryRegistry implements \TYPO3\CMS\Core\SingletonInterface {
         *              + position: insert position of the categories field
         * @return void
         */
-       protected function addToAllTCAtypes($tableName, $fieldName, $options) {
+       protected function addToAllTCAtypes($tableName, $fieldName, array $options) {
 
                // Makes sure to add more TCA to an existing structure
                if (isset($GLOBALS['TCA'][$tableName]['columns'])) {
@@ -288,7 +321,7 @@ class CategoryRegistry implements \TYPO3\CMS\Core\SingletonInterface {
         *              + label: backend label of the categories field
         * @return void
         */
-       protected function addTcaColumn($tableName, $fieldName, $options) {
+       protected function addTcaColumn($tableName, $fieldName, array $options) {
 
                // Makes sure to add more TCA to an existing structure
                if (isset($GLOBALS['TCA'][$tableName]['columns'])) {
index ec2d19b..fb74ea8 100644 (file)
@@ -41,18 +41,29 @@ class CategoryCollection extends \TYPO3\CMS\Core\Collection\AbstractRecordCollec
        static protected $storageTableName = 'sys_category';
 
        /**
+        * Name of the categories-relation field (used in the MM_match_fields/fieldname property of the TCA)
+        *
+        * @var string
+        */
+       protected $relationFieldName = 'categories';
+
+       /**
         * Creates this object.
         *
         * @param string $tableName Name of the table to be working on
+        * @param string $fieldName Name of the field where the categories relations are defined
         * @throws \RuntimeException
         */
-       public function __construct($tableName = NULL) {
+       public function __construct($tableName = NULL, $fieldName = NULL) {
                parent::__construct();
                if (!empty($tableName)) {
                        $this->setItemTableName($tableName);
                } elseif (empty($this->itemTableName)) {
                        throw new \RuntimeException('TYPO3\\CMS\\Core\\Category\\Collection\\CategoryCollection needs a valid itemTableName.', 1341826168);
                }
+               if (!empty($fieldName)) {
+                       $this->setRelationFieldName($fieldName);
+               }
        }
 
        /**
@@ -65,7 +76,11 @@ class CategoryCollection extends \TYPO3\CMS\Core\Collection\AbstractRecordCollec
         */
        static public function create(array $collectionRecord, $fillItems = FALSE) {
                /** @var $collection \TYPO3\CMS\Core\Category\Collection\CategoryCollection */
-               $collection = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Category\\Collection\\CategoryCollection', $collectionRecord['table_name']);
+               $collection = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
+                       'TYPO3\\CMS\\Core\\Category\\Collection\\CategoryCollection',
+                       $collectionRecord['table_name'],
+                       $collectionRecord['field_name']
+               );
                $collection->fromArray($collectionRecord);
                if ($fillItems) {
                        $collection->loadContents();
@@ -81,12 +96,18 @@ class CategoryCollection extends \TYPO3\CMS\Core\Collection\AbstractRecordCollec
         *
         * @param integer $id Id of database record to be loaded
         * @param boolean $fillItems Populates the entries directly on load, might be bad for memory on large collections
-        * @param string $tableName the table name
+        * @param string $tableName Name of table from which entries should be loaded
+        * @param string $fieldName Name of the categories relation field
         * @return \TYPO3\CMS\Core\Collection\CollectionInterface
         */
-       static public function load($id, $fillItems = FALSE, $tableName = '') {
-               $collectionRecord = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('*', static::$storageTableName, 'uid=' . intval($id) . \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause(static::$storageTableName));
+       static public function load($id, $fillItems = FALSE, $tableName = '', $fieldName = '') {
+               $collectionRecord = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
+                       '*',
+                       static::$storageTableName,
+                       'uid = ' . intval($id) . \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause(static::$storageTableName)
+               );
                $collectionRecord['table_name'] = $tableName;
+               $collectionRecord['field_name'] = $fieldName;
                return self::create($collectionRecord, $fillItems);
        }
 
@@ -99,14 +120,24 @@ class CategoryCollection extends \TYPO3\CMS\Core\Collection\AbstractRecordCollec
         */
        protected function getCollectedRecords() {
                $relatedRecords = array();
-               /** @var $GLOBALS['TYPO3_DB'] \TYPO3\CMS\Core\Database\DatabaseConnection */
+               // Assemble where clause
+               $where = 'AND ' . self::$storageTableName . '.uid = ' . intval($this->getIdentifier());
+               // Add condition on tablenames fields
+               $where .= ' AND sys_category_record_mm.tablenames = ' . $this->getDatabase()->fullQuoteStr(
+                       $this->getItemTableName(),
+                       'sys_category_record_mm'
+               );
+               // Add condition on fieldname field
+               $where .= ' AND sys_category_record_mm.fieldname = ' . $this->getDatabase()->fullQuoteStr(
+                       $this->getRelationFieldName(),
+                       'sys_category_record_mm'
+               );
                $resource = $this->getDatabase()->exec_SELECT_mm_query(
                        $this->getItemTableName() . '.*',
                        self::$storageTableName,
                        'sys_category_record_mm',
                        $this->getItemTableName(),
-                       'AND ' . self::$storageTableName . '.uid=' . intval($this->getIdentifier())
-                               . ' AND sys_category_record_mm.tablenames = "' . $this->getItemTableName() . '"'
+                       $where
                );
                if ($resource) {
                        while ($record = $this->getDatabase()->sql_fetch_assoc($resource)) {
@@ -215,6 +246,24 @@ class CategoryCollection extends \TYPO3\CMS\Core\Collection\AbstractRecordCollec
        }
 
        /**
+        * Sets the name of the categories relation field
+        *
+        * @param string $field
+        */
+       public function setRelationFieldName($field) {
+               $this->relationFieldName = $field;
+       }
+
+       /**
+        * Gets the name of the categories relation field
+        *
+        * @return string
+        */
+       public function getRelationFieldName() {
+               return $this->relationFieldName;
+       }
+
+       /**
         * Getter for the storage table name
         *
         * @return string
index 8e05232..3c386f6 100644 (file)
@@ -2095,6 +2095,7 @@ tt_content.menu {
                9 {
                        special = categories
                        special.value.field = selected_categories
+                       special.relation.field = category_field
                        special.sorting = title
                        special.order = asc
                        stdWrap {
index 718a0a2..cad7e78 100644 (file)
@@ -1534,6 +1534,7 @@ tt_content.menu {
                9 {
                        special = categories
                        special.value.field = selected_categories
+                       special.relation.field = category_field
                        special.sorting = title
                        special.order = asc
                        stdWrap {
index 8d8ff4b..88ff0f4 100644 (file)
@@ -1484,6 +1484,7 @@ tt_content.menu {
                9 {
                        special = categories
                        special.value.field = selected_categories
+                       special.relation.field = category_field
                        special.sorting = title
                        special.order = asc
                        stdWrap {
index 26f8b22..a008485 100644 (file)
@@ -1963,6 +1963,7 @@ tt_content.menu {
                9 {
                        special = categories
                        special.value.field = selected_categories
+                       special.relation.field = category_field
                        special.sorting = title
                        special.order = asc
                        stdWrap {
index ee41ce7..c799c1c 100644 (file)
@@ -1947,6 +1947,7 @@ tt_content.menu {
                9 {
                        special = categories
                        special.value.field = selected_categories
+                       special.relation.field = category_field
                        special.sorting = title
                        special.order = asc
                        stdWrap {
index 801b159..0949bb1 100644 (file)
@@ -1948,6 +1948,7 @@ tt_content.menu {
                9 {
                        special = categories
                        special.value.field = selected_categories
+                       special.relation.field = category_field
                        special.sorting = title
                        special.order = asc
                        stdWrap {
index 203faaa..96e358e 100644 (file)
@@ -53,13 +53,24 @@ class CategoryMenuUtility {
        public function collectPages($selectedCategories, $configuration, $parentObject) {
                $selectedPages = array();
                $categoriesPerPage = array();
+               // Determine the name of the relation field
+               $relationField = '';
+               if (isset($configuration['relation.'])) {
+                       $relationField = $parentObject->parent_cObj->stdWrap(
+                               $configuration['relation'],
+                               $configuration['relation.']
+                       );
+               } elseif (isset($configuration['relation'])) {
+                       $relationField = $configuration['relation'];
+               }
                // Get the pages for each selected category
                $selectedCategories = GeneralUtility::intExplode(',', $selectedCategories, TRUE);
                foreach ($selectedCategories as $aCategory) {
                        $collection = \TYPO3\CMS\Core\Category\Collection\CategoryCollection::load(
                                $aCategory,
                                TRUE,
-                               'pages'
+                               'pages',
+                               $relationField
                        );
                        $categoryUid = $collection->getUid();
                        // Loop on the results, overlay each page record found
index dbdd155..0d07180 100644 (file)
@@ -1771,6 +1771,17 @@ return array(
                                        ),
                                ),
                        )
+               ),
+               'category_field' => array(
+                       'label' => 'LLL:EXT:cms/locallang_ttc.xlf:category_field',
+                       'config' => array(
+                               'type' => 'select',
+                               'size' => 1,
+                               'minitems' => 0,
+                               'maxitems' => 1,
+                               'suppress_icons' => 1,
+                               'itemsProcFunc' => 'TYPO3\\CMS\\Core\\Category\\CategoryRegistry->getCategoryFieldsForTable',
+                       )
                )
        ),
        'types' => array(
@@ -1908,15 +1919,15 @@ return array(
                                --div--;LLL:EXT:cms/locallang_ttc.xlf:tabs.extended',
                        'subtype_value_field' => 'menu_type',
                        'subtypes_excludelist' => array(
-                               '0' => 'selected_categories',
-                               '1' => 'selected_categories',
-                               '2' => 'pages, selected_categories',
-                               '3' => 'selected_categories',
-                               '4' => 'selected_categories',
-                               '5' => 'selected_categories',
-                               '6' => 'selected_categories',
-                               '7' => 'selected_categories',
-                               '8' => 'selected_categories',
+                               '0' => 'selected_categories, category_field',
+                               '1' => 'selected_categories, category_field',
+                               '2' => 'pages, selected_categories, category_field',
+                               '3' => 'selected_categories, category_field',
+                               '4' => 'selected_categories, category_field',
+                               '5' => 'selected_categories, category_field',
+                               '6' => 'selected_categories, category_field',
+                               '7' => 'selected_categories, category_field',
+                               '8' => 'selected_categories, category_field',
                                '9' => 'pages',
                        )
                ),
@@ -2100,7 +2111,7 @@ return array(
                        'canNotCollapse' => 1
                ),
                'menu' => array(
-                       'showitem' => 'menu_type;LLL:EXT:cms/locallang_ttc.xlf:menu_type_formlabel, --linebreak--, pages;LLL:EXT:cms/locallang_ttc.xlf:pages.ALT.menu_formlabel, --linebreak--, selected_categories',
+                       'showitem' => 'menu_type;LLL:EXT:cms/locallang_ttc.xlf:menu_type_formlabel, --linebreak--, pages;LLL:EXT:cms/locallang_ttc.xlf:pages.ALT.menu_formlabel, --linebreak--, selected_categories, category_field',
                        'canNotCollapse' => 1
                ),
                'menu_accessibility' => array(
index 691e7bd..a4d7166 100644 (file)
@@ -330,6 +330,7 @@ CREATE TABLE tt_content (
        l18n_parent int(11) DEFAULT '0' NOT NULL,
        l18n_diffsource mediumblob,
        selected_categories text,
+       category_field varchar(64) DEFAULT '' NOT NULL,
 
        PRIMARY KEY (uid),
        KEY t3ver_oid (t3ver_oid,t3ver_wsid),