1. Again, like I noted in #2829398: Clean up JsonApiResource: the annotation, plugin type, plugin manager, plugin implementations, the dynamic routes generator and the request handler and #2829740: Clean up Drupal\jsonapi\Configuration\Resource(Config|Manager)(Interface) before, it's remarkable how much is tied to entities. I'd again urge you to consider making JSON API support entities only.
  2. Why does this introduce its own interface? Why can't this reuse \Drupal\rest\LinkManager\LinkManagerInterface? If the answer is because the JSON API module only depends on the Serialization module, not the REST module, then I'd say: but then let's duplicate REST's LinkManagerInterface until it is moved to the Serialization module in #2758897: Move rest module's "link manager" services to serialization module. #2758897 is in, this is no longer an issue.
  3. I think \Drupal\jsonapi\LinkManager\LinkManager::getRequestLink() should not be necessary? It should be able to use \Drupal\jsonapi\LinkManager\LinkManager::getEntityLink()?
  4. I think \Drupal\jsonapi\LinkManager\LinkManager::getEntityLink() should not be necessary; instead the JSON API module should register a new link relation for every entity type. So that you can do $entity->toUrl('jsonapi').
  5. That leaves just \Drupal\jsonapi\LinkManager\LinkManager::getPagerLinks(), which AFAICT belongs in a PagerLinkManagerInterface, much like \Drupal\serialization\LinkManager\TypeLinkManagerInterface and \Drupal\serialization\LinkManager\ConfigurableLinkManagerInterface which together make up \Drupal\serialization\LinkManager\LinkManagerInterface.

Comments

Wim Leers created an issue. See original summary.

e0ipso’s picture

I'd again urge you to consider making JSON API support entities only.

JSON API only supports entities.

We introduced the plugins in #2781383: [FEATURE] Use plugins for resources because we wanted to keep the door opened to 3rd party modules to configure the resources. This is already in place by allowing resources to be “turned off”.

e0ipso’s picture

  1. (See comment above)
  2. This Interface contains slightly different methods, since it applies to different link scenarios.
  3. That is only possible if the current request is for an individual entity. There are 4 endpoints per entity type, getEntityLink it won't work for collections, relationships and related resources.
  4. Like the concept. I'll need to do some digging on how to implement this.
  5. +1, but taking into account that we do need getRequestLink. How would you handle that?
wim leers’s picture

  1. I didn't think about the collection aspect. That's a good point. We should then at least rename it to getCurrentRequestLink().
  1. See 3.
wim leers’s picture

e0ipso’s picture

Title: Clean up \Drupal\jsonapi\LinkManager\LinkManager(Interface) » [PP-1] Clean up \Drupal\jsonapi\LinkManager\LinkManager(Interface)
e0ipso’s picture

naveenvalecha’s picture

Title: [PP-1] Clean up \Drupal\jsonapi\LinkManager\LinkManager(Interface) » Clean up \Drupal\jsonapi\LinkManager\LinkManager(Interface)
Status: Postponed » Active
wim leers’s picture

Issue summary: View changes

Indeed it finally landed :)

IS updated.

We'll still need to figure out how to proceed here.

dawehner’s picture

I think \Drupal\jsonapi\LinkManager\LinkManager::getEntityLink() should not be necessary; instead the JSON API module should register a new link relation for every entity type. So that you can do $entity->toUrl('jsonapi').

Note: This won't work yet as this requires \Drupal\Core\Entity\Entity to know that this route needs a UUID.

wim leers’s picture

wim leers’s picture

Assigned: Unassigned » wim leers

Looking at this again.

Core's status

So core has this:

  1. interface LinkManagerInterface extends TypeLinkManagerInterface, RelationLinkManagerInterface {}
  2. interface RelationLinkManagerInterface extends ConfigurableLinkManagerInterface {
      public function getRelationUri($entity_type, $bundle, $field_name, $context = array());
      public function getRelationInternalIds($relation_uri);
    }
    
  3. interface TypeLinkManagerInterface extends ConfigurableLinkManagerInterface {
      public function getTypeUri($entity_type, $bundle, $context = array());
      public function getTypeInternalIds($type_uri, $context = array());
    }
  4. interface ConfigurableLinkManagerInterface {
      public function setLinkDomain($domain);
    }
    

It used to live in the REST module, since #2758897: Move rest module's "link manager" services to serialization module all of that lives in the serialization module.

But… all this linkmanager stuff is only ever used by the HAL module's normalizers! And all of it is tied to entity types!!! So it's not at all about generic resources, as one would expect. It all only works for entities. So it's actually EntityTypeLinkManagerInterface and EntityRelationLinkManagerInterface, even if it doesn't want to admit that.

So that's two ways in which the link manager is utterly and totally flawed. It'd have been fine if this was the implementation, but the fact that this is an interface means that we're shackled to this API until Drupal 9. We can't even actually sanely expand it with additional kinds of links (because interface LinkManagerInterface extends TypeLinkManagerInterface, RelationLinkManagerInterface {}).

What an utter fail. :(

And then if I go look at where they're used, for example I see this in \Drupal\hal\Normalizer\ContentEntityNormalizer::normalize():

    /** @var $entity \Drupal\Core\Entity\ContentEntityInterface */
    $normalized = array(
      '_links' => array(
        'self' => array(
          'href' => $this->getEntityUri($entity),
        ),
        'type' => array(
          'href' => $this->linkManager->getTypeUri($entity->getEntityTypeId(), $entity->bundle(), $context),
        ),
      ),
    );

So we get that "type" link from the link manager, but not the "self" link. Sigh. :(

JSON API's status

The JSON API contrib module has this:

class LinkManager {
  public function getEntityLink($entity_id, ResourceType $resource_type, array $route_parameters, $key);
  public function getRequestLink(Request $request, $query = NULL);
  public function getPagerLinks(Request $request, array $link_context = []);
}

It does not extend the existing link manager, because that used to live in REST (which JSON API does not depend on) until just a few weeks ago. However, none of the links in the link manager would actually even be helpful! They all only make sense for the HAL normalization! And even then only for entities!

getEntityLink() clearly fulfills the exact same purpose as this in the HAL module: 'href' => $this->getEntityUri($entity),. So we could add a getEntityLink() method to the serialization module's link manager, which could then also be used by HAL's normalizers… but it wouldn't adequately address JSON API's entity link needs, because that $key parameter there allows also e.g. the "related" link for an entity to be generated.

Conclusion

  1. JSON API should indeed have its own link manager.
  2. The Serialization module's link manager is misplaced: it actually belongs in the HAL module! (Also proven by the fact that $this->linkManager appears 0 times in core/modules/serialization and 6 times in core/modules/hal.)
wim leers’s picture

  1. The Serialization module's link manager is misplaced: it actually belongs in the HAL module! (Also proven by the fact that $this->linkManager appears 0 times in core/modules/serialization and 6 times in core/modules/hal.)

This resulted in me opening #2854830: Move rest/serialization module's "link manager" services to HAL module.

wim leers’s picture

Status: Active » Closed (works as designed)

So, based on:

  1. the analysis in #12, which shows that \Drupal\jsonapi\LinkManager\LinkManager cannot possibly build on top of the Serialization module's LinkManager
  2. the fact that \Drupal\jsonapi\LinkManager\LinkManager is marked as @internal

I'm closing this. The code is good enough for now. There are no abstractions/concepts that this should be building on top of, because they're deeply flawed (as #12 demonstrated). Therefore there is nothing we can do right now, and we do continue to have the ability to make this code better in the future.

e0ipso’s picture

This was a great report Wim. Thanks for uncovering all that. As a matter of fact, the earliest prototypes of JSON API were based on HAL so I did explore re-using the link manager back in the day. I must have come to the same conclusion, but I can remember doing so.

Thanks!