/** * Reduce an array by a search value and keep the array structure. * * Comparison is type strict: * - For a given needle of type string, integer, array or boolean, * value and value type must match to occur in result array * - For a given object, a object within the array must be a reference to * the same object to match (not just different instance of same class) * * Example: * - Needle: 'findMe' * - Given array: * array( * 'foo' => 'noMatch', * 'bar' => 'findMe', * 'foobar => array( * 'foo' => 'findMe', * ), * ); * - Result: * array( * 'bar' => 'findMe', * 'foobar' => array( * 'foo' => findMe', * ), * ); * * See the unit tests for more examples and expected behaviour * * @param mixed $needle The value to search for * @param array $haystack The array in which to search * @return array $haystack array reduced matching $needle values */ public static function filterByValueRecursive($needle = '', array $haystack = array()) { $resultArray = array(); // Define a lambda function to be applied to all members of this array dimension // Call recursive if current value is of type array // Write to $resultArray (by reference!) if types and value match $callback = function (&$value, $key) use($needle, &$resultArray) { if ($value === $needle) { $resultArray[$key] = $value; } elseif (is_array($value)) { $subArrayMatches = \TYPO3\CMS\Core\Utility\ArrayUtility::filterByValueRecursive($needle, $value); if (count($subArrayMatches) > 0) { $resultArray[$key] = $subArrayMatches; } } }; // array_walk() is not affected by the internal pointers, no need to reset array_walk($haystack, $callback); // Pointers to result array are reset internally return $resultArray; }
/** * Add an item to a select field item list. * * Warning: Do not use this method for radio or check types, especially not * with $relativeToField and $relativePosition parameters. This would shift * existing database data 'off by one'. * * As an example, this can be used to add an item to tt_content CType select * drop-down after the existing 'mailform' field with these parameters: * - $table = 'tt_content' * - $field = 'CType' * - $item = array( * 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:CType.I.10', * 'login', * 'i/tt_content_login.gif', * ), * - $relativeToField = mailform * - $relativePosition = after * * @throws \InvalidArgumentException If given parameters are not of correct * @throws \RuntimeException If reference to related position fields can not * @param string $table Name of TCA table * @param string $field Name of TCA field * @param array $item New item to add * @param string $relativeToField Add item relative to existing field * @param string $relativePosition Valid keywords: 'before', 'after' * @return void */ public static function addTcaSelectItem($table, $field, array $item, $relativeToField = '', $relativePosition = '') { if (!is_string($table)) { throw new \InvalidArgumentException('Given table is of type "' . gettype($table) . '" but a string is expected.', 1303236963); } if (!is_string($field)) { throw new \InvalidArgumentException('Given field is of type "' . gettype($field) . '" but a string is expected.', 1303236964); } if (!is_string($relativeToField)) { throw new \InvalidArgumentException('Given relative field is of type "' . gettype($relativeToField) . '" but a string is expected.', 1303236965); } if (!is_string($relativePosition)) { throw new \InvalidArgumentException('Given relative position is of type "' . gettype($relativePosition) . '" but a string is expected.', 1303236966); } if ($relativePosition !== '' && $relativePosition !== 'before' && $relativePosition !== 'after' && $relativePosition !== 'replace') { throw new \InvalidArgumentException('Relative position must be either empty or one of "before", "after", "replace".', 1303236967); } if (!is_array($GLOBALS['TCA'][$table]['columns'][$field]['config']['items'])) { throw new \RuntimeException('Given select field item list was not found.', 1303237468); } // Make sure item keys are integers $GLOBALS['TCA'][$table]['columns'][$field]['config']['items'] = array_values($GLOBALS['TCA'][$table]['columns'][$field]['config']['items']); if ($relativePosition !== '') { // Insert at specified position $matchedPosition = ArrayUtility::filterByValueRecursive($relativeToField, $GLOBALS['TCA'][$table]['columns'][$field]['config']['items']); if (!empty($matchedPosition)) { $relativeItemKey = key($matchedPosition); if ($relativePosition === 'replace') { $GLOBALS['TCA'][$table]['columns'][$field]['config']['items'][$relativeItemKey] = $item; } else { if ($relativePosition === 'before') { $offset = $relativeItemKey; } else { $offset = $relativeItemKey + 1; } array_splice($GLOBALS['TCA'][$table]['columns'][$field]['config']['items'], $offset, 0, array(0 => $item)); } } else { // Insert at new item at the end of the array if relative position was not found $GLOBALS['TCA'][$table]['columns'][$field]['config']['items'][] = $item; } } else { // Insert at new item at the end of the array $GLOBALS['TCA'][$table]['columns'][$field]['config']['items'][] = $item; } }
/** * @test */ public function filterByValueRecursiveDoesNotMatchDifferentInstancesOfSameClass() { $this->assertEquals(array(), ArrayUtility::filterByValueRecursive(new \stdClass(), array(new \stdClass()))); }