willdurand / bazingahateoasbundle Goto Github PK
View Code? Open in Web Editor NEWIntegration of the Hateoas library into Symfony.
License: MIT License
Integration of the Hateoas library into Symfony.
License: MIT License
I see that one can now use JMSSerializer 2.x. Do you plan on adding more changes for the next version or can you tag it now?
The links are lost when the serialization groups are used :
/**
* @FOSRest\View(serializerGroups={"default"})
*/
public function cgetAction(){
/** @var Product[] $products */
$products = $this->get("api_product.product_service")->getAll();
return $products;
}
The links are lost due to the ExclusionManager which skip these links, the workaround which works in my projetc is to not skip these relations when exclusion is nullable
// \Hateoas\Serializer\ExclusionManager.php
private function shouldSkip($object, Exclusion $exclusion = null, SerializationContext $context)
{
// Do not skip links when exclusion is null
if (is_null($exclusion)) {
return false;
}
if (null !== $exclusion
&& null !== $exclusion->getExcludeIf()
&& $this->expressionEvaluator->evaluate($exclusion->getExcludeIf(), $object)
) {
return true;
}
if (!$context->getExclusionStrategy()) {
return false;
}
$propertyMetadata = new RelationPropertyMetadata($exclusion);
return $context->getExclusionStrategy()->shouldSkipProperty($propertyMetadata, $context);
}
But I do not have enough global understanding to know if it's a good or a bad workaround. Do you have any idea how this problem can be resolved properly ?
An issue I run into from time to time is that some links should only be visible when an user has certain permissions. For example, let's say I created a blog API and only admins should be able to delete comments. Something like this could be shown in the _links
object like so:
{
"_links": {
"delete": {
"href": "http://example.org/api/posts/14"
}
}
}
If I only want this route to be visible for users that have the ROLE_ADMIN
role, there's no easy way to do this at the moment besides creating a relation provider that only acts as a sort of proxy for the SecurityContextInterface
.
I propose adding something like an granted
attribute to the @RelationProvider
annotation:
/**
* @Hateoas\Relation(
* "delete",
* href = @Hateoas\Route(
* "post_delete",
* parameters = { "id" = "expr(object.getId())" }
* ),
* granted={"ROLE_ADMIN"}
* )
*/
class Post
{
// properties
}
Is this something that might be worth looking into?
PS: Clearly, this can be labeled as an enhancement.
Is possible to specify inside the config file that hateoas use xml for every entity instead of annotation?
Hi @willdurand ,
Is it possible to use symfony routes as the href
parameter for the relations, instead of using hardcoded URL, as this maks much more sense when using symfony?
Thanks! ๐
I believe that we can release current master as 1.4.0 (with 19 commits).
At present I, as I assume a lot of people have different permission for those that can view compared to those that can view, update, delete. I prefer the idea of stating what is possible early on rather than bouncing people back. Would it be possible to to include an 'is_granted' permission in the exclude if like
/**
"manager",
href = "expr('/api/users/' ~ object.getManager().getId())",
embedded = "expr(object.getManager())",
exclusion = @Hateoas\Exclusion(excludeIf = "!security_context.is_granted([ROLE_SOMETHING])")
JMS Serialiser provides the ability to Override Groups of Deeper Branches.
It seems this is not working when using BazingaHateoasBundle, any ideas?
Some people seem to have a similar issue.
In the master master/composer.json there is support for symfony 6.
But in the last Release (2.3.0 Latest) from 14 Oct 2021 the composer.json is still fixed on symfony 5.
The same Problem is on https://github.com/willdurand/Hateoas/blob/63e255bb762fd6fa79d973d99ac669daab56c638/composer.json (Latest, Tag: 3.7.0) where the dependency is
"symfony/expression-language": "~3.0 || ~4.0 || ~5.0"
and in the master branch the version is
"symfony/expression-language": "~3.0 || ~4.0 || ~5.0 || ~6.0"
Is there a plan when the new versions should be tagged so that composer can pull them with symfony 6 support?
The Problem with the pipeline is imho that the pulled release from willdurand/Hateoas
has not the same symfony/expression-language
needs with willdurand/BazingaHateoasBundle
in the master branch
Thank you very much!
Having the ability to use a different serialization context than JMS Serializer would be very beneficial. The use case is when you have an object graph that has the same object represented two different ways. Here is an example, and pardon the length but I feel at least one example would make discussion easier.
You have two objects: Post and User. A Post has a property called "author" which points to a User. Here is some pseudo-ish code for these two classes:
class Post {
/**
* @Type("User")
* @Groups({"posts"})
*/
var $author;
/**
* @Type("string")
* @Groups({"posts"})
*/
var $title;
/**
* @Type("string")
* @Groups({"posts"})
*/
var $content;
}
class User {
/**
* @Type("string")
* @Groups({"users", "posts", "embedded"})
*/
var $id;
/**
* @Type("string")
* @Groups({"users", "embedded"})
*/
var $name;
/**
* @Type("array")
* @Groups({"users", "embedded"})
*/
var $preferences;
}
You have two RESTful routes on your API for this content:
/api/posts/{id}
/api/users/{id}
Say you have the following two objects in your database:
Post:
{
"id": 1,
"author": {
"$ref" : "User",
"$id" : "1",
"$db" : "blog"
},
"title": "An Example Post",
"content": "<p>This is a great post.</p>"
}
User:
{
"id": 1,
"name": "John Doe",
"preferences": {
"lang": "en-US"
}
},
{
"id": 2,
"name": "Jane Doe",
"preferences": {
"lang": "en-US"
}
}
You make a GET to /api/posts/1 and get:
{
"id": 1,
"author": {
"id": 1
},
"title": "An Example Post",
"content": "<p>This is a great post.</p>",
"_embedded": {
"author": {
"id": 1,
"name": "John Doe",
"preferences": {
"lang": "en-US"
}
}
}
}
And, you want to update that Post to be User 2 by sending PUT /api/posts/1:
{
"id": 1,
"author": {
"id": 2
},
"title": "An Example Post",
"content": "<p>This is a great post.</p>"
}
This would work flawlessly if you could pass a SerializationContext to Hateoas Bundle that uses the "embedded" serialization group and separate one to JMS Serializer Bundle that uses the "posts" serialization group. The reason this configuration would be nice is that JMS will silently discard any modified data, but seamlessly update which User the Post references on $author.
As it stands, since Hateoas uses the context from JMS the serialization groups you send apply to all objects in the graph. So, you either exclude the author from the embedded data and force the API user to make a separate request to get the user or use a separate route to handle updating the references, such as: /api/posts/{id}/author.
Could you tag v.1.1.2
to publish the extension driver PR?
Question:
In the library's documentation, there is a point Configuring Metadata Locations.
However, I don't understand how to apply that with the bundle: nowhere is mentioned how we have to use the HateoasBuilder in the bundle.
I think by code inspection that, it could be managed with the Metadata/Driver/DriverChain of jms/serializer but I don't understand how.
For example, I want to do the same thing with HateoasBundle than I do here with JMSSerializerBundle:
jms_serializer:
metadata:
directories:
DoctrinePHPCR:
namespace_prefix: "Doctrine\\ODM\\PHPCR"
path: "@AppBundle/Resources/config/serializer/DoctrinePHPCR"
Bundle provides (undocumented) twig extension. Loading it slow downs TwigService initialization in container (as it initialize heavy guzzle client). We should have option to decide if it should be loaded in application (if we want to use it).
Proposed solution:
Tell me what do You think about this.
Given a custom url generator defined as a service :
parameters:
my.url_generator.class:
Acme\Foo\MyUrlGenerator
services:
my.url_generator:
class: %my.url_generator.class%
tags:
-
name: hateoas.url_generator
alias: my_url_generator
When I compile the container
Then the UrlGeneratorPass fails because it tries to construct a ReflectionClass from the string %my.url_generator.class%
.
Over there : https://github.com/willdurand/BazingaHateoasBundle/blob/master/DependencyInjection/Compiler/UrlGeneratorPass.php#L54
I understand that this is used to avoid vendor lock-in and to trim the Symfony UrlGeneratorInterface
from superfluous methods. But the check seams plain wrong in this specific case.
Let it read: This bundle integrates Hateoas into Symfony instead of Symfony2 as the project is not only compatible with Symfony2 but all upwards to the current version, Symfony4.
Making it Symfony may encourage potential patronage and usage.
I have an entity with a $filepath
property,
And I have a getWebPath()
method which gives the relative web path (currently, uploads/some/sub/folders/the-file-name.extension
)
But instead of serializing this relative web path :
{
"id": 1,
"filepath": "uploads/some/sub/folders/the-file-name.extension"
}
I would prefer to serialize the absolute web one :
{
"id": 1,
"filepath": "http://example.com/uploads/some/sub/folders/the-file-name.extension"
}
So I was just wondering, would it be a good practice to register a new method (asset()
for instance) inside expr() ?
/**
* @Hateoas\Relation(
* "filepath",
* href = "expr(asset(object.getWebPath(), true))"
* )
*/
Then how to register it in expr() ?
Hi
I'm trying to check PHP7 compatibility of the bundles we use in a big project
I saw that travis is failing for php7/symfony2.8 but the failure travis gets is different from the one I'm getting:
In this gist there's the complete test result
https://gist.github.com/marcoalbarelli/f10b6407ce3c230b8b9feb842e5308b9
It seems like the main issue here is how you try to inject mocks, but I didn't have the time to check the actual code.
This is the relevant part of the composer.lock
file
"name": "willdurand/hateoas-bundle",
"version": "1.1.1",
"target-dir": "Bazinga/Bundle/HateoasBundle",
"source": {
"type": "git",
"url": "https://github.com/willdurand/BazingaHateoasBundle.git",
"reference": "a53f6f1d3d8cda3fa8cdd90773cb48e9647a08c5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/willdurand/BazingaHateoasBundle/zipball/a53f6f1d3d8cda3fa8cdd90773cb48e9647a08c5",
"reference": "a53f6f1d3d8cda3fa8cdd90773cb48e9647a08c5",
"shasum": ""
},
"require": {
"jms/serializer-bundle": "~1.0",
"symfony/framework-bundle": "~2.2 || ~3.0",
"willdurand/hateoas": "~2.9"
},
"require-dev": {
"phpunit/phpunit": "~4.5",
"symfony/expression-language": "~2.4 || ~3.0",
"twig/twig": "~1.12"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-master": "1.2-dev"
}
},
"autoload": {
"psr-0": {
"Bazinga\\Bundle\\HateoasBundle": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "William Durand",
"email": "[email protected]"
}
],
"description": "Integration of Hateoas into Symfony2.",
"keywords": [
"HATEOAS",
"rest"
],
"time": "2016-02-22 13:12:41"
},
WDYT of being able to configure EL variables, that would be a bit like twig global variables:
bazinga_hateoas:
expression_language:
context_variables:
base_rel: https://rels.example.com
foo: bar
To then be able to:
/**
* @Hateoas\Relation("expr(base_rel ~ '/users')", ...)
*/
Hi,
Instead of using annotations i'd like to use as a configuration file. I've spotted the configuration here https://github.com/willdurand/Hateoas#yaml
Also i've placed a file called hateoas.yml in my bundles config directory but it seems that the bundle isn't autoloading it.
Would you please give me a hint. thanks
Hello,
I'm using this bundle for creating an api rest, and also the DIExtraBundle for service annotations.
The issue is that I must declare the service in xml for having a result from the SymfonyContainerResolver class in order of finding my custom relation provider.
I have tested with yml and it doesn't work neither. I've looked into code and I found that the SymfonyContainerResolver class uses the definition in xml for finding the service.
/**
* {@inheritdoc}
*/
public function getRelationProvider(RelationProviderConfiguration $configuration, $object)
{
if (!preg_match('/^(?P<service>[a-z0-9_.]+):(?P<method>[a-z0-9_]+)$/i', $configuration->getName(), $matches)) {
return null;
}
return array(
$this->container->get($matches['service']),
$matches['method']
);
}
is it possible of searching the service directly in the service container and not with a regular expression evaluating the xml declaration? That would be great considering the people who use DIExtraBundle or KnpRadBundle for extending annotations.
Thank you
Hello William,
The document of Hateoas states:
Important: This library is under heavy refactoring. If you are using it, you will find the code in the 1.0 branch. The master branch is a work in progress, and should not be used in production at the moment.
However if I'm not mistaken this Bundle's stable version uses latest of Hateoas?!
So if that is correct, then this Bundle should currently not be used for production stuff right?
I had this error
{ "code": 500, "message": "The token storage contains no authentication token. One possible reason may be that there is no firewall configured for this URL." }
after using
exclusion = @Hateoas\Exclusion( excludeIf = "expr(is_granted(['ROLE_ADMIN']))" )
Can anyone help me please
Hello,
When setting $limitParameterName
to another value :
public function getMediasAction(ParamFetcher $paramFetcher)
{
$queryBuilder = $this->getMediaHandler()->all();
$pagerAdapter = new DoctrineODMMongoDBAdapter($queryBuilder);
$pager = new Pagerfanta($pagerAdapter);
$pager->setCurrentPage($paramFetcher->get('page'));
$pager->setMaxPerPage($paramFetcher->get('hits_per_page'));
$pagerFactory = new PagerfantaFactory('page', 'hits_per_page');
return $pagerFactory->createRepresentation(
$pager,
new Route('dam_get_medias')
);
}
Limit property name doesn't change in output :
{
"page": 1,
"limit": 2,
"pages": 214,
"total": 428,
"_links": {
"self": {
"href": "/app_dev.php/api/medias?page=1&hits_per_page=2"
},
},
}
Is this a bug or correct behaviour ?
Best regards
It'd be nice to implement the Relation Classes according to PSR-13. (The PSR ist still in draft, but it could be a good project to test if the draft is complete.)
New major symfony version was released so it'd be great for this bundle to also support it!
It would be really useful to have a cache warmer registered automatically. We've done this manually by adding the following to our services.yaml
:
hateoas.configuration.metadata.cache.cache_warmer:
class: JMS\SerializerBundle\Cache\CacheWarmer
arguments:
$includePaths: ['%kernel.project_dir%/src/Entity']
$metadataFactory: '@hateoas.configuration.metadata_factory'
tags: [kernel.cache_warmer]
This bundle should provide an instance of Hateoas serializer as a service.
It isn't as simple as it seems: a serializer service could have been defined like:
<service id="hateoas.serializer_factory" class="Hateoas\HateoasBuilder" />
<service id="hateoas.serializer"
class="Hateoas\Hateoas"
factory-service="hateoas.serializer_factory"
factory-method="build">
</service>
But this declaration do not take care of the JMSSerializerBundle additions. In fact, this bundle adds some other handlers such as FormErrorHandler
to the serializer exposed via the jms_serializer.serializer
service.
I think that we need to reuse them, so in fact, we have at least two options:
HandlerRegistry
to add registered handlers on creation in the HateoasBuilderHateoasBuilder
at all but tagged services to add serializer subscribers (and build the link helper needed by Hateoas
constructor)I much prefer the second option but want some feedbacks before making a pull request with changes in this directions.
Line 36 of https://github.com/willdurand/BazingaHateoasBundle/blob/master/DependencyInjection/Compiler/ExtensionDriverPass.php results in the following syntax error:
PHP Parse error: syntax error, unexpected 'class' (T_CLASS), expecting
identifier (T_STRING) or variable (T_VARIABLE) or '{' or '$' in
myapp/vendor/willdurand/hateoas-bundle/Bazinga/Bundle/HateoasBundle/DependencyInjection/Compiler/ExtensionDriverPass.php
on line 36
[Symfony\Component\Debug\Exception\FatalErrorException]
Parse Error: syntax error, unexpected 'class' (T_CLASS), expecting
identifier (T_STRING) or variable (T_VARIABLE) or '{' or '$'
PHP 5.4.45-0+deb7u5
Solution is either to correct the required PHP version (5.5 according to php.net) in composer.json, or just provide the interface name as a string.
So that we can use the service()
and parameter()
functions rather than injecting the container and the request.
ping @adrienbrault
How should we handle this? Configuration?
bazinga_hateoas:
serializer:
xml: My\Custom\XmlSerializer
json: My\CustomJsonSerializer
I get two deprication messages on the latest version of Symfony
Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won't be supported in version 4.0. You should rename (or alias) the "hateoas.configuration.relations_repository" service to "Hateoas\Configuration\RelationsRepository" instead.
Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won't be supported in version 4.0. You should rename (or alias) the "hateoas.expression.evaluator" service to "Hateoas\Expression\ExpressionEvaluator" instead.
This bundle does not support symfony 5 yet
Currently, this bundle can only be installed on PHP 7.2/7.3/7.4.
Support for PHP 8 should be added.
<service id="hateoas.configuration.metadata.extension_driver" class="%hateoas.configuration.metadata.extension_driver.class%" public="false">
<argument type="service" id="hateoas.configuration.metadata.annotation_driver" />
</service>
<service id="hateoas.configuration.metadata.chain_driver" class="%jms_serializer.metadata.chain_driver.class%" public="false">
<argument type="collection">
<argument type="service" id="hateoas.configuration.metadata.yaml_driver" />
<argument type="service" id="hateoas.configuration.metadata.xml_driver" />
<argument type="service" id="hateoas.configuration.metadata.extension_driver" />
</argument>
</service>
I add a custom configuration extension, it never get executed, I found out that the ExtensionDriver is not enabled for yaml and xml driver, why is it?
I get the following error when using this bundle with symfony 7 and calling bin/console cache:clear
:
The service "hateoas.configuration.metadata.annotation_driver" has a dependency on a non-existent service "annotation_reader".
This is because the service "annotation_reader" has been removed in symfony/framework-bundle:7.0
.
My project doesn't use the annotations defined in https://github.com/willdurand/Hateoas/tree/master/src/Configuration/Annotation at all.
I was able to work around this issue by defining the service "annotation_reader" in my project manually.
It would be nice if annotations could be make optional in this bundle.
Maybe #104 can help to fix this issue by adding support for native attributes.
Since Symfony 2.6, the security.context service has been deprecated and split into two new services: security.authorization_checker and security.token_storage.
And now with Symfony 3.0 the security.context service has been totally removed.
I'm using the BazingaHateoasBundle on a profesionnal Symfony 3 project and got stuck because of that.
There is currently a pull request #50 to solve this problem ;)
Hi there,
We're using BazingaHateoasBundle v0.4.0 in Symfony 2.6, and we need to implement our own custom URL generator, however we have come across some issues in using our generator.
We followed the steps to implement one, however the registry reports that it can't find our generator, and that only the "symfony_generator" is available.
The "account_link_generator" url generator is not set. Available url generators are: symfony_generator.
This is the generator class (albeit slimmed-down)
<?php
namespace Sce\AccountDataBundle\Generator;
use Hateoas\UrlGenerator\UrlGeneratorInterface;
class HateoasAccountLinkGenerator implements UrlGeneratorInterface
{
/**
* {@inheritdoc}
*/
public function generate($name, array $parameters, $absolute = false)
{
return 'http://service.com/resource/'.$parameters['id'];
}
}
Here is the service definition:
sce_account_data.generator.hateoas_account_link_generator:
class: Sce\AccountDataBundle\Generator\HateoasAccountLinkGenerator
tags:
- { name: hateoas.url_generator, alias: account_link_generator }
And finally the annotation on the entity:
* @Hateoas\Relation(
* "self",
* href = @Hateoas\Route(
* name = "sce_account_data.account.get",
* parameters = {
* "id" = "expr(object.getId())"
* },
* absolute = true,
* generator = "account_link_generator"
* )
* )
I've had a look in URLGeneratorPass.php and added some debug, and from looking at var_dump(array_keys($container->getDefinitions()));
that our generator is defined already, and by looking at var_dump($registryDefinition->getMethodCalls())
that it has detected our generator and registered that it should call set on the registry object.
I've also made sure that the bundle is the last loaded bundle in the AppKernel to try and eliminate any other bundles overloading the definitions, however set
on the URLGeneratorRegistry just seem to be never called with our generator.
Do you have any idea why this is happening?
Happy to provide further details if required.
Many thanks
See: 93ea2a3#L1R58
Any particular reason why Metadata\MetadataFactory::setIncludeInterfaces()
is not enabled per default and/or no setting exists. Would be cool to use interfaces for shared metadata.
Are there anything stopping an upgrade of jms/serializer-bundle to version 2.0? The composer file has it at ~1.0.
https://github.com/schmittjoh/JMSSerializerBundle/releases/tag/2.0.0
Hi. Im trying to give users full route to blog posters. Is there a way to insert homepage route before expr?
/**
* @Hateoas\Relation(
* name = "poster_path",
* href = "expr(object.getWebPath())"
* )
*/
jms/serializer and jms/serializer-bundle have gone 1.0.
Can we get a compatible release?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.