build(deps): remove discoverable_entity_bundle_classes
As this module is no longer supported, remove it from the codebase and update all references to it within the custom code - instead manually wrapping nodes with the `Post` or `Talk` class, or returning it from a Repository. Fixes: #465
This commit is contained in:
parent
41e13fe078
commit
cae2091436
|
@ -19,7 +19,6 @@
|
||||||
"drupal/config_split": "^1.4",
|
"drupal/config_split": "^1.4",
|
||||||
"drupal/core-composer-scaffold": "^8.8",
|
"drupal/core-composer-scaffold": "^8.8",
|
||||||
"drupal/core-recommended": "^8.8",
|
"drupal/core-recommended": "^8.8",
|
||||||
"drupal/discoverable_entity_bundle_classes": "^1.0",
|
|
||||||
"drupal/gin": "^3.0",
|
"drupal/gin": "^3.0",
|
||||||
"drupal/gin_toolbar": "^1.0",
|
"drupal/gin_toolbar": "^1.0",
|
||||||
"drupal/honeypot": "^2.0",
|
"drupal/honeypot": "^2.0",
|
||||||
|
|
49
composer.lock
generated
49
composer.lock
generated
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "74c67a9e7b92495a730372d2a0d10466",
|
"content-hash": "4e568994c7b2ae5c97f5df4a8253f536",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "asm89/stack-cors",
|
"name": "asm89/stack-cors",
|
||||||
|
@ -2510,53 +2510,6 @@
|
||||||
"issues": "https://www.drupal.org/project/issues/ctools"
|
"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",
|
"name": "drupal/entity_reference_revisions",
|
||||||
"version": "1.8.0",
|
"version": "1.8.0",
|
||||||
|
|
|
@ -14,7 +14,6 @@ module:
|
||||||
ctools: 0
|
ctools: 0
|
||||||
datetime: 0
|
datetime: 0
|
||||||
dblog: 0
|
dblog: 0
|
||||||
discoverable_entity_bundle_classes: 0
|
|
||||||
dynamic_page_cache: 0
|
dynamic_page_cache: 0
|
||||||
entity_reference_revisions: 0
|
entity_reference_revisions: 0
|
||||||
field: 0
|
field: 0
|
||||||
|
|
67
phpstan-baseline.neon
Normal file
67
phpstan-baseline.neon
Normal file
|
@ -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
|
||||||
|
|
|
@ -7,10 +7,7 @@ parameters:
|
||||||
- *Test.php
|
- *Test.php
|
||||||
- *TestBase.php
|
- *TestBase.php
|
||||||
checkMissingIterableValueType: false
|
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:
|
includes:
|
||||||
- vendor/mglaman/phpstan-drupal/extension.neon
|
- vendor/mglaman/phpstan-drupal/extension.neon
|
||||||
- vendor/phpstan/phpstan-deprecation-rules/rules.neon
|
- vendor/phpstan/phpstan-deprecation-rules/rules.neon
|
||||||
|
- phpstan-baseline.neon
|
||||||
|
|
|
@ -4,6 +4,5 @@ core_version_requirement: ^8 || ^9
|
||||||
package: Custom
|
package: Custom
|
||||||
dependencies:
|
dependencies:
|
||||||
- drupal:node
|
- drupal:node
|
||||||
- discoverable_entity_bundle_classes:discoverable_entity_bundle_classes
|
|
||||||
- hook_event_dispatcher:hook_event_dispatcher
|
- hook_event_dispatcher:hook_event_dispatcher
|
||||||
- paragraphs:paragraphs
|
- paragraphs:paragraphs
|
||||||
|
|
|
@ -6,19 +6,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use Drupal\Core\Url;
|
use Drupal\Core\Url;
|
||||||
use Drupal\discoverable_entity_bundle_classes\Storage\Node\NodeStorage;
|
|
||||||
use Drupal\node\NodeInterface;
|
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().
|
* Implements hook_node_links_alter().
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -4,21 +4,14 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Drupal\opdavies_blog\Entity\Node;
|
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\Entity\Node;
|
||||||
|
use Drupal\node\NodeInterface;
|
||||||
use Drupal\taxonomy\Entity\Term;
|
use Drupal\taxonomy\Entity\Term;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
/**
|
final class Post {
|
||||||
* Defines an blog post node class.
|
|
||||||
*
|
|
||||||
* @ContentEntityBundleClass(
|
|
||||||
* label = @Translation("Blog post"),
|
|
||||||
* entity_type = "node",
|
|
||||||
* bundle = "post"
|
|
||||||
* );
|
|
||||||
*/
|
|
||||||
class Post extends Node implements ContentEntityBundleInterface {
|
|
||||||
|
|
||||||
public const FIELD_EXTERNAL_LINK = 'field_external_link';
|
public const FIELD_EXTERNAL_LINK = 'field_external_link';
|
||||||
public const FIELD_HAS_TWEET = 'field_has_tweet';
|
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_SENT_TO_SOCIAL_MEDIA = 'field_sent_to_social_media';
|
||||||
public const FIELD_TAGS = 'field_tags';
|
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 {
|
public function getExternalLink(): ?array {
|
||||||
return ($link = $this->get(self::FIELD_EXTERNAL_LINK)->get(0))
|
return ($link = $this->get(self::FIELD_EXTERNAL_LINK)->get(0))
|
||||||
? $link->getValue()
|
? $link->getValue()
|
||||||
: NULL;
|
: NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getNode(): NodeInterface {
|
||||||
|
|
||||||
|
return $this->node;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Collection|Term[]
|
* @return Collection|Term[]
|
||||||
*/
|
*/
|
||||||
|
@ -47,16 +59,32 @@ class Post extends Node implements ContentEntityBundleInterface {
|
||||||
return (bool) $this->get(self::FIELD_HAS_TWEET)->getString();
|
return (bool) $this->get(self::FIELD_HAS_TWEET)->getString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function id(): int {
|
||||||
|
return (int) $this->node->id();
|
||||||
|
}
|
||||||
|
|
||||||
public function isExternalPost(): bool {
|
public function isExternalPost(): bool {
|
||||||
return (bool) $this->getExternalLink();
|
return (bool) $this->getExternalLink();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function label(): string {
|
||||||
|
return $this->node->label();
|
||||||
|
}
|
||||||
|
|
||||||
public function markAsSentToSocialMedia(): self {
|
public function markAsSentToSocialMedia(): self {
|
||||||
$this->set(self::FIELD_SENT_TO_SOCIAL_MEDIA, TRUE);
|
$this->set(self::FIELD_SENT_TO_SOCIAL_MEDIA, TRUE);
|
||||||
|
|
||||||
return $this;
|
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 {
|
public function setTags(array $tags): void {
|
||||||
$this->set(self::FIELD_TAGS, $tags);
|
$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();
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,15 +28,16 @@ final class SortTagsAlphabeticallyWhenPostIsSaved implements EventSubscriberInte
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var Post $entity */
|
|
||||||
if ($entity->bundle() != 'post') {
|
if ($entity->bundle() != 'post') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$sortedTags = $entity->getTags()
|
$post = Post::createFromNode($entity);
|
||||||
|
|
||||||
|
$sortedTags = $post->getTags()
|
||||||
->sortBy(fn(TermInterface $tag) => $tag->label());
|
->sortBy(fn(TermInterface $tag) => $tag->label());
|
||||||
|
|
||||||
$entity->setTags($sortedTags->toArray());
|
$post->setTags($sortedTags->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,6 @@ class RelatedPostsBlock extends BlockBase implements ContainerFactoryPluginInter
|
||||||
$pluginId,
|
$pluginId,
|
||||||
$pluginDefinition
|
$pluginDefinition
|
||||||
): self {
|
): self {
|
||||||
// @phpstan-ignore-next-line
|
|
||||||
return new self(
|
return new self(
|
||||||
$configuration,
|
$configuration,
|
||||||
$pluginId,
|
$pluginId,
|
||||||
|
@ -66,7 +65,7 @@ class RelatedPostsBlock extends BlockBase implements ContainerFactoryPluginInter
|
||||||
|
|
||||||
$build['content'] = [
|
$build['content'] = [
|
||||||
'#items' => $relatedPosts
|
'#items' => $relatedPosts
|
||||||
->sortByDesc(fn(Post $post) => $post->getCreatedTime())
|
->sortByDesc(fn(Post $post) => $post->getNode()->getCreatedTime())
|
||||||
->map(fn(Post $post) => $this->generateLinkToPost($post))
|
->map(fn(Post $post) => $this->generateLinkToPost($post))
|
||||||
->slice(0, 3)
|
->slice(0, 3)
|
||||||
->toArray(),
|
->toArray(),
|
||||||
|
@ -93,7 +92,7 @@ class RelatedPostsBlock extends BlockBase implements ContainerFactoryPluginInter
|
||||||
|
|
||||||
private function generateLinkToPost(Post $post): Link {
|
private function generateLinkToPost(Post $post): Link {
|
||||||
return Link::createFromRoute(
|
return Link::createFromRoute(
|
||||||
$post->getTitle(),
|
$post->label(),
|
||||||
'entity.node.canonical',
|
'entity.node.canonical',
|
||||||
['node' => $post->id()]
|
['node' => $post->id()]
|
||||||
);
|
);
|
||||||
|
|
|
@ -68,21 +68,19 @@ final class PostPusherQueueWorker extends QueueWorkerBase implements ContainerFa
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$post->isLatestRevision()) {
|
if (!$post->getNode()->isLatestRevision()) {
|
||||||
$post = $this->nodeStorage->load($post->id());
|
$node = $this->nodeStorage->load($post->id());
|
||||||
|
$post = Post::createFromNode($node);
|
||||||
|
|
||||||
// @phpstan-ignore-next-line
|
|
||||||
if (!$this->shouldBePushed($post)) {
|
if (!$this->shouldBePushed($post)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->postPushers as $pusher) {
|
foreach ($this->postPushers as $pusher) {
|
||||||
// @phpstan-ignore-next-line
|
|
||||||
$pusher->push($post);
|
$pusher->push($post);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @phpstan-ignore-next-line
|
|
||||||
$post->set(Post::FIELD_SENT_TO_SOCIAL_MEDIA, TRUE);
|
$post->set(Post::FIELD_SENT_TO_SOCIAL_MEDIA, TRUE);
|
||||||
$post->save();
|
$post->save();
|
||||||
}
|
}
|
||||||
|
@ -92,7 +90,7 @@ final class PostPusherQueueWorker extends QueueWorkerBase implements ContainerFa
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$post->isPublished()) {
|
if (!$post->getNode()->isPublished()) {
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,10 +41,9 @@ final class PostFactory {
|
||||||
Post::FIELD_TAGS => $this->tags->toArray(),
|
Post::FIELD_TAGS => $this->tags->toArray(),
|
||||||
];
|
];
|
||||||
|
|
||||||
/** @var Post $post */
|
|
||||||
$post = Node::create($values + $overrides);
|
$post = Node::create($values + $overrides);
|
||||||
|
|
||||||
return $post;
|
return Post::createFromNode($post);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setTitle(string $title): self {
|
public function setTitle(string $title): self {
|
||||||
|
|
|
@ -16,9 +16,6 @@ final class PostTest extends EntityKernelTestBase {
|
||||||
'link',
|
'link',
|
||||||
'taxonomy',
|
'taxonomy',
|
||||||
|
|
||||||
// Contrib.
|
|
||||||
'discoverable_entity_bundle_classes',
|
|
||||||
|
|
||||||
// Custom.
|
// Custom.
|
||||||
'opdavies_blog',
|
'opdavies_blog',
|
||||||
'opdavies_blog_test',
|
'opdavies_blog_test',
|
||||||
|
|
|
@ -18,7 +18,6 @@ abstract class PostTestBase extends EntityKernelTestBase {
|
||||||
'link',
|
'link',
|
||||||
|
|
||||||
// Contrib.
|
// Contrib.
|
||||||
'discoverable_entity_bundle_classes',
|
|
||||||
'hook_event_dispatcher',
|
'hook_event_dispatcher',
|
||||||
'core_event_dispatcher',
|
'core_event_dispatcher',
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,9 @@ namespace Drupal\Tests\opdavies_blog\Kernel;
|
||||||
|
|
||||||
use Drupal\Core\Queue\QueueInterface;
|
use Drupal\Core\Queue\QueueInterface;
|
||||||
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
||||||
use Drupal\opdavies_blog\Entity\Node\Post;
|
|
||||||
use Drupal\Tests\node\Traits\NodeCreationTrait;
|
use Drupal\Tests\node\Traits\NodeCreationTrait;
|
||||||
|
use Drupal\node\NodeInterface;
|
||||||
|
use Drupal\opdavies_blog\Entity\Node\Post;
|
||||||
|
|
||||||
final class PushToSocialMediaTest extends EntityKernelTestBase {
|
final class PushToSocialMediaTest extends EntityKernelTestBase {
|
||||||
|
|
||||||
|
@ -20,7 +21,6 @@ final class PushToSocialMediaTest extends EntityKernelTestBase {
|
||||||
'link',
|
'link',
|
||||||
|
|
||||||
// Contrib.
|
// Contrib.
|
||||||
'discoverable_entity_bundle_classes',
|
|
||||||
'hook_event_dispatcher',
|
'hook_event_dispatcher',
|
||||||
'core_event_dispatcher',
|
'core_event_dispatcher',
|
||||||
|
|
||||||
|
@ -48,8 +48,8 @@ final class PushToSocialMediaTest extends EntityKernelTestBase {
|
||||||
$post = $item->data['post'];
|
$post = $item->data['post'];
|
||||||
|
|
||||||
$this->assertNotNull($post);
|
$this->assertNotNull($post);
|
||||||
$this->assertInstanceOf(Post::class, $post);
|
$this->assertInstanceOf(NodeInterface::class, $post);
|
||||||
$this->assertSame('1', $post->id());
|
$this->assertSame('post', $post->bundle());
|
||||||
$this->assertSame('Ignoring PHPCS sniffs within PHPUnit tests', $post->getTitle());
|
$this->assertSame('Ignoring PHPCS sniffs within PHPUnit tests', $post->getTitle());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
namespace Drupal\Tests\opdavies_blog\Kernel;
|
namespace Drupal\Tests\opdavies_blog\Kernel;
|
||||||
|
|
||||||
use Drupal\node\Entity\Node;
|
use Drupal\node\Entity\Node;
|
||||||
|
use Drupal\node\NodeInterface;
|
||||||
use Drupal\opdavies_blog\Entity\Node\Post;
|
use Drupal\opdavies_blog\Entity\Node\Post;
|
||||||
use Drupal\taxonomy\Entity\Vocabulary;
|
use Drupal\taxonomy\Entity\Vocabulary;
|
||||||
use Drupal\taxonomy\TermInterface;
|
use Drupal\taxonomy\TermInterface;
|
||||||
|
@ -26,8 +27,8 @@ final class ReorderBlogTagsTest extends PostTestBase {
|
||||||
Post::FIELD_TAGS => [3, 1, 2],
|
Post::FIELD_TAGS => [3, 1, 2],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/** @var Post $post */
|
$node = Node::load($post->id());
|
||||||
$post = Node::load($post->id());
|
$post = Post::createFromNode($node);
|
||||||
|
|
||||||
$this->assertSame(
|
$this->assertSame(
|
||||||
['Drupal', 'PHP', 'Symfony'],
|
['Drupal', 'PHP', 'Symfony'],
|
||||||
|
|
|
@ -3,5 +3,3 @@ description: Custom code for talks pages.
|
||||||
type: module
|
type: module
|
||||||
core_version_requirement: ^8 || ^9
|
core_version_requirement: ^8 || ^9
|
||||||
package: Custom
|
package: Custom
|
||||||
dependencies:
|
|
||||||
- discoverable_entity_bundle_classes:discoverable_entity_bundle_classes
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ declare(strict_types=1);
|
||||||
|
|
||||||
use Drupal\Core\Entity\EntityTypeInterface;
|
use Drupal\Core\Entity\EntityTypeInterface;
|
||||||
use Drupal\Core\Render\BubbleableMetadata;
|
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\TalkCounter;
|
||||||
use Drupal\opdavies_talks\Service\TalkDateUpdater;
|
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().
|
* Implements hook_token_info().
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Drupal\opdavies_talks\Collection;
|
namespace Drupal\opdavies_talks\Collection;
|
||||||
|
|
||||||
|
use Drupal\node\NodeInterface;
|
||||||
use Drupal\opdavies_talks\Entity\Node\Talk;
|
use Drupal\opdavies_talks\Entity\Node\Talk;
|
||||||
use Drupal\paragraphs\ParagraphInterface;
|
use Drupal\paragraphs\ParagraphInterface;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
@ -16,7 +17,8 @@ final class TalkCollection extends Collection {
|
||||||
* @return Collection|ParagraphInterface[]
|
* @return Collection|ParagraphInterface[]
|
||||||
*/
|
*/
|
||||||
public function getEvents(): Collection {
|
public function getEvents(): Collection {
|
||||||
return $this->flatMap(fn(Talk $talk): Collection => $talk->getEvents());
|
return $this
|
||||||
|
->flatMap(fn(Talk $talk): Collection => $talk->getEvents());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,27 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Drupal\opdavies_talks\Entity\Node;
|
namespace Drupal\opdavies_talks\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\Entity\Node;
|
||||||
|
use Drupal\node\NodeInterface;
|
||||||
use Drupal\paragraphs\ParagraphInterface;
|
use Drupal\paragraphs\ParagraphInterface;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
/**
|
final class Talk {
|
||||||
* Defines an talk node class.
|
|
||||||
*
|
|
||||||
* @ContentEntityBundleClass(
|
|
||||||
* label = @Translation("Talk"),
|
|
||||||
* entity_type = "node",
|
|
||||||
* bundle = "talk"
|
|
||||||
* );
|
|
||||||
*/
|
|
||||||
class Talk extends Node implements ContentEntityBundleInterface {
|
|
||||||
|
|
||||||
public const FIELD_EVENTS = 'field_events';
|
public const FIELD_EVENTS = 'field_events';
|
||||||
public const FIELD_EVENT_DATE = 'field_event_date';
|
public const FIELD_EVENT_DATE = 'field_event_date';
|
||||||
|
|
||||||
|
private NodeInterface $node;
|
||||||
|
|
||||||
|
public function __construct(EntityInterface $node) {
|
||||||
|
$this->node = $node;
|
||||||
|
}
|
||||||
|
|
||||||
public function addEvent(ParagraphInterface $event): void {
|
public function addEvent(ParagraphInterface $event): void {
|
||||||
$this->set(
|
$this->set(
|
||||||
self::FIELD_EVENTS,
|
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 {
|
public function getEvents(): Collection {
|
||||||
return Collection::make($this->get(self::FIELD_EVENTS)
|
return Collection::make($this->get(self::FIELD_EVENTS)
|
||||||
->referencedEntities());
|
->referencedEntities());
|
||||||
|
@ -41,16 +62,24 @@ class Talk extends Node implements ContentEntityBundleInterface {
|
||||||
return (int) $this->get(self::FIELD_EVENT_DATE)->getString();
|
return (int) $this->get(self::FIELD_EVENT_DATE)->getString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function id(): int {
|
||||||
* Find the date for the latest event.
|
return (int) $this->node->id();
|
||||||
*
|
}
|
||||||
* @return string|null
|
|
||||||
*/
|
public function label(): string {
|
||||||
public function findLatestEventDate(): ?string {
|
return $this->node->label();
|
||||||
return $this->getEvents()
|
}
|
||||||
->map(fn(ParagraphInterface $event) => $event->get('field_date')
|
|
||||||
->getString())
|
public function save(): void {
|
||||||
->max();
|
$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 {
|
public function setEvents(array $events): void {
|
||||||
|
@ -61,4 +90,9 @@ class Talk extends Node implements ContentEntityBundleInterface {
|
||||||
$this->set(self::FIELD_EVENT_DATE, $date);
|
$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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,8 +32,9 @@ final class UpdateTalkNodeBeforeSave implements EventSubscriberInterface {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var Talk $talk */
|
$node = $event->getEntity();
|
||||||
$talk = $event->getEntity();
|
$talk = Talk::createFromNode($node);
|
||||||
|
|
||||||
$this->reorderEvents($talk);
|
$this->reorderEvents($talk);
|
||||||
$this->updateCreatedDate($talk);
|
$this->updateCreatedDate($talk);
|
||||||
}
|
}
|
||||||
|
@ -42,9 +43,7 @@ final class UpdateTalkNodeBeforeSave implements EventSubscriberInterface {
|
||||||
$events = $talk->getEvents();
|
$events = $talk->getEvents();
|
||||||
$eventsByDate = $this->sortEventsByDate($events);
|
$eventsByDate = $this->sortEventsByDate($events);
|
||||||
|
|
||||||
// If the original event IDs don't match the sorted event IDs, update the
|
// If the original event IDs don't match the sorted event IDs, update the event field to use the sorted ones.
|
||||||
// event field to use the sorted ones.
|
|
||||||
// @phpstan-ignore-next-line
|
|
||||||
if ($events->map->id() != $eventsByDate->map->id()) {
|
if ($events->map->id() != $eventsByDate->map->id()) {
|
||||||
$talk->setEvents($eventsByDate->toArray());
|
$talk->setEvents($eventsByDate->toArray());
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ use Drupal\Core\Entity\EntityStorageInterface;
|
||||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||||
use Drupal\node\NodeInterface;
|
use Drupal\node\NodeInterface;
|
||||||
use Drupal\opdavies_talks\Collection\TalkCollection;
|
use Drupal\opdavies_talks\Collection\TalkCollection;
|
||||||
|
use Drupal\opdavies_talks\Entity\Node\Talk;
|
||||||
|
|
||||||
final class TalkRepository {
|
final class TalkRepository {
|
||||||
|
|
||||||
|
@ -20,7 +21,8 @@ final class TalkRepository {
|
||||||
public function findAll(): TalkCollection {
|
public function findAll(): TalkCollection {
|
||||||
$talks = $this->nodeStorage->loadByProperties($this->defaultProperties());
|
$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 {
|
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 {
|
private function defaultProperties(): array {
|
||||||
|
|
|
@ -47,7 +47,9 @@ final class TalkEventDateTest extends TalksTestBase {
|
||||||
|
|
||||||
$expected = Carbon::today()->addDays(4)->getTimestamp();
|
$expected = Carbon::today()->addDays(4)->getTimestamp();
|
||||||
|
|
||||||
$talk = Node::load($talk->id());
|
$node = Node::load($talk->id());
|
||||||
|
$talk = Talk::createFromNode($node);
|
||||||
|
|
||||||
$this->assertNextEventDateIs($talk, $expected);
|
$this->assertNextEventDateIs($talk, $expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +78,9 @@ final class TalkEventDateTest extends TalksTestBase {
|
||||||
|
|
||||||
$expected = Carbon::today()->subDays(2)->getTimestamp();
|
$expected = Carbon::today()->subDays(2)->getTimestamp();
|
||||||
|
|
||||||
$talk = Node::load($talk->id());
|
$node = Node::load($talk->id());
|
||||||
|
$talk = Talk::createFromNode($node);
|
||||||
|
|
||||||
$this->assertNextEventDateIs($talk, $expected);
|
$this->assertNextEventDateIs($talk, $expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +94,9 @@ final class TalkEventDateTest extends TalksTestBase {
|
||||||
$dateUpdater = $this->container->get(TalkDateUpdater::class);
|
$dateUpdater = $this->container->get(TalkDateUpdater::class);
|
||||||
$dateUpdater->__invoke();
|
$dateUpdater->__invoke();
|
||||||
|
|
||||||
$talk = Node::load($talk->id());
|
$node = Node::load($talk->id());
|
||||||
|
$talk = Talk::createFromNode($node);
|
||||||
|
|
||||||
$this->assertNoNextEventDate($talk);
|
$this->assertNoNextEventDate($talk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@ abstract class TalksTestBase extends EntityKernelTestBase {
|
||||||
'datetime',
|
'datetime',
|
||||||
|
|
||||||
// Contrib.
|
// Contrib.
|
||||||
'discoverable_entity_bundle_classes',
|
|
||||||
'entity_reference_revisions',
|
'entity_reference_revisions',
|
||||||
'paragraphs',
|
'paragraphs',
|
||||||
'hook_event_dispatcher',
|
'hook_event_dispatcher',
|
||||||
|
@ -45,14 +44,14 @@ abstract class TalksTestBase extends EntityKernelTestBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function createTalk(array $overrides = []): Talk {
|
protected function createTalk(array $overrides = []): Talk {
|
||||||
$talk = Node::create(array_merge([
|
$node = Node::create(array_merge([
|
||||||
'title' => 'Test Driven Drupal',
|
'title' => 'Test Driven Drupal',
|
||||||
'type' => 'talk',
|
'type' => 'talk',
|
||||||
], $overrides));
|
], $overrides));
|
||||||
|
|
||||||
$talk->save();
|
$node->save();
|
||||||
|
|
||||||
return $talk;
|
return Talk::createFromNode($node);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function setUp() {
|
protected function setUp() {
|
||||||
|
|
Loading…
Reference in a new issue