From 56f3434c4a465128abd5b96a2ebe14648e181f71 Mon Sep 17 00:00:00 2001 From: Oliver Davies Date: Fri, 29 May 2020 02:30:29 +0100 Subject: [PATCH] Automatically order events when a talk is saved To ensure that the event dates for a talk are always in the correct order, and to make the editing experience easier, this change automatically re-orders the events on a talk node to be based on the event date. Fixes #74 --- web/modules/custom/custom/custom.services.yml | 2 +- .../custom/custom/src/Entity/Node/Talk.php | 14 ++- ...nSave.php => UpdateTalkNodeBeforeSave.php} | 28 ++++- ...field.field.paragraph.event.field_name.yml | 18 +++ .../field.storage.paragraph.field_name.yml | 20 ++++ .../Kernel/EventsAreReorderedByDateTest.php | 113 ++++++++++++++++++ .../custom/tests/src/Kernel/TalksTestBase.php | 5 +- 7 files changed, 190 insertions(+), 10 deletions(-) rename web/modules/custom/custom/src/EventSubscriber/{UpdateTalkCreatedDateOnSave.php => UpdateTalkNodeBeforeSave.php} (54%) create mode 100644 web/modules/custom/custom/tests/modules/custom_test/config/install/field.field.paragraph.event.field_name.yml create mode 100644 web/modules/custom/custom/tests/modules/custom_test/config/install/field.storage.paragraph.field_name.yml create mode 100644 web/modules/custom/custom/tests/src/Kernel/EventsAreReorderedByDateTest.php diff --git a/web/modules/custom/custom/custom.services.yml b/web/modules/custom/custom/custom.services.yml index f55ff04..21bb957 100644 --- a/web/modules/custom/custom/custom.services.yml +++ b/web/modules/custom/custom/custom.services.yml @@ -1,4 +1,4 @@ services: - Drupal\custom\EventSubscriber\UpdateTalkCreatedDateOnSave: + Drupal\custom\EventSubscriber\UpdateTalkNodeBeforeSave: tags: - { name: event_subscriber } diff --git a/web/modules/custom/custom/src/Entity/Node/Talk.php b/web/modules/custom/custom/src/Entity/Node/Talk.php index 25db19e..157789d 100644 --- a/web/modules/custom/custom/src/Entity/Node/Talk.php +++ b/web/modules/custom/custom/src/Entity/Node/Talk.php @@ -18,13 +18,25 @@ use Illuminate\Support\Collection; */ class Talk extends Node implements ContentEntityBundleInterface { + public function addEvent(ParagraphInterface $event): void { + $this->set( + 'field_events', + $this->getEvents()->push($event)->toArray() + ); + } + + public function getEvents(): Collection { + return Collection::make($this->get('field_events') + ->referencedEntities()); + } + /** * Find the date for the latest event. * * @return string|null */ public function findLatestEventDate(): ?string { - return Collection::make($this->get('field_events')->referencedEntities()) + return $this->getEvents() ->map(fn(ParagraphInterface $event) => $event->get('field_date') ->getString()) ->max(); diff --git a/web/modules/custom/custom/src/EventSubscriber/UpdateTalkCreatedDateOnSave.php b/web/modules/custom/custom/src/EventSubscriber/UpdateTalkNodeBeforeSave.php similarity index 54% rename from web/modules/custom/custom/src/EventSubscriber/UpdateTalkCreatedDateOnSave.php rename to web/modules/custom/custom/src/EventSubscriber/UpdateTalkNodeBeforeSave.php index d352933..05e8c4b 100644 --- a/web/modules/custom/custom/src/EventSubscriber/UpdateTalkCreatedDateOnSave.php +++ b/web/modules/custom/custom/src/EventSubscriber/UpdateTalkNodeBeforeSave.php @@ -5,16 +5,17 @@ declare(strict_types=1); namespace Drupal\custom\EventSubscriber; use Carbon\Carbon; -use Drupal\Core\Entity\EntityInterface; -use Drupal\custom\Entity\Node; +use Drupal\custom\Entity\Node\Talk; +use Drupal\custom\Entity\Paragraph\Event; use Drupal\hook_event_dispatcher\Event\Entity\BaseEntityEvent; use Drupal\hook_event_dispatcher\HookEventDispatcherInterface; +use Drupal\paragraphs\ParagraphInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** - * Set the created date for a talk to be the last date that the talk is given. + * Update a talk node before it's saved. */ -final class UpdateTalkCreatedDateOnSave implements EventSubscriberInterface { +final class UpdateTalkNodeBeforeSave implements EventSubscriberInterface { public static function getSubscribedEvents() { return [ @@ -31,11 +32,26 @@ final class UpdateTalkCreatedDateOnSave implements EventSubscriberInterface { return; } + $this->reorderEvents($event->getEntity()); $this->updateCreatedDate($event->getEntity()); } - private function updateCreatedDate(EntityInterface $talk): void { - /** @var \Drupal\custom\Entity\Node $talk */ + private function reorderEvents(Talk $talk): void { + $events = $talk->getEvents(); + + $eventsByDate = $events + ->sortBy(fn(ParagraphInterface $event) => $event->get('field_date') + ->getString()) + ->values(); + + // If the original event IDs don't match the sorted event IDs, update the + // event field to use the sorted ones. + if ($events->map->id() != $eventsByDate->map->id()) { + $talk->set('field_events', $eventsByDate->toArray()); + } + } + + private function updateCreatedDate(Talk $talk): void { if (!$eventDate = $talk->findLatestEventDate()) { return; } diff --git a/web/modules/custom/custom/tests/modules/custom_test/config/install/field.field.paragraph.event.field_name.yml b/web/modules/custom/custom/tests/modules/custom_test/config/install/field.field.paragraph.event.field_name.yml new file mode 100644 index 0000000..c3779eb --- /dev/null +++ b/web/modules/custom/custom/tests/modules/custom_test/config/install/field.field.paragraph.event.field_name.yml @@ -0,0 +1,18 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.paragraph.field_name + - paragraphs.paragraphs_type.event +id: paragraph.event.field_name +field_name: field_name +entity_type: paragraph +bundle: event +label: 'Event name' +description: '' +required: true +translatable: false +default_value: { } +default_value_callback: '' +settings: { } +field_type: string diff --git a/web/modules/custom/custom/tests/modules/custom_test/config/install/field.storage.paragraph.field_name.yml b/web/modules/custom/custom/tests/modules/custom_test/config/install/field.storage.paragraph.field_name.yml new file mode 100644 index 0000000..6720b1e --- /dev/null +++ b/web/modules/custom/custom/tests/modules/custom_test/config/install/field.storage.paragraph.field_name.yml @@ -0,0 +1,20 @@ +langcode: en +status: true +dependencies: + module: + - paragraphs +id: paragraph.field_name +field_name: field_name +entity_type: paragraph +type: string +settings: + max_length: 255 + is_ascii: false + case_sensitive: false +module: core +locked: false +cardinality: 1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/web/modules/custom/custom/tests/src/Kernel/EventsAreReorderedByDateTest.php b/web/modules/custom/custom/tests/src/Kernel/EventsAreReorderedByDateTest.php new file mode 100644 index 0000000..67ba828 --- /dev/null +++ b/web/modules/custom/custom/tests/src/Kernel/EventsAreReorderedByDateTest.php @@ -0,0 +1,113 @@ +createEvent([ + 'field_date' => Carbon::today()->addWeeks(2), + 'field_name' => 'Drupal Bristol', + ]), + $this->createEvent([ + 'field_date' => Carbon::yesterday(), + 'field_name' => 'DrupalCamp London', + ]), + $this->createEvent([ + 'field_date' => Carbon::tomorrow(), + 'field_name' => 'PHP UK conference', + ]), + $this->createEvent([ + 'field_date' => Carbon::today()->addMonths(3), + 'field_name' => 'CMS Philly', + ]), + $this->createEvent([ + 'field_date' => Carbon::today()->subYear(), + 'field_name' => 'PHP South Wales', + ]), + ]; + + $talk = $this->createTalk([ + 'field_events' => $events, + ]); + + $this->assertSame( + [ + 'PHP South Wales', + 'DrupalCamp London', + 'PHP UK conference', + 'Drupal Bristol', + 'CMS Philly', + ], + $talk->getEvents() + ->map(fn(ParagraphInterface $event) => $event->get('field_name') + ->getString()) + ->toArray() + ); + } + + public function testUpdatingNode() { + $events = [ + $this->createEvent([ + 'field_date' => Carbon::today()->addWeeks(2), + 'field_name' => 'Drupal Bristol', + ]), + $this->createEvent([ + 'field_date' => Carbon::yesterday(), + 'field_name' => 'DrupalCamp London', + ]), + $this->createEvent([ + 'field_date' => Carbon::today()->addMonths(3), + 'field_name' => 'CMS Philly', + ]), + $this->createEvent([ + 'field_date' => Carbon::today()->subYear(), + 'field_name' => 'PHP South Wales', + ]), + ]; + + $talk = $this->createTalk([ + 'field_events' => $events, + ]); + + $this->assertSame( + [ + 'PHP South Wales', + 'DrupalCamp London', + 'Drupal Bristol', + 'CMS Philly', + ], + $talk->getEvents() + ->map(fn(ParagraphInterface $event) => $event->get('field_name') + ->getString()) + ->toArray() + ); + + $talk->addEvent($this->createEvent([ + 'field_date' => Carbon::tomorrow(), + 'field_name' => 'PHP UK conference', + ])); + $talk->save(); + + $this->assertSame( + [ + 'PHP South Wales', + 'DrupalCamp London', + 'PHP UK conference', + 'Drupal Bristol', + 'CMS Philly', + ], + $talk->getEvents() + ->map(fn(ParagraphInterface $event) => $event->get('field_name') + ->getString()) + ->toArray() + ); + } + +} diff --git a/web/modules/custom/custom/tests/src/Kernel/TalksTestBase.php b/web/modules/custom/custom/tests/src/Kernel/TalksTestBase.php index 5e7f174..90b0d94 100644 --- a/web/modules/custom/custom/tests/src/Kernel/TalksTestBase.php +++ b/web/modules/custom/custom/tests/src/Kernel/TalksTestBase.php @@ -4,7 +4,7 @@ declare(strict_types=1); namespace Drupal\Tests\custom\Kernel; -use Drupal\Core\Entity\EntityInterface; +use Drupal\custom\Entity\Node\Talk; use Drupal\KernelTests\Core\Entity\EntityKernelTestBase; use Drupal\node\Entity\Node; use Drupal\paragraphs\Entity\Paragraph; @@ -33,6 +33,7 @@ abstract class TalksTestBase extends EntityKernelTestBase { ]; protected function createEvent(array $overrides = []): ParagraphInterface { + /** @var \Drupal\paragraphs\ParagraphInterface $event */ $event = Paragraph::create(array_merge([ 'type' => 'event', ], $overrides)); @@ -40,7 +41,7 @@ abstract class TalksTestBase extends EntityKernelTestBase { return tap($event)->save(); } - protected function createTalk(array $overrides = []): EntityInterface { + protected function createTalk(array $overrides = []): Talk { $talk = Node::create(array_merge([ 'title' => 'Test Driven Drupal', 'type' => 'talk',