field_group-8.x-3.1/src/FormatterHelper.php
src/FormatterHelper.php
<?php
namespace Drupal\field_group;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Security\TrustedCallbackInterface;
/**
* Static methods for fieldgroup formatters.
*/
class FormatterHelper implements TrustedCallbackInterface {
/**
* Return an array of field_group_formatter options.
*/
public static function formatterOptions($type) {
$options = &drupal_static(__FUNCTION__);
if (!isset($options)) {
$options = [];
$manager = \Drupal::service('plugin.manager.field_group.formatters');
$formatters = $manager->getDefinitions();
foreach ($formatters as $formatter) {
if (in_array($type, $formatter['supported_contexts'])) {
$options[$formatter['id']] = $formatter['label'];
}
}
}
return $options;
}
/**
* Pre render callback for rendering groups on entities without theme hook.
*
* @param array $element
* Entity being rendered.
*
* @return array
* The update entity view.
*/
public static function entityViewPreRender(array $element) {
field_group_build_entity_groups($element, 'view');
return $element;
}
/**
* Process callback for field groups.
*
* @param array $element
* Form that is being processed.
* @param \Drupal\Core\Form\FormStateInterface|null $form_state
* The current state of the form.
* @param array $form
* The complete form structure.
*
* @return array
* The updated form.
*/
public static function formProcess(array &$element, ?FormStateInterface $form_state = NULL, array &$form = []): array {
if (empty($element['#field_group_form_process'])) {
$element['#field_group_form_process'] = TRUE;
if (empty($element['#fieldgroups'])) {
return $element;
}
// Create all groups and keep a flat list of references to these groups.
$group_references = [];
foreach ($element['#fieldgroups'] as $group_name => $group) {
if (!isset($element[$group_name])) {
$element[$group_name] = [];
}
$group_parents = $element['#array_parents'];
if (empty($group->parent_name)) {
if (isset($group->region)) {
$group_parents[] = $group->region;
}
}
else {
$group_parents[] = $group->parent_name;
}
$group_references[$group_name] = &$element[$group_name];
$element[$group_name]['#group'] = implode('][', $group_parents);
// Use array parents to set the group name.
// This will cover multilevel forms (eg paragraphs).
$parents = $element['#array_parents'];
$parents[] = $group_name;
$element[$group_name]['#parents'] = $parents;
$group_children_parent_group = implode('][', $parents);
if (isset($group->children)) {
foreach ($group->children as $child) {
if (!empty($element[$child]['#field_group_ignore'])) {
continue;
}
$element[$child]['#group'] = $group_children_parent_group;
}
}
}
foreach ($element['#fieldgroups'] as $group_name => $group) {
$field_group_element = &$element[$group_name];
// Let modules define their wrapping element.
// Note that the group element has no properties, only elements.
// The intention here is to have the opportunity to alter the
// elements, as defined in hook_field_group_formatter_info.
// Note, implement $element by reference!
if (method_exists(\Drupal::moduleHandler(), 'invokeAllWith')) {
// On Drupal >= 9.4 use the new method.
\Drupal::moduleHandler()->invokeAllWith('field_group_form_process', function (callable $hook) use (&$field_group_element, &$group, &$element) {
$hook($field_group_element, $group, $element);
});
}
else {
// @phpstan-ignore-next-line
foreach (\Drupal::moduleHandler()->getImplementations('field_group_form_process') as $module) {
$function = $module . '_field_group_form_process';
$function($field_group_element, $group, $element);
}
}
// Allow others to alter the pre_render.
\Drupal::moduleHandler()->alter('field_group_form_process', $field_group_element, $group, $element);
}
// Allow others to alter the complete processed build.
\Drupal::moduleHandler()->alter('field_group_form_process_build', $element, $form_state, $form);
}
return $element;
}
/**
* Pre render callback for rendering groups in forms.
*
* @param array $element
* Form that is being rendered.
*
* @return array
* The updated group.
*/
public static function formGroupPreRender(array $element) {
// Open any closed field groups that contain elements with errors.
if (!empty($element['#fieldgroups'])) {
foreach ($element['#fieldgroups'] as $fieldgroup) {
$closed = isset($element[$fieldgroup->group_name]['#open']) && !$element[$fieldgroup->group_name]['#open'];
if ($closed) {
foreach ($fieldgroup->children as $child) {
if (isset($element[$child]) && static::groupElementsContainErrors($element[$child])) {
$element[$fieldgroup->group_name]['#open'] = TRUE;
break;
}
}
}
}
}
return $element;
}
/**
* Determines if an elements array contains validation errors.
*
* @param array $elements
* The elements array to check for errors.
*
* @return bool
* TRUE if the elements array contains validation errors, otherwise FALSE.
*/
protected static function groupElementsContainErrors(array $elements) {
// Any errors at this level of the elements array?
if (!empty($elements['#errors']) || !empty($elements['#children_errors'])) {
return TRUE;
}
// Dive down.
foreach (Element::children($elements) as $child) {
if (static::groupElementsContainErrors($elements[$child])) {
return TRUE;
}
}
// No errors.
return FALSE;
}
/**
* {@inheritdoc}
*/
public static function trustedCallbacks() {
return ['entityViewPreRender', 'formProcess', 'formGroupPreRender'];
}
}
