Automatically update the created date for talks

Automatically update the created dates for talk nodes so that they match
the most-future event. This means that the talks are ordered correctly
on the Talks page.
This commit is contained in:
Oliver Davies 2020-05-08 18:46:31 +01:00
parent fe5340d1c7
commit 7a9bf80a89
18 changed files with 412 additions and 6 deletions

View file

@ -73,6 +73,14 @@
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
<arrangement>
<groups>
<group>
<type>GETTERS_AND_SETTERS</type>
<order>KEEP</order>
</group>
</groups>
</arrangement>
</codeStyleSettings>
</code_scheme>
</component>

View file

@ -9,10 +9,14 @@ symfony php vendor/bin/phpcs -n \
--exclude="Drupal.Commenting.ClassComment,Drupal.Commenting.FunctionComment" \
web/modules/custom
# symfony php vendor/bin/phpcs -ns \
# --standard="Drupal,DrupalPractice" \
# --extensions="php,module,inc,install,test,profile,theme" \
# --exclude="Drupal.Commenting.ClassComment,Drupal.Commenting.DocComment,Drupal.Commenting.FunctionComment,Drupal.NamingConventions.ValidFunctionName" \
# web/modules/custom/**/tests
symfony php vendor/bin/phpcs -n \
--standard="Drupal,DrupalPractice" \
--extensions="php,module,inc,install,test,profile,theme" \
--exclude="Drupal.Commenting.ClassComment,Drupal.Commenting.DocComment,Drupal.Commenting.FunctionComment,Drupal.NamingConventions.ValidFunctionName" \
web/modules/custom/**/tests
vendor/bin/phpstan analyze
symfony php vendor/bin/phpunit \
-c web/core \
"$@"

View file

@ -20,6 +20,7 @@
"drupal/config_ignore": "^2.2",
"drupal/core-composer-scaffold": "^8.8",
"drupal/core-recommended": "^8.8",
"drupal/hook_event_dispatcher": "^1.28",
"drupal/markdown": "^1.3",
"drupal/metatag": "^1.11",
"drupal/migrate_plus": "^5.0",

104
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "48b1c5f01bc8e5486f59462cb6b68de4",
"content-hash": "2800d415e7b235b84c6922ceaa045ed8",
"packages": [
{
"name": "asm89/stack-cors",
@ -2177,6 +2177,108 @@
"source": "https://git.drupalcode.org/project/entity_reference_revisions"
}
},
{
"name": "drupal/hook_event_dispatcher",
"version": "1.28.0",
"source": {
"type": "git",
"url": "https://git.drupalcode.org/project/hook_event_dispatcher.git",
"reference": "8.x-1.28"
},
"dist": {
"type": "zip",
"url": "https://ftp.drupal.org/files/projects/hook_event_dispatcher-8.x-1.28.zip",
"reference": "8.x-1.28",
"shasum": "a8e836015f27c87ad0584452cea3bfb02f0e4f2b"
},
"require": {
"drupal/core": "^8 || ^9"
},
"require-dev": {
"drupal/coder": "8.3.1",
"drupal/core": "^8.8",
"drupal/eck": "^1.0@alpha",
"drupal/paragraphs": "^1.10",
"drupal/webform": "*",
"mockery/mockery": "^1.3",
"php-parallel-lint/php-parallel-lint": "^1.0",
"phpmd/phpmd": "2.7.0",
"phpunit/phpunit": "^7",
"squizlabs/php_codesniffer": "^3.4"
},
"suggest": {
"drupal/paragraphs": "Enables the creation of paragraphs entities.",
"drupal/token": "Provides additional tokens not supported by core (most notably fields), as well as a UI for browsing tokens."
},
"type": "drupal-module",
"extra": {
"drupal": {
"version": "8.x-1.28",
"datestamp": "1588695401",
"security-coverage": {
"status": "covered",
"message": "Covered by Drupal's security advisory policy"
}
}
},
"autoload": {
"psr-4": {
"Drupal\\hook_event_dispatcher\\": "src/",
"Drupal\\webform_event_dispatcher\\": "modules/webform_event_dispatcher/src/"
}
},
"autoload-dev": {
"psr-4": {
"Drupal\\Tests\\hook_event_dispatcher\\": "tests/src/",
"Drupal\\Tests\\webform_event_dispatcher\\": "modules/webform_event_dispatcher/tests/src/",
"Drupal\\Tests\\": "vendor/drupal/core/tests/Drupal/Tests",
"Drupal\\TestTools\\": "vendor/drupal/core/tests/Drupal/TestTools",
"Drupal\\views\\": "vendor/drupal/core/modules/views/src/"
}
},
"notification-url": "https://packages.drupal.org/8/downloads",
"scripts": {
"test": [
"@phplint",
"@phpunit",
"@phpcs",
"@phpmd"
],
"phplint": [
"vendor/bin/parallel-lint --exclude vendor/ --exclude .idea/ -e php,module,inc,install,profile,theme ."
],
"phpunit": [
"vendor/bin/phpunit --configuration=phpunit.xml"
],
"phpcs": [
"vendor/bin/phpcs --ignore=vendor/,.idea/ --standard=vendor/drupal/coder/coder_sniffer/Drupal/ruleset.xml --extensions=php,module,inc,install,profile,theme --report=full --warning-severity=0 ."
],
"phpcbf": [
"vendor/bin/phpcbf --ignore=vendor/,.idea/ --standard=vendor/drupal/coder/coder_sniffer/Drupal/ruleset.xml --extensions=php,module,inc,install,profile,theme ."
],
"phpmd": [
"vendor/bin/phpmd --exclude vendor/,.idea/ --suffixes php,module,inc,install,profile,theme . text phpmd.xml"
]
},
"license": [
"GPL-2.0-or-later"
],
"authors": [
{
"name": "pdenooijer",
"homepage": "https://www.drupal.org/user/3175437"
},
{
"name": "robin.ingelbrecht",
"homepage": "https://www.drupal.org/user/2339074"
}
],
"description": "Dispatches events for several drupal core hooks.",
"homepage": "https://www.drupal.org/project/hook_event_dispatcher",
"support": {
"source": "https://git.drupalcode.org/project/hook_event_dispatcher"
}
},
{
"name": "drupal/markdown",
"version": "1.3.0",

View file

@ -17,6 +17,7 @@ module:
field_ui: 0
file: 0
filter: 0
hook_event_dispatcher: 0
image: 0
link: 0
markdown: 0

View file

@ -7,6 +7,9 @@ parameters:
- *Test.php
- *TestBase.php
checkMissingIterableValueType: false
ignoreErrors:
- '#Call to an undefined method Drupal\\Core\\Entity\\EntityInterface::get()#'
- '#Call to an undefined method Drupal\\Core\\Entity\\EntityInterface::set()#'
includes:
- vendor/mglaman/phpstan-drupal/extension.neon
- vendor/phpstan/phpstan-deprecation-rules/rules.neon

View file

@ -3,3 +3,7 @@ type: module
core: 8.x
core_version_requirements: ^8 || ^9
package: Custom
dependencies:
- drupal:node
- hook_event_dispatcher:hook_event_dispatcher
- paragraphs:paragraphs

View file

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

View file

@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
namespace Drupal\custom\EventSubscriber;
use Drupal\Core\Entity\EntityInterface;
use Drupal\hook_event_dispatcher\Event\Entity\BaseEntityEvent;
use Drupal\hook_event_dispatcher\HookEventDispatcherInterface;
use Drupal\paragraphs\ParagraphInterface;
use Illuminate\Support\Collection;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Set the created date for a talk to be the last date that the talk is given.
*/
final class UpdateTalkCreatedDateOnSave implements EventSubscriberInterface {
public static function getSubscribedEvents() {
return [
HookEventDispatcherInterface::ENTITY_INSERT => 'entityInsertOrUpdate',
HookEventDispatcherInterface::ENTITY_UPDATE => 'entityInsertOrUpdate',
];
}
public function entityInsertOrUpdate(BaseEntityEvent $event): void {
if ($event->getEntity()->getEntityTypeId() != 'node') {
return;
}
if ($event->getEntity()->bundle() != 'talk') {
return;
}
$this->updateCreatedDate($event->getEntity());
}
private function updateCreatedDate(EntityInterface $talk): void {
if (!$eventDate = $this->findLatestEventDate($talk)) {
return;
}
$talkDate = (new \DateTime($eventDate))->getTimestamp();
if ($talkDate == $talk->get('created')->getString()) {
return;
}
$talk->set('created', $talkDate);
}
/**
* Find the date for the latest event.
*
* @return string|null
*/
private function findLatestEventDate(EntityInterface $talk) {
return Collection::make($talk->get('field_events')->referencedEntities())
->map(fn(ParagraphInterface $event) => $event->get('field_date')
->getString())
->max();
}
}

View file

@ -12,6 +12,8 @@ use Drupal\paragraphs\Entity\Paragraph;
use Illuminate\Support\Collection;
/**
* A migrate destination for a talk node.
*
* @MigrateDestination(
* id="opd_talk"
* )

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,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,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,5 @@
name: Custom Test
type: module
core: 8.x
core_version_requirements: ^8 || ^9
hidden: true

View file

@ -0,0 +1,100 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\custom\Kernel;
use Drupal\Core\Entity\EntityInterface;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
use Drupal\node\Entity\Node;
use Drupal\paragraphs\Entity\Paragraph;
use Drupal\paragraphs\ParagraphInterface;
final class UpdatesTalkCreatedDateTest extends EntityKernelTestBase {
/**
* {@inheritDoc}
*/
public static $modules = [
// Core.
'node',
'file',
'datetime',
// Contrib.
'entity_reference_revisions',
'paragraphs',
'hook_event_dispatcher',
// Custom.
'custom',
'custom_test',
];
public function testCreatingNode() {
$eventDate = (new \DateTime('today'))->modify('+1 week');
$eventDateToFormat = $eventDate->format(DateTimeItemInterface::DATE_STORAGE_FORMAT);
$eventDateToTimestamp = $eventDate->getTimestamp();
$talk = $this->createTalk($eventDateToFormat);
$this->assertEqual($eventDateToTimestamp, $talk->get('created')
->getString());
}
public function testUpdatingNode() {
$talk = $this->createTalk();
$eventDate = (new \DateTime('today'))->modify('+1 week');
$eventDateToFormat = $eventDate->format(DateTimeItemInterface::DATE_STORAGE_FORMAT);
$eventDateToTimestamp = $eventDate->getTimestamp();
$event = $this->createEvent($eventDateToFormat);
$talk->set('field_events', [$event]);
$talk->save();
$this->assertEqual($eventDateToTimestamp, $talk->get('created')
->getString());
}
protected function setUp() {
parent::setUp();
$this->installEntitySchema('paragraph');
$this->installSchema('node', ['node_access']);
$this->installConfig(['custom_test']);
}
private function createTalk(?string $eventDateToFormat = NULL): EntityInterface {
if ($eventDateToFormat) {
$event = $this->createEvent($eventDateToFormat);
}
$talk = Node::create([
'title' => 'TDD - Test Driven Drupal',
'type' => 'talk',
]);
if (isset($event)) {
$talk->set('field_events', [$event]);
}
$talk->save();
return $talk;
}
private function createEvent(string $eventDateToFormat): ParagraphInterface {
$event = Paragraph::create([
'field_date' => $eventDateToFormat,
'type' => 'event',
]);
$event->save();
return $event;
}
}