Skip to content

Commit ec731ad

Browse files
author
Pavel Batanov
committed
Initial public release
0 parents  commit ec731ad

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+5908
-0
lines changed

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
vendor/
2+
.idea/
3+
build/
4+
var/
5+
target/
6+
*.phar
7+
composer.lock

README.md

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
# Doctrine-faced RPC Client
2+
3+
## Usage
4+
5+
```php
6+
class \MyVendor\Api\Entity\MyEntity {
7+
/** @var string */
8+
private $id;
9+
/** @var string */
10+
private $payload;
11+
12+
public function getId() { return $this->id; }
13+
14+
public function getPayload() { return $this->payload; }
15+
}
16+
17+
```
18+
19+
`Resources/config/api/MyEntity.api.yml` content:
20+
21+
```yml
22+
MyVendor\Api\Entity\MyEntity:
23+
type: entity
24+
id:
25+
id:
26+
type: int
27+
28+
fields:
29+
payload:
30+
type: string
31+
32+
client:
33+
name: my-client
34+
entityPath: my-entity
35+
```
36+
37+
Configure `EntityManager`
38+
```php
39+
class RpcClient implements RpcClientInterface {
40+
/** RpcClient impl */
41+
}
42+
43+
$client = new RpcClient();
44+
45+
$registry = new ClientRegistry();
46+
$registry->add('my-client', $client);
47+
48+
$configuration = new Configuration();
49+
$configuration->setMetadataFactory(new EntityMetadataFactory());
50+
$configuration->setRegistry($this->registry);
51+
$configuration->setEntityFactoryClass(ProxyFactory::class);
52+
$configuration->setProxyDir(CACHE_DIR . '/doctrine/proxy/');
53+
$configuration->setProxyNamespace('MyVendor\Api\Proxy');
54+
$driver = new MappingDriverChain();
55+
$driver->addDriver(
56+
new YmlMetadataDriver(
57+
new SymfonyFileLocator(
58+
[
59+
__DIR__ . '/../Resources/config/api/' => 'MyVendor\Api\Entity',
60+
],
61+
'.api.yml',
62+
DIRECTORY_SEPARATOR)
63+
),
64+
'MyVendor\Api\Entity'
65+
);
66+
$configuration->setDriver($driver);
67+
68+
$manager = new EntityManager($configuration);
69+
```
70+
71+
72+
Call entity-manager to retrieve entities through your api
73+
```php
74+
$samples = $manager->getRepository(\MyVendor\Api\Entity\MyEntity::class)->findBy(['payload'=>'sample']);
75+
foreach ($samples as $sample) {
76+
var_dump($sample->getId());
77+
}
78+
```
79+
80+
## References
81+
82+
You could reference other API entities via relation annotations. General bi-directional self-reference relation below:
83+
84+
```yml
85+
MyVendor\Api\Entity\MyEntity:
86+
type: entity
87+
id:
88+
id:
89+
type: int
90+
91+
fields:
92+
payload:
93+
type: string
94+
95+
manyToOne:
96+
parent:
97+
target: MyVendor\Api\Entity\MyEntity
98+
inversedBy: children
99+
oneToMany:
100+
children:
101+
target: MyVendor\Api\Entity\MyEntity
102+
mappedBy: parent
103+
```
104+
105+
In order to make `*toMany` relations works flawlessly you should define the mapped class property
106+
as `Entity[]|ArrayCollection` as hydrator will substitute your relation property with lazy-loading collection interface.
107+
108+
### Note on lazy-loading
109+
110+
Generic API is not a DB, so eager reference pre-fetching will always result in additional API query,
111+
so current implementation always assumes that all requests are extra-lazy.
112+
This means that no data will be fetched until you really need it, and you'll have only lazy proxy object before that happens.
113+
114+
Keep it in the mind
115+
116+
## Hacking into fetching process
117+
118+
### Custom repository
119+
120+
Just call defined RpcClient from your repository
121+
122+
```php
123+
class MyRepository extends \Bankiru\Api\Doctrine\EntityRepository {
124+
public function callCustomRpcMethod()
125+
{
126+
$request = new \Bankiru\Api\Rpc\RpcRequest('my-method',['param1'=>'value1']);
127+
$data = $this->getClient()->invoke([$request])->getResponse($request);
128+
129+
return $data;
130+
}
131+
}
132+
```
133+
134+
Or more portable with mapping
135+
136+
```yml
137+
MyVendor\Api\Entity\MyEntity:
138+
type: entity
139+
id:
140+
id:
141+
type: int
142+
143+
fields:
144+
payload:
145+
type: string
146+
147+
repositoryClass: MyVendor\Api\Repository\MyRepository # This will override repository for MyEntity
148+
client:
149+
name: my-client
150+
# entityPath: my-entity autoconfigures find and search methods for you as following, but it is not overridable
151+
# You can also specify path separator as
152+
# entityPathSeparator: "-"
153+
# To make autogenerated methods look like my-entity-find
154+
methods:
155+
find: my-entity\find # find method is mandatory to find calls work
156+
search: my-entity\search # find method is mandatory to findBy calls work
157+
custom: my-custom-method # do some custom stuff
158+
```
159+
160+
```php
161+
class MyRepository extends \Bankiru\Api\Doctrine\EntityRepository {
162+
public function callCustomRpcMethod()
163+
{
164+
$request = new \Bankiru\Api\Rpc\RpcRequest(
165+
$this->getMetadata()->getMethodContainer()->getMethod('custom'),
166+
['param1'=>'value1']
167+
);
168+
$data = $this->getClient()->invoke([$request])->getResponse($request);
169+
170+
return $data;
171+
}
172+
}
173+
```
174+
175+
### Custom searcher
176+
177+
`Searcher` and `SearchTransformer` are the interfaces classes that converts doctrine-faced `findBy` arguments (criteria, order, limit, offset)
178+
to your custom api query set. This library provides you with the default `DoctrineNativeSearcher` class which translated these arguments to plain criteria array.
179+
180+
You can override searcher via `.api.yml` mapping file:
181+
182+
```yml
183+
MyVendor\Api\Entity\MyEntity:
184+
client:
185+
searcher: Bankiru\Api\Rpc\DoctrineNativeSearcher # The defaults
186+
```
187+
188+
`Searcher` is required to implement the respective `Bankiru\Api\Doctrine\Rpc\Searcher` interface.
189+
190+
### Custom hydrator
191+
192+
`Hydrator` is the object factory which takes the plain ('dehydrated') data and converts it to rich ('hydrated') object with collections, relationship etc.
193+
Current Hydrator accepts only plain-list parametrized objects.
194+
195+
### Custom field types
196+
197+
You could register additional field types to configuration `TypeRegistry` (`Configuration::getTypeRegistry()`). Just implement the `Type` and register it via `TypeRegistry::add('alias', $type)`
198+
199+
### TBD
200+
201+
* No embeddables
202+
* No relation inheritance
203+
204+
### Todo
205+
206+
* Bad `Finder`|`Searcher`|`Counter` instantiation
207+
* No cache invalidation
208+
*

composer.json

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"name": "bankiru/doctrine-api-client",
3+
"description": "Doctrine-faced RPC API Client",
4+
"authors": [
5+
{
6+
"name": "Pavel Batanov",
7+
"email": "batanov@banki.ru"
8+
}
9+
],
10+
"license": "MIT",
11+
"minimum-stability": "stable",
12+
"require": {
13+
"php": "~5.5|~7.0",
14+
"psr/cache": "~1.0",
15+
"psr/log": "~1.0",
16+
"scaytrase/rpc-common": "~1.0",
17+
"doctrine/common": "~2.5",
18+
"doctrine/collections": "~1.3",
19+
"symfony/yaml": "~2.3|~3.0",
20+
"symfony/property-access": "~2.3|~3.0"
21+
},
22+
"require-dev": {
23+
"phpunit/phpunit": "~4.5|~5.1",
24+
"scaytrase/json-rpc-client": "~1.0, >=1.0.1"
25+
},
26+
"suggest": {
27+
"psr/cache-implementation": "For entity cache support",
28+
"psr/log-implementation": "For logging support"
29+
},
30+
"autoload": {
31+
"psr-4": {
32+
"Bankiru\\Api\\": "src/Bankiru/Api"
33+
}
34+
},
35+
"extra": {
36+
"branch-alias": {
37+
"dev-master": "1.0-dev"
38+
}
39+
}
40+
}

phpunit.xml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<phpunit
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.8/phpunit.xsd"
6+
backupGlobals="false"
7+
backupStaticAttributes="false"
8+
colors="true"
9+
convertErrorsToExceptions="true"
10+
convertNoticesToExceptions="true"
11+
convertWarningsToExceptions="true"
12+
processIsolation="false"
13+
stopOnFailure="false"
14+
bootstrap="./vendor/autoload.php">
15+
16+
<php>
17+
<const name="CACHE_DIR" value="./var/cache/test"/>
18+
</php>
19+
20+
<testsuites>
21+
<testsuite name="Doctrine API Tests">
22+
<directory>./src/</directory>
23+
</testsuite>
24+
</testsuites>
25+
26+
<filter>
27+
<whitelist>
28+
<directory>./src</directory>
29+
<exclude>
30+
<directory>./vendor</directory>
31+
</exclude>
32+
</whitelist>
33+
</filter>
34+
</phpunit>

src/Bankiru/Api/ClientRegistry.php

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
namespace Bankiru\Api;
4+
5+
use ScayTrase\Api\Rpc\RpcClientInterface;
6+
7+
final class ClientRegistry implements ClientRegistryInterface
8+
{
9+
/** @var RpcClientInterface[] */
10+
private $clients = [];
11+
12+
/**
13+
* @param string $name
14+
*
15+
* @return RpcClientInterface
16+
* @throws \OutOfBoundsException
17+
*/
18+
public function get($name)
19+
{
20+
if (!$this->has($name)) {
21+
throw new \OutOfBoundsException(sprintf('Client "%s" not registered', $name));
22+
}
23+
24+
return $this->clients[$name];
25+
}
26+
27+
/**
28+
* @param string $name
29+
*
30+
* @return bool
31+
*/
32+
public function has($name)
33+
{
34+
return array_key_exists($name, $this->clients);
35+
}
36+
37+
/**
38+
* @param string $name
39+
* @param RpcClientInterface $client
40+
*
41+
* @return void
42+
* @throws \InvalidArgumentException
43+
*/
44+
public function add($name, RpcClientInterface $client)
45+
{
46+
if ($this->has($name)) {
47+
throw new \InvalidArgumentException();
48+
}
49+
50+
$this->replace($name, $client);
51+
}
52+
53+
/**
54+
* @param string $name
55+
* @param RpcClientInterface $client
56+
*
57+
* @return void
58+
*/
59+
public function replace($name, RpcClientInterface $client)
60+
{
61+
$this->clients[$name] = $client;
62+
}
63+
64+
/**
65+
* @return RpcClientInterface[]
66+
*/
67+
public function all()
68+
{
69+
return $this->clients;
70+
}
71+
}

0 commit comments

Comments
 (0)