Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.

Symfony & Doctrine

395 Aufrufe

Veröffentlicht am

Introducción a Doctrine, y algunas notas sobre su integración en Symfony, la separación entre el concepto Entity de Doctrine y nuestras entidades de dominio (DDD)

Veröffentlicht in: Ingenieurwesen
  • Loggen Sie sich ein, um Kommentare anzuzeigen.

Symfony & Doctrine

  1. 1. Symfony & Databases: Doctrine FTW Configuración, creación y mapeo de Entidades, uso bajo DDD @obokaman
  2. 2. ¿Qué es Doctrine? Un conjunto de componentes que nos ofrecen un sistema de persistencia de datos en PHP Aporta una capa de abstracción entre nuestra aplicación y la base de datos Compatible tanto con BD relacionales (MySQL, PostgreSQL, SQLite…) como con BD NoSQL (MongoDB, CouchDB…)
  3. 3. Doctrine ORM Doctrine DBAL PDO Enlaza el modelo relacional de la BD 
 con modelado basado en objetos. API de abstracción de acceso a base
 de datos propia de Doctrine API básica de acceso a base de datos
  4. 4. DBAL: ¿Qué pinta tiene? <?php
 
 use DoctrineDBALConfiguration;
 use DoctrineDBALDriverManager;
 
 $config = new Configuration();
 
 $connection_params = [
 'dbname' => 'uvinum',
 'user' => 'uvinum',
 'password' => 'alpanpanyalvinovino',
 'host' => 'localhost',
 'drive' => 'pdo_mysql'
 ];
 
 $connection = DriverManager::getConnection($connection_params, $config);
 $statement = $connection->query("SELECT * FROM users WHERE email = :email AND name = :name");
 $statement->bindValue(':email', 'albert.garcia@uvinum.com');
 $statement->bindValue(':country', 'Albert');
 
 while ($row = $statement->fetch())
 {
 echo '<p>' . $row['username'] . '</p>';
 }
  5. 5. ¿Y lo del ORM? Tiene buena pinta…
  6. 6. ORM: ¿Qué pinta tiene? $connection_params = [
 // ...
 ];
 $config = Setup::createAnnotationMetadataConfiguration(['path/to/entities']);
 $entity_manager = EntityManager::create($connection_params, $config);
 
 $entity_repository = $entity_manager->getRepository(MyEntity::class);
 $entity = $entity_repository->find(12);
 $entity->changeEmail('new@email.com');
 $entity_manager->persist($entity);
 $entity_manager->flush();
  7. 7. ORM Modelado Code First VS DB First Transforma datos de la BD a objetos PHP (entidades)… y viceversa. Permite definir las relaciones entre múltiples entidades y transformarlas en relaciones entre campos de la BD. Ahorra repetir mismas estructuras de SQL en todos los repositorios. Aplica automáticamente buenas prácticas (protección SQL Injection, p.e.)
  8. 8. Un momeeeEento…
  9. 9. ORM: contras / riesgos Agrega overhead Puede ejecutar queries SQL no óptimas (si no se usa adecuadamente) Alto riesgo de acoplarnos a Doctrine en nuestra aplicación al tratar de sacar el 100% de partido a temas como el Unit of Work.
  10. 10. ORM: El Entity Manager Data Mapper
 El objeto de Doctrine que implementa este patrón es el Entity Manager. Realiza las operaciones en BD relacionadas con las Entities gestionadas. Hidrata los objetos con los datos obtenidos de BD Unit of Work
 Empleado por el Entity Manager para acceder a la BD de forma transaccional. Mantiene el estado de las entidades gestionadas por el Entity Manager. Permite aplicar a la BD únicamente aquellas operaciones necesarias para reflejar los cambios sufridos por las entidades gestionadas.
  11. 11. Entidades y mapeo de datos
  12. 12. Entidades y mapeo de datos /**
 * @ORMEntity
 * @ORMTable(name="product")
 */
 class Product
 {
 /**
 * @ORMColumn(type="integer")
 * @ORMId
 * @ORMGeneratedValue(strategy="AUTO")
 */
 private $id;
 
 /**
 * @ORMColumn(type="string", length=100)
 */
 private $name;
 
 /**
 * @ORMColumn(type="decimal", scale=2)
 */
 private $price;
 
 /**
 * @ORMColumn(type="text")
 */
 private $description;
 }
 AppBundleEntityProduct:
 type: entity
 table: product
 id:
 id:
 type: integer
 generator: { strategy: AUTO }
 fields:
 name:
 type: string
 length: 100
 price:
 type: decimal
 scale: 2
 description:
 type: text Annotations YAML
  13. 13. Custom Types http://doctrine-orm.readthedocs.io/en/latest/cookbook/custom-mapping-types.html <?php
 use DoctrineDBALTypesTextType;
 use DoctrineDBALPlatformsAbstractPlatform;
 class DeliveryTimeType extends TextType
 {
 const DELIVERY_TIME = 'delivery_time';
 public function convertToPHPValue($value, AbstractPlatform $platform)
 {
 $value = parent::convertToPHPValue($value, $platform);
 $value = explode('|', $value);
 return new DeliveryTime(
 $value[0],
 new Hours($value[1])
 );
 }
 public function convertToDatabaseValue($value, AbstractPlatform $platform)
 {
 return implode(
 '|',
 [
 $value->shipping_method(),
 $value->toHours()
 ]
 );
 }
 public function getName()
 {
 return self::DELIVERY_TIME;
 }
 }
  14. 14. Relaciones entre entidades One-to-One
 Por ejemplo, una relación entre un usuario y un carrito. One-to-Many
 Por ejemplo una relación de una categoría con los productos que cuelgan de ella Many-to-One
 Por ejemplo un empleado a su departamento. Many-to-Many
 Por ejemplo una dirección de email a una lista de distribución http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html
  15. 15. Relaciones entre entidades /**
 * @ORMEntity()
 */
 class Company
 {
 //...
 
 /**
 * @ORMOneToOne(targetEntity="Employee", inversedBy="owned_company", cascade={"remove"})
 */
 private $owner;
 
 /**
 * @ORMOneToMany(targetEntity="Department", mappedBy="company", cascade={"remove"})
 */
 private $departments;
 
 /**
 * @ORMOneToMany(targetEntity="Employee", mappedBy="company", cascade={"remove"})
 */
 private $employees;
 
 /**
 * @ORMManyToMany(targetEntity="Employee", inversedBy="managed_companies", cascade={"remove"})
 * @ORMJoinTable(name="company_manager")
 */
 private $managers;
  16. 16. Integración en Symfony
  17. 17. Configuración doctrine:
 dbal:
 driver: pdo_sqlite
 host: "%database_host%"
 port: "%database_port%"
 dbname: "%database_name%"
 user: "%database_user%"
 password: "%database_password%"
 charset: UTF8
 path: "%database_path%"
 
 orm:
 auto_generate_proxy_classes: "%kernel.debug%"
 naming_strategy: doctrine.orm.naming_strategy.underscore
 auto_mapping: true app/config/config.yml parameters:
 database_host: 127.0.0.1
 database_port: ~
 database_name: symfony
 database_user: root
 database_password: ~
 database_path: '%kernel.root_dir%/db.db3' app/config/parameters.yml.dist
  18. 18. Entidades de dominio != Entidades Doctrine Nuestro dominio no debería conocer detalles de implementación / infraestructura (Acoplamiento a ArrayCollections, p.e.) Reutilizar las entidades de Doctrine puede obligarnos a aplicar cambios a nuestro Domain Model (collections de entidades relacionadas) Métodos de nuestras entidades podrían ejecutar “mágicamente” lógica de Doctrine. (Lazy loading de entidades relacionadas, p.e.)
  19. 19. Entidades de dominio != Entidades Doctrine
  20. 20. Entidades de dominio != Entidades Doctrine
  21. 21. Entidades de dominio != Entidades Doctrine
  22. 22. Solución propuesta: Separación y mapeo mediante nuestros repositorios class UserRepository implements UserRepositoryContract
 {
 /** @var EntityManager */
 private $em;
 /** @var DoctrineUserRepository */
 private $repo;
 public function __construct(EntityManager $an_entity_manager)
 {
 $this->em = $an_entity_manager;
 $this->repo = $this->em->getRepository(DoctrineUser::class);
 }
 public function find(UserId $a_user_id)
 {
 $result = $this->repo->find((string) $a_user_id);
 return $this->hydrateItem($result);
 } private function hydrateItem(DoctrineUser $result = null)
 {
 if (empty($result)) return null;
 $creation_date = DateTimeImmutable::createFromMutable($result->getCreationDate());
 $user = new User(
 new UserId($result->getId()), $result->getName(), new Email($result->getEmail()), $creation_date
 );
 return $user;
 }
 }
  23. 23. Comandos útiles $ bin/console doctrine:create:database (--force)
 crea la base de datos (vacía) en base a la configuración de la aplicación
 $ bin/console doctrine:drop:database (--force)
 elimina la base de datos a la que se referencia en la configuración
 $ bin/console doctrine:schema:update (--force)
 aplica las modificaciones necesarias al esquema de BD para que encaje con el modelado y el mapeo de campos y relaciones de los entities actuales
 $ bin/console doctrine:mapping:info
 nos muestra todas las entidades mapeadas actualmente con Doctrine
  24. 24. Herramientas interesantes Fixtures
 Las Fixtures se usan para cargar en la BD una serie de datos iniciales o de prueba que nos permitan disponer de una versión inicial “usable” de nuestra aplicación, lista para entornos de desarrollo o testing.
 
 http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html Migrations
 Las Migrations añaden la posibilidad de automatizar la aplicación de las queries necesarias para modificar la estructura de BD para que siga las modificaciones realizadas al mapeo de entidades y sus relaciones, así como para mover los datos necesarios a las nuevas estructuras de datos. 
 
 http://symfony.com/doc/current/bundles/DoctrineMigrationsBundle/index.html
  25. 25. Comandos útiles $ bin/console doctrine:fixtures:load
 ejecuta las Fixtures que estén disponibles
 $ bin/console doctrine:migrations:diff
 crea una clase Migration que contiene las queries necesarias para migrar los datos de la BD actual para que cumpla con el modelado y mapeado de datos de las Entities actuales.
 $ bin/console doctrine:migrations:execute 201609…
 ejecuta una migración de datos contenida en determinada clase Migration

  26. 26. Mola, pero… Vamos a meterle mano al código
  27. 27. Workshop 1.Aplicaremos los cambios necesarios al proyecto en symfony_playground para gestionar nuestra entity User con el ORM de Doctrine 2.Aparece una nueva Entity “Skills”. Cada usuario debería poder tener una lista de Skills (habilidades). No se podrán “compartir” skills entre usuarios. Las skills pertenecerán únicamente a un usuario. Si eliminamos un usuario, deberán eliminarse también sus skills.

×