Skip to content

bankiru/doctrine-api-client

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

60 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Latest Stable Version Total Downloads Latest Unstable Version License

Build Status Scrutinizer Code Quality Code Coverage SensioLabsInsight

Doctrine-faced RPC Client

Implementation of doctrine\common interfaces with RPC interfaces (scaytrase\rpc-common)

Usage

class \MyVendor\Api\Entity\MyEntity {
    /** @var string */
    private $id;
    /** @var string */
    private $payload;
    
    public function getId() { return $this->id; }
    
    public function getPayload() { return $this->payload; }
}
  

Resources/config/api/MyEntity.api.yml content:

MyVendor\Api\Entity\MyEntity:
  type: entity
  id:
    id:
      type: int

  fields:
    payload:
      type: string
    
  client:
    name: my-client
    entityPath: my-entity

Configure EntityManager

class RpcClient implements RpcClientInterface {
    /** RpcClient impl */
}

$client = new RpcClient();

$registry = new ClientRegistry();
$registry->add('my-client', $client);

$configuration = new Configuration();
$configuration->setMetadataFactory(new EntityMetadataFactory());
$configuration->setRegistry($this->registry);
$configuration->setProxyDir(CACHE_DIR . '/doctrine/proxy/');
$configuration->setProxyNamespace('MyVendor\Api\Proxy');
$driver = new MappingDriverChain();
$driver->addDriver(
    new YmlMetadataDriver(
        new SymfonyFileLocator(
            [
                __DIR__ . '/../Resources/config/api/' => 'MyVendor\Api\Entity',
            ],
            '.api.yml',
            DIRECTORY_SEPARATOR)
    ),
    'MyVendor\Api\Entity'
);
$configuration->setDriver($driver);

$manager = new EntityManager($configuration);    

Call entity-manager to retrieve entities through your api

$samples = $manager->getRepository(\MyVendor\Api\Entity\MyEntity::class)->findBy(['payload'=>'sample']);
foreach ($samples as $sample) {
   var_dump($sample->getId());
} 

References

You could reference other API entities via relation annotations. General bi-directional self-reference relation below:

MyVendor\Api\Entity\MyEntity:
  type: entity
  id:
    id:
      type: int

  fields:
    payload:
      type: string
    
  manyToOne:
    parent:
        target: MyVendor\Api\Entity\MyEntity
        inversedBy: children
  oneToMany:
    children:
        target: MyVendor\Api\Entity\MyEntity
        mappedBy: parent

In order to make *toMany relations works flawlessly you should define the mapped class property as Entity[]|ArrayCollection as hydrator will substitute your relation property with lazy-loading collection interface.

Note on lazy-loading

Generic API is not a DB, so eager reference pre-fetching will always result in additional API query (excluding super-APIs with prefetching features, not supported now), so current implementation always assumes that all requests are extra-lazy. This means that no data will be fetched until you really need it, and you'll have only lazy proxy object before that happens.

Keep it in the mind

Hacking into fetching process

Custom repository

Just call defined RpcClient from your repository

class MyRepository extends \Bankiru\Api\Doctrine\EntityRepository 
{
    public function callCustomRpcMethod()
    {
        $request = new \Bankiru\Api\Rpc\RpcRequest('my-method',['param1'=>'value1']);
        $data = $this->getClient()->invoke([$request])->getResponse($request);
        
        return $data;
    }
} 

Or more portable with mapping

MyVendor\Api\Entity\MyEntity:
  type: entity
  id:
    id:
      type: int

  fields:
    payload:
      type: string
    
  repositoryClass: MyVendor\Api\Repository\MyRepository # This will override repository for MyEntity
  api:
    factory: Vendor\Api\CrudsApiFactory
    
  client:
    name: my-client
    # entityPath: my-entity autoconfigures find and search methods for you as following, but it is not overridable
    # You can also specify path separator as
    # entityPathSeparator: "-"
    # To make autogenerated methods look like my-entity-find 
    methods: 
        find: my-entity\find      # find method is mandatory to find calls work
        search: my-entity\search  # find method is mandatory to findBy calls work
        custom: my-custom-method  # do some custom stuff
class MyRepository extends \Bankiru\Api\Doctrine\EntityRepository 
{
    public function callCustomRpcMethod()
    {
        $request = new \Bankiru\Api\Rpc\RpcRequest(
            $this->getClientMethod('custom'),
            ['param1'=>'value1']
        );
        $data = $this->getClient()->invoke([$request])->getResponse($request);
        
        return $data;
    }
} 

Custom Cruds API

Custom field types

You could register additional field types to configuration TypeRegistry (Configuration::getTypeRegistry()). Just implement the Type and register it via TypeRegistry::add('alias', $type). Doctrine ORM types are simple transformers with no real dependencies available but for this implementation we gone a bit further and make the types DI capable, so you can register any instance of Type as type, so it could be DI-enabled service, with any logic you need

TBD

  • No embeddables