class EntityconnectFormUtils in Entity connect 8.2
Contains form alter, callbacks and utility methods for entityconnect.
Hierarchy
- class \Drupal\entityconnect\EntityconnectFormUtils
Expanded class hierarchy of EntityconnectFormUtils
1 file declares its use of EntityconnectFormUtils
- entityconnect.module in ./
entityconnect.module - Contains entityconnect.module.
File
- src/
EntityconnectFormUtils.php, line 14
Namespace
Drupal\entityconnectView source
class EntityconnectFormUtils {
/**
* Adds entityconnect settings to the entity reference field config.
*
* @param array $form
* The form to add to.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The state of the form.
*/
public static function fieldConfigEditFormAlter(array &$form, FormStateInterface $form_state) {
$field = $form_state
->getFormObject()
->getEntity();
$type = $field
->getType();
if ($type == 'entity_reference') {
$defaults = $field
->getThirdPartySettings('entityconnect');
if (!$defaults) {
$config = \Drupal::config('entityconnect.administration_config');
$defaults = $config
->get();
}
AdministrationForm::attach($form['third_party_settings'], $defaults);
}
}
/**
* Add the entityconnect button(s) to the form.
*
* It's done here since we only have access to the actual widget element
* in hook_field_widget_form_alter().
*
* @param array $form
* The form to add to.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The state of the form.
*/
public static function entityFormAlter(array &$form, FormStateInterface $form_state) {
// Get the applicable entity reference fields from the form.
$ref_fields = static::getReferenceFields($form, $form_state);
// Attach our custom process callback to each entity reference element.
if ($ref_fields) {
foreach ($ref_fields as $field) {
// Merge our #process callback with the defaults.
$elementInfo = \Drupal::service('element_info');
$form[$field]['#process'] = array_merge($elementInfo
->getInfoProperty($form[$field]['#type'], '#process', []), [
[
'\\Drupal\\entityconnect\\EntityconnectWidgetProcessor',
'process',
],
]);
// Add our #validate callback to the entity form.
// This prevents the exception on entityconnect elements caused by
// submitting the form without using the entityconnect buttons.
$form['#validate'] = !isset($form['#validate']) ? [] : $form['#validate'];
array_unshift($form['#validate'], [
'\\Drupal\\entityconnect\\EntityconnectFormUtils',
'validateForm',
]);
}
}
}
/**
* Form API #validate callback for a form with entity_reference fields.
*
* Removes the entityconnect button values from form_state to prevent
* exceptions.
*
* @param array $form
* The form to validate.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The state of the form.
*/
public static function validateForm(array &$form, FormStateInterface $form_state) {
$ref_fields = static::getReferenceFields($form, $form_state);
foreach ($ref_fields as $field) {
// Extract the values for this field from $form_state->getValues().
$path = array_merge($form['#parents'], [
$field,
]);
$key_exists = NULL;
$ref_values = NestedArray::getValue($form_state
->getValues(), $path, $key_exists);
if ($key_exists) {
foreach ($ref_values as $key => $value) {
if (strpos($key, '_entityconnect') !== FALSE) {
$form_state
->unsetValue(array_merge($path, [
$key,
]));
}
}
}
}
}
/**
* Extracts all reference fields from the given form.
*
* @param array $form
* The form to extract fields from.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The state of the form.
*
* @return array
* The array of reference fields extracted.
*/
public static function getReferenceFields(array &$form, FormStateInterface $form_state) {
$ref_fields = [];
/** @var \Drupal\Core\Entity\EntityInterface $entity */
$entity = NULL;
// Get the entity if this is an entity form.
if (method_exists($form_state
->getFormObject(), 'getEntity')) {
$entity = $form_state
->getFormObject()
->getEntity();
}
// Bail out if not a fieldable entity form.
if (empty($entity) || !$entity
->getEntityType()
->entityClassImplements('\\Drupal\\Core\\Entity\\FieldableEntityInterface')) {
return $ref_fields;
}
// Get the entity reference elements from this form.
$field_defs = $entity
->getFieldDefinitions();
foreach (Element::getVisibleChildren($form) as $child) {
if (!isset($field_defs[$child])) {
continue;
}
$field_definition = $field_defs[$child];
if ($field_definition
->getType() == 'entity_reference') {
// Fields must be configurable.
if ($field_definition instanceof FieldConfig) {
$ref_fields[] = $child;
}
}
}
\Drupal::moduleHandler()
->alter('entityconnect_ref_fields', $ref_fields);
return $ref_fields;
}
/**
* Alters child create form.
*
* We add a value field to hold the parent build_cache_id
* then we add a cancel button that run entityconnect_child_form_cancel
* and a new submit button.
*
* @param array $form
* The child form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Child form state.
* @param string $form_id
* Child form id.
* @param string $cache_id
* Cache id of parent data.
*/
public static function childFormAlter(array &$form, FormStateInterface $form_state, $form_id, $cache_id) {
// Exclude some forms to be processed.
$exclude_forms = [
'search_block_form',
];
// Allow other modules to alter exclude forms list.
\Drupal::moduleHandler()
->alter('entityconnect_exclude_forms', $exclude_forms);
if (in_array($form_id, $exclude_forms)) {
return;
}
$form['parent_build_cache_id'] = [
'#type' => 'value',
'#value' => $cache_id,
];
$form['actions']['cancel'] = [
'#type' => 'submit',
'#value' => t('Cancel'),
'#submit' => [
[
'\\Drupal\\entityconnect\\EntityconnectFormUtils',
'childFormCancel',
],
],
'#parent_build_cache_id' => $cache_id,
'#limit_validation_errors' => [],
'#weight' => 1000,
];
if (isset($form['submit']['#submit'])) {
$form['submit']['#submit'][] = [
'\\Drupal\\entityconnect\\EntityconnectFormUtils',
'childFormSubmit',
];
}
else {
foreach (array_keys($form['actions']) as $action) {
if (!in_array($action, [
'preview',
'delete',
]) && isset($form['actions'][$action]['#type']) && $form['actions'][$action]['#type'] === 'submit') {
$form['actions'][$action]['#submit'][] = [
'\\Drupal\\entityconnect\\EntityconnectFormUtils',
'childFormSubmit',
];
}
}
}
// Setup the child form delete button.
if (!empty($form['actions']['delete']) && !empty($form['actions']['delete']['#type']) && strpos($form_id, '_confirm_delete') === FALSE && strpos($form_id, 'delete_form') === FALSE) {
$delete_button =& $form['actions']['delete'];
if ($delete_button['#type'] == 'link') {
/** @var \Drupal\Core\Url $url */
$url =& $delete_button['#url'];
$url
->setOption('query', [
'build_cache_id' => $cache_id,
'child' => 1,
]);
}
elseif ($delete_button['#type'] == 'submit') {
$form['actions']['delete']['#submit'][] = [
'\\Drupal\\entityconnect\\EntityconnectFormUtils',
'childFormDeleteSubmit',
];
}
}
$data = [
'form' => &$form,
'form_state' => &$form_state,
'form_id' => $form_id,
];
\Drupal::moduleHandler()
->alter('entityconnect_child_form', $data);
}
/**
* Complete entityreference field on parent form with the target_id value.
*
* This is for when we return to the parent page
* we find the cached form and form_state clean up the form_state a bit
* and mark it to be rebuilt.
*
* If the cache has a target_id we set that in the input.
*
* @param array $form
* Parent form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Parent form state.
* @param array $cache_data
* Parent cache data.
*/
public static function returnFormAlter(array &$form, FormStateInterface $form_state, array $cache_data) {
if (empty($form_state
->get('#entityconnect_processed'))) {
$old_form = $cache_data['form'];
/** @var \Drupal\Core\Form\FormStateInterface $old_form_state */
$old_form_state = $cache_data['form_state'];
// Save the storage and input from the original form state.
$form_state
->setStorage($old_form_state
->getStorage());
$form_state
->setUserInput($old_form_state
->getUserInput());
$triggeringElement = $old_form_state
->getTriggeringElement();
// Gets the parents of the triggering element (our entityconnect button)
// which is at the same level as the reference field. Since we will be
// traversing the actual form, use #array_parents as opposed to #parents.
$parents = is_array($triggeringElement) && !empty($triggeringElement['#array_parents']) ? $triggeringElement['#array_parents'] : NULL;
$key_exists = NULL;
// Now get the reference field container.
$widget_container = EntityconnectNestedArray::getValue($old_form, $parents, $key_exists);
if ($key_exists) {
if (isset($widget_container['widget'])) {
$widget_container = $widget_container['widget'];
}
}
else {
// @ToDo: probably need something to happen in this case.
return;
}
// Now get the actual parents for traversing user input.
$parents = $widget_container['#parents'];
$widget_container_type = isset($widget_container['#type']) ? $widget_container['#type'] : 'autocomplete';
/** @var \Drupal\field\FieldStorageConfigInterface $field_info */
$field_info = $cache_data['field_info'];
if (isset($cache_data['target_id']) && empty($cache_data['cancel'])) {
// Load the the target entity.
$entity_type = $cache_data['target_entity_type'];
$entity_storage = \Drupal::entityTypeManager()
->getStorage($entity_type);
$entity = $entity_storage
->load($cache_data['target_id']);
if ($cache_data['target_id']) {
$target_id = $entity ? $entity
->id() : '';
// ['#default_value'] should have differents build
// function of the widget type.
switch ($widget_container_type) {
// Autocomplete.
case 'autocomplete':
if ($field_info
->getType() == 'entity_reference') {
$element['target_id'] = $target_id ? sprintf('%s (%s)', $entity
->label(), $target_id) : '';
// Autocomplete tags style.
if ($element['target_id'] && !empty($widget_container['target_id']['#tags']) && !empty($widget_container['target_id']['#value'])) {
$element['target_id'] .= ', ' . $widget_container['target_id']['#value'];
}
}
break;
// Select list.
case 'select':
if ($widget_container['#multiple'] == FALSE || !$target_id) {
$element['target_id'] = $target_id;
}
else {
$element['target_id'] = $widget_container['#value'] + [
$target_id => $target_id,
];
}
break;
// Radios widget.
case 'radios':
$element['target_id'] = $target_id;
break;
// Checkboxes widget.
case 'checkboxes':
$element['target_id'] = $widget_container['#value'];
if ($target_id) {
$element['target_id'] += [
$target_id => $target_id,
];
}
break;
default:
$data = [
'data' => &$cache_data,
'widget_container' => $widget_container,
'widget_container_type' => $widget_container_type,
'field_info' => $field_info,
'element_value' => NULL,
];
\Drupal::moduleHandler()
->alter('entityconnect_return_form', $data);
break;
}
}
// This is the input we already got from the old form state.
$input = $form_state
->getUserInput();
if (isset($element)) {
static::alterFormStateInput($input, $widget_container_type, $parents, $element['target_id']);
}
elseif (!empty($data['element_value'])) {
static::alterFormStateInput($input, $widget_container_type, $parents, $data['element_value']);
}
// Include the alterations from above.
$form_state
->setUserInput($input);
}
// Rebuild the form.
$form_state
->setRebuild();
// The combination of having user input and rebuilding the form means
// that it will attempt to cache the form state which will fail if it is
// a GET request.
$form_state
->setRequestMethod('POST');
// Return processing is complete.
$form_state
->set('#entityconnect_processed', TRUE);
}
}
/**
* Sets the redirect to admin/entityconnect/redirect page.
*
* @param array $form
* Child form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Child form state.
*/
public static function childFormCancel(array $form, FormStateInterface $form_state) {
$triggeringElement = $form_state
->getTriggeringElement();
$cache_id = $triggeringElement['#parent_build_cache_id'];
if ($cache_id && \Drupal::getContainer()
->get('entityconnect.cache')
->get($cache_id)) {
$form_state
->setRedirect('entityconnect.return', [
'cache_id' => $cache_id,
'cancel' => 1,
]);
}
}
/**
* Form API callback: Submit callback for child form.
*
* Sets submit button on child create form.
*
* On submission of a child form we set:
* the target_id in the cache entry
* the redirect to our redirect page.
*
* @param array $form
* Child form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Child form state.
*/
public static function childFormSubmit(array $form, FormStateInterface $form_state) {
$cache_id = $form_state
->getValue('parent_build_cache_id');
if ($cache_id && ($cache_data = \Drupal::getContainer()
->get('entityconnect.cache')
->get($cache_id))) {
$entity = $form_state
->getFormObject()
->getEntity();
if ($entity) {
$cache_data['target_id'] = $entity
->id();
}
else {
$entity_type = $cache_data['target_entity_type'];
$data = [
'form' => &$form,
'form_state' => &$form_state,
'entity_type' => $entity_type,
'data' => &$cache_data,
];
\Drupal::moduleHandler()
->alter('entityconnect_child_form_submit', $data);
}
\Drupal::getContainer()
->get('entityconnect.cache')
->set($cache_id, $cache_data);
$form_state
->setRedirect('entityconnect.return', [
'cache_id' => $cache_id,
]);
}
}
/**
* Sets delete button on child form.
*
* On deletion submission of a child form we set:
* the form_state redirect with build cache id.
*
* @param array $form
* Child form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Child form state.
*/
public static function childFormDeleteSubmit(array $form, FormStateInterface $form_state) {
$triggering_element = $form_state
->getTriggeringElement();
if (in_array('delete', $triggering_element['#parents'])) {
$redirect = $form_state
->getRedirect();
$query = $redirect
->getOption('query');
if (!is_array($query)) {
$query = [];
}
$redirect
->setOption('query', $query + [
'build_cache_id' => $form_state
->getValue('parent_build_cache_id'),
'child' => 1,
]);
}
}
/**
* Used to update the form state value.
*
* Form state value is updated for entityreference after adding a new entity.
*
* @param array $input
* The user input from form state that we need to change.
* @param string $widget_type
* The type of the widget used for reference field.
* @param array $parents
* The array of all parents of the field.
* We used them to change the value to the right level in the array.
* @param mixed $element
* The value we need to insert.
*/
public static function alterFormStateInput(array &$input, $widget_type, array $parents, $element) {
switch ($widget_type) {
case 'autocomplete':
case 'multiple_selects':
array_push($parents, 'target_id');
break;
case 'textfield':
case 'radios':
case 'checkboxes':
default:
break;
}
EntityconnectNestedArray::setValue($input, $parents, $element);
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
EntityconnectFormUtils:: |
public static | function | Used to update the form state value. | |
EntityconnectFormUtils:: |
public static | function | Alters child create form. | |
EntityconnectFormUtils:: |
public static | function | Sets the redirect to admin/entityconnect/redirect page. | |
EntityconnectFormUtils:: |
public static | function | Sets delete button on child form. | |
EntityconnectFormUtils:: |
public static | function | Form API callback: Submit callback for child form. | |
EntityconnectFormUtils:: |
public static | function | Add the entityconnect button(s) to the form. | |
EntityconnectFormUtils:: |
public static | function | Adds entityconnect settings to the entity reference field config. | |
EntityconnectFormUtils:: |
public static | function | Extracts all reference fields from the given form. | |
EntityconnectFormUtils:: |
public static | function | Complete entityreference field on parent form with the target_id value. | |
EntityconnectFormUtils:: |
public static | function | Form API #validate callback for a form with entity_reference fields. |