This is related to my other question: Persisting entities using a REST API.
For a project in Symfony2 I need to be able to persist entities using an remote (third-party) RESTful API. I also want to be able to retrieve entities with data from that API.
In other words, my objects are saved in the third-party database. They are not saved in my own database. Whenever I need to save data, or find data, I use their REST API.
I have been pointed to several libraries, including one made by Doctrine itself. However, none of them offers me what I'm looking for. The one made by Doctrine is the best option, but uses the Active Record pattern and doesn't offer all the sweet Doctrine 2 stuff. Don't get me wrong, I've been using Active Record implementations for a long time, but I've fallen in love with Doctrine's Data Mapper pattern now.
Ideally, I'd like to be able to use Doctrine's ORM and simply replace the database-specific part with logic that saves entities using an API call. (and of course retrieves them using that same API). This way I can save my entities using roughly the same syntax:
// current way to save $entity in database:
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
// desired way to save $entity using REST API:
// (just an example, it doesn't have to be exactly like this)
$em = $this->getDoctrine()->getManager('rest');
$em->persist($entity);
$em->flush();
Note that I'm not trying to build my own API, I'm simply trying to communicate with a third party API in order to save my entities. I'm relatively new to Doctrine, but I'm liking it so far. I really like the idea of seperating the persistence logic from the entities, but so far I can't find out how I can use that to save them using an API.
There is an article in Symfony's documentation, describing how to work with multiple Entity Managers. I'm looking for a solution similar to this, but with an entity manager that enables me to use REST instead of the DB.
I've been trying to tweak Doctrine's ORM myself, but I only end up rewriting half their code because it (seems to be) too tightly coupled to the Database-specific logic. I might be doing something stupid of course.
So my question is, is there a way to replace / override the database-specific parts of Doctrine's ORM with custom ones? Without rewriting a lot of things that should be common for all persistence methods? Has it been done before? Or is it simply not possible because Doctrine is intended for use with a database and isn't flexible enough for other uses?
My own progress
CakePHP seems to be able to do this, by letting you define a custom DataSource. This way you can save your models using an SQL database, but also using an API, sessions, etc. I want to do roughly the same, but using Doctrine instead of CakePHP.
Update 1
The actual database queries seem to be executed by the
Doctrine\ORM\Persisters\BasicEntityPersister
class. There are several other xxxPersister classes, to deal with different types of inheritance. It might be possible to replace the xxxPersister classes with our own, so we can replace the DB code with REST API code.
The persister objects are created within the getEntityPersister()
method of the Doctrine\ORM\UnitOfWork
class. The classnames are hardcoded so we need to override Doctrine\ORM\UnitOfWork
if we want to use our own persisters.
Update 2
Doctrine\ORM\UnitOfWork
seems to be hardcoded into Doctrine\ORM\EntityManager
, so we need to override that one as well. However, this class seems to contain some database-specific parts. For instance, it's constructor requires a Doctrine\DBAL\Connection
object as parameter. Perhaps it's better to create our own EntityManger (implementing the Doctrine\Common\Persistence\ObjectManager
interface), as long as that doesn't take too much time / effort.
Update 3
The database-specific code for retrieving/loading/finding objects lives in the same class as the code for persisting / deleting etc: the Doctrine\ORM\Persisters\xxxPersister
classes. So if we are able to replace them with our own, in order to persist objects, we can retrieve objects as well. When you call $entityRepository->findAll()
, for instance, it will return $entityRepository->findBy(array())
(because findAll()
is simply an alias for findBy(array())
) which will run the following code:
$persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName);
return $persister->loadAll($criteria, $orderBy, $limit, $offset);
In other words, once we get EntityManager
to create the right UnitOfWork
and xxxPersister
objects, we will be able to use the find
methods in the EntityRepository
.
Update 4
I discovered that a new feature is developed for Doctrine: custom persisters (also see this). This should make it easier to use a custom persister class. I don't know yet if it will enable us to create a non-DB persister, but it looks promising. However, the last updates were in August, so I'm not sure if it's still in active development.
I'm not sure, but you can try to use lifecycle callback events for entities to perform persisting logic via REST.
I wanted to do a similar thing, so I built this library to help expose doctrine entities as RESTful resources. It has a fair amount of features, and allows you to define exactly what you want to have exposed via both pull (GET) and push (POST/PUT/PATCH) methods.
http://leedavis81.github.io/drest/
https://github.com/leedavis81/drest
Hope it helps
You might use https://github.com/doctrine/rest to build a REST client, which talks to the target server. The essential part here is the mapping from entity (local) to REST API (target).
In short: Doctrine2 (local DB) -> Rest client (entity to rest mapping) -> Request (target server)
Doctrine/Rest provides also the other way around: a Doctrine Rest Server, to expose your local entities via REST (requests to your server). But thats not what you are looking for.
DoctrineRestDriver is exactly doing what you are looking for. https://github.com/CircleOfNice/DoctrineRestDriver
Configure Doctrine:
doctrine: dbal: driver_class: "Circle\\DoctrineRestDriver\\Driver" host: "http://www.your-url.com/api" port: 80 user: "Circle" password: "CantRenember"
Build entity:
Let's assume we have used the value http://www.yourSite.com/api/products for the product entity's @Table annotation.
Controller:
You can even use DQL or native queries.
As a ready-to-use solution wasn't available, I decided to write my own. I called it RAPL. It's heavily inspired by Doctrine's ORM (in fact, it uses many of the interfaces provided by Doctrine Common).
Using RAPL I can simply write a small YAML file to configure the mapping between my entities and the web service, allowing me to persist/retrieve entities using the custom EntityManager.
I think you are in not right way.
I'm not ready to dig into the documentation now, but I understand doctrine stack as:
ORM -> DQL (doctrine query language) ->dbal ->Some database sql
And point for implementation you feature in DBAL as custom database driver.
I think create common REST-Driver realy interesting feature and it will do easy integration with third-party services.