View source
<?php
namespace Drupal\field_collection\Plugin\Field\FieldWidget;
use Drupal\Component\Utility\Html;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Component\Utility\NestedArray;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Render\Element;
use Drupal\field_collection\Entity\FieldCollectionItem;
class FieldCollectionEmbedWidget extends WidgetBase {
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$field_name = $this->fieldDefinition
->getName();
$parents = array_merge($element['#field_parents'], [
$field_name,
$delta,
]);
$element += [
'#element_validate' => [
[
static::class,
'validate',
],
],
'#parents' => $parents,
'#field_name' => $field_name,
];
if ($this->fieldDefinition
->getFieldStorageDefinition()
->getCardinality() == 1) {
$element['#type'] = 'fieldset';
}
$field_state = static::getWidgetState($element['#field_parents'], $field_name, $form_state);
if (isset($field_state['entity'][$delta])) {
$field_collection_item = $field_state['entity'][$delta];
}
else {
$field_collection_item = $items[$delta]
->getFieldCollectionItem(TRUE);
$field_state['entity'][$delta] = $field_collection_item;
}
static::setWidgetState($element['#field_parents'], $field_name, $form_state, $field_state);
$display = entity_get_form_display('field_collection_item', $field_name, 'default');
$display
->buildForm($field_collection_item, $element, $form_state);
if (empty($element['#required'])) {
$element['#after_build'][] = [
static::class,
'delayRequiredValidation',
];
$form['#attributes']['novalidate'] = 'novalidate';
}
if ($this->fieldDefinition
->getFieldStorageDefinition()
->getCardinality() == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
$options = [
'query' => [
'element_parents' => implode('/', $element['#parents']),
],
];
$element['actions'] = [
'#type' => 'actions',
'remove_button' => [
'#delta' => $delta,
'#name' => implode('_', $parents) . '_remove_button',
'#type' => 'submit',
'#value' => t('Remove'),
'#validate' => [],
'#submit' => [
[
static::class,
'removeSubmit',
],
],
'#limit_validation_errors' => [],
'#ajax' => [
'callback' => [
$this,
'ajaxRemove',
],
'options' => $options,
'effect' => 'fade',
'wrapper' => $form['#wrapper_id'],
],
'#weight' => 1000,
],
];
}
return $element;
}
protected function formMultipleElements(FieldItemListInterface $items, array &$form, FormStateInterface $form_state) {
if (count($items) > 0 && !$form_state
->isRebuilding()) {
$field_name = $this->fieldDefinition
->getName();
$cardinality = $this->fieldDefinition
->getFieldStorageDefinition()
->getCardinality();
$parents = $form['#parents'];
if ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
$field_state = static::getWidgetState($parents, $field_name, $form_state);
$field_state['items_count']--;
static::setWidgetState($parents, $field_name, $form_state, $field_state);
}
}
$form['#wrapper_id'] = Html::getUniqueID($items
->getName());
$elements = parent::formMultipleElements($items, $form, $form_state);
$elements['#prefix'] = '<div id="' . $form['#wrapper_id'] . '">';
$elements['#suffix'] = '</div>';
$elements['add_more']['#ajax']['wrapper'] = $form['#wrapper_id'];
return $elements;
}
public static function delayRequiredValidation($element, FormStateInterface $form_state) {
if ($form_state
->isProcessingInput()) {
static::collectRequiredElements($element, $element['#field_collection_required_elements']);
}
return $element;
}
private static function collectRequiredElements(&$element, &$required_elements) {
foreach (Element::children($element) as $key) {
if (isset($element[$key]) && $element[$key]) {
static::collectRequiredElements($element[$key], $required_elements);
}
}
if (!empty($element['#required'])) {
$element['#required'] = FALSE;
$required_elements[] =& $element;
$element += [
'#pre_render' => [],
];
array_unshift($element['#pre_render'], [
static::class,
'renderRequired',
]);
}
}
public static function renderRequired($element) {
$element['#required'] = TRUE;
return $element;
}
public static function validate($element, FormStateInterface $form_state, $form) {
$field_parents = $element['#field_parents'];
$field_name = $element['#field_name'];
$field_state = static::getWidgetState($field_parents, $field_name, $form_state);
$field_collection_item = $field_state['entity'][$element['#delta']];
$display = entity_get_form_display('field_collection_item', $field_name, 'default');
$display
->extractFormValues($field_collection_item, $element, $form_state);
if (!$field_collection_item
->isEmpty() && !empty($element['#field_collection_required_elements'])) {
foreach ($element['#field_collection_required_elements'] as &$elements) {
if (isset($elements['#needs_validation'])) {
$is_countable = is_array($elements['#value']) || $elements['#value'] instanceof \Countable;
$is_empty_multiple = $is_countable && count($elements['#value']) == 0;
$is_empty_string = is_string($elements['#value']) && Unicode::strlen(trim($elements['#value'])) == 0;
$is_empty_value = $elements['#value'] === 0;
$is_empty_option = isset($elements['#options']['_none']) && $elements['#value'] == '_none';
if ($is_empty_multiple || $is_empty_string || $is_empty_value || $is_empty_option) {
if (isset($elements['#required_error'])) {
$form_state
->setError($elements, $elements['#required_error']);
}
else {
if (isset($elements['#title'])) {
$form_state
->setError($elements, t('@name field is required.', [
'@name' => $elements['#title'],
]));
}
else {
$form_state
->setError($elements);
}
}
}
}
}
}
if ($form_state
->isSubmitted() && !$form_state
->hasAnyErrors()) {
$field = NestedArray::getValue($form_state
->getValues(), $element['#parents']);
$element_widget = NestedArray::getValue($form, array_slice($element['#array_parents'], 0, -1));
if (isset($element['_weight']) && $element_widget['#cardinality_multiple']) {
$field['_weight'] = $element['_weight']['#value'];
}
$field['entity'] = $field_collection_item;
$form_state
->setValue($element['#parents'], $field);
}
}
public static function removeSubmit($form, FormStateInterface $form_state) {
$button = $form_state
->getTriggeringElement();
$delta = $button['#delta'];
$address = array_slice($button['#array_parents'], 0, -4);
$address_state = array_slice($button['#parents'], 0, -3);
$parent_element = NestedArray::getValue($form, array_merge($address, [
'widget',
]));
$field_name = $parent_element['#field_name'];
$parents = $parent_element['#field_parents'];
$field_state = static::getWidgetState($parents, $field_name, $form_state);
for ($i = $delta; $i <= $field_state['items_count']; $i++) {
$old_element_address = array_merge($address, [
'widget',
$i + 1,
]);
$old_element_state_address = array_merge($address_state, [
$i + 1,
]);
$new_element_state_address = array_merge($address_state, [
$i,
]);
$moving_element = NestedArray::getValue($form, $old_element_address);
$moving_element_value = NestedArray::getValue($form_state
->getValues(), $old_element_state_address);
$moving_element_input = NestedArray::getValue($form_state
->getUserInput(), $old_element_state_address);
$moving_element_field = NestedArray::getValue($form_state
->get('field_storage'), array_merge([
'#parents',
], $address));
$moving_element['#parents'] = $new_element_state_address;
$form_state
->setValueForElement($moving_element, $moving_element_value);
$user_input = $form_state
->getUserInput();
NestedArray::setValue($user_input, $moving_element['#parents'], $moving_element_input);
$form_state
->setUserInput($user_input);
NestedArray::setValue($form_state
->get('field_storage'), array_merge([
'#parents',
], $moving_element['#parents']), $moving_element_field);
if (isset($field_state['entity'][$i + 1])) {
$field_state['entity'][$i] = $field_state['entity'][$i + 1];
}
else {
unset($field_state['entity'][$i]);
}
}
if ($field_state['items_count'] > 0) {
$field_state['items_count']--;
}
else {
$field_state['entity'][0] = FieldCollectionItem::create([
'field_name' => $field_name,
]);
}
$input = NestedArray::getValue($form_state
->getUserInput(), $address_state);
uasort($input, '_field_collection_sort_items_helper');
$weight = -1 * $field_state['items_count'];
foreach ($input as $key => $item) {
if ($item) {
$input[$key]['_weight'] = $weight++;
}
}
$user_input = $form_state
->getUserInput();
NestedArray::setValue($user_input, $address_state, $input);
$form_state
->setUserInput($user_input);
static::setWidgetState($parents, $field_name, $form_state, $field_state);
$form_state
->setRebuild();
}
function ajaxRemove(array $form, FormStateInterface &$form_state) {
$button = $form_state
->getTriggeringElement();
return NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -3));
}
public static function addMoreSubmit(array $form, FormStateInterface $form_state) {
$button = $form_state
->getTriggeringElement();
$element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -1));
$field_name = $element['#field_name'];
$parents = $element['#field_parents'];
$field_state = static::getWidgetState($parents, $field_name, $form_state);
$field_state['items_count']++;
$field_state['entity'][$field_state['items_count']] = FieldCollectionItem::create([
'field_name' => $field_name,
]);
static::setWidgetState($parents, $field_name, $form_state, $field_state);
$form_state
->setRebuild();
}
}