/** * Creates an items list for the given properties. * * @param \Drupal\Core\TypedData\DataDefinitionInterface[] $properties * The property definitions, keyed by their property names. * @param string $active_property_path * The relative property path to the active property. * @param \Drupal\Core\Url $base_url * The base URL to which property path parameters should be added for * the navigation links. * @param string $parent_path * (optional) The common property path prefix of the given properties. * @param string $label_prefix * (optional) The prefix to use for the labels of created fields. * * @return array * A render array representing the given properties and, possibly, nested * properties. */ protected function getPropertiesList(array $properties, $active_property_path, Url $base_url, $parent_path = '', $label_prefix = '') { $list = array('#theme' => 'search_api_form_item_list'); $active_item = ''; if ($active_property_path) { list($active_item, $active_property_path) = explode(':', $active_property_path, 2) + array(1 => ''); } $type_mapping = Utility::getFieldTypeMapping(); $query_base = $base_url->getOption('query'); foreach ($properties as $key => $property) { $this_path = $parent_path ? $parent_path . ':' : ''; $this_path .= $key; $label = $property->getLabel(); $property = Utility::getInnerProperty($property); $can_be_indexed = TRUE; $nested_properties = array(); $parent_child_type = NULL; if ($property instanceof ComplexDataDefinitionInterface) { $can_be_indexed = FALSE; $nested_properties = $property->getPropertyDefinitions(); $main_property = $property->getMainPropertyName(); if ($main_property && isset($nested_properties[$main_property])) { $parent_child_type = $property->getDataType() . '.'; $property = $nested_properties[$main_property]; $parent_child_type .= $property->getDataType(); unset($nested_properties[$main_property]); $can_be_indexed = TRUE; } // Don't add the additional 'entity' property for entity reference // fields which don't target a content entity type. if ($property instanceof FieldItemDataDefinition && in_array($property->getDataType(), array('field_item:entity_reference', 'field_item:image', 'field_item:file'))) { $entity_type = $this->getEntityTypeManager()->getDefinition($property->getSetting('target_type')); if (!$entity_type->isSubclassOf('Drupal\\Core\\Entity\\ContentEntityInterface')) { unset($nested_properties['entity']); } } } // Don't allow indexing of properties with unmapped types. Also, prefer // a "parent.child" type mapping (taking into account the parent property // for, e.g., text fields). $type = $property->getDataType(); if ($parent_child_type && !empty($type_mapping[$parent_child_type])) { $type = $parent_child_type; } elseif (empty($type_mapping[$type])) { // Remember the type only if it was not explicitly mapped to FALSE. if (!isset($type_mapping[$type])) { $this->unmappedFields[$type][] = $label_prefix . $label; } $can_be_indexed = FALSE; } // If the property can neither be expanded nor indexed, just skip it. if (!($nested_properties || $can_be_indexed)) { continue; } $nested_list = array(); $expand_link = array(); if ($nested_properties) { if ($key == $active_item) { $link_url = clone $base_url; $query_base['property_path'] = $parent_path; $link_url->setOption('query', $query_base); $expand_link = array('#type' => 'link', '#title' => '(-) ', '#url' => $link_url); $nested_list = $this->getPropertiesList($nested_properties, $active_property_path, $base_url, $this_path, $label_prefix . $label . ' » '); } else { $link_url = clone $base_url; $query_base['property_path'] = $this_path; $link_url->setOption('query', $query_base); $expand_link = array('#type' => 'link', '#title' => '(+) ', '#url' => $link_url); } } $item = array('#type' => 'container', '#attributes' => array('class' => array('container-inline'))); if ($expand_link) { $item['expand_link'] = $expand_link; } $item['label']['#markup'] = Html::escape($label) . ' '; if ($can_be_indexed) { $item['add'] = array('#type' => 'submit', '#name' => Utility::createCombinedId($this->getParameter('datasource') ?: NULL, $this_path), '#value' => $this->t('Add'), '#submit' => array('::addField', '::save'), '#property' => $property, '#prefixed_label' => $label_prefix . $label, '#data_type' => $type_mapping[$type]); } if ($nested_list) { $item['properties'] = $nested_list; } $list[] = $item; } return $list; }
/** * Retrieves a nested property definition from an array of definitions. * * @param \Drupal\Core\TypedData\DataDefinitionInterface[] $properties * The given array of base definitions. * @param string[] $keys * An array of keys to apply to the definitions to arrive at the one that * should be returned. * * @return \Drupal\Core\TypedData\DataDefinitionInterface|null * The requested property definition, or NULL if it could not be found. */ protected function getNestedDefinition(array $properties, array $keys) { $key = array_shift($keys); if (!isset($properties[$key])) { return NULL; } $property = Utility::getInnerProperty($properties[$key]); if (!$keys) { return $property; } if (!$property instanceof ComplexDataDefinitionInterface) { return NULL; } return $this->getNestedDefinition($property->getPropertyDefinitions(), $keys); }