Skip to content

3. Manipulating entities through forms

  • Add the following use statements to src/Entity/Event.php:

    use Drupal\Core\Entity\ContentEntityForm;
    use Drupal\Core\Entity\ContentEntityDeleteForm;
  • Add the following to the handlers section of the attributes in src/Entity/Event.php:

    'form' => [
      'add' => ContentEntityForm::class,
      'edit' => ContentEntityForm::class,
      'delete' => ContentEntityDeleteForm::class,
    ]
  • Add the following to the links section of the attributes in src/Entity/Event.php:

    'add-form' => '/admin/content/events/add',
    'edit-form' => '/admin/content/events/manage/{event}',
    'delete-form' => '/admin/content/events/manage/{event}/delete',
    The entire Event.php file at this point
    <?php
    
    namespace Drupal\event\Entity;
    
    use Drupal\Core\Entity\Attribute\ContentEntityType;
    use Drupal\Core\Entity\ContentEntityBase;
    use Drupal\Core\Entity\ContentEntityDeleteForm;
    use Drupal\Core\Entity\ContentEntityForm;
    use Drupal\Core\StringTranslation\TranslatableMarkup;
    use Drupal\Core\Entity\EntityPublishedInterface;
    use Drupal\Core\Entity\EntityPublishedTrait;
    use Drupal\Core\Entity\EntityTypeInterface;
    use Drupal\Core\Field\BaseFieldDefinition;
    use Drupal\entity\EntityAccessControlHandler;
    use Drupal\entity\EntityPermissionProvider;
    use Drupal\entity\Routing\DefaultHtmlRouteProvider;
    use Drupal\user\EntityOwnerInterface;
    use Drupal\user\EntityOwnerTrait;
    use Drupal\Core\Datetime\DrupalDateTime;
    use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
    
    /**
     * Defines the event entity class.
     */
    #[ContentEntityType(
    id: 'event',
    label: new TranslatableMarkup('Event'),
    entity_keys: [
        'id' => 'id',
        'uuid' => 'uuid',
        'label' => 'title',
        'owner' => 'author',
        'published' => 'published',
    ],
    handlers: [
        'access' => EntityAccessControlHandler::class,
        'permission_provider' => EntityPermissionProvider::class,
        'route_provider' => [
            'default' => DefaultHtmlRouteProvider::class,
        ],
        'form' => [
            'add' => ContentEntityForm::class,
            'edit' => ContentEntityForm::class,
            'delete' => ContentEntityDeleteForm::class,
        ]
    ],
    links: [
        'canonical' => "/event/{event}",
        'add-form' => '/admin/content/events/add',
        'edit-form' => '/admin/content/events/manage/{event}',
        'delete-form' => '/admin/content/events/manage/{event}/delete',
    ],
    admin_permission: 'administer event',
    base_table: 'event',
    )]
    class Event extends ContentEntityBase implements EntityOwnerInterface, EntityPublishedInterface {
    
        use EntityOwnerTrait, EntityPublishedTrait;
    
        public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
            // Get the field definitions for 'id' and 'uuid' from the parent.
            $fields = parent::baseFieldDefinitions($entity_type);
    
            $fields['title'] = BaseFieldDefinition::create('string')
                ->setLabel(t('Title'))
                ->setRequired(TRUE);
    
            $fields['date'] = BaseFieldDefinition::create('datetime')
                ->setLabel(t('Date'))
                ->setRequired(TRUE)
                ->setDisplayOptions('view', [
                    'label' => 'inline',
                    'settings' => [
                        'format_type' => 'html_date',
                    ],
                    'weight' => 0,
                ]);
    
            $fields['description'] = BaseFieldDefinition::create('text_long')
                ->setLabel(t('Description'))
                ->setDisplayOptions('view', [
                    'label' => 'hidden',
                    'weight' => 10,
                ]);
    
            // Get the field definitions for 'author' and 'published' from the trait.
            $fields += static::ownerBaseFieldDefinitions($entity_type);
            $fields += static::publishedBaseFieldDefinitions($entity_type);
    
            return $fields;
        }
    
        /**
         * @return string
         */
        public function getTitle() {
            return $this->get('title')->value;
        }
    
        /**
         * @param string $title
         *
         * @return $this
         */
        public function setTitle($title) {
            return $this->set('title', $title);
        }
    
        /**
         * @return \Drupal\Core\Datetime\DrupalDateTime
         */
        public function getDate() {
            return $this->get('date')->date;
        }
    
        /**
         * @param \Drupal\Core\Datetime\DrupalDateTime $date
         *
         * @return $this
         */
        public function setDate(DrupalDateTime $date) {
            return $this->set('date', $date->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT));
        }
    
        /**
         * @return \Drupal\filter\Render\FilteredMarkup
         */
        public function getDescription() {
            return $this->get('description')->processed;
        }
    
        /**
         * @param string $description
         * @param string $format
         *
         * @return $this
         */
        public function setDescription($description, $format) {
            return $this->set('description', [
                'value' => $description,
                'format' => $format,
            ]);
        }
    
    }
  • Rebuild caches

    Run drush cache:rebuild

  • Visit /admin/content/events/add

    Note that a route exists and a Save button is shown, but no actual form fields are shown.

  • Visit /admin/content/events/manage/2

    Note that a route exists and Save and Delete buttons are shown, but no actual form fields are shown.

  • Add the following to the $fields['title'] section of the baseFieldDefinitions() method of the Event class before the semicolon:

    ->setDisplayOptions('form', ['weight' => 0])
  • Add the following to the $fields['date'] section of the baseFieldDefinitions() method of the Event class before the semicolon:

    ->setDisplayOptions('form', ['weight' => 10])
  • Add the following to the $fields['description'] section of the baseFieldDefinitions() method of the Event class before the semicolon:

    ->setDisplayOptions('form', ['weight' => 20])
  • Add the following before the return statement of the baseFieldDefinitions() method of the Event class:

    $fields['published']->setDisplayOptions('form', [
      'settings' => [
        'display_label' => TRUE,
      ],
      'weight' => 30,
    ]);
The entire Event.php file at this point
<?php

namespace Drupal\event\Entity;

use Drupal\Core\Entity\Attribute\ContentEntityType;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\ContentEntityDeleteForm;
use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Entity\EntityPublishedInterface;
use Drupal\Core\Entity\EntityPublishedTrait;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\entity\EntityAccessControlHandler;
use Drupal\entity\EntityPermissionProvider;
use Drupal\entity\Routing\DefaultHtmlRouteProvider;
use Drupal\user\EntityOwnerInterface;
use Drupal\user\EntityOwnerTrait;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;

/**
 * Defines the event entity class.
 */
#[ContentEntityType(
id: 'event',
label: new TranslatableMarkup('Event'),
entity_keys: [
    'id' => 'id',
    'uuid' => 'uuid',
    'label' => 'title',
    'owner' => 'author',
    'published' => 'published',
],
handlers: [
    'access' => EntityAccessControlHandler::class,
    'permission_provider' => EntityPermissionProvider::class,
    'route_provider' => [
        'default' => DefaultHtmlRouteProvider::class,
    ],
    'form' => [
        'add' => ContentEntityForm::class,
        'edit' => ContentEntityForm::class,
        'delete' => ContentEntityDeleteForm::class,
    ]
],
links: [
    'canonical' => "/event/{event}",
    'add-form' => '/admin/content/events/add',
    'edit-form' => '/admin/content/events/manage/{event}',
    'delete-form' => '/admin/content/events/manage/{event}/delete',
],
admin_permission: 'administer event',
base_table: 'event',
)]
class Event extends ContentEntityBase implements EntityOwnerInterface, EntityPublishedInterface {

    use EntityOwnerTrait, EntityPublishedTrait;

    public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
        // Get the field definitions for 'id' and 'uuid' from the parent.
        $fields = parent::baseFieldDefinitions($entity_type);

        $fields['title'] = BaseFieldDefinition::create('string')
            ->setLabel(t('Title'))
            ->setRequired(TRUE)
            ->setDisplayOptions('form', ['weight' => 0]);

        $fields['date'] = BaseFieldDefinition::create('datetime')
            ->setLabel(t('Date'))
            ->setRequired(TRUE)
            ->setDisplayOptions('view', [
                'label' => 'inline',
                'settings' => [
                    'format_type' => 'html_date',
                ],
                'weight' => 0,
            ])
            ->setDisplayOptions('form', ['weight' => 10]);

        $fields['description'] = BaseFieldDefinition::create('text_long')
            ->setLabel(t('Description'))
            ->setDisplayOptions('view', [
                'label' => 'hidden',
                'weight' => 10,
            ])
            ->setDisplayOptions('form', ['weight' => 20]);

        // Get the field definitions for 'owner' and 'published' from the traits.
        $fields += static::ownerBaseFieldDefinitions($entity_type);
        $fields += static::publishedBaseFieldDefinitions($entity_type);

        $fields['published']->setDisplayOptions('form', [
            'settings' => [
                'display_label' => TRUE,
            ],
            'weight' => 30,
        ]);

        return $fields;
    }

    /**
     * @return string
     */
    public function getTitle() {
        return $this->get('title')->value;
    }

    /**
     * @param string $title
     *
     * @return $this
     */
    public function setTitle($title) {
        return $this->set('title', $title);
    }

   /**
    * @return \Drupal\Core\Datetime\DrupalDateTime
    */
    public function getDate() {
        return $this->get('date')->date;
    }

    /**
     * @param \Drupal\Core\Datetime\DrupalDateTime $date
     *
     * @return $this
     */
    public function setDate(DrupalDateTime $date) {
        return $this->set('date', $date->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT));
    }

    /**
     * @return \Drupal\filter\Render\FilteredMarkup
     */
    public function getDescription() {
        return $this->get('description')->processed;
    }

    /**
     * @param string $description
     * @param string $format
     *
     * @return $this
     */
    public function setDescription($description, $format) {
        return $this->set('description', [
            'value' => $description,
            'format' => $format,
        ]);
    }

}
  • Rebuild caches

    Run drush cache:rebuild

  • Add an event in the user interface

    Visit /admin/content/events/add

    Note that the form fields are displayed.

    Enter a title, date and description and press Save.

    Verify that the event was saved by checking that a new row was created in the {event} table.

    Note that no message is displayed and no redirect is performed.

  • Add a src/Form directory

  • Add a src/Form/EventForm.php file with the following:

    <?php
    
    namespace Drupal\event\Form;
    
    use Drupal\Core\Entity\ContentEntityForm;
    use Drupal\Core\Form\FormStateInterface;
    
    class EventForm extends ContentEntityForm {
    
      public function save(array $form, FormStateInterface $form_state) {
        parent::save($form, $form_state);
    
        $entity = $this->getEntity();
        $entity_type = $entity->getEntityType();
    
        $arguments = [
          '@entity_type' => $entity_type->getSingularLabel(),
          '%entity' => $entity->label(),
          'link' => $entity->toLink($this->t('View'), 'canonical')->toString(),
        ];
    
        $this->logger($entity->getEntityTypeId())->notice('The @entity_type %entity has been saved.', $arguments);
        $this->messenger()->addStatus($this->t('The @entity_type %entity has been saved.', $arguments));
    
        $form_state->setRedirectUrl($entity->toUrl('canonical'));
      }
    
    }
  • Add the following use statement to src/Entity/Event.php

    use Drupal\event\Form\EventForm;

    and replace the value of the add and edit annotation keys in the form handlers section of the annotation in src/Entity/Event.php with EventForm::class.

  • Rebuild caches

    Run drush cache:rebuild

  • Edit an event in the user interface

    Visit /admin/content/events/manage/3

    Note that a route exists and form fields are displayed including proper default values.

    Modify the title, date and description and published status and press Save.

    Note that a message is displayed and a redirect is performed.

    Verify that the values in the respective row in the {event} table have been updated. Also note that the default values of the form fields are correct on the reloaded page.

  • Delete an event in the user interface

    Visit /admin/content/events/manage/3/delete

    Note that a route exists and a confirmation form is shown.

    Press Delete.

    Note that a message is shown and you are redirected to the front page.

    Verify that the respective row in the {event} table has been deleted.