Rename custom module directories

- Rename `opdavies_blog` to `blog`.
- Rename `opdavies_blog_test` to `blog_test`.
- Rename `opdavies_talks` to `talks`.
- Rename `opdavies_talks_test` to `talks_test`.

The files within the directories haven't changed, so there is no
breaking change caused by renaming the directories.

 Please enter the commit message for your changes. Lines starting
This commit is contained in:
Oliver Davies 2020-09-04 21:19:17 +01:00
parent d7d5a6c8a3
commit cbe60209e6
59 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,7 @@
name: Oliver Davies Talks
description: Custom code for talks pages.
type: module
core_version_requirement: ^8 || ^9
package: Custom
dependencies:
- discoverable_entity_bundle_classes:discoverable_entity_bundle_classes

View file

@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
use Drupal\opdavies_talks\Repository\TalkRepository;
/**
* Set talk type for all existing talks.
*/
function opdavies_talks_update_8001(): void {
$talkRepository = \Drupal::service(TalkRepository::class);
foreach ($talkRepository->getAll() as $talk) {
$talk->set('field_type', 'talk');
$talk->save();
}
}

View file

@ -0,0 +1,45 @@
<?php
/**
* @file
* Custom code for talks pages.
*/
declare(strict_types=1);
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\discoverable_entity_bundle_classes\Storage\Node\NodeStorage;
use Drupal\opdavies_talks\Service\TalkDateUpdater;
/**
* Implements hook_cron().
*/
function opdavies_talks_cron(): void {
$dateUpdater = Drupal::service(TalkDateUpdater::class);
$dateUpdater->__invoke();
}
/**
* Implements hook_views_data_alter().
*/
function opdavies_talks_views_data_alter(array &$data): void {
$data['node__field_event_date']['event_sort'] = [
'title' => t('Custom event sort'),
'group' => t('Content'),
'help' => t('Sort events by past/future, then distance from now.'),
'sort' => [
'field' => 'field_event_date_value',
'id' => 'event_sort',
],
];
}
/**
* Implements hook_entity_type_build().
*/
function opdavies_talks_entity_type_build(array &$entityTypes): void {
/** @var EntityTypeInterface[] $entityTypes */
if (isset($entityTypes['node'])) {
$entityTypes['node']->setStorageClass(NodeStorage::class);
}
}

View file

@ -0,0 +1,13 @@
services:
Drupal\opdavies_talks\EventSubscriber\UpdateTalkNodeBeforeSave:
tags:
- { name: event_subscriber }
Drupal\opdavies_talks\Repository\TalkRepository:
autowire: true
Drupal\opdavies_talks\Service\TalkCounter:
autowire: true
Drupal\opdavies_talks\Service\TalkDateUpdater:
autowire: true

View file

@ -0,0 +1,57 @@
<?php
namespace Drupal\opdavies_talks\Entity\Node;
use Drupal\discoverable_entity_bundle_classes\ContentEntityBundleInterface;
use Drupal\node\Entity\Node;
use Drupal\paragraphs\ParagraphInterface;
use Illuminate\Support\Collection;
/**
* Defines an talk node class.
*
* @ContentEntityBundleClass(
* label = @Translation("Talk"),
* entity_type = "node",
* bundle = "talk"
* );
*/
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());
}
public function getNextDate(): ?int {
if ($this->get('field_event_date')->isEmpty()) {
return NULL;
}
return (int) $this->get('field_event_date')->getString();
}
/**
* Find the date for the latest event.
*
* @return string|null
*/
public function findLatestEventDate(): ?string {
return $this->getEvents()
->map(fn(ParagraphInterface $event) => $event->get('field_date')
->getString())
->max();
}
public function setNextDate(int $date): void {
$this->set('field_event_date', $date);
}
}

View file

@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace Drupal\opdavies_talks\EventSubscriber;
use Carbon\Carbon;
use Drupal\hook_event_dispatcher\Event\Entity\BaseEntityEvent;
use Drupal\hook_event_dispatcher\HookEventDispatcherInterface;
use Drupal\opdavies_talks\Entity\Node\Talk;
use Drupal\paragraphs\ParagraphInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Update a talk node before it's saved.
*/
final class UpdateTalkNodeBeforeSave implements EventSubscriberInterface {
public static function getSubscribedEvents() {
return [
HookEventDispatcherInterface::ENTITY_PRE_SAVE => 'onEntityPreSave',
];
}
public function onEntityPreSave(BaseEntityEvent $event): void {
if ($event->getEntity()->getEntityTypeId() != 'node') {
return;
}
if ($event->getEntity()->bundle() != 'talk') {
return;
}
/** @var \Drupal\opdavies_blog\Entity\Node\Talk $talk */
$talk = $event->getEntity();
$this->reorderEvents($talk);
$this->updateCreatedDate($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;
}
$talkDate = Carbon::parse($eventDate)->getTimestamp();
if ($talkDate == $talk->get('created')->getString()) {
return;
}
$talk->set('created', $talkDate);
}
}

View file

@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
namespace Drupal\opdavies_talks\Plugin\views\sort;
use Carbon\Carbon;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\views\Annotation\ViewsSort;
use Drupal\views\Plugin\views\sort\Date;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* @ViewsSort("event_sort")
*/
final class Event extends Date {
private TimeInterface $time;
public function __construct(
array $configuration,
$pluginId,
$pluginDefinition,
TimeInterface $time
) {
parent::__construct($configuration, $pluginId, $pluginDefinition);
$this->time = $time;
}
public static function create(
ContainerInterface $container,
array $configuration,
$pluginId,
$pluginDefinition
) {
return new static(
$configuration,
$pluginId,
$pluginDefinition,
$container->get('datetime.time')
);
}
public function query() {
$this->ensureMyTable();
$currentDate = Carbon::parse('today')->getTimestamp();
$dateAlias = "$this->tableAlias.$this->realField";
// Is this event in the past?
$this->query->addOrderBy(
NULL,
sprintf("%d > %s", $currentDate, $dateAlias),
$this->options['order'],
"in_past"
);
// How far in the past/future is this event?
$this->query->addOrderBy(
NULL,
sprintf('ABS(%s - %d)', $dateAlias, $currentDate),
$this->options['order'],
"distance_from_now"
);
}
}

View file

@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace Drupal\opdavies_talks\Repository;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\opdavies_blog\Entity\Node\Talk;
use Illuminate\Support\Collection;
final class TalkRepository {
private EntityStorageInterface $nodeStorage;
public function __construct(EntityTypeManagerInterface $entityTypeManager) {
$this->nodeStorage = $entityTypeManager->getStorage('node');
}
/**
* @return Collection|Talk[]
*/
public function getAll(bool $publishedOnly = FALSE): Collection {
$properties = ['type' => 'talk'];
if ($publishedOnly) {
$properties['status'] = TRUE;
}
return new Collection(
$this->nodeStorage->loadByProperties($properties)
);
}
}

View file

@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Drupal\opdavies_talks\Service;
use Carbon\Carbon;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\node\NodeInterface;
use Drupal\opdavies_talks\Entity\Node\Talk;
use Drupal\paragraphs\ParagraphInterface;
use Illuminate\Support\Collection;
final class TalkCounter {
private EntityStorageInterface $nodeStorage;
public function __construct(EntityTypeManagerInterface $entityTypeManager) {
$this->nodeStorage = $entityTypeManager->getStorage('node');
}
public function getCount(): int {
$today = Carbon::today()->format('Y-m-d H:i:s');
return $this->getTalks()
->flatMap(fn(Talk $talk) => $talk->getEvents())
->filter(fn(ParagraphInterface $event) => $event->get('field_date')->getString() <= $today)
->count();
}
private function getTalks(): Collection {
$talks = $this->nodeStorage->loadByProperties([
'status' => NodeInterface::PUBLISHED,
'type' => 'talk',
]);
return new Collection($talks);
}
}

View file

@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace Drupal\opdavies_talks\Service;
use Carbon\Carbon;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
use Drupal\opdavies_talks\Entity\Node\Talk;
use Drupal\opdavies_talks\Repository\TalkRepository;
use Drupal\paragraphs\ParagraphInterface;
final class TalkDateUpdater {
private TalkRepository $talkRepository;
private TimeInterface $time;
public function __construct(
TalkRepository $talkRepository,
TimeInterface $time
) {
$this->talkRepository = $talkRepository;
$this->time = $time;
}
public function __invoke(): void {
foreach ($this->talkRepository->getAll() as $talk) {
$this->updateNextEventDate($talk);
}
}
private function updateNextEventDate(Talk $talk) {
if (!$nextDate = $this->findNextEventDate($talk)) {
return;
}
$nextDateTimestamp = Carbon::parse($nextDate)
->getTimestamp();
if ($nextDateTimestamp == $talk->getNextDate()) {
return;
}
$talk->setNextDate($nextDateTimestamp);
$talk->save();
}
private function findNextEventDate(Talk $talk): ?string {
$currentTime = Carbon::today()
->format(DateTimeItemInterface::DATE_STORAGE_FORMAT);
$dates = $talk->getEvents()
->map(fn(ParagraphInterface $event) => $event->get('field_date')
->getString())
->sort();
if ($dates->isEmpty()) {
return NULL;
}
// If a future date is found, return it.
if ($futureDate = $dates->first(fn(string $eventDate) => $eventDate > $currentTime)) {
return $futureDate;
}
// If no future date is found, return the last past date.
return $dates->last();
}
}

View file

@ -0,0 +1,21 @@
uuid: 5bb25694-3431-4c5e-9dc2-c7c46a91eab5
langcode: en
status: true
dependencies:
config:
- field.storage.node.field_event_date
- node.type.talk
module:
- datetime
id: node.talk.field_event_date
field_name: field_event_date
entity_type: node
bundle: talk
label: 'Next event date'
description: ''
required: false
translatable: false
default_value: { }
default_value_callback: ''
settings: { }
field_type: datetime

View file

@ -0,0 +1,30 @@
langcode: en
status: true
dependencies:
config:
- field.storage.node.field_events
- node.type.talk
- paragraphs.paragraphs_type.event
module:
- entity_reference_revisions
id: node.talk.field_events
field_name: field_events
entity_type: node
bundle: talk
label: Events
description: ''
required: false
translatable: false
default_value: { }
default_value_callback: ''
settings:
handler: 'default:paragraph'
handler_settings:
negate: 0
target_bundles:
event: event
target_bundles_drag_drop:
event:
enabled: true
weight: 2
field_type: entity_reference_revisions

View file

@ -0,0 +1,20 @@
langcode: en
status: true
dependencies:
config:
- field.storage.paragraph.field_date
- paragraphs.paragraphs_type.event
module:
- datetime
id: paragraph.event.field_date
field_name: field_date
entity_type: paragraph
bundle: event
label: Date
description: ''
required: true
translatable: false
default_value: { }
default_value_callback: ''
settings: { }
field_type: datetime

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 @@
uuid: e718e9e3-0765-4cf4-b7e8-cccf41ee3d1a
langcode: en
status: true
dependencies:
module:
- datetime
- node
id: node.field_event_date
field_name: field_event_date
entity_type: node
type: datetime
settings:
datetime_type: date
module: datetime
locked: false
cardinality: 1
translatable: true
indexes: { }
persist_with_no_fields: false
custom_storage: false

View file

@ -0,0 +1,20 @@
langcode: en
status: true
dependencies:
module:
- entity_reference_revisions
- node
- paragraphs
id: node.field_events
field_name: field_events
entity_type: node
type: entity_reference_revisions
settings:
target_type: paragraph
module: entity_reference_revisions
locked: false
cardinality: -1
translatable: true
indexes: { }
persist_with_no_fields: false
custom_storage: false

View file

@ -0,0 +1,19 @@
langcode: en
status: true
dependencies:
module:
- datetime
- paragraphs
id: paragraph.field_date
field_name: field_date
entity_type: paragraph
type: datetime
settings:
datetime_type: date
module: datetime
locked: false
cardinality: 1
translatable: true
indexes: { }
persist_with_no_fields: false
custom_storage: false

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,10 @@
langcode: en
status: true
dependencies: { }
name: Talk
type: talk
description: ''
help: ''
new_revision: true
preview_mode: 1
display_submitted: false

View file

@ -0,0 +1,9 @@
langcode: en
status: true
dependencies: { }
id: event
label: Event
icon_uuid: null
icon_default: null
description: ''
behavior_plugins: { }

View file

@ -0,0 +1,196 @@
langcode: en
status: true
dependencies:
config:
- core.entity_view_mode.node.teaser
- node.type.talk
- system.menu.main
module:
- node
- opdavies_talks
- user
id: talks
label: Talks
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
display:
default:
display_plugin: default
id: default
display_title: Master
position: 0
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: none
options:
items_per_page: 0
offset: 0
style:
type: html_list
options:
row_class: ''
default_row_class: true
uses_fields: false
type: ul
wrapper_class: ''
class: space-y-8
row:
type: 'entity:node'
options:
view_mode: teaser
fields:
title:
id: title
table: node_field_data
field: title
entity_type: node
entity_field: title
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false
word_boundary: false
ellipsis: false
strip_tags: false
html: false
hide_empty: false
empty_zero: false
settings:
link_to_entity: true
plugin_id: field
relationship: none
group_type: group
admin_label: ''
exclude: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_alter_empty: true
click_sort_column: value
type: string
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
filters:
status:
value: '1'
table: node_field_data
field: status
plugin_id: boolean
entity_type: node
entity_field: status
id: status
expose:
operator: ''
operator_limit_selection: false
operator_list: { }
group: 1
type:
id: type
table: node_field_data
field: type
value:
talk: talk
entity_type: node
entity_field: type
plugin_id: bundle
expose:
operator_limit_selection: false
operator_list: { }
sorts:
event_sort:
id: event_sort
table: node__field_event_date
field: event_sort
relationship: none
group_type: group
admin_label: ''
order: ASC
exposed: false
expose:
label: ''
granularity: second
plugin_id: event_sort
title: Talks
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }
display_extenders: { }
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- 'user.node_grants:view'
- user.permissions
tags: { }
page_1:
display_plugin: page
id: page_1
display_title: Page
position: 1
display_options:
display_extenders: { }
path: talks
menu:
type: normal
title: Talks
description: ''
expanded: false
parent: ''
weight: -48
context: '0'
menu_name: main
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- 'user.node_grants:view'
- user.permissions
tags: { }

View file

@ -0,0 +1,4 @@
name: Custom Test
type: module
core_version_requirement: ^8 || ^9
hidden: true

View file

@ -0,0 +1,66 @@
<?php
declare(strict_types = 1);
namespace Drupal\Tests\opdavies_talks\Kernel;
use Carbon\Carbon;
use Drupal\node\NodeInterface;
use Drupal\opdavies_talks\Service\TalkCounter;
use PHPUnit\Framework\Assert;
class CountPreviousTalksTest extends TalksTestBase {
private TalkCounter $talkCounter;
/** @test */
public function previous_talks_are_counted(): void {
$this->createTalk([
'field_events' => [
$this->createEvent(),
$this->createEvent(),
],
]);
$this->createTalk([
'field_events' => [
$this->createEvent(),
],
]);
Assert::assertSame(3, $this->talkCounter->getCount());
}
/** @test */
public function future_talks_are_not_counted(): void {
$this->createTalk([
'field_events' => [
$this->createEvent([
'field_date' => Carbon::now()->subDay(),
]),
$this->createEvent([
'field_date' => Carbon::now()->addDay(),
]),
],
]);
Assert::assertSame(1, $this->talkCounter->getCount());
}
/** @test */
public function unpublished_talks_are_not_counted(): void {
$this->createTalk([
'field_events' => [$this->createEvent()],
'status' => NodeInterface::NOT_PUBLISHED,
]);
Assert::assertSame(0, $this->talkCounter->getCount());
}
protected function setUp() {
parent::setUp();
$this->talkCounter = $this->container->get(TalkCounter::class);
}
}

View file

@ -0,0 +1,113 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\opdavies_talks\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

@ -0,0 +1,71 @@
<?php
namespace Drupal\Tests\opdavies_talks\Kernel\Repository;
use Drupal\node\NodeInterface;
use Drupal\opdavies_talks\Entity\Node\Talk;
use Drupal\opdavies_talks\Repository\TalkRepository;
use Drupal\Tests\node\Traits\NodeCreationTrait;
use Drupal\Tests\opdavies_talks\Kernel\TalksTestBase;
final class TalkRepositoryTest extends TalksTestBase {
use NodeCreationTrait;
private TalkRepository $talkRepository;
/** @test */
public function get_all_talks() {
$this->createTalk(['title' => 'TDD - Test Driven Drupal']);
$this->createTalk(['title' => 'Taking Flight with Tailwind CSS']);
$this->createTalk(['title' => 'Upgrading to Drupal 9']);
$talks = $this->talkRepository->getAll();
$this->assertCount(3, $talks);
$this->assertSame(
[
1 => 'TDD - Test Driven Drupal',
2 => 'Taking Flight with Tailwind CSS',
3 => 'Upgrading to Drupal 9',
],
$talks->map(fn(Talk $talk) => $talk->label())->toArray()
);
}
/** @test */
public function get_all_published_talks() {
$this->createTalk([
'title' => 'TDD - Test Driven Drupal',
'status' => NodeInterface::PUBLISHED,
]);
$this->createTalk([
'title' => 'Taking Flight with Tailwind CSS',
'status' => NodeInterface::NOT_PUBLISHED,
]);
$talks = $this->talkRepository->getAll(TRUE);
$this->assertCount(1, $talks);
$this->assertSame('TDD - Test Driven Drupal', $talks->first()->label());
}
/** @test */
public function it_only_returns_talk_nodes() {
$this->createNode(['type' => 'page']);
$talks = $this->talkRepository->getAll();
$this->assertEmpty($talks);
}
protected function setUp() {
parent::setUp();
$this->installConfig(['filter']);
$this->talkRepository = $this->container->get(TalkRepository::class);
}
}

View file

@ -0,0 +1,103 @@
<?php
namespace Drupal\Tests\opdavies_talks\Kernel;
use Carbon\Carbon;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
use Drupal\node\Entity\Node;
use Drupal\opdavies_talks\Entity\Node\Talk;
use Drupal\opdavies_talks\Service\TalkDateUpdater;
final class TalkEventDateTest extends TalksTestBase {
/** @test */
public function talk_event_dates_are_set_to_the_next_future_date(): void {
$dateFormat = DateTimeItemInterface::DATE_STORAGE_FORMAT;
$talk = $this->createTalk([
'field_event_date' => NULL,
'field_events' => [
$this->createEvent([
'field_date' => Carbon::today()
->subWeeks(2)
->format($dateFormat),
]),
$this->createEvent([
'field_date' => Carbon::today()
->subDays(2)
->format($dateFormat),
]),
$this->createEvent([
'field_date' => Carbon::today()
->addDays(4)
->format($dateFormat),
]),
$this->createEvent([
'field_date' => Carbon::today()
->addDays(10)
->format($dateFormat),
]),
],
]);
$dateUpdater = $this->container->get(TalkDateUpdater::class);
$dateUpdater->__invoke();
$expected = Carbon::today()->addDays(4)->getTimestamp();
$talk = Node::load($talk->id());
$this->assertNextEventDateIs($talk, $expected);
}
/** @test */
public function talk_event_dates_are_set_to_the_last_past_date(): void {
$dateFormat = DateTimeItemInterface::DATE_STORAGE_FORMAT;
$talk = $this->createTalk([
'field_event_date' => NULL,
'field_events' => [
$this->createEvent([
'field_date' => Carbon::today()
->subDays(4)
->format($dateFormat),
]),
$this->createEvent([
'field_date' => Carbon::today()
->subDays(2)
->format($dateFormat),
]),
],
]);
$dateUpdater = $this->container->get(TalkDateUpdater::class);
$dateUpdater->__invoke();
$expected = Carbon::today()->subDays(2)->getTimestamp();
$talk = Node::load($talk->id());
$this->assertNextEventDateIs($talk, $expected);
}
/** @test */
public function next_event_date_is_empty_if_there_are_no_events(): void {
$talk = $this->createTalk([
'field_event_date' => NULL,
'field_events' => [],
]);
$dateUpdater = $this->container->get(TalkDateUpdater::class);
$dateUpdater->__invoke();
$talk = Node::load($talk->id());
$this->assertNoNextEventDate($talk);
}
private function assertNextEventDateIs(Talk $talk, $expected): void {
$this->assertSame($expected, $talk->getNextDate());
}
private function assertNoNextEventDate(Talk $talk): void {
$this->assertNull($talk->getNextDate());
}
}

View file

@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\opdavies_talks\Kernel;
use Carbon\Carbon;
use Drupal\views\ResultRow;
use Illuminate\Support\Collection;
final class TalksPageSortTest extends TalksTestBase {
public static $modules = [
'views',
'opdavies_talks',
];
/**
* @test
*/
public function upcoming_talks_are_shown_first_followed_by_past_talks_and_ordered_by_distance() {
$this->createTalk([
'field_event_date' => Carbon::today()->addDays(4)->getTimestamp(),
]);
$this->createTalk([
'field_event_date' => Carbon::today()->subDays(2)->getTimestamp(),
]);
$this->createTalk([
'field_event_date' => Carbon::today()->addDay()->getTimestamp(),
]);
$this->createTalk([
'field_event_date' => Carbon::today()->subDays(10)->getTimestamp(),
]);
$talkIds = (new Collection(views_get_view_result('talks')))
->map(fn(ResultRow $row) => (int) $row->_entity->id());
$this->assertSame([3, 1, 2, 4], $talkIds->toArray());
}
}

View file

@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\opdavies_talks\Kernel;
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
use Drupal\node\Entity\Node;
use Drupal\opdavies_talks\Entity\Node\Talk;
use Drupal\paragraphs\Entity\Paragraph;
use Drupal\paragraphs\ParagraphInterface;
abstract class TalksTestBase extends EntityKernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
// Core.
'node',
'file',
'datetime',
// Contrib.
'discoverable_entity_bundle_classes',
'entity_reference_revisions',
'paragraphs',
'hook_event_dispatcher',
// Custom.
'opdavies_talks',
'opdavies_talks_test',
];
protected $strictConfigSchema = FALSE;
protected function createEvent(array $overrides = []): ParagraphInterface {
/** @var \Drupal\paragraphs\ParagraphInterface $event */
$event = Paragraph::create(array_merge([
'type' => 'event',
], $overrides));
return tap($event)->save();
}
protected function createTalk(array $overrides = []): Talk {
$talk = Node::create(array_merge([
'title' => 'Test Driven Drupal',
'type' => 'talk',
], $overrides));
return tap($talk)->save();
}
protected function setUp() {
parent::setUp();
$this->installEntitySchema('paragraph');
$this->installSchema('node', ['node_access']);
$this->installConfig(['opdavies_talks_test']);
}
}

View file

@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\opdavies_talks\Kernel;
use Carbon\Carbon;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
final class UpdatesTalkCreatedDateTest extends TalksTestBase {
public function testCreatingNode() {
$eventDate = Carbon::today()->addWeek();
$eventDateFormat = $eventDate
->format(DateTimeItemInterface::DATE_STORAGE_FORMAT);
$eventDateTimestamp = $eventDate->getTimestamp();
$talk = $this->createTalk([
'field_events' => [
$this->createEvent(['field_date' => $eventDateFormat]),
],
]);
$this->assertEqual($eventDateTimestamp, $talk->getCreatedTime());
}
public function testUpdatingNode() {
$talk = $this->createTalk();
$originalCreatedTime = $talk->getCreatedTime();
$eventDate = Carbon::today()->addWeek();
$eventDateFormat = $eventDate
->format(DateTimeItemInterface::DATE_STORAGE_FORMAT);
$eventDateTimestamp = $eventDate->getTimestamp();
$talk->addEvent(
$this->createEvent(['field_date' => $eventDateFormat])
);
$talk->save();
$this->assertNotSame($originalCreatedTime, $talk->getCreatedTime());
$this->assertSame($eventDateTimestamp, $talk->getCreatedTime());
}
}