collection-8.x-1.x-dev/src/CollectionContentEntityFormAlter.php
src/CollectionContentEntityFormAlter.php
<?php
namespace Drupal\collection;
use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\Session\AccountProxy;
use Drupal\collection\CollectionContentManager;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\ContentEntityDeleteForm;
use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\StringTranslation\StringTranslationTrait;
/**
* A service to alter a content entity form.
*/
class CollectionContentEntityFormAlter {
use StringTranslationTrait;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManager
*/
private $entityTypeManager;
/**
* The current user account.
*
* @var \Drupal\Core\Session\AccountProxy
*/
private $account;
/**
* The collection content manager service.
*
* @var \Drupal\collection\CollectionContentManager
*/
protected $collectionContentManager;
/**
* CollectionContentEntityFormAlter constructor.
*
* @param \Drupal\Core\Entity\EntityTypeManager $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Session\AccountProxy $current_user
* The current user.
* @param \Drupal\collection\CollectionContentManager $collection_content_manager
* The collection content manager.
*/
public function __construct(EntityTypeManager $entity_type_manager, AccountProxy $current_user, CollectionContentManager $collection_content_manager) {
$this->entityTypeManager = $entity_type_manager;
$this->account = $current_user;
$this->collectionContentManager = $collection_content_manager;
}
/**
* Returns extra/pseudo fields for any content entity that can be collected.
*
* @see collection_entity_extra_field_info().
*
* @return array
* A nested array of 'pseudo-field' elements. See
* EntityFieldManagerInterface::getExtraFields().
*/
public function getExtraFieldInfo() {
static $extra;
if (isset($extra)) {
return $extra;
}
$extra = [];
$collection_item_type_storage = $this->entityTypeManager->getStorage('collection_item_type');
/** @var \Drupal\collection\Entity\CollectionItemType $collection_item_type */
foreach ($collection_item_type_storage->loadMultiple() as $collection_item_type) {
foreach ($collection_item_type->getAllowedBundles() as $allowed_bundle) {
list($entity_type_id, $bundle) = explode('.', $allowed_bundle);
$extra[$entity_type_id][$bundle]['form']['ief_collection_items'] = [
'label' => t('Collections'),
'weight' => 50,
'visible' => TRUE,
];
}
}
return $extra;
}
/**
* Alter a content entity add/edit form.
*
* @see collection_form_alter().
*
* @param array $form
* A Form API form.
* @param FormStateInterface $form_state
* The form state.
* @param string $form_id
* The form ID.
*/
public function alterForm(array &$form, FormStateInterface $form_state, $form_id) {
$form_object = $form_state->getFormObject();
if (!$form_object instanceof ContentEntityForm || $form_object instanceof ContentEntityDeleteForm) {
return;
}
if ($form_state->get('form_display')->getComponent('ief_collection_items')) {
$entity = $form_object->getEntity();
if ($entity->isNew()) {
$this->addNewCollectionItem($form, $form_state, $entity);
}
else {
$this->addExistingCollectionItems($form, $form_state, $entity);
}
}
}
/**
* Add a new collection items inline form.
*
* Embed a collection item add form in any content entity (e.g. node) forms
* if there is a `collection` query parameter.
*
* @param array $form
* A content entity add/edit form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity for this ContentEntityForm.
*/
public function addNewCollectionItem(array &$form, FormStateInterface $form_state, EntityInterface $entity) {
$collection_storage = $this->entityTypeManager->getStorage('collection');
$collection_item_storage = $this->entityTypeManager->getStorage('collection_item');
// Check to see if there is a query param requesting the addition to an
// existing collection.
if (($collection_id_from_param = \Drupal::request()->query->get('collection')) && ($collection_from_param = $collection_storage->load($collection_id_from_param))) {
// Ensure that the current user can add items to this collection.
if (!$collection_from_param->access('update')) {
return;
}
$allowed_types = $collection_from_param->type->entity->getAllowedCollectionItemTypes($entity->getEntityTypeId(), $entity->bundle());
if (empty($allowed_types)) {
return;
}
$collection_item_new = $collection_item_storage->create([
'type' => reset($allowed_types),
'collection' => $collection_from_param,
'canonical' => TRUE,
]);
$form['ief_collection_items'] = [
'#type' => 'details',
'#title' => $this->t('Collections'),
'#open' => TRUE,
];
if (!isset($form['actions']['submit']['#submit'])) {
$form['ief_collection_items']['collection_new'] = [
'#markup' => $this->t('Missing submit handler.'),
];
return;
}
$form['ief_collection_items']['collection_new'] = [
'#type' => 'fieldset',
'#title' => $collection_from_param->label(),
'#open' => TRUE,
'#attributes' => ['class' => ['collection-item', 'collection-item--' . $collection_item_new->bundle()]],
];
$form['ief_collection_items']['collection_new']['item'] = [
'#type' => 'inline_entity_form',
'#entity_type' => 'collection_item',
'#bundle' => $collection_item_new->bundle(),
'#default_value' => $collection_item_new,
'#form_mode' => 'mini',
'#save_entity' => FALSE,
'#op' => 'add',
'#ief_id' => 'collection_item_subform-new',
// Do not validate this collection_item for new content entities,
// because validators like UniqueItemValidator and
// SingleCanonicalItemValidator are only relevant for existing content
// entities. In this case, there is no $collection_item->item entity
// until the content entity form is submitted and `handleNewCollection`
// is called. The missing `item` entity causes some validators to fail.
'#element_validate' => [],
];
// This will allow the methods in EntityInlineForm to be called (e.g.
// buildEntity).
if (!$form_state->get(['inline_entity_form'])) {
$form_state->set(['inline_entity_form'], []);
}
// This additional submit handler is called to save the collection_item
// _after_ the host content entity. This is so we have access to the new
// content entity id.
$form['actions']['submit']['#submit'][] = [static::class, 'handleNewCollection'];
}
}
/**
* Add collection items for this entity to the form.
*
* Embed collection item edit forms in any content entity (e.g. node) forms
* if that entity is in one or more collections.
*
* @param array $form
* A content entity add/edit form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity for this ContentEntityForm.
*/
public function addExistingCollectionItems(array &$form, FormStateInterface $form_state, EntityInterface $entity) {
$collection_item_storage = $this->entityTypeManager->getStorage('collection_item');
// Load all collection items that reference this entity.
$collection_items = $this->collectionContentManager->getCollectionItemsForEntity($entity, 'update');
if (($collection_item_count = count($collection_items)) === 0) {
return;
}
$form['ief_collection_items'] = [
'#type' => 'details',
'#title' => $this->t('Collections'),
'#open' => TRUE,
];
foreach ($collection_items as $collection_item) {
$collection = $collection_item->collection->entity;
$form['ief_collection_items']['collection_' . $collection->id()] = [
'#type' => 'details',
'#title' => $collection->label() . ($collection_item->isCanonical() ? ' *' : ''),
'#open' => $collection_item_count === 1,
'#attributes' => ['class' => ['collection-item', 'collection-item--' . $collection_item->bundle()]],
];
$form['ief_collection_items']['collection_' . $collection->id()]['item_' . $collection_item->id()] = [
'#type' => 'inline_entity_form',
'#entity_type' => 'collection_item',
'#bundle' => $collection_item->bundle(),
'#default_value' => $collection_item,
'#form_mode' => 'mini',
'#save_entity' => TRUE,
'#op' => 'edit',
'#ief_id' => 'collection_item_subform-' . $collection_item->id(),
];
}
$form['ief_collection_items']['note'] = [
'#markup' => $this->t('* Canonical collection'),
'#theme_wrappers' => [
'container' => [
'#attributes' => ['class' => ['collection-items-inline-note']],
],
],
];
// Set the bare minimum 'inline_entity_form' value to the form state. This is
// required to add the proper submit handlers to the node add/edit form. See
// inline_entity_form_form_alter() in inline_entity_form.module
if (!$form_state->get(['inline_entity_form'])) {
$form_state->set(['inline_entity_form'], []);
}
}
/**
* Submit callback for a content entity with an inline collection item form.
*
* The collection item is saved here, rather than via `inline_entity_form`
* `#save_entity`, because we're doing the opposite of what IEF usually does.
*
* Normally, IEF forms create a new entity that will be added to a reference
* field on the content entity.
*
* Instead, we need the content entity saved first, so that the
* collection_item can refer to it.
*
* @see self::addNewCollectionItem().
*/
public static function handleNewCollection($form, FormStateInterface $form_state) {
$collection_item = $form['ief_collection_items']['collection_new']['item']['#entity'] ?? FALSE;
$collection_item->item = $form_state->getformObject()->getEntity();
$collection_item->save();
}
}
