Skip to content
This repository has been archived by the owner on Jan 21, 2020. It is now read-only.

Hinting of embedded resources #118

Open
nobesnickr opened this issue Aug 30, 2015 · 3 comments
Open

Hinting of embedded resources #118

nobesnickr opened this issue Aug 30, 2015 · 3 comments

Comments

@nobesnickr
Copy link

I've been struggling with a problem for some time regarding how relationship are handled on entities, specifically when embedded into a resource. The problem (and I assume it is a fairly common one) is how to provide relationships in a sane and dependable way without returning the entire (or most of the) database in embedded resources (and dealing with looping recursion problems) or always needing to fetch relationships. HAL spec is a bit fuzzy on how _embedded resources are supplied which makes it harder to create a dependable abstract consumers. For example, HAL Clients don't know if the non-rendered entity is actually rendered or not (or is just a link) or whether it is a full or partial rendering. The first case is an easier problem to solve abstractly and a few suggestions are below.

For the sake of example lets assume the following:

  • We have a table of people and each person has a relationship for their best friend and their gender.
    ** person --> one-to-many (bestFriend) --> person
  • We have render_embedded_entities set to false

We can either:

  1. Depend on clients to inspect the payload and if the only property is the _links object, the client should understand to fetch that resource also when needed. This is how it is currently working
{
    "id": "123",
    "name": "Jane Doe",
   "gender": "female",
    "_embedded": {
        "bestFriend": {
            "_links": {
                "self": {
                    "href": "http://abc.tld/api/person/456"
                }
            }
        }
    },
    "_links": {
        "self": {
            "href": "http://abc.tld/api/person/123"
        }
    }
}
  1. Providing a 'profile' parameter which can be used as a hint
    Link Only:
{
    "id": "123",
    "name": "Jane Doe",
    "gender": "female",
    "_embedded": {
        "bestFriend": {
            "_profile": "link",
            "_links": {
                "self": {
                    "href": "http://abc.tld/api/person/456"
                }
            }
        }
    },
    "_links": {
        "self": {
            "href": "http://abc.tld/api/person/123"
        }
    }
}

Partial Resource:

{
    "id": "123",
    "name": "Jane Doe",
    "gender": "female",
    "_embedded": {
        "bestFriend": {
            "name": "John Smith",
            "_profile": "partial",
            "_links": {
                "self": {
                    "href": "http://abc.tld/api/person/456"
                }
            }
        }
    },
    "_links": {
        "self": {
            "href": "http://abc.tld/api/person/123"
        }
    }
}
  1. Provide the embedded entities as _links on the resource instead of embedding the entity that contains only the link (removes any ambiguity).
{
    "id": "123",
    "name": "Jane Doe",
    "gender": "female",
    "_links": {
        "self": {
            "href": "http://abc.tld/api/person/123"
        },
        "bestFriend": {
            "href": "http: //abc.tld/api/person/456"
        }
    }
}

I've created PR #118 to show an example of how we can enabling linking insteading of embedding and would love to get feedback on how others are solving this kind of issue, and/or if I should focus time on building out one of the above potential solutions.

@nobesnickr nobesnickr changed the title Use _links instead of unrendered embedded entity Hinting of embedded resources Aug 30, 2015
@Wilt
Copy link
Contributor

Wilt commented Aug 31, 2015

In our project we do exactly like you propose in your last solution. Resources that are not embedded are linked. This seems to work very well in practice. We also took this a step further. Resources that are already rendered once in the response will not be rendered again in another resource in the same response.

For example. We render a resource collection.
The first resource in the collection has a resource A embedded. The second resource has the same resource A embedded. Instead of embedding it again in the second resource it will be linked.

Right now we solve this with some customization of the Hal plugin. We add a hash for each entity that is rendered into an array and while rendering we check every time whether the object with this hash was already once rendered in the same response with a isRendered method (check if the hash is in the array). If it was rendered we only add a link and remove the element from the array that is in the process of being rendered.

We can do this because on the client side we cache the json objects by their respective self link. During parsing of the json response on the client all links with the same self-href are automatically exchanged with the fully rendered instance in the client sided object model.
This means that we need the full json object only once in our response, all other instances can simply be linked since the client will automatically exchange those with the full object during parsing.

This delivers a performance increase since on the server we only need to render each entity once and our json response object is much more compact since we don't duplicate any json data.

This is implementation is according to the caching specs in hal section 8.3.

We first tried to solve this in the EntityExtractor added to the project which almost does the same (map of hashes in $serializedEntities and checking whether a $entity is serialized (is rendered) in the extract method like this:

if (isset($this->serializedEntities[$entity])) {
    return $this->serializedEntities[$entity];
}

Which virtually comes down to our isRendered method.

I would be interested in implementing such solution this part of the library.

@Roi19
Copy link

Roi19 commented May 5, 2017

@nobesnickr
Hi, sorry to disturb you, i would like to ask you a question about how you nested the entities. can you please give a detailed explanation on how to configure the 2 classes to get this:
{
"id": "123",
"name": "Jane Doe",
"gender": "female",
"_embedded": {
"bestFriend": {
"_profile": "link",
"_links": {
"self": {
"href": "http://abc.tld/api/person/456"
}
}
}
},
"_links": {
"self": {
"href": "http://abc.tld/api/person/123"
}
}
}
on apigility?.
Thank you

@weierophinney
Copy link
Member

This repository has been closed and moved to laminas-api-tools/api-tools-hal; a new issue has been opened at laminas-api-tools/api-tools-hal#16.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants