diff --git a/composer.json b/composer.json index 2cf6f00..cccc27a 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,6 @@ "drupal/config_split": "^1.4", "drupal/core-composer-scaffold": "^8.8", "drupal/core-recommended": "^8.8", - "drupal/discoverable_entity_bundle_classes": "^1.0", "drupal/gin": "^3.0", "drupal/gin_toolbar": "^1.0", "drupal/honeypot": "^2.0", diff --git a/composer.lock b/composer.lock index cf13989..dde3d1b 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "74c67a9e7b92495a730372d2a0d10466", + "content-hash": "4e568994c7b2ae5c97f5df4a8253f536", "packages": [ { "name": "asm89/stack-cors", @@ -2510,53 +2510,6 @@ "issues": "https://www.drupal.org/project/issues/ctools" } }, - { - "name": "drupal/discoverable_entity_bundle_classes", - "version": "1.0.0-alpha2", - "source": { - "type": "git", - "url": "https://git.drupalcode.org/project/discoverable_entity_bundle_classes.git", - "reference": "8.x-1.0-alpha2" - }, - "dist": { - "type": "zip", - "url": "https://ftp.drupal.org/files/projects/discoverable_entity_bundle_classes-8.x-1.0-alpha2.zip", - "reference": "8.x-1.0-alpha2", - "shasum": "c8f1e27475f3a1b7d7694949baaf337ed126c05a" - }, - "require": { - "drupal/core": "~8.0" - }, - "require-dev": { - "drupal/paragraphs": "*" - }, - "type": "drupal-module", - "extra": { - "drupal": { - "version": "8.x-1.0-alpha2", - "datestamp": "1553798282", - "security-coverage": { - "status": "not-covered", - "message": "Project has not opted into security advisory coverage!" - } - } - }, - "notification-url": "https://packages.drupal.org/8/downloads", - "license": [ - "GPL-2.0+" - ], - "authors": [ - { - "name": "amcgowanca", - "homepage": "https://www.drupal.org/user/802138" - } - ], - "description": "An experimental approach for implementing entity classes for specific bundles.", - "homepage": "https://www.drupal.org/project/discoverable_entity_bundle_classes", - "support": { - "source": "https://git.drupalcode.org/project/discoverable_entity_bundle_classes" - } - }, { "name": "drupal/entity_reference_revisions", "version": "1.8.0", diff --git a/config/core.extension.yml b/config/core.extension.yml index 45f572d..8c55f76 100644 --- a/config/core.extension.yml +++ b/config/core.extension.yml @@ -14,7 +14,6 @@ module: ctools: 0 datetime: 0 dblog: 0 - discoverable_entity_bundle_classes: 0 dynamic_page_cache: 0 entity_reference_revisions: 0 field: 0 diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 0000000..5508445 --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,67 @@ +parameters: + ignoreErrors: + - + message: "#^Property Drupal\\\\opdavies_blog\\\\Entity\\\\Node\\\\Post\\:\\:\\$node \\(Drupal\\\\node\\\\NodeInterface\\) does not accept Drupal\\\\Core\\\\Entity\\\\EntityInterface\\.$#" + count: 1 + path: web/modules/custom/blog/src/Entity/Node/Post.php + + - + message: "#^Call to an undefined method Drupal\\\\Core\\\\Field\\\\FieldItemListInterface\\:\\:referencedEntities\\(\\)\\.$#" + count: 1 + path: web/modules/custom/blog/src/Entity/Node/Post.php + + - + message: "#^Method Drupal\\\\opdavies_blog\\\\Entity\\\\Node\\\\Post\\:\\:set\\(\\) has parameter \\$value with no typehint specified\\.$#" + count: 1 + path: web/modules/custom/blog/src/Entity/Node/Post.php + + - + message: "#^Call to deprecated method url\\(\\) of class Drupal\\\\Core\\\\Entity\\\\EntityInterface\\:\nin drupal\\:8\\.0\\.0 and is removed from drupal\\:9\\.0\\.0\\.\nPlease use toUrl\\(\\) instead\\.$#" + count: 1 + path: web/modules/custom/blog/src/Entity/Node/Post.php + + - + message: "#^Parameter \\#5 \\$relatedPostsRepository of class Drupal\\\\opdavies_blog\\\\Plugin\\\\Block\\\\RelatedPostsBlock constructor expects Drupal\\\\opdavies_blog\\\\Repository\\\\RelatedPostsRepository, object\\|null given\\.$#" + count: 1 + path: web/modules/custom/blog/src/Plugin/Block/RelatedPostsBlock.php + + - + message: "#^Call to an undefined method Drupal\\\\Core\\\\Field\\\\FieldItemListInterface\\:\\:referencedEntities\\(\\)\\.$#" + count: 1 + path: web/modules/custom/blog/src/Repository/RelatedPostsRepository.php + + - + message: "#^Call to an undefined method GuzzleHttp\\\\ClientInterface\\:\\:post\\(\\)\\.$#" + count: 1 + path: web/modules/custom/blog/src/Service/PostPusher/IftttPostPusher.php + + - + message: "#^Call to an undefined method GuzzleHttp\\\\ClientInterface\\:\\:post\\(\\)\\.$#" + count: 1 + path: web/modules/custom/blog/src/Service/PostPusher/IntegromatPostPusher.php + + - + message: "#^Property Drupal\\\\opdavies_talks\\\\Entity\\\\Node\\\\Talk\\:\\:\\$node \\(Drupal\\\\node\\\\NodeInterface\\) does not accept Drupal\\\\Core\\\\Entity\\\\EntityInterface\\.$#" + count: 1 + path: web/modules/custom/talks/src/Entity/Node/Talk.php + + - + message: "#^Call to an undefined method Drupal\\\\Core\\\\Field\\\\FieldItemListInterface\\:\\:referencedEntities\\(\\)\\.$#" + count: 1 + path: web/modules/custom/talks/src/Entity/Node/Talk.php + + - + message: "#^Method Drupal\\\\opdavies_talks\\\\Entity\\\\Node\\\\Talk\\:\\:set\\(\\) has parameter \\$value with no typehint specified\\.$#" + count: 1 + path: web/modules/custom/talks/src/Entity/Node/Talk.php + + - + message: "#^Call to an undefined method Illuminate\\\\Support\\\\HigherOrderCollectionProxy\\:\\:id\\(\\)\\.$#" + count: 2 + path: web/modules/custom/talks/src/EventSubscriber/UpdateTalkNodeBeforeSave.php + + - + message: "#^Call to an undefined method Drupal\\\\views\\\\Plugin\\\\views\\\\query\\\\QueryPluginBase\\:\\:addOrderBy\\(\\)\\.$#" + count: 2 + path: web/modules/custom/talks/src/Plugin/views/sort/Event.php + diff --git a/phpstan.neon b/phpstan.neon index ba94528..5c62f71 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -7,10 +7,7 @@ parameters: - *Test.php - *TestBase.php checkMissingIterableValueType: false - ignoreErrors: - - '#Call to an undefined method Drupal\\Core\\Field\\FieldItemListInterface::referencedEntities()#' - - '#Call to an undefined method Drupal\\views\\Plugin\\views\\query\\QueryPluginBase::addOrderBy().#' - - '#Call to an undefined method GuzzleHttp\\ClientInterface::post().#' includes: - vendor/mglaman/phpstan-drupal/extension.neon - vendor/phpstan/phpstan-deprecation-rules/rules.neon + - phpstan-baseline.neon diff --git a/web/modules/custom/blog/opdavies_blog.info.yml b/web/modules/custom/blog/opdavies_blog.info.yml index 2469aa4..0da1e10 100644 --- a/web/modules/custom/blog/opdavies_blog.info.yml +++ b/web/modules/custom/blog/opdavies_blog.info.yml @@ -4,6 +4,5 @@ core_version_requirement: ^8 || ^9 package: Custom dependencies: - drupal:node - - discoverable_entity_bundle_classes:discoverable_entity_bundle_classes - hook_event_dispatcher:hook_event_dispatcher - paragraphs:paragraphs diff --git a/web/modules/custom/blog/opdavies_blog.module b/web/modules/custom/blog/opdavies_blog.module index 062e242..6324abe 100644 --- a/web/modules/custom/blog/opdavies_blog.module +++ b/web/modules/custom/blog/opdavies_blog.module @@ -6,19 +6,8 @@ */ use Drupal\Core\Url; -use Drupal\discoverable_entity_bundle_classes\Storage\Node\NodeStorage; use Drupal\node\NodeInterface; -/** - * Implements hook_entity_type_build(). - */ -function opdavies_blog_entity_type_build(array &$entityTypes): void { - /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entityTypes */ - if (isset($entityTypes['node'])) { - $entityTypes['node']->setStorageClass(NodeStorage::class); - } -} - /** * Implements hook_node_links_alter(). */ diff --git a/web/modules/custom/blog/src/Entity/Node/Post.php b/web/modules/custom/blog/src/Entity/Node/Post.php index 7b61ddc..3134260 100644 --- a/web/modules/custom/blog/src/Entity/Node/Post.php +++ b/web/modules/custom/blog/src/Entity/Node/Post.php @@ -4,21 +4,14 @@ declare(strict_types=1); namespace Drupal\opdavies_blog\Entity\Node; -use Drupal\discoverable_entity_bundle_classes\ContentEntityBundleInterface; +use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Field\FieldItemListInterface; use Drupal\node\Entity\Node; +use Drupal\node\NodeInterface; use Drupal\taxonomy\Entity\Term; use Illuminate\Support\Collection; -/** - * Defines an blog post node class. - * - * @ContentEntityBundleClass( - * label = @Translation("Blog post"), - * entity_type = "node", - * bundle = "post" - * ); - */ -class Post extends Node implements ContentEntityBundleInterface { +final class Post { public const FIELD_EXTERNAL_LINK = 'field_external_link'; public const FIELD_HAS_TWEET = 'field_has_tweet'; @@ -26,12 +19,31 @@ class Post extends Node implements ContentEntityBundleInterface { public const FIELD_SENT_TO_SOCIAL_MEDIA = 'field_sent_to_social_media'; public const FIELD_TAGS = 'field_tags'; + private NodeInterface $node; + + public function __construct(EntityInterface $node) { + $this->node = $node; + } + + public function bundle(): string { + return 'post'; + } + + public function get(string $name): FieldItemListInterface { + return $this->node->get($name); + } + public function getExternalLink(): ?array { return ($link = $this->get(self::FIELD_EXTERNAL_LINK)->get(0)) ? $link->getValue() : NULL; } + public function getNode(): NodeInterface { + + return $this->node; + } + /** * @return Collection|Term[] */ @@ -47,16 +59,32 @@ class Post extends Node implements ContentEntityBundleInterface { return (bool) $this->get(self::FIELD_HAS_TWEET)->getString(); } + public function id(): int { + return (int) $this->node->id(); + } + public function isExternalPost(): bool { return (bool) $this->getExternalLink(); } + public function label(): string { + return $this->node->label(); + } + public function markAsSentToSocialMedia(): self { $this->set(self::FIELD_SENT_TO_SOCIAL_MEDIA, TRUE); return $this; } + public function save(): void { + $this->node->save(); + } + + public function set(string $name, $value): void { + $this->node->set($name, $value); + } + public function setTags(array $tags): void { $this->set(self::FIELD_TAGS, $tags); } @@ -65,4 +93,13 @@ class Post extends Node implements ContentEntityBundleInterface { return (bool) $this->get(self::FIELD_SEND_TO_SOCIAL_MEDIA)->getString(); } + public function url(string $type, array $options = []): string { + return $this->node->url($type, $options); + } + + public static function createFromNode(EntityInterface $node): self { + // TODO: ensure that this is a node and a `post` type. + return new self($node); + } + } diff --git a/web/modules/custom/blog/src/EventSubscriber/SortTagsAlphabeticallyWhenPostIsSaved.php b/web/modules/custom/blog/src/EventSubscriber/SortTagsAlphabeticallyWhenPostIsSaved.php index b032744..7f86363 100644 --- a/web/modules/custom/blog/src/EventSubscriber/SortTagsAlphabeticallyWhenPostIsSaved.php +++ b/web/modules/custom/blog/src/EventSubscriber/SortTagsAlphabeticallyWhenPostIsSaved.php @@ -28,15 +28,16 @@ final class SortTagsAlphabeticallyWhenPostIsSaved implements EventSubscriberInte return; } - /** @var Post $entity */ if ($entity->bundle() != 'post') { return; } - $sortedTags = $entity->getTags() + $post = Post::createFromNode($entity); + + $sortedTags = $post->getTags() ->sortBy(fn(TermInterface $tag) => $tag->label()); - $entity->setTags($sortedTags->toArray()); + $post->setTags($sortedTags->toArray()); } } diff --git a/web/modules/custom/blog/src/Plugin/Block/RelatedPostsBlock.php b/web/modules/custom/blog/src/Plugin/Block/RelatedPostsBlock.php index 0ab9150..1544e43 100644 --- a/web/modules/custom/blog/src/Plugin/Block/RelatedPostsBlock.php +++ b/web/modules/custom/blog/src/Plugin/Block/RelatedPostsBlock.php @@ -44,7 +44,6 @@ class RelatedPostsBlock extends BlockBase implements ContainerFactoryPluginInter $pluginId, $pluginDefinition ): self { - // @phpstan-ignore-next-line return new self( $configuration, $pluginId, @@ -66,7 +65,7 @@ class RelatedPostsBlock extends BlockBase implements ContainerFactoryPluginInter $build['content'] = [ '#items' => $relatedPosts - ->sortByDesc(fn(Post $post) => $post->getCreatedTime()) + ->sortByDesc(fn(Post $post) => $post->getNode()->getCreatedTime()) ->map(fn(Post $post) => $this->generateLinkToPost($post)) ->slice(0, 3) ->toArray(), @@ -93,7 +92,7 @@ class RelatedPostsBlock extends BlockBase implements ContainerFactoryPluginInter private function generateLinkToPost(Post $post): Link { return Link::createFromRoute( - $post->getTitle(), + $post->label(), 'entity.node.canonical', ['node' => $post->id()] ); diff --git a/web/modules/custom/blog/src/Plugin/QueueWorker/PostPusherQueueWorker.php b/web/modules/custom/blog/src/Plugin/QueueWorker/PostPusherQueueWorker.php index eb42b2b..781d541 100644 --- a/web/modules/custom/blog/src/Plugin/QueueWorker/PostPusherQueueWorker.php +++ b/web/modules/custom/blog/src/Plugin/QueueWorker/PostPusherQueueWorker.php @@ -68,21 +68,19 @@ final class PostPusherQueueWorker extends QueueWorkerBase implements ContainerFa return; } - if (!$post->isLatestRevision()) { - $post = $this->nodeStorage->load($post->id()); + if (!$post->getNode()->isLatestRevision()) { + $node = $this->nodeStorage->load($post->id()); + $post = Post::createFromNode($node); - // @phpstan-ignore-next-line if (!$this->shouldBePushed($post)) { return; } } foreach ($this->postPushers as $pusher) { - // @phpstan-ignore-next-line $pusher->push($post); } - // @phpstan-ignore-next-line $post->set(Post::FIELD_SENT_TO_SOCIAL_MEDIA, TRUE); $post->save(); } @@ -92,7 +90,7 @@ final class PostPusherQueueWorker extends QueueWorkerBase implements ContainerFa return FALSE; } - if (!$post->isPublished()) { + if (!$post->getNode()->isPublished()) { return FALSE; } diff --git a/web/modules/custom/blog/tests/modules/blog_test/src/Factory/PostFactory.php b/web/modules/custom/blog/tests/modules/blog_test/src/Factory/PostFactory.php index d6a83d5..4f5bedf 100644 --- a/web/modules/custom/blog/tests/modules/blog_test/src/Factory/PostFactory.php +++ b/web/modules/custom/blog/tests/modules/blog_test/src/Factory/PostFactory.php @@ -41,10 +41,9 @@ final class PostFactory { Post::FIELD_TAGS => $this->tags->toArray(), ]; - /** @var Post $post */ $post = Node::create($values + $overrides); - return $post; + return Post::createFromNode($post); } public function setTitle(string $title): self { diff --git a/web/modules/custom/blog/tests/src/Kernel/Entity/Node/PostTest.php b/web/modules/custom/blog/tests/src/Kernel/Entity/Node/PostTest.php index c80c084..fac9d75 100644 --- a/web/modules/custom/blog/tests/src/Kernel/Entity/Node/PostTest.php +++ b/web/modules/custom/blog/tests/src/Kernel/Entity/Node/PostTest.php @@ -16,9 +16,6 @@ final class PostTest extends EntityKernelTestBase { 'link', 'taxonomy', - // Contrib. - 'discoverable_entity_bundle_classes', - // Custom. 'opdavies_blog', 'opdavies_blog_test', diff --git a/web/modules/custom/blog/tests/src/Kernel/PostTestBase.php b/web/modules/custom/blog/tests/src/Kernel/PostTestBase.php index 4bd6382..3488772 100644 --- a/web/modules/custom/blog/tests/src/Kernel/PostTestBase.php +++ b/web/modules/custom/blog/tests/src/Kernel/PostTestBase.php @@ -18,7 +18,6 @@ abstract class PostTestBase extends EntityKernelTestBase { 'link', // Contrib. - 'discoverable_entity_bundle_classes', 'hook_event_dispatcher', 'core_event_dispatcher', diff --git a/web/modules/custom/blog/tests/src/Kernel/PushToSocialMediaTest.php b/web/modules/custom/blog/tests/src/Kernel/PushToSocialMediaTest.php index 20eda97..0f60b2a 100644 --- a/web/modules/custom/blog/tests/src/Kernel/PushToSocialMediaTest.php +++ b/web/modules/custom/blog/tests/src/Kernel/PushToSocialMediaTest.php @@ -6,8 +6,9 @@ namespace Drupal\Tests\opdavies_blog\Kernel; use Drupal\Core\Queue\QueueInterface; use Drupal\KernelTests\Core\Entity\EntityKernelTestBase; -use Drupal\opdavies_blog\Entity\Node\Post; use Drupal\Tests\node\Traits\NodeCreationTrait; +use Drupal\node\NodeInterface; +use Drupal\opdavies_blog\Entity\Node\Post; final class PushToSocialMediaTest extends EntityKernelTestBase { @@ -20,7 +21,6 @@ final class PushToSocialMediaTest extends EntityKernelTestBase { 'link', // Contrib. - 'discoverable_entity_bundle_classes', 'hook_event_dispatcher', 'core_event_dispatcher', @@ -48,8 +48,8 @@ final class PushToSocialMediaTest extends EntityKernelTestBase { $post = $item->data['post']; $this->assertNotNull($post); - $this->assertInstanceOf(Post::class, $post); - $this->assertSame('1', $post->id()); + $this->assertInstanceOf(NodeInterface::class, $post); + $this->assertSame('post', $post->bundle()); $this->assertSame('Ignoring PHPCS sniffs within PHPUnit tests', $post->getTitle()); } diff --git a/web/modules/custom/blog/tests/src/Kernel/ReorderBlogTagsTest.php b/web/modules/custom/blog/tests/src/Kernel/ReorderBlogTagsTest.php index e1c449c..238269e 100644 --- a/web/modules/custom/blog/tests/src/Kernel/ReorderBlogTagsTest.php +++ b/web/modules/custom/blog/tests/src/Kernel/ReorderBlogTagsTest.php @@ -5,6 +5,7 @@ namespace Drupal\Tests\opdavies_blog\Kernel; use Drupal\node\Entity\Node; +use Drupal\node\NodeInterface; use Drupal\opdavies_blog\Entity\Node\Post; use Drupal\taxonomy\Entity\Vocabulary; use Drupal\taxonomy\TermInterface; @@ -26,8 +27,8 @@ final class ReorderBlogTagsTest extends PostTestBase { Post::FIELD_TAGS => [3, 1, 2], ]); - /** @var Post $post */ - $post = Node::load($post->id()); + $node = Node::load($post->id()); + $post = Post::createFromNode($node); $this->assertSame( ['Drupal', 'PHP', 'Symfony'], diff --git a/web/modules/custom/talks/opdavies_talks.info.yml b/web/modules/custom/talks/opdavies_talks.info.yml index de2957f..a1b07e3 100644 --- a/web/modules/custom/talks/opdavies_talks.info.yml +++ b/web/modules/custom/talks/opdavies_talks.info.yml @@ -3,5 +3,3 @@ description: Custom code for talks pages. type: module core_version_requirement: ^8 || ^9 package: Custom -dependencies: - - discoverable_entity_bundle_classes:discoverable_entity_bundle_classes diff --git a/web/modules/custom/talks/opdavies_talks.module b/web/modules/custom/talks/opdavies_talks.module index aeecccf..0962187 100644 --- a/web/modules/custom/talks/opdavies_talks.module +++ b/web/modules/custom/talks/opdavies_talks.module @@ -9,7 +9,6 @@ declare(strict_types=1); use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Render\BubbleableMetadata; -use Drupal\discoverable_entity_bundle_classes\Storage\Node\NodeStorage; use Drupal\opdavies_talks\Service\TalkCounter; use Drupal\opdavies_talks\Service\TalkDateUpdater; @@ -36,16 +35,6 @@ function opdavies_talks_views_data_alter(array &$data): void { ]; } -/** - * 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); - } -} - /** * Implements hook_token_info(). */ diff --git a/web/modules/custom/talks/src/Collection/TalkCollection.php b/web/modules/custom/talks/src/Collection/TalkCollection.php index 2391ba1..1b03682 100644 --- a/web/modules/custom/talks/src/Collection/TalkCollection.php +++ b/web/modules/custom/talks/src/Collection/TalkCollection.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Drupal\opdavies_talks\Collection; +use Drupal\node\NodeInterface; use Drupal\opdavies_talks\Entity\Node\Talk; use Drupal\paragraphs\ParagraphInterface; use Illuminate\Support\Collection; @@ -16,7 +17,8 @@ final class TalkCollection extends Collection { * @return Collection|ParagraphInterface[] */ public function getEvents(): Collection { - return $this->flatMap(fn(Talk $talk): Collection => $talk->getEvents()); + return $this + ->flatMap(fn(Talk $talk): Collection => $talk->getEvents()); } } diff --git a/web/modules/custom/talks/src/Entity/Node/Talk.php b/web/modules/custom/talks/src/Entity/Node/Talk.php index 195cc4a..9756217 100644 --- a/web/modules/custom/talks/src/Entity/Node/Talk.php +++ b/web/modules/custom/talks/src/Entity/Node/Talk.php @@ -1,26 +1,27 @@ node = $node; + } + public function addEvent(ParagraphInterface $event): void { $this->set( self::FIELD_EVENTS, @@ -28,6 +29,26 @@ class Talk extends Node implements ContentEntityBundleInterface { ); } + /** + * 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 get(string $name): FieldItemListInterface { + return $this->node->get($name); + } + + public function getCreatedTime(): int { + return (int) $this->node->getCreatedTime(); + } + public function getEvents(): Collection { return Collection::make($this->get(self::FIELD_EVENTS) ->referencedEntities()); @@ -41,16 +62,24 @@ class Talk extends Node implements ContentEntityBundleInterface { return (int) $this->get(self::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 id(): int { + return (int) $this->node->id(); + } + + public function label(): string { + return $this->node->label(); + } + + public function save(): void { + $this->node->save(); + } + + public function set(string $name, $value): void { + $this->node->set($name, $value); + } + + public function setCreatedTime(int $timestamp): void { + $this->node->setCreatedTime($timestamp); } public function setEvents(array $events): void { @@ -61,4 +90,9 @@ class Talk extends Node implements ContentEntityBundleInterface { $this->set(self::FIELD_EVENT_DATE, $date); } + public static function createFromNode(EntityInterface $node): self { + // TODO: ensure that this is a node and a `talk` type. + return new self($node); + } + } diff --git a/web/modules/custom/talks/src/EventSubscriber/UpdateTalkNodeBeforeSave.php b/web/modules/custom/talks/src/EventSubscriber/UpdateTalkNodeBeforeSave.php index 5cd4b25..34ef3c7 100644 --- a/web/modules/custom/talks/src/EventSubscriber/UpdateTalkNodeBeforeSave.php +++ b/web/modules/custom/talks/src/EventSubscriber/UpdateTalkNodeBeforeSave.php @@ -32,8 +32,9 @@ final class UpdateTalkNodeBeforeSave implements EventSubscriberInterface { return; } - /** @var Talk $talk */ - $talk = $event->getEntity(); + $node = $event->getEntity(); + $talk = Talk::createFromNode($node); + $this->reorderEvents($talk); $this->updateCreatedDate($talk); } @@ -42,9 +43,7 @@ final class UpdateTalkNodeBeforeSave implements EventSubscriberInterface { $events = $talk->getEvents(); $eventsByDate = $this->sortEventsByDate($events); - // If the original event IDs don't match the sorted event IDs, update the - // event field to use the sorted ones. - // @phpstan-ignore-next-line + // 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->setEvents($eventsByDate->toArray()); } diff --git a/web/modules/custom/talks/src/Repository/TalkRepository.php b/web/modules/custom/talks/src/Repository/TalkRepository.php index 1a6a728..b3457d6 100644 --- a/web/modules/custom/talks/src/Repository/TalkRepository.php +++ b/web/modules/custom/talks/src/Repository/TalkRepository.php @@ -8,6 +8,7 @@ use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\node\NodeInterface; use Drupal\opdavies_talks\Collection\TalkCollection; +use Drupal\opdavies_talks\Entity\Node\Talk; final class TalkRepository { @@ -20,7 +21,8 @@ final class TalkRepository { public function findAll(): TalkCollection { $talks = $this->nodeStorage->loadByProperties($this->defaultProperties()); - return new TalkCollection($talks); + return (new TalkCollection($talks)) + ->map(fn(NodeInterface $node): Talk => Talk::createFromNode($node)); } public function findAllPublished(): TalkCollection { @@ -31,7 +33,8 @@ final class TalkRepository { ], )); - return new TalkCollection($talks); + return (new TalkCollection($talks)) + ->map(fn(NodeInterface $node): Talk => Talk::createFromNode($node)); } private function defaultProperties(): array { diff --git a/web/modules/custom/talks/tests/src/Kernel/TalkEventDateTest.php b/web/modules/custom/talks/tests/src/Kernel/TalkEventDateTest.php index 7377a28..e188fc8 100644 --- a/web/modules/custom/talks/tests/src/Kernel/TalkEventDateTest.php +++ b/web/modules/custom/talks/tests/src/Kernel/TalkEventDateTest.php @@ -47,7 +47,9 @@ final class TalkEventDateTest extends TalksTestBase { $expected = Carbon::today()->addDays(4)->getTimestamp(); - $talk = Node::load($talk->id()); + $node = Node::load($talk->id()); + $talk = Talk::createFromNode($node); + $this->assertNextEventDateIs($talk, $expected); } @@ -76,7 +78,9 @@ final class TalkEventDateTest extends TalksTestBase { $expected = Carbon::today()->subDays(2)->getTimestamp(); - $talk = Node::load($talk->id()); + $node = Node::load($talk->id()); + $talk = Talk::createFromNode($node); + $this->assertNextEventDateIs($talk, $expected); } @@ -90,7 +94,9 @@ final class TalkEventDateTest extends TalksTestBase { $dateUpdater = $this->container->get(TalkDateUpdater::class); $dateUpdater->__invoke(); - $talk = Node::load($talk->id()); + $node = Node::load($talk->id()); + $talk = Talk::createFromNode($node); + $this->assertNoNextEventDate($talk); } diff --git a/web/modules/custom/talks/tests/src/Kernel/TalksTestBase.php b/web/modules/custom/talks/tests/src/Kernel/TalksTestBase.php index c408b00..935d11f 100644 --- a/web/modules/custom/talks/tests/src/Kernel/TalksTestBase.php +++ b/web/modules/custom/talks/tests/src/Kernel/TalksTestBase.php @@ -20,7 +20,6 @@ abstract class TalksTestBase extends EntityKernelTestBase { 'datetime', // Contrib. - 'discoverable_entity_bundle_classes', 'entity_reference_revisions', 'paragraphs', 'hook_event_dispatcher', @@ -45,14 +44,14 @@ abstract class TalksTestBase extends EntityKernelTestBase { } protected function createTalk(array $overrides = []): Talk { - $talk = Node::create(array_merge([ + $node = Node::create(array_merge([ 'title' => 'Test Driven Drupal', 'type' => 'talk', ], $overrides)); - $talk->save(); + $node->save(); - return $talk; + return Talk::createFromNode($node); } protected function setUp() {