Problem/Motivation

The serialized representation of a book node doesn't contain the book metadata.
This means on deserializing the book information is lost.

Proposed resolution

Add a normlizer for book data.

Remaining tasks

Patch, test, review

User interface changes

None

API changes

None

Data model changes

New information in serialized representation of a book node.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

larowlan created an issue. See original summary.

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

larowlan’s picture

There are tests and a normalizer in entity pilot module that can be borrowed here

jan.stoeckler’s picture

Assigned: Unassigned » jan.stoeckler

I'll take a stab at this if it's alright.

jan.stoeckler’s picture

Status: Active » Needs review
Issue tags: +neworleans2016
FileSize
10 KB

This is basically a copy & paste effort, as suggested. I'll be happy to add to that if needed.

Status: Needs review » Needs work

The last submitted patch, 5: 2698785-5.patch, failed testing.

jan.stoeckler’s picture

Status: Needs work » Needs review
FileSize
9.98 KB
483 bytes

Oops.

tstoeckler’s picture

Status: Needs review » Needs work

Yay, thanks for this! I must admit that I don't know too much about Book module, so this is a lot of nitpicks and a couple of general remarks, although a couple of things are specific to Rest, albeit not Book.

+/**
+ * @file
+ * Contains \Drupal\book\BookServiceProvider.
+ */

Here, and elsewhere: This is no longer required, and will bug anyone running PHPCS on core. Please remove.

+ * Creates a service modifier to add a book normalizer.

This is pretty nitpicky, but this doesn't actually create a service modifier, it is one. How about: Provides a book normalizer service if HAL module is installed.

+class BookServiceProvider implements ServiceModifierInterface {

We're adding a service, so we should instead implement ServiceProviderInterface. That has a register() method, instead of alter() but it works the same way. It just fits better semantically with what we're doing here.

+  /**
+   * Psuedo field name for embedding parent book.
+   *
+   * @var string
+   */
+  const PSUEDO_PARENT_FIELD_NAME = 'book_parent';
+
+  /**
+   * Psuedo field name for embedding book.
+   *
+   * @var string
+   */
+  const PSUEDO_BOOK_FIELD_NAME = 'book';

It's not clear to me from reading the patch why this is needed. Can we at least add some docs?

+    // Ensure this normalizer is only registered if hal.module is active.

Again very nitpicky, but:
hal.module -> HAL module
active -> installed

+      $service_definition = new Definition('Drupal\book\Normalizer\BookNormalizer', array(

Here, and elsewhere: Could be changed to use the new array syntax.

+      // The priority must be higher than that of
+      // serializer.normalizer.entity.hal in hal.services.yml.

Yay, thank you so much for this!!! In my opinion every service priority should be documented explicitly, but we rarely do it. Thanks, this is great!

+namespace Drupal\book\Normalizer;
+
+
+use Drupal\Core\Config\ConfigFactoryInterface;

Too many newlines.

+use Drupal\Core\Entity\EntityManagerInterface;

Let's use the EntityTypeManagerInterface instead.

+  protected $supportedInterfaceOrClass = 'Drupal\node\NodeInterface';

For extra credit we can use NodeInterface::class now.

+  /**
+   * Constructs a new BookNormalizer object.
+   *
+   * @param \Drupal\rest\LinkManager\LinkManagerInterface $link_manager
+   *   Link manager.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   Entity manager.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   Module handler.
+   */

The config factory is missing.

+    $this->allowedTypes = $config_factory->get('book.settings')->get('allowed_types');

Because the config can be updated while this service persists, it's discouraged to actually store config values in a service instead of the config factory. In this particular the actual use-case that would be broken is probably a bit far-fetched, but I would still consider it a best practice to inject the config factory itself, instead of just the values.

+    $embedded_cache = [];

In my opinion the fact that this variable is being used as a cache is not as interesting as what it actually contains. I would suggest naming it $embedded_nodes.

+        'bid' => self::PSUEDO_BOOK_FIELD_NAME,
+        'pid' => self::PSUEDO_PARENT_FIELD_NAME

Let's be nice to the extenders (TM) and use static::.

+        if (!empty($context['included_fields']) && !in_array($field_name, $context['included_fields'], TRUE)) {
+          // Only normalizing specific fields.
+          continue;
+        }
+        if (isset($entity->book[$field_name]) && $embedded_entity = $this->entityManager->getStorage('node')
+            ->load($entity->book[$field_name])
+        ) {

I think it's strange to have a guard condition (the 1st if) and then wrap the rest of the code in another if. Either we should make all conditions part of the guard or just put all the conditions in the second if. Because there's another if-else inside there, I would lean towards the former. Also, the second if is formatted strangely.

+          if (!isset($embedded_cache[$embedded_entity->id()])) {

$embedded_cache is never actually populated inside of this if. That also means that the else-branch will never be reached. Given that I think it would be nice to add a unit test for this class.

+            $langcode = isset($context['langcode']) ? $context['langcode'] : NULL;
+
+            // Normalize the target entity.
+            $embedded = $this->serializer->normalize($embedded_entity, $format, ['included_fields' => ['uuid']]);
+            $link = $embedded['_links']['self'];
+            // If the field is translatable, add the langcode to the link
+            // relation object. This does not indicate the language of the
+            // target entity.
+            if ($langcode) {
+              $embedded['lang'] = $link['lang'] = $langcode;
+            }

This looks similar to some code in EntityReferenceItemTrait. Does it make sense to add a EmbeddingNormalizerTrait (or similar)?

+              $embedded['lang'] = $link['lang'] = $langcode;

I realize this is copied from EntityReferenceItemNormalizer but I don't think our coding standards allow this.

+            $link['lang'] = $embedded['lang'];

Per the above this code will never actually be reached, but it seems as though this should be wrapped in a if (isset($embedded['lang'])).

+          if ($entity = $this->entityManager->loadEntityByUuid('node', $uuid)) {

This is a genuine question, I really don't know: Should we use the EntityResolver for this?

+      'book' => [
+        'bid' => 'new',
+      ],

Is setting the bid to new something we should handle in the book denormalization?

+        'bid' => $top_level_node_id,
+        'pid' => $top_level_node_id,

It's a bit unfortunate that we don't use different IDs here for testing purposes.

jan.stoeckler’s picture

I cannot address your questions, but tried to fix everything else. For some reason the modified test fails on my machine, but I cannot successfully debug it, so let's see what the testbot finds.

jan.stoeckler’s picture

Status: Needs work » Needs review

And, of course, I forgot to change the issue status. Sorry!

larowlan’s picture

Status: Needs review » Needs work

It's not clear to me from reading the patch why this is needed. Can we at least add some docs?

The embedded links added to the hal output need reference a particular field.

As per #2577923: MenuLinkContent entities pointing to nodes are not deployable: LinkItem should have a "target_uuid" computed property we should also be adding something to serializer module (basically the bid/pid bits without the embedding).

+++ b/core/modules/book/src/Normalizer/BookNormalizer.php
@@ -0,0 +1,134 @@
+  /**
+   * {@inheritdoc}
+   */
+  protected $supportedInterfaceOrClass = NodeInterface::class;

This represents a hole in our serialization APIs. What we're doing here is effectively hijacking serializing of *all* nodes just for the sake of one module - book. If we need another module to operate on a different node-type (eg. in contrib), then it cannot live alongside this normalizer without extending from it.

I think we should instead look at injecting the event dispatcher into the existing ContentEntityNormalizer and dispatching an event in both normalize and denormalize methods and then add an event listener in the book module, that way other modules can interact with node normalizing.
I know book is doing the wrong thing (not declaring field definitions) and that is the source of the issue, but there are heaps of contrib modules that do the same thing.

And yes, I realise this is c/p from my code in EntityPilot but I can get away with it there, I don't think we can in core.

Thoughts?

larowlan’s picture

jan.stoeckler’s picture

Status: Needs work » Needs review
FileSize
23.12 KB
18.48 KB

I hadn't caught comment #12, so for now we're not using the proposed trait, but i'll be happy to implement it if the general direction of this patch is fine.

Status: Needs review » Needs work

The last submitted patch, 13: 2698785-13.patch, failed testing.

jan.stoeckler’s picture

On a related note, could https://drupal.org/node/2419825 be related to this issue in some way/would it make sense to leverage the work done there?

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Anas_maw’s picture

Hey, any updates on this issue, serializing book still does not include book metadata.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.8.x-dev

Drupal 8.6.x will not receive any further development aside from security fixes. Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

larowlan’s picture

Category: Bug report » Feature request
Issue tags: +Bug Smash Initiative

In retrospect, this is a feature request, not a bug

Version: 8.9.x-dev » 9.2.x-dev

Drupal 8 is end-of-life as of November 17, 2021. There will not be further changes made to Drupal 8. Bugfixes are now made to the 9.3.x and higher branches only. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.15 was released on June 1st, 2022 and is the final full bugfix release for the Drupal 9.3.x series. Drupal 9.3.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.4.x-dev branch from now on, and new development or disruptive changes should be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.9 was released on December 7, 2022 and is the final full bugfix release for the Drupal 9.4.x series. Drupal 9.4.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.5.x-dev branch from now on, and new development or disruptive changes should be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

quietone’s picture

Status: Needs work » Postponed

This extension is being deprecated, see #3376070: [Meta] Tasks to deprecate Book module. It will be removed from core and moved to a contrib project, #3376101: [11.x] [Meta] Tasks to remove Book.

This is now Postponed. The status is set according to two policies. The Remove a core extension and move it to a contributed project and the Extensions approved for removal policies.

This issue may be re-opened if it can be considered critical, If unsure, re-open the issue and ask in a comment.

quietone’s picture

Assigned: jan.stoeckler » Unassigned

Version: 9.5.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.