Coder Social home page Coder Social logo

zf-hal's People

Contributors

acabala avatar artdevgame avatar bartbrinkman avatar boukeversteegh avatar evandotpro avatar ezimuel avatar gsomoza avatar localheinz avatar maks3w avatar michaelgooden avatar michalbundyra avatar neeckeloo avatar nyholm avatar oxodesign avatar ppaulis avatar ralphschindler avatar remybreuils avatar samsonasik avatar steverhoades avatar telkins avatar thomasvargiu avatar tjlytle avatar tomhanderson avatar vixriihi avatar weierophinney avatar wilt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

zf-hal's Issues

Error in latest version (1.4.2)

/var/www/vendor/zfcampus/zf-hal/src/Entity.php at line : 55
E_USER_DEPRECATED : Direct property access to ZF\Hal\Entity::$entity is deprecated, use getters instead.

Update zf-hal `ZF\Hal\Plugin` to support resources with $id = 0

I have some resources with identifier $id = 0. This is currently not rendering correctly inside ZF\Hal\Plugin\Hal.

This is related to the createEntity method on line 751 in ZF\Hal\Plugin\Hal which should explicitly check if ($id === false) in the if clause on line 763 instead of the if($id). Right now the method wrongly returns an ApiProblem if the entity has $id = 0.

Link to ZF\Hal\Plugin\Hal:createEntity: https://github.com/zfcampus/zf-hal/blob/master/src/Plugin/Hal.php#L751

Also check related issue in zf-hal: zfcampus/zf-rest#35

Invalid page size parameter causes a 500 Internal Server Error

If you configure a page size parameter, values that are not positive integers (or -1) will result in a 500 Internal Server Error and a stack trace. The exceptions that trigger this (vendor/zfcampus/zf-hal/src/Collection.php line 302 and 310) indicate the problem is one of validation rather than something going wonky on the server. IMHO, this should not be a 500, but could either be a 400 or 422 or it could "fail safe" back to using the configured default and not causing an error at all.

Infinite references when self-referenced

HAL will loop into error when resources have self or circular references.

I started work on this by limiting the depth to render a given resource to a specified number but I think a better approach is to give a boolean to render reference details for a resource.

I still want the _embedded [ $key ] [ _links ] [ self ]
for all embedded resources but I don't want the resource details which will prevent recursion. So I think a boolean config default and metadata map variable should be added which specifies "display details of embedded references?" for a given resource.

What's your view?

renderEntity event: Hal entity contains array instead of original entity object

Transcript from #apigility freenode.

[16:14] <matuszeman> Hi there. I'm failing in implementing a functionality in renderEntity listener. It looks like that an entity available from hal object is array instead of entity object. Any help?
[16:16] == lynxpardinus [[email protected]] has joined #apigility
[16:24] == shafox [[email protected]] has joined #apigility
[16:24] == MANCHUCK [[email protected]] has joined #apigility
[16:34] <weierophinney> matuszeman: that's usually the case when fetching database records; unless you have a hydrating resultset setup, you'll get arrays by default.
[16:39] <matuszeman> weierophinney: resource returns entity object but that's it's converted into array here: ZF\Hal\Plugin\Hal::createEntityFromMetadata() - line 700
[16:42] <weierophinney> matuszeman: hm... renderEntity() actually triggers the renderEntity event _before_ calling createEntityFromMetadata(). So, if you're getting an array passed to the listener, that means that you're deriving the entity earlier than that.
[16:42] <weierophinney> matuszeman: is this a REST or RPC service?
[16:42] <matuszeman> rest
[16:44] <weierophinney> what operation? GET, POST, PATCH, etc.?
[16:44] <matuszeman> get ... collections are ok.
[16:45] <weierophinney> aha -- RestController::get() calls Hal::createEntity(), which in turn calls Hal::createEntityFromMetadata()
[16:46] <weierophinney> which is why you have an array by the time you get to renderEntity()
[16:46] <matuszeman> yep.
[16:46] <weierophinney> matuszeman: I'm thinking perhaps we should trigger renderEntity from createEntity() as well.
[16:46] <weierophinney> Could you write a feature request for that in the zf-hal tracker, and write up what you're trying to do that prompts it?
[16:47] <matuszeman> what's the URL?
[16:50] <matuszeman> I've also noticed that we trigger get.post event with both entity and resource params being same object.. is it for BC?
[16:51] <matuszeman> weierophinney: .... in restful controller.
[16:51] <weierophinney> matuszeman: yes.
[16:51] <weierophinney> matuszeman: technically, we likely should have removed those for 1.0, but I didn't want to break apps for anyone who stuck with us throughout the beta process.
[16:53] <matuszeman> so what's intended behaviour? where should we get entity object from? Hal entity only?
[16:53] <matuszeman> I'd like to quick-fix it somehow so I can start using it that way.
[16:54] <weierophinney> matuszeman: intended behavior should likely be anytime we are trying to render or create an entity, we should trigger renderEntity.
[16:55] <weierophinney> matuszeman: I'm actually not sure that createEntity is doing the right thing. I'm thinking it should not do the "createEntityFromMetadata()" call.
[16:55] <weierophinney> and instead should just pass what it was given into the Hal Entity or Collection.
[16:55] <weierophinney> matuszeman: removing that call from createEntity() would actually solve your problem, too, as then renderEntity would operate on the original object.
[16:56] <matuszeman> it was my impression also.. but I'm pretty new to apigility so I wasn't sure.

createEntityFromMetadata not use entityExtractor result

Using Apigility, I configured a service with Reflection Hydrator (in my case a custom hydrator which transforms the array properties keys into camel-case).

The problem is that every elaboration on the object made by the hydrator are unused: I notice in ResourceFactory.php there is:

55)    $data = $this->entityExtractor->extract($object);
       [...]
72)    $halEntity = new Entity($object, $id);

where $object is the original object.

If I pass $data to the Entity constructor, as I expected, everything works.
Did I misunderstand the meaning of this lines?

`DefaultRenderingStrategy` throwing circular dependency exception on route render failure.

When we request a collection and we set an invalid (non existing) route for the child resources in our metadata_map the rendering fails and returns a ZF\Hal\Exception\CircularReferenceException.
It is hard to debug since the real cause of the issue lies in the fromLink method on line 698 where the rendering of the url from the $linkDefinition config fails.

The problem lies in DefaultRenderingStrategy at line 105 where it only catches errors in events of type MvcEvent::EVENT_RENDER_ERROR.

On failure Url helper returns an exception of type Zend\Mvc\Router\Exception\RuntimeException but this exception is not caught since $e->getName() equals MvcEvent::EVENT_RENDER ('render') and not MvcEvent::EVENT_RENDER_ERROR ('render.error').

After failure the Hal plugin tries to render the same Collection resource again and then the CircularReferenceException occurs. Somehow the MvcEvent::EVENT_RENDER_ERROR triggered on line 113 never results in output.

I don't know how to properly solve this. Maybe a discussion here could help me understand on how to deal with this issue...

Curies

I've been looking around and there doesn't seem to be much support for "curies" in this project.

At first I thought it would be OK to just create a new Link with rel "curies" and then the rest of the info, but it turns out that the reserved "curies" rel is meant to be an array / collection of elements instead of a single link.

Have I missed a way to specify curies? And if not: would you be interested (short-term) in a PR?

An Embedded Collection in an Entity Only Renders up to 10 Entities in Apigility

I have created a resource that when fetched GET /resource/{id} returns a response (on success/found) with the following template:

Resource:

{
    "name": "Name",
    "code" "Code",
    "_embedded" : {
        "foos" : [{
            "name" : "bar1",
            "value" : "value1",
       },
       {
            "name" : "bar2",
            "value" : "value2",
       }]
    }

}

Code for fetch($id)

fetch($id){

    $resource = ... // query resource

    $resourceEntity = new ResourceEntity();
    $resourceEntity->exchangeArray($resource);

    $foos = ... // query foos
    $fooAdapter = new Adapter\ArrayAdapter($foos);
    $fooCollection = new Foo\FooCollection($fooAdapter);

    $resourceEntity->foos = $fooCollection;

    return $resourceEntity;

}

My problem is that when the fooCollection consists of more than 10 entities only 10 entities gets rendered in response.

Things I tried so far:

  • I checked if this has to do with the default page size of the FooCollection in the module.config.php but the setting is equal to 25 in my case.
  • I tried to look at the Zend documentation to see if I can force the Paginator to be set to -1, since by doing so (I think) will not paginate the result.
  • I tried not used a Collection to just a plain array for the "foos" property. I showed all the results but it did not render each foo entities as a HAL entity

Add a post event trigger in the renderEntity method of the Hal plugin

The renderEntity method of the \ZF\Hal\Plugin\Hal plugin triggers an event on the first line of that method. It would be very nice if an event is also triggered add the bottom of that method, just before the entity is returned.

That way I can modifiy some data in the array before it's returned.

/**
     * Render an individual entity
     *
     * Creates a hash representation of the Entity. The entity is first
     * converted to an array, and its associated links are injected as the
     * "_links" member. If any members of the entity are themselves
     * Entity objects, they are extracted into an "_embedded" hash.
     *
     * @param  Entity $halEntity
     * @return array
     */
    public function renderEntity(Entity $halEntity, $renderEntity = true)
    {
        $this->getEventManager()->trigger(__FUNCTION__, $this, array('entity' => $halEntity));

        //...

        // Add something like this:
        $this->getEventManager()->trigger(__FUNCTION__ . '.post', $this, array('entity' => $entity));

        return $entity;
}

Would be nice to allow multiple identifiers for a route

I think this would be a useful feature but I'm also open to feedback on how I might be doing it wrong or should be doing something differently. I have a route which includes 3 identifiers. My route essentially looks like

/api/apiname/resource/:resourceId[/subtype1/:subtype1/subtype2/:subtype2]

I don't really want to make another resource which was just resource id and subtype 1, and I don't think putting in 2 optional parts in the route is a good way to go. It doesn't appear that I can set any more than a single string as the identifier. In the example above, the HAL links for the entity won't include both subtype1 and subtype2, but does contain up to :resourceId.

If route identifier were allowed to be an array, it seems that the above would be possible, but considering the number of places that this touches, I wanted to get some feedback on it before going forward with a change or PR.

To me, it seems this could be a good idea and I don't see a problem with being able to support it, but would appreciate feedback.

Page query param added to collection self link

When I request a collection route while using pagination I get the links rendered as follows:

    "_links": {
        "self": {
            "href": "http://hostname/api/v1/users?page=1"
        },
        "first": {
            "href": "http://hostname/api/v1/users"
        },
        "last": {
            "href": "http://hostname/api/v1/users?page=2"
        }
    },

The url I requested was http://hostname/api/v1/users. The self link in the response gets a pagination param while the first page doesn't. Shouldn't it be the other way around?
So like this:

    "_links": {
        "self": {
            "href": "http://hostname/api/v1/users"
        },
        "first": {
            "href": "http://hostname/api/v1/users?page=1"
        },
        "last": {
            "href": "http://hostname/api/v1/users?page=2"
        }
    },

On the client I would like to dynamically create links to my other pages by adding a page query parameter including a page number to the collection self link. Right now I cannot do this because the self link already holds a page param. I would expect the self link to be a base collection link.

Dynamic route params for collections using the metadata_map

When using the metadata_map to create further links, there is a problem with the route parameters.

Following config:

    'zf-hal' => [
        'metadata_map' => [
            'List\\List\\List' => [
                'route_name' => 'lists.rest.list',
                'route_identifier_name' => 'listId',
                'entity_identifier_name' => 'id',
                'links' => [
                    [
                        'rel' => 'reference-list',
                        'route'=> [
                            'name' => 'lists.rest.referencelist',
                        ],
                    ],
                ],
            ],
        ],
    ],
    'router' => [
        'routes' => [
            'lists.rest.list' => [
                'type' => 'segment',
                'options' => [
                    'route' =>  '/rest/list[/:listId]',
                ],
            ],
            'lists.rest.referencelist' => [
                'type' => 'segment',
                'options' => [
                    'route' =>  '/rest/list/:listId/reference[/:referenceId]',
                ],
            ],
        ],
    ],

If you query a single resource from lists.rest.list (/rest/list/1) the links will generated perfectly.
But if you query the collection from lists.rest.list (/rest/list) you will receive an error.

The reason is, that in this case the route param listId is unknown.
I thought, that the param should be set based on the mapping configuration (route_identifier_name and entity_identifier_name).

Is there an error in the configuration?
Or is there a bug in the mapping?

Rename `hydrators` key to `extractors`

Would it not be better to rename the hydrators key to extractors. In the whole zf-hal (or any other related) module the hydrators are never used for hydration but merely for extraction. So the name of this key is only confusing IMO.

I understand this can be a BC issue, but maybe it can be changed in version 2.0?

Api problem message as json

When I make request on non existing resource page /users?page=1234567 I receive:

{
  "type": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html",
  "title": "Conflict",
  "status": 409,
  "detail": "Invalid page provided"
}

As you see it a api problem. However in Content-Type you can find application/json instead of application/problem+json

Hal::setEntityExtractor dependency and EntityExtractor extractEntity method is marked private

I tried to add some custom logic to an EntityExtractor. First I thought of making a custom class implementing the Zend\Stdlib\Extractor\ExtractionInterface, but since the Hal::setEntityExtractor method is dependent on EntityExtractor I was unable to set my custom class. Would it not be enough to check for ExtractionInterface in the setter method?

Then when I moved to extending the existing ZF\Hal\Extractor\EntityExtractor class it turned out to be impossible to overwrite the extractEntity method since the method is marked private. Would it be possible to make it to a protected method so it becomes possible to redefine the method in my custom (extended) class.

createResourceFromMetadata uses entity getIdentifierName for route parameter

There is a weak correlation between

config[zf-rest][controllerName][identifier_name]

which refers to the identifier parameter name used for routes and

config[zf-hal][metadata_map][entityName][identifier_name]

which refers to the entity primary key.

When these values are not equal the parameters passed to marshalSelfLinkFromMetadata are invalid because the identifier name passed to the self linking function is the entity primary key and not the route parameter name.

Incorrect identifier name is fetched: https://github.com/zfcampus/zf-hal/blob/master/src/ZF/Hal/Plugin/Hal.php#L607


A good reason for these parameters to not be equal is when you use 'id' consistently for your resource primary keys and to avoid putting 'id' into scope when dispatching controller actions to resources from RPC calls where it would be "bad" to have a generic "id" in scope when dispatching calls to resource controllers because it directs them to return only a matching record. So, by making the route parameter different than the entity id you can avoid this.

Self Links are Forced

"Self" links are forced in \ZF\Hal\Plugin\Hal::createEntityFromMetadata(), around line 764 (toward the end of the function):

$entity   = new Entity($data, $id);
$links    = $entity->getLinks();
$this->marshalMetadataLinks($metadata, $links);
if (!$links->has('self')) {
    $link = $this->marshalSelfLinkFromMetadata($metadata, $object, $id, $metadata->getRouteIdentifierName());
    $links->add($link);
}

What's interesting is that links for collections are indeed not required in method createCollectionFromMetadata(), around line 867:

if (!$links->has('self')
        // this extra condition avoids the "self" link unless a url or route are specified
        && ($metadata->hasUrl() || $metadata->hasRoute())
    ) {
    $link = $this->marshalSelfLinkFromMetadata($metadata, $object);
    $links->add($link);
}

In the HAL specification "self" links are not "required". I have a use-case with Apigility where I want to have the HAL mapping configured for an entity (so that it gets extracted) but while providing NO "self" links (simply because there's no REST service for that entity).

Would it be ok if we make the condition in createEntityFromMetadata() similar to the condition in createCollectionFromMetadata()?

Query params are not included in Links

When query parameters are appended to endpoint URIs, they aren't included in the generated Links. For example:

Let's say we GET a collection at http://domain.com/resource. If the request contains a filter to the collection via a query parameter, e.g. http://domain.com/resource?foo=bar, the generated Links will not include the query parameters of the request, and will be of the format http://domain.com/resource?page=x.

I'm not sure if this is by design, but it causes issues with discoverability.

Differentiate embedded entities and not ones when rendering

Currently, the entity rendering makes no difference if it is embedded or not.
Could it be possible to allow to treat embedded entities differently ?

E.g: I have an entity User, and I want filter this one differently when it is embedded (to remove more properties and make it lighter).

The filtering is the responsibility of the hydrator, but it does not know when the entity is embedded. Whereas the Hal plugin knows it. So I think it should be passed from the Hal plugin to the hydrator. But I fear it leads to bad design. So maybe there is another way ?

5 minutes later:

A better design, could be to allow to provide a factory to the hydrator key under metadata_map. In this factory, the context would be injected, it would allow to create a custom hydrator.

Hal tries to extract entity when content negotiation is set to Json (then fails with exception)

Resolution as mentioned below:

we should likely make it possible to create the Hal\Entity without needing to serialize it first

Transcript from IRC:

<djule5> weierophinney: I want to return plain json representations, but it seems that Apigility is dependent on the Hal module.
<weierophinney> djule5: you can choose a different content negotiation selector for all API services, and while HalJson is the default for REST, you can also select just Json, which will return bare JSON. THAT SAID: we do not do any auto-extraction of objects to JSON for you; you will need to define that logic yourself, or ensure you return JsonSerializable objects.
<djule5> weierophinney: Yes, that's basically what I've been doing. I also tried deleting the zf-hal config array, and it seemed to work. However, going back to apigility-admin and saving a service, it creates back the zf-hal array with the default values for the service and then calling the service returns me a "Zend\Stdlib\Hydrator\ArraySerializable::extract expects the provided object to implement getArrayCopy()". Do I need to have zf-hal config if none of my services return HalJson?
<weierophinney> djule5: the admin API creates that information, even if you're not using it.
<weierophinney> djule5: you _could_ potentially use that information yourself to do the object extraction, too.
<djule5> weierophinney: Following our discussion yesterday, would you recommend not using the admin interface when not using HalJson (if I don't want the zf-hal config)? Or could zf-apigility-admin take in account the chosen Content Negotiation Selector (i.e. Json) and not regenerate the zf-hal/metadata_map accordingly?
<weierophinney> djule5: the admin will continue generating the zf-hal information, even if ultimately it won't be used. I'd use the admin interface, personally; lot less to remember and worry about in the configuration.
<djule5> weierophinney: the problem is that it's actually used, the Hal plugin gets called and tries to extract the entity (in ZF\Hal\Plugin\Hal::convertEntityToArray) using the hydrator set by the admin interface (i.e. ArraySerializable) which I do not implement the required methods on my Entity. I would expect my Entity left untouched by Hal and forwarded to ZF\ContentNegotiation\JsonModel to be serialized, or maybe I'm missing something?
<weierophinney> djule5: run a `composer update` in your project. :) We actually fixed that behavior in zf-hal for this latest release, so that it retains the original entity in the ZF\Hal\Entity object now.
<weierophinney> djule5: and you're right -- we _are_ casting to a ZF\Hal\Entity/ZF\Hal\Collection.
<weierophinney> djule5: however, we have some logic in ZF\ContentNegotiation\JsonModel to pull the original entity/collection out of the HAL objects.
<weierophinney> djule5: so with the upgrade, it should now work.
<djule5> weierophinney: interesting... I actually ran the update yesterday, and I'm testing the behavior right now and it's still doing it (500 with "Zend\Stdlib\Hydrator\ArraySerializable::extract expects the provided object to implement getArrayCopy()")
<weierophinney> djule5: ah -- that makes sense. The logic when creating the HAL entity does use the hydrator to cast to array in order to be able to extract the identifier (as a Hal\Entity object requires both the original entity AND an identifier)
<weierophinney> djule5: can you file an issue for that, please? I'll see if I can find a way to create a flag for disabling that.
<weierophinney> djule5: what it does is serialize, extract the identifier, but then injects the original object in to the Hal\Entity. (We cache the seriailized version for when serialization happens later)
<weierophinney> (if it ever happens)
<djule5> weierophinney: yes ok I see what you mean... I do think however that I was getting the same behavior before that fix you're referring to (I'm looking at the commit diff on github)
<djule5> weierophinney: Hal is just responsing accordingly to the metadata map config... hence why I was thinking probably it should just not be there in the first place
<weierophinney> djule5: the problem is that we do the Hal\{Entity|Collection} creation in zf-hal. We don't want to do a ton of conditional logic in there based on the selected view model -- that's the whole point of having zf-content-negotiation in the first place. That said, we should likely make it possible to create the Hal\Entity without needing to serialize it first. File the issue, and I'll work on a fix; I have a few ideas.

Add pagination metadata to collection resources

It may be useful to add "total" and "per_page" properties to paginated collections, like the following:

{
    "_links": { /* ... */ },
    "_embedded": { /* ... */ },
    "total": 137,
    "per_page": 25
}

The primary question I have is: should this behavior be toggle-able, or just included by default?

Also, for non-paginated collections, we may want to provide a "total"; again, the question is whether or not this should be toggle-able.

Hinting of embedded resources

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.

circular dependancy exception

98209f2

This commit has created a circular dependancy, the viewHelperFactory depends on the controllerPluginFactory and vice-versa.

Resulting in a Zend\ServiceManager\Exception\CircularDependencyFoundException :

Circular dependency for LazyServiceLoader was found for instance Hal

Shouldn't the Plugin/Hal.php object itself be managed via the ServiceManager and returned from the view and controller plugin factories ?

422 status when my entity has no identifier on creation

When a user does a POST request to my service I put the payload on a queue and handle the entity persistance later on.
In this case I don't have an identifier for the entity available on creation and the RestController will throw an 422 error No entity identifier present following entity creation. In the commit history I see something has changed to support null identifiers [https://github.com/zfcampus/zf-hal/commit/55a7abfb36248a325341d3cb501118781a1ba484]. Imo the ApiProblem in RestController [https://github.com/zfcampus/zf-rest/blob/master/src/RestController.php#L326] should also be removed to handle this case correctly.

Allow -1 page size in Collection

-1 would mean that we want all items in first page.
Obviously, whoever sets -1 as page size, will know what it implies.

The use case scenario is the implementation of static/small lists using REST.
GET /roles

Sometimes it doesn't make sense paginating collections under 50 items for example, if they have a really small payload, and we had to show all of the items in 1 page anyway. At the moment I'm just setting a high enough default value, but it looks hacky.

No support for special characters - returns empty response

Not sure if this is Hal related, Rest related, Paginator\Adapter\DbSelect related or something else, but, if you have special characters in fields (i.e., foreign languages) in the database and attempt to return those values through a REST resource's fetch() or fetchAll(), you will get a completely empty response. Headers indicate status code 200, but zero content. If I remove the rows with special characters, or request a record without special characters, the API works as expected. This occurs wether returning a Collection or an Entity. The SQL data can be downloaded here: http://spectravp.com/locale_countries.sql

Of note: The DB charset is all UTF-8; the DB Adapter is configured for UTF-8. However, if I specifically encode the data in question in the entity with mb_convert_encoding(), the API response is normal and as expected.

first and last pagination links injected multiple times

Hi folks,

I wrote a caching plugin and likely ran into an edge case, which under normal circumstances might not get triggered.
What it does: save the full ViewModel (HalJsonModel) onResponse (listener on MvcEvent::EVENT_FINISH) and inject it (if available in the cache) via onDispatch (listener on MvcEvent::EVENT_DISPATCH) and stopPropagation.

For some reason, the collection links will be reinjected after I set the cached Model into the MvcEvent/Reponse.

Normal HAL result:

{
  "_links": {
    "self": {
      "href": "https://myapi.dev/books?page=1"
    },
    "first": {
      "href": "https://myapi.dev/books"
    },
    "last": {
      "href": "https://myapi.dev/books?page=1"
    }
  },
  "_embedded": {
    "books": [
      {

With my caching plugin:

{
  "_links": {
    "self": {
      "href": "https://myapi.dev/books?page=1"
    },
    "first": [
      {
        "href": "https://myapi.dev/books"
      },
      {
        "href": "https://myapi.dev/books"
      }
    ],
    "last": [
      {
        "href": "https://myapi.dev/books?page=1"
      },
      {
        "href": "https://myapi.dev/books?page=1"
      }
    ],
  },
  "_embedded": {
    "books": [
      {

From my reaserch this is triggered here:
https://github.com/zfcampus/zf-hal/blob/master/src/Plugin/Hal.php#L554

Or further down the road:
https://github.com/zfcampus/zf-hal/blob/master/src/Link/PaginationInjector.php#L73
https://github.com/zfcampus/zf-hal/blob/master/src/Link/PaginationInjector.php#L80

Now my question:
Why do the "first" and "last" links don't use the "overwrite" flag, like "self" already utilizes?
Beside the fact that there can only be one "first" page ;-) it feels wrong, that a JSON object is suddenly turning into an array.

Any ideas how to prevent that behaviour or could that be fixed in zf-hal directly?

Regards
Kevin

Self links not generated when returning \ZF\Hal\Entity as payload in RPC endpoint

When I instantiate a \ZF\Hal\Entity with the entity and entityId, and return the Entity as the payload, the _links property is empty.

However if I create the entity using the ZF\Hal\Plugin\Hal::createEntity and explicitly pass in the route then the links are properly generated.

From my understanding thought this step should not be necessary? This same issue was raised in the google group.

I can't see any misconfiguration in my metadata_map or route info.

Support for read-only resources that are loaded from cache

We have quite some resources in our application that are read-only. Those resources will render the same for each request which means once the hal/json response is rendered it could simply be stored in a cache and on the next request we skip a lot of overhead by returning the cached hal/json.

I could imagine a read-only field in the controller config which defaults to false and a cache service in the Hal plugin to support such functionality.

Is there any interest in a pull request for something like this for read-only resources.

EventManagerAwareInterface problem with zf3

Related to: zendframework/zend-mvc#205

It's a common practice to attach listeners to event managers in a Delegator Factory.

With zend-mvc ^3.0 and zend-eventmanager ^3.0 it's a potentially BC break.
Can we consider to replace the EventManagerAwareInterface with EventsCapableAwareInterface in the project?

In this particular case, in my project I have a Listener attached to ZF\Hal\Plugin\Hal event manager that injects links into the rendered entity.

Links from metadata are not included in rendered resource _links

I created metadata for an entity and added additional links in this metadata. In the rendered result these links are not included. I think the cause lies in https://github.com/zfcampus/zf-hal/blob/master/src/Plugin/Hal.php#L758

The $halEntity is properly created through the createEntityFromMetadata method (including my links and all), but in the next line it is directly overwritten by $halEntity = new Entity($entity, $halEntity->id);. I don't understand why this line of code is there. Should it not simply be deleted? It totally overwrites the previously created result of createEntityFromMetadata and makes the whole first case redundant.

from the last merge

exception 'Zend\ServiceManager\Exception\ServiceNotCreatedException' with message 'While attempting to create hal(alias: Hal) an invalid factory was registered for this instance type.'

The 'page_size' parameter should allow 0 values

Hi, we use PhlyRestfully and now Apigility since several years ;-).

We often need to call a Web Service to count a number of entities. The HAL standard and zf-hal allow to get a count very easily using the total_items property of the fetched HAL collection.

For example...

GET https://myserver.com/comments?page=1&page_size=1
{
  "_links": { ... },
  "_embedded": {
    "comments": [ ... ]
  },
  "page_count": 45,
  "page_size": 1,
  "total_items": 45,
  "page": 1
}

Then on the client side its very simple (sample in JS / jQuery here).

$.get(url, function(payload) { console.log(payload.total_items); });

It works, but I think this is "sub-optimal" because the library forbid page_size values equal to 0 (see

if ($size < 1 && $size !== -1) {
).

In our case its has the following consequences

  • This forces an additional request in our database to always fetch at least one entity ;
  • It increases the size of the returned payload.

So, why the page_size parameter cannot be equal to 0 ? If their are good reasons what the best practices to count entities efficiently with REST / HAL ? If their are no good reasons could it be possible to authorize page_size 0 values ?

Thanks

Is it possible to hint at the HTTP method with this library?

I'm not sure if I've managed this before or not, thought I had but can't seem to find the configuration... Is it possible to return a HTTP method inside a link? I want to achieve something similar to Paypal's HATEOAS implementation, where the HTTP method is returned along with a link.

My metadata map currently looks like this:

    'Users\\V1\\Rest\\User\\UserEntity' => array(
        'entity_identifier_name' => 'id',
        'route_name' => 'users.rest.user',
        'route_identifier_name' => 'user_id',
        'hydrator' => 'Zend\\Hydrator\\ClassMethods',
        'links' => array(
            'role' => array(
                'rel' => 'create-role',
                'route' => array(
                    'name'    => 'users.rest.roles',
                    'options' => array(
                        'method' => 'GET'
                    ),
                ),
            )
        ),
    ),

Though it is currently unsuccessful, I've tried 'params' as well but to no avail.

(Apigility) zf-hal metadata_map.{CollectionClass}.entity_identifier_name does not properly render the Entity Identifier of embedded entities in a Collection response.

I am trying to change the Entity Identifier of a Resource in the Admin UI to something different (ie. a name property instead of the default id), so that I can modify the identifier in the __self link of Entities. I Change the Entity Identifier in the Admin UI from id to name, when I request for the Entity every thing looks fine, but when I tried to request for a Collection the embedded Entities are rendered with the their __self still using the default identifier.

I have already verified that the entity_identifier_name key in the zf-hal.metadata_map has been updated.

VehicleEntity.php:

namespace App\V1\Rest\Vehicle;

class VehicleEntity
{
    public $id;
    public $name;
    public $group_id;
    public $truck_type;
    public $tons;
    public $cbm;

    public function getArrayCopy() {
        return array(
            'id' => $this->id,
            'name' => $this->name,
            'group_id' => $this->group_id,
            'truck_type' => $this->truck_type,
            'tons' => $this->tons,
            'cbm' => $this->cbm,
        );
    }

    public function exchangeArray(array $array) {
        $this->id = $array['id'];
        $this->name = $array['name'];
        $this->group_id = $array['group_id'];
        $this->truck_type = $array['truck_type'];
        $this->tons = $array['tons'];
        $this->cbm = $array['cbm'];
    }
}

module.config.php:

'zf-hal' => array(
        'renderer' => array(
            'render_embedded_entities' => false,
        ),
        'metadata_map' => array(
            'App\\V1\\Rest\\Vehicle\\VehicleEntity' => array(
                'entity_identifier_name' => 'name',
                'route_name' => 'kookaburra.rest.vehicle',
                'route_identifier_name' => 'truck_id',
                'hydrator' => 'Zend\\Stdlib\\Hydrator\\ObjectProperty',
            ),
            'App\\V1\\Rest\\Vehicle\\VehicleCollection' => array(
                'entity_identifier_name' => 'name',
                'route_name' => 'kookaburra.rest.vehicle',
                'route_identifier_name' => 'name',
                'is_collection' => true,
            ),
        ),
    ),

VehicleMapper.php: (Usage).

Here's how I supply data to the collection:

public function fetchAll($params = array())
{
    $select = new Sql\Select('vehicle');
    $select->columns(array('name', 'group_id', 'truck_type', 'tons', 'cbm' ));
    $paginatorAdapter = new DbSelect($select, $this->adapter);

    $collection = new VehicleCollection($paginatorAdapter);

    return $collection;
}

In Admin UI:

Route matches: /vehicle[/:truck_id]
Route identifier name: truck_id
Entity identifier name: name

Is this an expected behavior or am I just missing something?

Using:

  • zfcampus/zf-apigility: ~1.1
  • PHP 5.4
  • MS SQL Server 2012

ZF\Hal\Metadata\Metadata::getHydrator returns ExtractionInterface|null instead of HydratorInterface|null

Hey there,
currently using the ZF\Hal\Metadata\Metadata to grab the hydrator defined in module config related to a specific entity.
As I wanted to inject the hydrator to my Zend\Stdlib\Hydrator\HydratorAwareInterface which requires the Zend\Stdlib\Hydrator\HydratorInterface, I get some IDE warnings which is quite annoying.

Since the Zend\Stdlib\Hydrator\HydratorInterface implements the Zend\Stdlib\Extractor\ExtractionInterface, there should be no problem to switch to the correct Interface.

If there is any problem, you should rename the method as of #128 since this naming is quite confusing.

Allow hydrators to set `_embedded`

I have an odd case where the entity (hal resource) has a property name that is also (sometimes) the name of a link and embedded object. Basically a message can be sent by a user (which a link and embedded object exist for), or a non-user where some basic information had been gathered to identify them (but no link or embedded resource because they're not an user).

Quick example:

{
    "sender": {
        "name": "Test User",
        "number": "15556667878"
    },
    "_links": {
        "sender": {
            "href": "http://example.com/users/12345"
        }
    },
    "_embedded": {
        "sender": {
            "id": "12345",
            "name": "Test User",
            "number": "15556667878"
        }
    }
}

Because the hydrator can send back a link collection, which is merged into the entities links during render, it's possible to have both a link and an entity property with the same name. However, it's not possible to have both a entity property and an embedded entity with the same name, since both are passed from the hydrator using a simple array.

My thought is to add support for hydrators to return an _embedded key (optionally), and anything found there will be treated as embedded entities.

If I put this together as a PR, any opposition to merging it in?

HalJsonModel should not allow marking as non-terminal

A number of people on the mailing list and on IRC have noted that when using the ContentNegotiation\ViewModel with controllers that will be returning HAL, they must set the terminate flag to true to prevent attempts to render within a layout.

This is due to the fact that while the terminate flag is true by default in ZF\Hal\View\HalJsonModel, it's not true by default in ZF\ContentNegotiation\ViewModel (and should not be!) -- which means that the value of the flag in the original view model is used when casting to HalJsonModel.

$entity created using Metadata is converted to array (extracted) twice. Why is this?

If in the createEntity method the $entity is available in the MetadataMap it will be changed into a hal Entity in the first case of the switch using the createEntityFromMetadata method:

https://github.com/zfcampus/zf-hal/blob/master/src/Plugin/Hal.php#L765

During this process the data is extracted from the entity by using the convertEntityToArray method:

https://github.com/zfcampus/zf-hal/blob/master/src/Plugin/Hal.php#L708

The result is stored in a $generatedEntity variable. Instead of using this generated entity directly another instance of hal Entity is created like this:

$halEntity = new Entity($entity, $generatedEntity->id);

Since in the first param $entity is an object the convertEntityToArray will be called once more when later rendering the entity in the renderEntity method. So we will be hydrating the same entity twice:

https://github.com/zfcampus/zf-hal/blob/master/src/Plugin/Hal.php#L500

Why is this? Why not stick with the $generatedEntity that holds the array?

Different output from onRenderEntity and onRenderCollection when adding additional Entities in Events

As stated by Matus Zeman @matuszeman here https://groups.google.com/a/zend.com/forum/#!topic/apigility-users/zck43pReklw there seems to be a bug in the Hal helper when rendering single entities.

Quoted from the google group:
"I believe there is a bug in Hal helper (https://github.com/zfcampus/zf-hal/blob/master/src/Plugin/Hal.php#L1140) where convertEntityToArray() method caches converted entities so when you set new entities or links into entity using 'renderEntity' event convertEntityToArray() returns original entity anyway ignoring everything - https://github.com/zfcampus/zf-hal/blob/master/src/Plugin/Hal.php#L495"

Code for single Entity (http://192.168.33.101/events/2182)

    public function onRenderEntity($e)
    {
        $halEntity = $e->getParam('entity');
        $version = $this->isEntityOfInterest($halEntity->entity, 'Events', 1);

        if (false !== $version) {
            if($halEntity->entity instanceof EventsEntity) {
                $repository = $this->sm->get('Umid\V1\Rest\Unternehmen\UnternehmenResource');
                $item = $repository->fetch($halEntity->entity['events_location_Id']);
                $halEntity->entity['unternehmen'] = $item;
            }
            $this->injectEventsEntityLinks($halEntity, $version);
            return;
        }
    }

Dump of $halEntity

object(ZF\Hal\Entity)[637]
  protected 'id' => string '2182' (length=4)
  protected 'links' => 
    object(ZF\Hal\Link\LinkCollection)[635]
      protected 'links' => 
        array (size=1)
          'self' => 
            object(ZF\Hal\Link\Link)[636]
              protected 'props' => 
                array (size=0)
                  empty
              protected 'relation' => string 'self' (length=4)
              protected 'route' => string 'umid.rest.events' (length=16)
              protected 'routeOptions' => 
                array (size=0)
                  empty
              protected 'routeParams' => 
                array (size=1)
                  'events_id' => string '2182' (length=4)
              protected 'url' => null
  protected 'entity' => 
    object(Umid\V1\Rest\Events\EventsEntity)[502]
      public 'Id' => string '2182' (length=4)
      ...
      public 'unternehmen' => 
        object(Umid\V1\Rest\Unternehmen\UnternehmenEntity)[696]
          public 'Id' => string '12608' (length=5)
          public 'land_Id' => string '1' (length=1)
          public 'bundesland_Id' => string '0' (length=1)
          ...

Output - embedded entity is missing

{
    Id: "2182"
    events_location_Id: "12608"
    location_Id: null
    unternehmen_Id: "12608"
    sehenswuerdigkeit_Id: "0"
    veranstalter_Id: "12608"
    ...
    -_links: {
        -self: {
            href: "http:\/\/192.168.33.101\/events\/2182"
        }
    }
}

Code for Collection (http://192.168.33.101/events)

    public function onRenderCollectionEntity($e)
    {
        $entity = $e->getParam('entity');
        $version = $this->isEntityOfInterest($entity, 'Events', 1);

        if (false !== $version) {
            $halEntity = new HalEntity($entity, $entity['Id']);
            if($halEntity instanceof EventsEntity) {
                $repository = $this->sm->get('Umid\V1\Rest\Unternehmen\UnternehmenResource');
                $item = $repository->fetch($halEntity['events_location_Id']);
                $halEntity['unternehmen'] = $item;
            }
            $this->injectEventsEntityLinks($halEntity, $version);
            $e->setParam('entity', $halEntity);
            return;
        }
    }

Dump of $halEntity

object(ZF\Hal\Entity)[685]
  protected 'id' => string '2182' (length=4)
  protected 'links' => 
    object(ZF\Hal\Link\LinkCollection)[687]
      protected 'links' => 
        array (size=1)
          'self' => 
            object(ZF\Hal\Link\Link)[690]
              protected 'props' => 
                array (size=0)
                  empty
              protected 'relation' => string 'self' (length=4)
              protected 'route' => string 'umid.rest.events' (length=16)
              protected 'routeOptions' => 
                array (size=0)
                  empty
              protected 'routeParams' => 
                array (size=1)
                  'events_id' => string '2182' (length=4)
              protected 'url' => null
  protected 'entity' => 
    object(Umid\V1\Rest\Events\EventsEntity)[667]
      public 'Id' => string '2182' (length=4)
      ...
      public 'unternehmen' => 
        object(Umid\V1\Rest\Unternehmen\UnternehmenEntity)[716]
          public 'Id' => string '12608' (length=5)
          public 'land_Id' => string '1' (length=1)
          public 'bundesland_Id' => string '0' (length=1)
          ...

Output - with embedded entity

{
    Id: "2182"
    events_location_Id: "12608"
    location_Id: null
    unternehmen_Id: "12608"
    sehenswuerdigkeit_Id: "0"
    veranstalter_Id: "12608"
    ...
    -_embedded: {
        -unternehmen: {
            Id: "12608"
            land_Id: "1"
            bundesland_Id: "0"
            landkreis_Id: "0"
            region_Id: "0"
            ...
            -_links: {
                -self: {
                    href: "http:\/\/192.168.33.101\/unternehmen\/12608"
                }
            }
        }
    }
    -_links: {
        -self: {
            href: "http:\/\/192.168.33.101\/events\/2182"
        }
    }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.