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
This commit is contained in:
Oliver Davies 2020-05-29 02:30:29 +01:00
parent 45fda0fed1
commit 56f3434c4a
7 changed files with 190 additions and 10 deletions

View file

@ -1,4 +1,4 @@
services:
Drupal\custom\EventSubscriber\UpdateTalkCreatedDateOnSave:
Drupal\custom\EventSubscriber\UpdateTalkNodeBeforeSave:
tags:
- { name: event_subscriber }

View file

@ -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();

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,113 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\custom\Kernel;
use Carbon\Carbon;
use Drupal\paragraphs\ParagraphInterface;
final class EventsAreReorderedByDateTest extends TalksTestBase {
public function testCreatingNode() {
$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::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()
);
}
}

View file

@ -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',