restful example automatic php rest symfony doctrine2 persistence

php - example - Guarde entidades en una API REST en lugar de DB utilizando Doctrine 2



symfony 4 rest api example (6)

Como una solución lista para usar no estaba disponible, decidí escribir la mía. Lo llamé RAPL . Está fuertemente inspirado en el ORM de Doctrine (de hecho, usa muchas de las interfaces proporcionadas por Doctrine Common).

Usando RAPL, puedo simplemente escribir un pequeño archivo YAML para configurar el mapeo entre mis entidades y el servicio web, lo que me permite persistir / recuperar entidades usando el EntityManager personalizado.

Esto está relacionado con mi otra pregunta: entidades persistentes que usan una API REST .

Para un proyecto en Symfony2, necesito poder persistir entidades usando una API RESTful remota (de terceros) . También quiero poder recuperar entidades con datos de esa API.

En otras palabras, mis objetos se guardan en la base de datos de terceros . No se guardan en mi propia base de datos. Cada vez que necesito guardar datos o buscar datos, uso su API REST.

Me han señalado varias bibliotecas, incluida una hecha por Doctrine . Sin embargo, ninguno de ellos me ofrece lo que estoy buscando. El hecho por Doctrine es la mejor opción, pero usa el patrón Active Record y no ofrece todas las cosas dulces de Doctrine 2. No me malinterpreten, he estado usando implementaciones de Active Record durante mucho tiempo, pero ahora me he enamorado del patrón Data Mapper de Doctrine.

Idealmente, me gustaría poder usar el ORM de Doctrine y simplemente reemplazar la parte específica de la base de datos con la lógica que guarda entidades usando una llamada API. (y por supuesto los recupera usando esa misma API). De esta manera puedo guardar mis entidades usando aproximadamente la misma sintaxis:

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

Tenga en cuenta que no estoy tratando de construir mi propia API, simplemente estoy tratando de comunicarme con una API de terceros para poder guardar mis entidades. Soy relativamente nuevo en Doctrine, pero me está gustando hasta ahora. Realmente me gusta la idea de separar la lógica de persistencia de las entidades, pero hasta ahora no puedo averiguar cómo puedo usar eso para guardarlas usando una API.

Hay un artículo en la documentación de Symfony que describe cómo trabajar con múltiples Administradores de Entidades . Estoy buscando una solución similar a esta, pero con un administrador de entidades que me permite usar REST en lugar de DB.

He intentado modificar el ORM de Doctrine, pero solo termino reescribiendo la mitad de su código porque (parece estar) muy unido a la lógica específica de la base de datos. Podría estar haciendo algo estúpido, por supuesto.

Entonces mi pregunta es, ¿hay alguna manera de reemplazar / anular las partes específicas de la base de datos de ORM de Doctrine con las personalizadas ? ¿Sin reescribir muchas cosas que deberían ser comunes para todos los métodos de persistencia? ¿Se ha hecho antes? ¿O simplemente no es posible porque Doctrine está destinado para su uso con una base de datos y no es lo suficientemente flexible para otros usos?

Mi propio progreso

CakePHP parece ser capaz de hacer esto, permitiéndole definir un DataSource personalizado. De esta manera puede guardar sus modelos usando una base de datos SQL, pero también usando una API, sesiones, etc. Quiero hacer más o menos lo mismo, pero usando Doctrine en lugar de CakePHP.

Actualización 1

Las consultas reales de la base de datos parecen ejecutarse por la clase Doctrine/ORM/Persisters/BasicEntityPersister . Hay varias otras clases xxxPersister, para tratar con diferentes tipos de herencia. Podría ser posible reemplazar las clases xxxPersister por las nuestras, de modo que podamos reemplazar el código DB con el código API REST.

Los objetos de persistencia se crean dentro del método getEntityPersister() de la clase Doctrine/ORM/UnitOfWork . Los nombres de clase están codificados, por lo que debemos anular Doctrine/ORM/UnitOfWork si queremos usar nuestros propios datos persistentes.

Actualización 2

Doctrine/ORM/UnitOfWork parece estar codificado en Doctrine/ORM/EntityManager , por lo que debemos reemplazarlo también. Sin embargo, esta clase parece contener algunas partes específicas de la base de datos. Por ejemplo, su constructor requiere un objeto Doctrine/DBAL/Connection como parámetro. Quizás sea mejor crear nuestro propio EntityManger (implementando la interfaz Doctrine/Common/Persistence/ObjectManager ), siempre que eso no tome demasiado tiempo / esfuerzo.

Actualización 3

El código específico de la base de datos para recuperar / cargar / encontrar objetos vive en la misma clase que el código para persistir / eliminar, etc.: las clases Doctrine/ORM/Persisters/xxxPersister . Entonces, si podemos reemplazarlos con los nuestros, para poder persistir en los objetos, también podemos recuperarlos. Cuando llame $entityRepository->findAll() , por ejemplo, devolverá $entityRepository->findBy(array()) (porque findAll() es simplemente un alias para findBy(array()) ) que ejecutará el siguiente código :

$persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName); return $persister->loadAll($criteria, $orderBy, $limit, $offset);

En otras palabras, una vez que obtengamos EntityManager para crear los UnitOfWork adecuados UnitOfWork y xxxPersister , podremos usar los métodos find en el EntityRepository .

Actualización 4

Descubrí que se desarrolló una nueva función para Doctrine: persisters personalizados (también vea this ). Esto debería facilitar el uso de una clase de persistencia personalizada. Aún no sé si nos permitirá crear un servidor persistente no DB, pero parece prometedor. Sin embargo, las últimas actualizaciones fueron en agosto, así que no estoy seguro si todavía está en desarrollo activo.


Creo que no estás en lo correcto.
No estoy listo para profundizar en la documentación ahora, pero entiendo doctrine stack como:

ORM -> DQL (lenguaje de consulta de doctrina) -> dbal -> Algunos SQL de base de datos

Y apunte a la implementación que presenta en DBAL como controlador de base de datos personalizado.

Creo que crear una característica interesante de REST-Driver es muy común y facilitará la integración con servicios de terceros.


DoctrineRestDriver está haciendo exactamente lo que estás buscando. https://github.com/CircleOfNice/DoctrineRestDriver

Configurar Doctrine:

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

Entidad de compilación:

/** * 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; } }

Supongamos que hemos utilizado el valor http://www.yourSite.com/api/products para la anotación @Table de la entidad del producto.

Controlador:

<?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(); } }

Incluso puede usar consultas DQL o nativas.



Puede usar https://github.com/doctrine/rest para compilar un cliente REST, que se comunica con el servidor de destino. La parte esencial aquí es la asignación de la entidad (local) a la API REST (objetivo).

En resumen: Doctrine2 (DB local) -> Cliente de reposo (mapeo de entidad a reposo) -> Solicitud (servidor de destino)

Doctrine / Rest también proporciona al revés: un Doctrine Rest Server, para exponer sus entidades locales a través de REST (solicitudes a su servidor). Pero eso no es lo que estás buscando.


Quería hacer algo similar, así que construí esta biblioteca para ayudar a exponer las entidades doctrinales como recursos RESTful. Tiene una buena cantidad de características y le permite definir exactamente lo que quiere que esté expuesto mediante los métodos de extracción (GET) y de inserción (POST / PUT / PATCH).

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

https://github.com/leedavis81/drest

Espero eso ayude