├── .idea
├── .name
├── misc.xml
├── php.xml
├── scopes
│ └── scope_settings.xml
├── encodings.xml
├── vcs.xml
├── inspectionProfiles
│ ├── profiles_settings.xml
│ └── Project_Default.xml
├── modules.xml
├── codeStyleSettings.xml
└── restapi-example.iml
├── Packages
└── Application
│ ├── Helmich.Products
│ ├── Classes
│ │ └── Helmich
│ │ │ └── Products
│ │ │ ├── Domain
│ │ │ ├── Repository
│ │ │ │ ├── ProductRepository.php
│ │ │ │ └── ManufacturerRepository.php
│ │ │ └── Model
│ │ │ │ ├── Manufacturer.php
│ │ │ │ ├── InventoryChange.php
│ │ │ │ └── Product.php
│ │ │ └── Command
│ │ │ └── ExampleDataCommandController.php
│ └── composer.json
│ ├── Helmich.ProductsApiSimple
│ ├── composer.json
│ ├── Configuration
│ │ ├── Routes.Entity.yaml
│ │ └── Routes.yaml
│ └── Classes
│ │ └── Helmich
│ │ └── ProductsApiSimple
│ │ └── Controller
│ │ ├── ManufacturerController.php
│ │ └── ProductController.php
│ └── Helmich.ProductsApiAdvanced
│ ├── composer.json
│ ├── Configuration
│ ├── Routes.Manufacturer.yaml
│ ├── Routes.Product.yaml
│ └── Routes.yaml
│ └── Classes
│ └── Helmich
│ └── ProductsApiAdvanced
│ ├── Dto
│ ├── ManufacturerReferenceDto.php
│ ├── ManufacturerDto.php
│ └── ProductDto.php
│ ├── Controller
│ ├── InventoryChangeController.php
│ ├── ManufacturerController.php
│ └── ProductController.php
│ ├── Normalizer
│ ├── ManufacturerNormalizer.php
│ ├── InventoryChangeNormalizer.php
│ ├── Decorator
│ │ ├── ProductHypermediaDecorator.php
│ │ └── ManufacturerHypermediaDecorator.php
│ └── ProductNormalizer.php
│ └── Mapper
│ └── ProductMapper.php
├── .gitignore
├── LICENSE.txt
├── composer.json
├── Configuration
└── Routes.yaml
├── README.md
└── README.de.md
/.idea/.name:
--------------------------------------------------------------------------------
1 | restapi-example
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/php.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/scopes/scope_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/Packages/Application/Helmich.Products/Classes/Helmich/Products/Domain/Repository/ProductRepository.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Packages/Application/Helmich.ProductsApiAdvanced/Configuration/Routes.Manufacturer.yaml:
--------------------------------------------------------------------------------
1 | - name: List all
2 | uriPattern: ''
3 | defaults:
4 | @action: list
5 | httpMethods:
6 | - GET
7 |
8 | - name: Show one
9 | uriPattern: '/{manufacturer}'
10 | defaults:
11 | @action: show
12 | httpMethods:
13 | - GET
14 |
15 | - name: List by manufacturer
16 | uriPattern: '/{manufacturer}/products'
17 | defaults:
18 | @controller: Product
19 | @action: listByManufacturer
20 | httpMethods:
21 | - GET
22 |
--------------------------------------------------------------------------------
/Packages/Application/Helmich.ProductsApiAdvanced/Classes/Helmich/ProductsApiAdvanced/Dto/ManufacturerReferenceDto.php:
--------------------------------------------------------------------------------
1 | identifier = $identifier;
19 | }
20 |
21 | /**
22 | * @return string
23 | */
24 | public function getIdentifier() {
25 | return $this->identifier;
26 | }
27 | }
--------------------------------------------------------------------------------
/Packages/Application/Helmich.ProductsApiSimple/Configuration/Routes.Entity.yaml:
--------------------------------------------------------------------------------
1 | - name: List all s
2 | uriPattern: ''
3 | defaults:
4 | @action: list
5 | httpMethods:
6 | - GET
7 |
8 | - name: Create new
9 | uriPattern: ''
10 | defaults:
11 | @action: create
12 | httpMethods:
13 | - POST
14 |
15 | - name: Show
16 | uriPattern: '/{}'
17 | defaults:
18 | @action: show
19 | httpMethods:
20 | - GET
21 |
22 | - name: Update
23 | uriPattern: '/{.__identity}'
24 | defaults:
25 | @action: update
26 | httpMethods:
27 | - PUT
28 |
29 | - name: Delete
30 | uriPattern: '/{}'
31 | defaults:
32 | @action: delete
33 | httpMethods:
34 | - DELETE
--------------------------------------------------------------------------------
/Packages/Application/Helmich.ProductsApiAdvanced/Configuration/Routes.Product.yaml:
--------------------------------------------------------------------------------
1 | - name: List all
2 | uriPattern: ''
3 | defaults:
4 | @action: list
5 | httpMethods:
6 | - GET
7 |
8 | - name: Create
9 | uriPattern: ''
10 | defaults:
11 | @action: create
12 | httpMethods:
13 | - POST
14 |
15 | - name: Show one
16 | uriPattern: '/{product}'
17 | defaults:
18 | @action: show
19 | httpMethods:
20 | - GET
21 |
22 | - name: Update
23 | uriPattern: '/{productIdentifier}'
24 | defaults:
25 | @action: update
26 | httpMethods:
27 | - PUT
28 |
29 | - name: Delete
30 | uriPattern: '/{product}'
31 | defaults:
32 | @action: delete
33 | httpMethods:
34 | - DELETE
35 |
36 | - name: List inventory changes
37 | uriPattern: '/{product}/inventory-changes'
38 | defaults:
39 | @controller: InventoryChange
40 | @action: list
41 | httpMethods:
42 | - GET
--------------------------------------------------------------------------------
/Packages/Application/Helmich.Products/Classes/Helmich/Products/Domain/Model/Manufacturer.php:
--------------------------------------------------------------------------------
1 | name = $name;
29 | $this->location = $location;
30 | }
31 |
32 | /**
33 | * @return string
34 | */
35 | public function getName() {
36 | return $this->name;
37 | }
38 |
39 | /**
40 | * @return string
41 | */
42 | public function getLocation() {
43 | return $this->location;
44 | }
45 |
46 | }
--------------------------------------------------------------------------------
/Packages/Application/Helmich.ProductsApiAdvanced/Classes/Helmich/ProductsApiAdvanced/Controller/InventoryChangeController.php:
--------------------------------------------------------------------------------
1 | registerNormalizerForClass(InventoryChange::class, new InventoryChangeNormalizer());
16 | }
17 | }
18 |
19 | public function listAction(Product $product) {
20 | $changes = $product->getInventoryChanges();
21 | $this->view->assign('inventoryChanges', $changes);
22 | }
23 | }
--------------------------------------------------------------------------------
/Packages/Application/Helmich.ProductsApiAdvanced/Classes/Helmich/ProductsApiAdvanced/Normalizer/ManufacturerNormalizer.php:
--------------------------------------------------------------------------------
1 | $this->persistenceManager->getIdentifierByObject($object),
25 | 'name' => $object->getName(),
26 | 'location' => $object->getLocation()
27 | ];
28 | }
29 |
30 | }
--------------------------------------------------------------------------------
/Packages/Application/Helmich.ProductsApiAdvanced/Classes/Helmich/ProductsApiAdvanced/Normalizer/InventoryChangeNormalizer.php:
--------------------------------------------------------------------------------
1 | persistenceManager->getIdentifierByObject($object);
20 | return [
21 | 'identifier' => $identifier,
22 | 'quantity' => $object->getAmount(),
23 | 'date' => $object->getDate()->format('r')
24 | ];
25 | }
26 | throw new \InvalidArgumentException('Expected instanceof ' . InventoryChange::class . ', got ' . get_class($object) . '!');
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/Packages/Application/Helmich.ProductsApiAdvanced/Classes/Helmich/ProductsApiAdvanced/Normalizer/Decorator/ProductHypermediaDecorator.php:
--------------------------------------------------------------------------------
1 | uriBuilder = $uriBuilder;
17 | $this->actual = $actual;
18 | }
19 |
20 | public function objectToScalar($object) {
21 | $scalar = $this->actual->objectToScalar($object);
22 | $scalar['href'] = $this->uriBuilder->reset()->uriFor('show', ['product' => $object], 'Product');
23 | $scalar['links'] = [
24 | [
25 | 'rel' => 'inventoryChanges',
26 | 'href' => $this->uriBuilder->reset()->uriFor('list', ['product' => $object], 'InventoryChange')
27 | ]
28 | ];
29 |
30 | return $scalar;
31 | }
32 | }
--------------------------------------------------------------------------------
/Packages/Application/Helmich.Products/Classes/Helmich/Products/Command/ExampleDataCommandController.php:
--------------------------------------------------------------------------------
1 | productRepository->add($product);
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Martin Helmich
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/Packages/Application/Helmich.ProductsApiAdvanced/Classes/Helmich/ProductsApiAdvanced/Normalizer/ProductNormalizer.php:
--------------------------------------------------------------------------------
1 | persistenceManager->getIdentifierByObject($object);
21 | return [
22 | 'identifier' => $identifier,
23 | 'name' => $object->getName(),
24 | 'quantity' => $object->getQuantity(),
25 | 'price' => $object->getPrice(),
26 | 'manufacturer' => $object->getManufacturer()
27 | ];
28 | }
29 | throw new \InvalidArgumentException('Expected instanceof ' . Product::class . ', got ' . get_class($object) . '!');
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "helmich/flow-restapi-example",
3 | "description": "Example application for REST webservices with TYPO3 Flow",
4 | "license": "MIT",
5 | "config": {
6 | "vendor-dir": "Packages/Libraries",
7 | "bin-dir": "bin"
8 | },
9 | "require": {
10 | "typo3/flow": "2.3.*",
11 | "typo3/welcome": "2.3.*",
12 | "doctrine/migrations": "@dev",
13 | "symfony/serializer": "~2.6",
14 | "helmich/flow-resttools": "dev-master"
15 | },
16 | "require-dev": {
17 | "typo3/kickstart": "2.3.*",
18 | "typo3/buildessentials": "2.3.*",
19 | "phpunit/phpunit": "4.0.*",
20 | "mikey179/vfsstream": "1.2.*"
21 | },
22 | "suggest": {
23 | "ext-pdo_sqlite": "For running functional tests out-of-the-box this is required"
24 | },
25 | "scripts": {
26 | "post-update-cmd": "TYPO3\\Flow\\Composer\\InstallerScripts::postUpdateAndInstall",
27 | "post-install-cmd": "TYPO3\\Flow\\Composer\\InstallerScripts::postUpdateAndInstall",
28 | "post-package-update": "TYPO3\\Flow\\Composer\\InstallerScripts::postPackageUpdateAndInstall",
29 | "post-package-install": "TYPO3\\Flow\\Composer\\InstallerScripts::postPackageUpdateAndInstall"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Packages/Application/Helmich.ProductsApiAdvanced/Classes/Helmich/ProductsApiAdvanced/Normalizer/Decorator/ManufacturerHypermediaDecorator.php:
--------------------------------------------------------------------------------
1 | uriBuilder = $uriBuilder;
20 | $this->actual = $actual;
21 | $this->withLinks = $withLinks;
22 | }
23 |
24 | public function objectToScalar($object) {
25 | $scalar = $this->actual->objectToScalar($object);
26 | $scalar['href'] = $this->uriBuilder->reset()->uriFor('show', ['manufacturer' => $object], 'Manufacturer');
27 |
28 | if ($this->withLinks) {
29 | $scalar['links'] = [
30 | [
31 | 'rel' => 'products',
32 | 'href' => $this->uriBuilder->reset()->uriFor('listByManufacturer', ['manufacturer' => $object], 'Product')
33 | ]
34 | ];
35 | }
36 |
37 | return $scalar;
38 | }
39 | }
--------------------------------------------------------------------------------
/Packages/Application/Helmich.ProductsApiAdvanced/Classes/Helmich/ProductsApiAdvanced/Dto/ManufacturerDto.php:
--------------------------------------------------------------------------------
1 | identifier = $identifier;
39 | $this->name = $name;
40 | $this->location = $location;
41 | }
42 |
43 | /**
44 | * @return string
45 | */
46 | public function getIdentifier() {
47 | return $this->identifier;
48 | }
49 |
50 | /**
51 | * @return string
52 | */
53 | public function getName() {
54 | return $this->name;
55 | }
56 |
57 | /**
58 | * @return string
59 | */
60 | public function getLocation() {
61 | return $this->location;
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/Packages/Application/Helmich.ProductsApiSimple/Configuration/Routes.yaml:
--------------------------------------------------------------------------------
1 | # #
2 | # Routes configuration #
3 | # #
4 | # This file contains the configuration for the MVC router. #
5 | # Just add your own modifications as necessary. #
6 | # #
7 | # Please refer to the Flow manual for possible configuration options. #
8 | # #
9 |
10 | - name: 'Products'
11 | uriPattern: 'products(.{@format})'
12 | defaults:
13 | @package: Helmich.ProductsApiSimple
14 | @controller: Product
15 | @format: json
16 | subRoutes:
17 | ProductSubroutes:
18 | package: Helmich.ProductsApiSimple
19 | suffix: Entity
20 | variables:
21 | entity: product
22 |
23 | - name: 'Manufacturers'
24 | uriPattern: 'manufacturers(.{@format})'
25 | defaults:
26 | @package: Helmich.ProductsApiSimple
27 | @controller: Manufacturer
28 | @format: json
29 | subRoutes:
30 | ManufacturerSubroutes:
31 | package: Helmich.ProductsApiSimple
32 | suffix: Entity
33 | variables:
34 | entity: manufacturer
--------------------------------------------------------------------------------
/Configuration/Routes.yaml:
--------------------------------------------------------------------------------
1 | # #
2 | # Routes configuration #
3 | # #
4 | # This file contains the configuration for the MVC router. #
5 | # Just add your own modifications as necessary. #
6 | # #
7 | # Please refer to the Flow manual for possible configuration options. #
8 | # #
9 |
10 | ##
11 | # Routes for the two packages providing REST APIs for the products
12 | # domain model. Note how you can use route prefixes (like "api/v1")
13 | # to add different versions of your API.
14 | #
15 |
16 | - name: 'Products v1'
17 | uriPattern: 'api/v1/'
18 | subRoutes:
19 | ProductsSimpleSubroutes:
20 | package: Helmich.ProductsApiSimple
21 |
22 | - name: 'Products v2'
23 | uriPattern: 'api/v2/'
24 | subRoutes:
25 | ProductsAdvancedSubroutes:
26 | package: Helmich.ProductsApiAdvanced
27 |
28 | ##
29 | # Flow subroutes
30 | #
31 |
32 | -
33 | name: 'Flow'
34 | uriPattern: ''
35 | defaults:
36 | '@format': 'html'
37 | subRoutes:
38 | FlowSubroutes:
39 | package: TYPO3.Flow
40 |
--------------------------------------------------------------------------------
/Packages/Application/Helmich.Products/Classes/Helmich/Products/Domain/Model/InventoryChange.php:
--------------------------------------------------------------------------------
1 | product = $product;
41 | $this->amount = $amount;
42 | $this->purpose = $purpose;
43 | $this->date = new \DateTime();
44 | }
45 |
46 | /**
47 | * @return Product
48 | */
49 | public function getProduct() {
50 | return $this->product;
51 | }
52 |
53 | /**
54 | * @return int
55 | */
56 | public function getAmount() {
57 | return $this->amount;
58 | }
59 |
60 | /**
61 | * @return \DateTime
62 | */
63 | public function getDate() {
64 | return $this->date;
65 | }
66 |
67 | }
--------------------------------------------------------------------------------
/.idea/codeStyleSettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | RESTful Webservices with TYPO3 Flow
2 | ===================================
3 |
4 | Martin Helmich
5 |
6 | Synopsis
7 | --------
8 |
9 | This repository contains an example application that demonstrates on how
10 | to implement RESTful Webservices with TYPO3 Flow. It contains three packages:
11 |
12 | - `Helmich.Products` contains entity classes and repositories around a simple inventory management domain model
13 | - `Helmich.ProductsApiSimple` contains a simple REST-like API (level 2 of the
14 | [Richardson Maturity Model](http://martinfowler.com/articles/richardsonMaturityModel.html))
15 | which allows you to CRUD products and manufacturers using respective HTTP methods.
16 | - `Helmich.ProductsApiAdvanced` contains an advanced API (level 3 of the RMM), which is _hypermedia controlled_
17 | (which means that there are links between resources) and also shows some design patterns to decouple your
18 | REST resource representations from your domain entities.
19 |
20 | Installation
21 | ------------
22 |
23 | You can install this application using [Composer](http://getcomposer.org):
24 |
25 | composer create-project helmich/flow-restapi-example
26 |
27 | Follow the [TYPO3 Flow installation instructions](http://docs.typo3.org/flow/TYPO3FlowDocumentation/Quickstart/Index.html#installing-typo3-flow)
28 | in all other ways. Also [set up your database](http://docs.typo3.org/flow/TYPO3FlowDocumentation/Quickstart/Index.html#database-setup)
29 | and run the database migrations:
30 |
31 | ./flow doctrine:migrate
32 |
--------------------------------------------------------------------------------
/Packages/Application/Helmich.ProductsApiSimple/Classes/Helmich/ProductsApiSimple/Controller/ManufacturerController.php:
--------------------------------------------------------------------------------
1 | JsonView::class];
17 |
18 | /**
19 | * @var ManufacturerRepository
20 | * @Flow\Inject
21 | */
22 | protected $manufacturerRepository;
23 |
24 | protected function initializeView(ViewInterface $view) {
25 | if ($view instanceof JsonView) {
26 | $manufacturerConfiguration = [
27 | '_exposeObjectIdentifier' => TRUE,
28 | '_exposedObjectIdentifierKey' => 'identifier'
29 | ];
30 | $view->setConfiguration([
31 | 'value' => [
32 | 'manufacturers' => ['_descendAll' => $manufacturerConfiguration],
33 | 'manufacturer' => $manufacturerConfiguration
34 | ]
35 | ]);
36 | }
37 | }
38 |
39 | public function listAction() {
40 | $manufacturers = $this->manufacturerRepository->findAll();
41 | $this->view->assign('value', ['manufacturers' => $manufacturers]);
42 | }
43 |
44 | public function showAction(Manufacturer $manufacturer) {
45 | $this->view->assign('value', ['manufacturer' => $manufacturer]);
46 | }
47 | }
--------------------------------------------------------------------------------
/README.de.md:
--------------------------------------------------------------------------------
1 | RESTful Webservices mit TYPO3 Flow
2 | ==================================
3 |
4 | Martin Helmich
5 |
6 | Zusammenfassung
7 | ---------------
8 |
9 | Dieses Repository enthält eine Beispiel-Anwendung, die die Entwicklung von
10 | REST-Webservices mit TYPO3 Flow veranschaulicht. Sie enhält drei Pakete:
11 |
12 | - `Helmich.Products` enthält Entitäts-Klassen und Repositories um ein einfaches Fachmodell rund um eine Inventarverwaltung
13 | - `Helmich.ProductsApiSimple` enthält eine einfach REST-ähnliche API (Stufe 2 des
14 | [Richardson Maturity Model](http://martinfowler.com/articles/richardsonMaturityModel.html)), über die
15 | Produkte und Hersteller über die entsprechenden HTTP-Methoden ausgelesen, erstellt, verändert und gelöscht werden können.
16 | - `Helmich.ProductsApiAdvanced` enhält eine erweiterte API (Level 3 des RMM), deren Ressourcen mit Hyperlinks
17 | untereinander verknüpft sind. Sie zeigt außerdem einige Designmuster, mit denen die Ressourcen-Repräsentation
18 | des REST-Webservices von den Entitäten des Fachmodells entkoppelt werden kann.
19 |
20 | Installation
21 | ------------
22 |
23 | Diese Anwendung kann über [Composer](http://getcomposer.org) installiert werden:
24 |
25 | composer create-project helmich/flow-restapi-example
26 |
27 | Folgen Sie anschließend den [Installationsanweisungen von TYPO3](http://docs.typo3.org/flow/TYPO3FlowDocumentation/Quickstart/Index.html#installing-typo3-flow).
28 | Richten Sie auch Ihre [Datenbank ein](http://docs.typo3.org/flow/TYPO3FlowDocumentation/Quickstart/Index.html#database-setup)
29 | und führen Sie die Doctrine-Migrationen aus:
30 |
31 | ./flow doctrine:migrate
32 |
--------------------------------------------------------------------------------
/Packages/Application/Helmich.ProductsApiAdvanced/Classes/Helmich/ProductsApiAdvanced/Controller/ManufacturerController.php:
--------------------------------------------------------------------------------
1 | registerNormalizerForClass(Manufacturer::class, new ManufacturerHypermediaDecorator(new ManufacturerNormalizer(), TRUE, $this->uriBuilder));
30 | }
31 | }
32 |
33 | public function listAction() {
34 | $this->view->assign('manufacturers', $this->manufacturerRepository->findAll());
35 | }
36 |
37 | public function showAction(Manufacturer $manufacturer) {
38 | $this->view->assign('manufacturer', $manufacturer);
39 | }
40 |
41 | }
--------------------------------------------------------------------------------
/Packages/Application/Helmich.ProductsApiAdvanced/Classes/Helmich/ProductsApiAdvanced/Mapper/ProductMapper.php:
--------------------------------------------------------------------------------
1 | manufacturerRepository->findByIdentifier($productDto->getManufacturer()->getIdentifier());
32 | $product = new Product(
33 | $productDto->getName(),
34 | $productDto->getPrice(),
35 | $manufacturer,
36 | $productDto->getQuantity()
37 | );
38 | return $product;
39 | }
40 |
41 | public function updateProduct($identifier, ProductDto $productDto) {
42 | /** @var Product $product */
43 | $product = $this->productRepository->findByIdentifier($identifier);
44 | $manufacturer = $this->manufacturerRepository->findByIdentifier($productDto->getManufacturer()->getIdentifier());
45 |
46 | if (NULL === $product) {
47 | throw new \InvalidArgumentException('Product does not exist!');
48 | }
49 |
50 | $product->setName($productDto->getName());
51 | $product->setPrice($productDto->getPrice());
52 | $product->setQuantity($productDto->getQuantity());
53 | $product->setManufacturer($manufacturer);
54 |
55 | return $product;
56 | }
57 |
58 | }
--------------------------------------------------------------------------------
/Packages/Application/Helmich.ProductsApiAdvanced/Configuration/Routes.yaml:
--------------------------------------------------------------------------------
1 | # #
2 | # Routes configuration #
3 | # #
4 | # This file contains the configuration for the MVC router. #
5 | # Just add your own modifications as necessary. #
6 | # #
7 | # Please refer to the Flow manual for possible configuration options. #
8 | # #
9 |
10 |
11 | - name: 'Products'
12 | uriPattern: 'products(.{@format})'
13 | defaults:
14 | @package: Helmich.ProductsApiAdvanced
15 | @controller: Product
16 | @format: json
17 | subRoutes:
18 | ProductSubroutes:
19 | package: Helmich.ProductsApiAdvanced
20 | suffix: Product
21 |
22 | - name: 'Manufacturers'
23 | uriPattern: 'manufacturers(.{@format})'
24 | defaults:
25 | @package: Helmich.ProductsApiAdvanced
26 | @controller: Manufacturer
27 | @format: json
28 | subRoutes:
29 | ManufacturerSubroutes:
30 | package: Helmich.ProductsApiAdvanced
31 | suffix: Manufacturer
32 |
33 | #- name: 'List Products'
34 | # uriPattern: 'products(.{@format})'
35 | # defaults:
36 | # @package: Helmich.ProductsApiAdvanced
37 | # @controller: Product
38 | # @action: list
39 | # @format: json
40 | # httpMethods: [GET]
41 | #
42 | #- name: 'Create products'
43 | # uriPattern: products
44 | # defaults:
45 | # @package: Helmich.ProductsApiAdvanced
46 | # @controller: Product
47 | # @action: create
48 | # @format: json
49 | # httpMethods: [POST]
50 | #
51 | #- name: 'Show product'
52 | # uriPattern: 'products/{product}(.{@format})'
53 | # defaults:
54 | # @package: Helmich.ProductsApiAdvanced
55 | # @controller: Product
56 | # @action: show
57 | # @format: json
58 | # httpMethods: [GET]
--------------------------------------------------------------------------------
/Packages/Application/Helmich.ProductsApiAdvanced/Classes/Helmich/ProductsApiAdvanced/Dto/ProductDto.php:
--------------------------------------------------------------------------------
1 | identifier = $identifier;
53 | $this->name = $name;
54 | $this->quantity = $quantity;
55 | $this->price = $price;
56 | $this->manufacturer = $manufacturer;
57 | }
58 |
59 | /**
60 | * @return string
61 | */
62 | public function getIdentifier() {
63 | return $this->identifier;
64 | }
65 |
66 | /**
67 | * @return string
68 | */
69 | public function getName() {
70 | return $this->name;
71 | }
72 |
73 | /**
74 | * @return int
75 | */
76 | public function getQuantity() {
77 | return $this->quantity;
78 | }
79 |
80 | /**
81 | * @return float
82 | */
83 | public function getPrice() {
84 | return $this->price;
85 | }
86 |
87 | /**
88 | * @return ManufacturerReferenceDto
89 | */
90 | public function getManufacturer() {
91 | return $this->manufacturer;
92 | }
93 |
94 | }
--------------------------------------------------------------------------------
/Packages/Application/Helmich.ProductsApiSimple/Classes/Helmich/ProductsApiSimple/Controller/ProductController.php:
--------------------------------------------------------------------------------
1 | JsonView::class];
17 |
18 | /**
19 | * @var ProductRepository
20 | * @Flow\Inject
21 | */
22 | protected $productRepository;
23 |
24 | protected function initializeView(ViewInterface $view) {
25 | if ($view instanceof JsonView) {
26 | $productConfiguration = [
27 | '_exposeObjectIdentifier' => TRUE,
28 | '_exposedObjectIdentifierKey' => 'identifier',
29 | '_descend' => [
30 | 'manufacturer' => [
31 | '_exclude' => ['__isInitialized__'],
32 | '_exposeObjectIdentifier' => TRUE,
33 | '_exposedObjectIdentifierKey' => 'identifier'
34 | ]
35 | ]
36 | ];
37 | $view->setConfiguration([
38 | 'value' => [
39 | 'products' => ['_descendAll' => $productConfiguration],
40 | 'product' => $productConfiguration
41 | ]
42 | ]);
43 | }
44 | }
45 |
46 | public function listAction() {
47 | $products = $this->productRepository->findAll();
48 | $this->view->assign('value', ['products' => $products]);
49 | }
50 |
51 | public function showAction(Product $product) {
52 | $this->view->assign('value', ['product' => $product]);
53 | }
54 |
55 | protected function initializeCreateAction() {
56 | $config = $this->arguments['product']->getPropertyMappingConfiguration();
57 | $config->setTypeConverterOption('TYPO3\Flow\Property\TypeConverter\PersistentObjectConverter', PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED, TRUE);
58 | $config->allowAllProperties();
59 | }
60 |
61 | public function createAction(Product $product) {
62 | $this->productRepository->add($product);
63 | $this->response->setStatus(201);
64 | $this->view->assign('value', ['product' => $product]);
65 | }
66 |
67 | protected function initializeUpdateAction() {
68 | $config = $this->arguments['product']->getPropertyMappingConfiguration();
69 | $config->setTypeConverterOption('TYPO3\Flow\Property\TypeConverter\PersistentObjectConverter', PersistentObjectConverter::CONFIGURATION_MODIFICATION_ALLOWED, TRUE);
70 | $config->allowAllProperties();
71 | }
72 |
73 | public function updateAction(Product $product) {
74 | $this->productRepository->update($product);
75 | $this->view->assign('value', ['product' => $product]);
76 | }
77 |
78 | public function deleteAction(Product $product) {
79 | $this->productRepository->remove($product);
80 | $this->response->setStatus(204);
81 | }
82 |
83 | }
--------------------------------------------------------------------------------
/Packages/Application/Helmich.Products/Classes/Helmich/Products/Domain/Model/Product.php:
--------------------------------------------------------------------------------
1 |
33 | * @ORM\OneToMany(mappedBy="product", cascade={"persist", "remove"})
34 | */
35 | protected $inventoryChanges;
36 |
37 | /**
38 | * @var Manufacturer
39 | * @ORM\ManyToOne(cascade={"persist"})
40 | */
41 | protected $manufacturer;
42 |
43 | /**
44 | * Default constructor.
45 | *
46 | * @param string $name The product name.
47 | * @param float $price The product price.
48 | * @param Manufacturer $manufacturer The manufacturer.
49 | * @param int $quantity The initial quantity.
50 | */
51 | public function __construct($name, $price, Manufacturer $manufacturer, $quantity = 0) {
52 | $this->name = $name;
53 | $this->quantity = $quantity;
54 | $this->price = $price;
55 | $this->manufacturer = $manufacturer;
56 | $this->inventoryChanges = new ArrayCollection();
57 |
58 | if ($quantity != 0) {
59 | $this->inventoryChanges->add(new InventoryChange($this, $quantity, 'Initial stock'));
60 | }
61 | }
62 |
63 | /**
64 | * @return string
65 | */
66 | public function getName() {
67 | return $this->name;
68 | }
69 |
70 | /**
71 | * @param string $name
72 | */
73 | public function setName($name) {
74 | $this->name = $name;
75 | }
76 |
77 | /**
78 | * @return int
79 | */
80 | public function getQuantity() {
81 | return $this->quantity;
82 | }
83 |
84 | /**
85 | * @param int $quantity
86 | */
87 | public function setQuantity($quantity) {
88 | $difference = $quantity - $this->quantity;
89 | if ($difference !== 0) {
90 | if ($difference > 0) {
91 | $purpose = 'Added items to stock.';
92 | } else {
93 | $purpose = 'Removed items from stock.';
94 | }
95 |
96 | $this->addInventoryChange(new InventoryChange($this, $difference, $purpose));
97 | }
98 | }
99 |
100 | /**
101 | * @return float
102 | */
103 | public function getPrice() {
104 | return (float)$this->price;
105 | }
106 |
107 | /**
108 | * @param float $price
109 | */
110 | public function setPrice($price) {
111 | $this->price = $price;
112 | }
113 |
114 | /**
115 | * @return Collection
116 | */
117 | public function getInventoryChanges() {
118 | return $this->inventoryChanges;
119 | }
120 |
121 | /**
122 | * @param InventoryChange $inventoryChange
123 | */
124 | public function addInventoryChange(InventoryChange $inventoryChange) {
125 | $this->quantity += $inventoryChange->getAmount();
126 | $this->inventoryChanges->add($inventoryChange);
127 | }
128 |
129 | /**
130 | * @return Manufacturer
131 | */
132 | public function getManufacturer() {
133 | return $this->manufacturer;
134 | }
135 |
136 | /**
137 | * @param Manufacturer $manufacturer
138 | */
139 | public function setManufacturer(Manufacturer $manufacturer) {
140 | $this->manufacturer = $manufacturer;
141 | }
142 |
143 |
144 | }
--------------------------------------------------------------------------------
/Packages/Application/Helmich.ProductsApiAdvanced/Classes/Helmich/ProductsApiAdvanced/Controller/ProductController.php:
--------------------------------------------------------------------------------
1 | registerNormalizerForClass(Product::class, new ProductHypermediaDecorator(new ProductNormalizer(), $this->uriBuilder));
35 | $view->registerNormalizerForClass(Manufacturer::class, new ManufacturerHypermediaDecorator(new ManufacturerNormalizer(), FALSE, $this->uriBuilder));
36 | }
37 | }
38 |
39 | public function listAction() {
40 | $this->view->assign('products', $this->productRepository->findAll());
41 | }
42 |
43 | public function listByManufacturerAction(Manufacturer $manufacturer) {
44 | $this->view->assign('products', $this->productRepository->findByManufacturer($manufacturer));
45 | }
46 |
47 | public function initializeCreateAction() {
48 | $propertyMappingConfiguration = $this->arguments['product']->getPropertyMappingConfiguration();
49 | $propertyMappingConfiguration->allowAllProperties();
50 | $propertyMappingConfiguration->forProperty('manufacturer')->allowAllProperties();
51 | }
52 |
53 | public function showAction(Product $product) {
54 | $this->view->assign('product', $product);
55 | }
56 |
57 | public function createAction(ProductDto $product) {
58 | $realProduct = $this->productMapper->createProduct($product);
59 | $this->productRepository->add($realProduct);
60 |
61 | $this->view->assign('product', $realProduct);
62 | $this->response->setStatus(201);
63 | }
64 |
65 | public function initializeUpdateAction() {
66 | $propertyMappingConfiguration = $this->arguments['product']->getPropertyMappingConfiguration();
67 | $propertyMappingConfiguration->allowAllProperties();
68 | $propertyMappingConfiguration->skipUnknownProperties(TRUE);
69 | $propertyMappingConfiguration->forProperty('manufacturer')->allowAllProperties();
70 | $propertyMappingConfiguration->forProperty('manufacturer')->skipUnknownProperties();
71 | }
72 |
73 | /**
74 | * @param string $productIdentifier
75 | * @param ProductDto $product
76 | * @throws \TYPO3\Flow\Persistence\Exception\IllegalObjectTypeException
77 | */
78 | public function updateAction($productIdentifier, ProductDto $product) {
79 | $realProduct = $this->productMapper->updateProduct($productIdentifier, $product);
80 | $this->productRepository->update($realProduct);
81 |
82 | $this->view->assign('product', $realProduct);
83 | }
84 |
85 | public function deleteAction(Product $product) {
86 | $this->productRepository->remove($product);
87 | $this->view->assign('product', $product);
88 | }
89 |
90 | }
--------------------------------------------------------------------------------
/.idea/restapi-example.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------