diff --git a/README.md b/README.md index 4a2c7a6..615ac48 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ A php library to describe resources and serve different hypermedia formats based ## how ? - - Declare your different resources: +Example integration with a symfony controller ```php use Hippiemedia\Resource; @@ -16,7 +16,6 @@ use Hippiemedia\Operation; use Hippiemedia\Field; use Hippiemedia\Format; use Hippiemedia\Negotiate; -use Hippiemedia\UrlGenerator; final class Hypermedia { diff --git a/composer.json b/composer.json index 3136025..3b27d8d 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ "require": { "lstrojny/functional-php": "^1.0", "willdurand/negotiation": "^2.0", - "symfony/routing": "^3.0 | ^4.0" + "docteurklein/json-chunks": "^1.0" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index ed4ad3a..2b1e197 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,44 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "cbe09d71af04b4b0611ae80efa114778", + "content-hash": "59a18a20bafc2d4c1125bb79cdf1037a", "packages": [ + { + "name": "docteurklein/json-chunks", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/docteurklein/json-chunks.git", + "reference": "61199e6ba97880191854bb75d5d50f2d8f345e89" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/docteurklein/json-chunks/zipball/61199e6ba97880191854bb75d5d50f2d8f345e89", + "reference": "61199e6ba97880191854bb75d5d50f2d8f345e89", + "shasum": "" + }, + "require-dev": { + "phpspec/phpspec": "^4.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "DocteurKlein\\JsonChunks\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Klein Florian", + "email": "florian.klein@free.fr" + } + ], + "description": "JSON encoder using generators to stream", + "time": "2018-09-13T09:49:59+00:00" + }, { "name": "lstrojny/functional-php", "version": "1.9.0", @@ -145,83 +181,6 @@ ], "time": "2018-12-03T16:47:05+00:00" }, - { - "name": "symfony/routing", - "version": "v4.2.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/routing.git", - "reference": "649460207e77da6c545326c7f53618d23ad2c866" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/649460207e77da6c545326c7f53618d23ad2c866", - "reference": "649460207e77da6c545326c7f53618d23ad2c866", - "shasum": "" - }, - "require": { - "php": "^7.1.3" - }, - "conflict": { - "symfony/config": "<4.2", - "symfony/dependency-injection": "<3.4", - "symfony/yaml": "<3.4" - }, - "require-dev": { - "doctrine/annotations": "~1.0", - "psr/log": "~1.0", - "symfony/config": "~4.2", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/http-foundation": "~3.4|~4.0", - "symfony/yaml": "~3.4|~4.0" - }, - "suggest": { - "doctrine/annotations": "For using the annotation loader", - "symfony/config": "For using the all-in-one router or any loader", - "symfony/dependency-injection": "For loading routes from a service", - "symfony/expression-language": "For using expression matching", - "symfony/http-foundation": "For using a Symfony Request object", - "symfony/yaml": "For using the YAML loader" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Routing\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Routing Component", - "homepage": "https://symfony.com", - "keywords": [ - "router", - "routing", - "uri", - "url" - ], - "time": "2018-12-03T22:08:12+00:00" - }, { "name": "willdurand/negotiation", "version": "v2.3.1", diff --git a/example/whatever.php b/example/whatever.php new file mode 100644 index 0000000..5124bf1 --- /dev/null +++ b/example/whatever.php @@ -0,0 +1,19 @@ +accepts()); + +echo implode("\n", iterator_to_array($format(Resource::whatever()), false)); diff --git a/spec/Hippiemedia/Format/HtmlSpec.php b/spec/Hippiemedia/Format/HtmlSpec.php new file mode 100644 index 0000000..8e7e352 --- /dev/null +++ b/spec/Hippiemedia/Format/HtmlSpec.php @@ -0,0 +1,20 @@ +getWrappedObject(), false)); + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->loadHTML($html); + } +} diff --git a/src/Format/Hal.php b/src/Format/Hal.php index 567c488..9a76ab0 100644 --- a/src/Format/Hal.php +++ b/src/Format/Hal.php @@ -6,6 +6,7 @@ use Hippiemedia\Format; use Hippiemedia\Resource; use Hippiemedia\Link; +use DocteurKlein\JsonChunks\Encode; final class Hal implements Format { @@ -15,6 +16,11 @@ public function accepts(): string } public function __invoke(Resource $resource): iterable + { + return Encode::from($this->normalize($resource)); + } + + private function normalize(Resource $resource): iterable { $linksByRel = array_merge( f\group($resource->links, f\invoker('rel')), @@ -40,7 +46,7 @@ public function __invoke(Resource $resource): iterable }), '_embedded' => array_merge(f\map($operationsByRel, function($operations) { return f\map(array_values($operations), function($operation) { - return $this(new Resource($operation->url, [ + return $this->normalize(new Resource($operation->url, [ '_templates' => [ 'default' => [ 'title' => $operation->title, @@ -63,8 +69,8 @@ public function __invoke(Resource $resource): iterable ], array_merge([new Link(['self'], $operation->url, $operation->templated)], $operation->links), [])); }); }), f\map($resource->embedded, function($resources) { - return f\map($resources, $this); - }, $this)) + return array_map([$this, 'normalize'], $resources); + })) ]); } } diff --git a/src/Format/Html.php b/src/Format/Html.php new file mode 100644 index 0000000..ed1f1e5 --- /dev/null +++ b/src/Format/Html.php @@ -0,0 +1,65 @@ +state, true); + yield '
'; + yield "
{$state}
"; + yield from $this->links($resource); + yield from $this->operations($resource); + yield '
'; + } + + private function links(Resource $resource): iterable + { + yield ''; + } + + private function operations(Resource $resource): iterable + { + foreach ($resource->operations as $operation) { + yield << +
+ {$operation->title} + HTML + ; + yield from $this->fields($operation); + + yield << + + + HTML; + } + } + + private function fields(Operation $operation): iterable + { + foreach ($operation->fields as $field) { + yield << + HTML; + } + } +} diff --git a/src/Format/Siren.php b/src/Format/Siren.php index 3f41cab..598a08e 100644 --- a/src/Format/Siren.php +++ b/src/Format/Siren.php @@ -6,6 +6,7 @@ use Hippiemedia\Format; use Hippiemedia\Resource; use Hippiemedia\Link; +use DocteurKlein\JsonChunks\Encode; final class Siren implements Format { @@ -15,6 +16,11 @@ public function accepts(): string } public function __invoke(Resource $resource): iterable + { + return Encode::from($this->normalize($resource)); + } + + private function normalize(Resource $resource): iterable { return [ 'class' => [], @@ -31,7 +37,7 @@ public function __invoke(Resource $resource): iterable 'entities' => array_reduce(array_keys($resource->embedded), function($acc, $rel) use($resource) { $resources = $resource->embedded[$rel]; return array_merge($acc, f\map($resources, function($resource) use($rel) { - return array_merge($this($resource), ['rel' => [$rel]]); + return array_merge($this->normalize($resource), ['rel' => [$rel]]); })); }, []), 'actions' => f\map(array_values($resource->operations), function($operation) { diff --git a/src/Infra/UrlGenerator/SymfonyRouter.php b/src/Infra/UrlGenerator/SymfonyRouter.php deleted file mode 100644 index ce0d8ad..0000000 --- a/src/Infra/UrlGenerator/SymfonyRouter.php +++ /dev/null @@ -1,31 +0,0 @@ -router = $router; - } - - function url(string $route, array $params = []): string - { - return $this->router->generate($route, array_map('strval', $params), RouterInterface::ABSOLUTE_URL); - } - - function template(string $route, array $params = []): string - { - $route = $this->router->getRouteCollection()->get($route); - - $context = $this->router->getContext(); - - return sprintf('%s://%s/%s', $context->getScheme(), $context->getHost(), ltrim($route->getPath(), '/').(empty($params) ? '' : '?'.urldecode(http_build_query($params)))); - } -} diff --git a/src/Operation.php b/src/Operation.php index d4a0de2..a5a0b76 100644 --- a/src/Operation.php +++ b/src/Operation.php @@ -33,7 +33,7 @@ public static function whatever(array $data = []) { return new self( $data['rel'] ?? 'rel', - $data['method'] ?? 'method', + $data['method'] ?? 'POST', $data['url'] ?? 'url', $data['templated'] ?? false, $data['title'] ?? 'title', diff --git a/src/Resource.php b/src/Resource.php index 7ded832..f1aff5d 100644 --- a/src/Resource.php +++ b/src/Resource.php @@ -35,7 +35,7 @@ public static function whatever(array $data = []) $data['state'] ?? ['state'], $data['links'] ?? [Link::whatever()], $data['operations'] ?? [Operation::whatever()], - $data['embedded'] ?? ['rel' => [self::whatever(['embedded' => []])]], + $data['embedded'] ?? ['rel' => [self::whatever(['state' => ['SUB'], 'embedded' => []])]], $data['is_deprecated'] ?? false ); }