Sauvegarder les entities dans une API REST au lieu de DB en utilisant la Doctrine 2

ceci est lié à mon autre question: entités persistantes utilisant une API REST .

pour un projet en Symfony2 je dois pouvoir persister entités en utilisant un distant (tiers) RESTful API . Je veux aussi être capable de récupérer des entités avec des données à partir de cette API.

En d'autres termes, mes objets sont enregistrés dans la base de données tierce . Ils sont pas enregistré dans ma propre base de données. Chaque fois que j'ai besoin de sauvegarder ou de trouver des données, j'utilise leur API REST.

j'ai été rappelé à plusieurs bibliothèques, y compris les faite par la Doctrine elle-même . Cependant, aucun d'eux ne m'offre ce que je cherche. Celui fait par Doctrine est la meilleure option, mais utilise le modèle de dossier actif et n'offre pas tous les trucs de doctrine douce 2. Ne vous méprenez pas, j'ai utilisé Active Record implémentations depuis longtemps, mais je suis tombé amoureux du motif Data Mapper de Doctrine maintenant.

idéalement, j'aimerais pouvoir utiliser L'ORM de Doctrine et simplement remplacer la partie spécifique à la base de données par la logique qui sauve des entités en utilisant un appel API. (et bien sûr les récupère en utilisant la même API). De cette façon, je peux sauver mes entités en utilisant à peu près la même syntaxe:

// 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();

notez que je n'essaie pas de construire ma propre API, j'essaie simplement de communiquer avec une API tiers afin de sauver mes entités. Je suis relativement nouveau en Doctrine, mais je l'aime jusqu'à présent. J'aime vraiment l'idée de séparer la logique de persistance des entités, mais jusqu'à présent je ne peux pas trouver comment je peux l'utiliser pour les sauver en utilisant une API.

il y a un article dans la documentation de Symfony, qui décrit comment travailler avec plusieurs gestionnaires D'entités . Je suis à la recherche d'une solution similaire, mais avec une entité gestionnaire de me permet d'utiliser le repos au lieu de la base de données.

j'ai essayé de modifier moi-même L'ORM de Doctrine, mais je ne finis par réécrire la moitié de leur code parce qu'il (semble être) trop étroitement couplé à la logique spécifique de la base de données. Je fais peut-être quelque chose de stupide, bien sûr.

donc ma question Est, y a-t-il un moyen de remplacer / outrepasser les parties spécifiques à la base de données de L'ORM de la Doctrine avec des personnalisés ? Sans réécrire beaucoup de choses cela devrait être commun à toutes les méthodes de persistance? Il a été fait avant? Ou est-ce tout simplement impossible parce que la Doctrine est destinée à être utilisée avec une base de données et n'est pas assez souple pour d'autres utilisations?

mon propre progrès

CakePHP semble être capable de faire cela, en vous permettant de définir un personnalisé DataSource . De cette façon, vous pouvez enregistrer vos modèles en utilisant une base de données SQL, mais aussi en utilisant une API, des sessions, etc. Je veux faire à peu près la même chose, mais en utilisant la Doctrine au lieu de CakePHP.

"1519580920 mise à jour" Update 1

les requêtes de base de données actuelles semblent être exécutées par le DoctrineORMPersistersBasicEntityPersister classe . Il existe plusieurs autres classes xxxPersister, pour traiter les différents types d'héritage. Il est possible de remplacer les classes xxxPersister par les nôtres, nous pouvons donc remplacer le code DB par le code REST API.

les objets persister sont créés dans la méthode getEntityPersister() de la classe DoctrineORMUnitOfWork . Les noms de classe sont codés en dur, nous devons donc outrepasser DoctrineORMUnitOfWork si nous voulons utiliser nos propres persisters.

Update 2

DoctrineORMUnitOfWork semble être codé en dur DoctrineORMEntityManager , nous devons donc aussi l'annuler. Cependant, cette classe semble contenir certaines parties spécifiques à la base de données. Par exemple, son constructeur nécessite un DoctrineDBALConnection objet comme paramètre. Peut-être est-il préférable de créer notre propre EntityManger (implémentation de l'interface DoctrineCommonPersistenceObjectManager ), à condition que cela ne prenne pas trop de temps ou d'effort.

Update 3

le code spécifique à la base de données pour extraire/charger/trouver des objets vit dans la même classe que le code pour persisting / deleting etc: Les classes DoctrineORMPersistersxxxPersister . Donc, si nous sommes en mesure de les remplacer par nos propres, afin de conserver les objets, nous pouvons récupérer des objets. Lorsque vous appelez $entityRepository->findAll() , par exemple, il retournera $entityRepository->findBy(array()) (parce que findAll() est simplement un alias pour findBy(array()) ) qui exécutera le code suivant:

$persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName);

return $persister->loadAll($criteria, $orderBy, $limit, $offset);

en d'autres termes, une fois que nous aurons EntityManager pour créer les objets UnitOfWork et xxxPersister , nous pourrons utiliser les méthodes find dans le EntityRepository .

Jour 4

I découvert qu'une nouvelle fonctionnalité est développée pour la Doctrine: persisters de coutume (voir aussi ce ). Cela devrait faciliter l'utilisation d'une classe persister personnalisée. Je ne sais pas encore si cela nous permettra de créer un persister non-DB, mais ça semble prometteur. Cependant, les dernières mises à jour ont eu lieu en août, donc je ne suis pas sûr que ce soit encore en cours de développement.

24
demandé sur Nic Wortel 2013-12-13 02:40:05

6 réponses

vous pouvez utiliser https://github.com/doctrine/rest pour construire un client REST, qui parle au serveur cible. La partie essentielle ici est la mise en correspondance de l'API entity (local) vers L'API REST (target).

En bref: Doctrine2 (local DB) -> Reste de client (entité reste de la cartographie) -> Request (serveur cible)

Doctrine / Rest fournit également l'autre voie autour: un serveur de doctrine Rest, pour exposer vos entités locales via REST (demande à votre serveur). Mais ce n'est pas ce que vous cherchez.

10
répondu Jens A. Koch 2013-12-16 03:00:21

DoctrineRestDriver fait exactement ce que vous recherchez. https://github.com/CircleOfNice/DoctrineRestDriver

Configuration De La Doctrine:

doctrine: dbal: driver_class: "Circle\DoctrineRestDriver\Driver" host: "http://www.your-url.com/api" port: 80 user: "Circle" password: "CantRenember"

Construire l'entité:

/**
 * This annotation marks the class as managed entity:
 *
 * @ORM\Entity
 *
 * You can either only use a resource name or the whole url of
 * the resource to define your target. In the first case the target 
 * url will consist of the host, configured in your options and the 
 * given name. In the second one your argument is used as it is.
 * Important: The resource name must begin with its protocol.
 *
 * @ORM\Table("products|http://www.yourSite.com/api/products")
 */
class Product {

    /**
     * @ORM\Column(type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=100)
     */
    private $name;

    public function getId() {
        return $this->id;
    }

    public function setName($name) {
        $this->name = $name;
        return $this;
    }

    public function getName() {
        return $this->name;
    }
}

supposons que nous ayons utilisé la valeur http://www.yourSite.com/api/products pour l'annotation @Table de l'entité de produit.

Contrôleur:

<?php

namespace CircleBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\HttpFoundation\Response;

class UserController extends Controller {

    /**
     * Sends the following request to the API:
     * POST http://www.yourSite.com/api/products HTTP/1.1
     * {"name": "Circle"}
     *
     * Let's assume the API responded with:
     * HTTP/1.1 200 OK
     * {"id": 1, "name": "Circle"}
     *
     * Response body is "1"
     */
    public function createAction() {
        $em     = $this->getDoctrine()->getManager();
        $entity = new CircleBundle\Entity\Product();
        $entity->setName('Circle');
        $em->persist($entity);
        $em->flush();

        return new Response($entity->getId());
    }

    /**
     * Sends the following request to the API by default:
     * GET http://www.yourSite.com/api/products/1 HTTP/1.1
     *
     * which might respond with:
     * HTTP/1.1 200 OK
     * {"id": 1, "name": "Circle"}
     *
     * Response body is "Circle"
     */
    public function readAction($id = 1) {
        $em     = $this->getDoctrine()->getManager();
        $entity = $em->find('CircleBundle\Entity\Product', $id);

        return new Response($entity->getName());
    }

    /**
     * Sends the following request to the API:
     * GET http://www.yourSite.com/api/products HTTP/1.1
     *
     * Example response:
     * HTTP/1.1 200 OK
     * [{"id": 1, "name": "Circle"}]
     *
     * Response body is "Circle"
     */
    public function readAllAction() {
        $em       = $this->getDoctrine()->getManager();
        $entities = $em->getRepository('CircleBundle\Entity\Product')->findAll();

        return new Response($entities->first()->getName());
    }

    /**
     * After sending a GET request (readAction) it sends the following
     * request to the API by default:
     * PUT http://www.yourSite.com/api/products/1 HTTP/1.1
     * {"name": "myName"}
     *
     * Let's assume the API responded the GET request with:
     * HTTP/1.1 200 OK
     * {"id": 1, "name": "Circle"}
     *
     * and the PUT request with:
     * HTTP/1.1 200 OK
     * {"id": 1, "name": "myName"}
     *
     * Then the response body is "myName"
     */
    public function updateAction($id = 1) {
        $em     = $this->getDoctrine()->getManager();
        $entity = $em->find('CircleBundle\Entity\Product', $id);
        $entity->setName('myName');
        $em->flush();

        return new Response($entity->getName());
    }

    /**
     * After sending a GET request (readAction) it sends the following
     * request to the API by default:
     * DELETE http://www.yourSite.com/api/products/1 HTTP/1.1
     *
     * If the response is:
     * HTTP/1.1 204 No Content
     *
     * the response body is ""
     */
    public function deleteAction($id = 1) {
        $em     = $this->getDoctrine()->getManager();
        $entity = $em->find('CircleBundle\Entity\Product', $id);
        $em->remove($entity);
        $em->flush();

        return new Response();
    }
}

vous pouvez même utiliser DQL ou requêtes natives.

7
répondu Tobias 2016-03-27 20:15:44

comme une solution prête à l'emploi n'était pas disponible, j'ai décidé d'écrire la mienne. Je l'ai appelé RAPL . Elle est fortement inspirée de L'ORM de la Doctrine (en fait, elle utilise plusieurs des interfaces fournies par la Doctrine Common).

en utilisant RAPL je peux simplement écrire un petit fichier YAML pour configurer la correspondance entre mes entités et le service web, me permettant de persister/récupérer des entités en utilisant le Custom EntityManager.

3
répondu Nic Wortel 2014-04-30 13:27:30

je pense que vous n'êtes pas dans la bonne direction.

Je ne suis pas prêt à creuser dans la documentation maintenant, mais je comprends la pile de doctrine comme:

ORM - > DQL (doctrine query language) - >dbal- > Some database sql

et le point pour la mise en œuvre vous la fonctionnalité dans DBAL comme pilote de base de données personnalisé.

je pense que créer repos commun-pilote réelle caractéristique intéressante et il fera l'intégration facile avec des tiers service.

2
répondu nonlux 2013-12-19 10:24:06

Je n'en suis pas sûr, mais vous pouvez essayer d'utiliser lifecycle callback events pour que les entités exécutent une logique persistante via REST.

1
répondu lisachenko 2013-12-21 13:06:24

je voulais faire une chose similaire, donc j'ai construit cette bibliothèque pour aider à exposer les entités de doctrine comme des ressources reposantes. Il a une bonne quantité de fonctionnalités, et vous permet de définir exactement ce que vous voulez avoir exposé à la fois par pull (GET) et push (POST/PUT/PATCH) méthodes.

http://leedavis81.github.io/drest /

https://github.com/leedavis81/drest

j'Espère que ça aide

0
répondu Lee Davis 2015-10-20 09:05:50