ORM Doctrine

Richard Melo

@allucardster

Agenda

  • Qué es Doctrine?
  • Entidades
  • Relaciones 
  • Gestion de datos

Qué es un ORM?

"Es una técnica de programación que nos permite convertir datos entre un lenguaje de programación orientado a objetos y una base de datos relacional"

Qué es Doctrine?

"Es un ORM escrito en PHP."

Características

  • Configuración
  • Automatización
  • DQL
  • Hooks y eventos

Entidades

YAML

# src/Acme/StoreBundle/Resources/config/doctrine/Product.orm.yml
Acme\StoreBundle\Entity\Product:
    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
// src/Acme/StoreBundle/Entity/Product.php
namespace Acme\StoreBundle\Entity;
 
use Doctrine\ORM\Mapping as ORM;

class Product
{
    /**
     * @var integer
     */
    protected $id;
 
    /**
     * @var string
     */
    protected $name;
 
    /**
     * @var float
     */
    protected $price;
 
    /**
     * @var string
     */
    protected $description;
}
// src/Acme/StoreBundle/Entity/Product.php
namespace Acme\StoreBundle\Entity;
 
use Doctrine\ORM\Mapping as ORM;
 
/**
 * @ORM\Entity
 * @ORM\Table(name="product")
 */
class Product
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;
 
    /**
     * @ORM\Column(type="string", length=100)
     */
    protected $name;
 
    /**
     * @ORM\Column(type="decimal", scale=2)
     */
    protected $price;
 
    /**
     * @ORM\Column(type="text")
     */
    protected $description;
}
  • string: SQL VARCHAR.
  • integer: SQL INT.
  • boolean: SQL boolean o equivalente (TINYINT)
  • date: SQL DATETIME.
  • time: SQL TIME.
  • datetime: SQL DATETIME/TIMESTAMP 
  • text: SQL CLOB.
  • float: SQL Float 

Algunos tipos de datos

Setters y Getters

php app/console doctrine:generate:entities Acme/StoreBundle/Entity/Product
// src/Acme/StoreBundle/Entity/Product.php
namespace Acme\StoreBundle\Entity;
 
use Doctrine\ORM\Mapping as ORM;
 
/**
 * @ORM\Entity
 * @ORM\Table(name="product")
 */
class Product
{
    //...

    /**
     * @return string
     */
    getName()
    {
        return $this->name;
    }

    /**
     * @return Product
     */
    setName($name)
    {
        $this->name = $name;
        
        return $this;
    }

    //...
}
// src/Acme/StoreBundle/Entity/Product.php

namespace Acme\StoreBundle\Entity;

use Doctrine\ORM\EntityRepository;

/**
 * ProductRepository
 *
 * This class was generated by the Doctrine ORM. Add your own custom
 * repository methods below.
 */
class ProductRepository extends EntityRepository
{
}
// consulta por la clave principal (generalmente 'id')
$product = $repository->find($id);
 
// métodos con nombres dinámicos para buscar un valor en función de alguna columna
$product = $repository->findOneById($id);
$product = $repository->findOneByName('foo');
 
// obtiene todos los productos
$products = $repository->findAll();
 
// busca productos basándose en el valor de una columna
$products = $repository->findByPrice(19.99);

// busca un producto con ese nombre y ese precio
$product = $repository->findOneBy(array(
    'name'  => 'foo', 'price' => 19.99
));
 
// obtiene todos los productos con un nombre determinado
// y ordena los resultados por precio
$product = $repository->findBy(
    array('name'  => 'foo'),
    array('price' => 'ASC')
);
$ app/console doctrine:database:create
$ app/console doctrine:schema:update --force

Persistiendo Datos

// src/Acme/StoreBundle/Controller/DefaultController.php
 
// ...
use Acme\StoreBundle\Entity\Product;
use Symfony\Component\HttpFoundation\Response;
// ...

public function createAction()
{
    $product = new Product();
    $product->setName('A Foo Bar');
    $product->setPrice('19.99');
    $product->setDescription('Lorem ipsum dolor');
 
    $em = $this->getDoctrine()->getManager();
    $em->persist($product);
    $em->flush();
 
    return new Response('Created product id '.$product->getId());
}

//...

Buscando Datos

// src/Acme/StoreBundle/Controller/DefaultController.php
 
// ...
use Acme\StoreBundle\Entity\Product;
use Symfony\Component\HttpFoundation\Response;
// ...

public function showAction($id)
{
    $product = $this->getDoctrine()
        ->getRepository('AcmeStoreBundle:Product')
        ->find($id);
 
    if (!$product) {
        throw $this->createNotFoundException('No product found for id '.$id);
    }

    //...
}

//...

Actualizando Datos

// src/Acme/StoreBundle/Controller/DefaultController.php
 
// ...
use Acme\StoreBundle\Entity\Product;
use Symfony\Component\HttpFoundation\Response;
// ...

public function updateAction($id)
{
    $em = $this->getDoctrine()->getManager();
    $product = $em->getRepository('AcmeStoreBundle:Product')->find($id);
 
    if (!$product) {
        throw $this->createNotFoundException(
            'No product found for id '.$id
        );
    }
 
    $product->setName('New product name!');
    $em->flush();
 
    //...
}

//...

Eliminando Datos

// src/Acme/StoreBundle/Controller/DefaultController.php
 
// ...
use Acme\StoreBundle\Entity\Product;
use Symfony\Component\HttpFoundation\Response;
// ...

public function removeAction($id)
{
    $em = $this->getDoctrine()->getManager();
    $product = $em->getRepository('AcmeStoreBundle:Product')->find($id);
 
    if (!$product) {
        throw $this->createNotFoundException(
            'No product found for id '.$id
        );
    }

    $em->remove($product);
    $em->flush();
 
    //...
}

//...

DQL

// ...

$em = $this->getDoctrine()->getManager();

$query = $em->createQuery(
    'SELECT p
       FROM AcmeStoreBundle:Product p
      WHERE p.price > :price
   ORDER BY p.price ASC'
)->setParameter('price', '19.99');
 
$products = $query->getResult();

//...

// SELECT * FROM product WHERE price > 19.99 ORDER BY price ASC;

Query Builder

// ...

$repository = $this->getDoctrine()->getRepository('AcmeStoreBundle:Product');
 
$query = $repository->createQueryBuilder('p')
    ->where('p.price > :price')
    ->setParameter('price', '19.99')
    ->orderBy('p.price', 'ASC')
    ->getQuery();
 
$products = $query->getResult();

//...

// SELECT * FROM product WHERE price > 19.99 ORDER BY price ASC;
// src/Acme/StoreBundle/Entity/Product.php

namespace Acme\StoreBundle\Entity;

use Doctrine\ORM\EntityRepository;

/**
 * ProductRepository
 *
 * This class was generated by the Doctrine ORM. Add your own custom
 * repository methods below.
 */
class ProductRepository extends EntityRepository
{
    public function custonFindByPrice($price)
    {
        $em = $this->getEntityManager();
        $query = $em->createQuery(
          'SELECT p FROM AcmeStoreBundle:Product p WHERE p.price > :price ORDER BY p.price ASC'
        );
        $query->setParameter('price', $price);
        return $query->getResult();
    }
}
// src/Acme/StoreBundle/Controller/DefaultController.php
 
// ...
use Acme\StoreBundle\Entity\Product;
use Symfony\Component\HttpFoundation\Response;
// ...

public function findByPriceAction($price)
{
    $products = $this->getDoctrine()
        ->getRepository('AcmeStoreBundle:Product')
        ->custonFindByPrice($price)

    //...
}

//...
// src/Acme/StoreBundle/Entity/Product.php

namespace Acme\StoreBundle\Entity;

use Doctrine\ORM\EntityRepository;

/**
 * ProductRepository
 *
 * This class was generated by the Doctrine ORM. Add your own custom
 * repository methods below.
 */
class ProductRepository extends EntityRepository
{
    public function getBaseQB()
    {
        $qb = $this->createQueryBuilder('p')
            ->orderBy('p.price', 'ASC')
        ;
        return $qb;
    }

    public function custonFindByPrice($price)
    {
        $qb = $this->getBaseQB();
        $qb->where('p.price > :price')
        $qb->setParameter('price', $price);
        $query = $qb->getQuery();
        return $query->getResult();
    }
}

Relaciones

Tipos de Relaciones

  • One to many
  • One to one
  • Many to many

One to Many

// src/Acme/StoreBundle/Entity/Category.php
namespace Acme\StoreBundle\Entity;
 
use Doctrine\ORM\Mapping as ORM;
 
/**
 * @ORM\Entity
 * @ORM\Table(name="category")
 */
class Category
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;
 
    /**
     * @ORM\Column(type="string", length=100)
     */
    protected $name;

    /**
     * @ORM\OneToMany(targetEntity="Product", mappedBy="category")
     */
    protected $products;
 
    public function __construct()
    {
        $this->products = new ArrayCollection();
    }
}
// src/Acme/StoreBundle/Entity/Category.php
namespace Acme\StoreBundle\Entity;
 
use Doctrine\ORM\Mapping as ORM;
 
/**
 * @ORM\Entity
 * @ORM\Table(name="product")
 */
class Product
{
    //...

    /**
     * @ORM\ManyToOne(targetEntity="Category", mappedBy="products")
     */
    protected $category;

    /**
     * @return Category
     */
    public function getCategory()
    {
        return $this->category;
    }

    /**
     * @return Product
     */
    public function setCategory(Category $category)
    {
        $this->category = $category;
        
        return $this;
    }
}

One to One

// src/Acme/StoreBundle/Entity/Category.php
namespace Acme\StoreBundle\Entity;
 
use Doctrine\ORM\Mapping as ORM;
 
/**
 * @ORM\Entity
 * @ORM\Table(name="ticket")
 */
class Ticket
{
    //...
    /**
     * @ORM\OneToOne(targetEntity="Seat", mappedBy="ticket")
     */
    protected $seat;

    /**
     * @return Seat
     */
    public function getSeat()
    {
        return $this->seat;
    }

    /**
     * @return Ticket
     */
    public function setSeat(Seat $seat)
    {
        $this->seat = $seat;
        
        return $this;
    }
}
// src/Acme/StoreBundle/Entity/Category.php
namespace Acme\StoreBundle\Entity;
 
use Doctrine\ORM\Mapping as ORM;
 
/**
 * @ORM\Entity
 * @ORM\Table(name="seat")
 */
class Seat
{
    //...
    /**
     * @ORM\OneToOne(targetEntity="Ticket", mappedBy="seat")
     */
    protected $ticket;

    /**
     * @return Ticket
     */
    public function getTicket()
    {
        return $this->ticket;
    }

    /**
     * @return Seat
     */
    public function setTicket(Ticket $ticket)
    {
        $this->ticket = $ticket;
        
        return $this;
    }
}

Many to Many

// src/Acme/StoreBundle/Entity/User.php
namespace Acme\StoreBundle\Entity;
 
use Doctrine\ORM\Mapping as ORM;
 
/**
 * @ORM\Entity
 * @ORM\Table(name="user")
 */
class User
{
    // ...

    /**
     * @ManyToMany(targetEntity="Group", inversedBy="users")
     * @JoinTable(name="users_groups")
     */
    protected $groups;

    public function __construct() {
        $this->groups = new \Doctrine\Common\Collections\ArrayCollection();
    }

    // ...
}
// src/Acme/StoreBundle/Entity/User.php
namespace Acme\StoreBundle\Entity;
 
use Doctrine\ORM\Mapping as ORM;
 
/**
 * @ORM\Entity
 * @ORM\Table(name="group")
 */
class Group
{
    // ...
    /**
     * @ManyToMany(targetEntity="User", mappedBy="groups")
     */
    private $users;

    public function __construct() {
        $this->users = new \Doctrine\Common\Collections\ArrayCollection();
    }

    // ...
}
id first_name last_name
1 Jhon Doe
2 Pedro Perez
id user_id group_id
1 1 1
2 1 2
3 2 1
4 2 2
id name
1 Admin
2 Users

Persistiendo Datos Relacionados

// src/Acme/StoreBundle/Controller/DefaultController.php
 
// ...
use Acme\StoreBundle\Entity\Product;
use Symfony\Component\HttpFoundation\Response;
// ...

public function createAction()
{
    $category = new Category();
    $category->setName('Main Products');

    $product = new Product();
    $product->setName('A Foo Bar');
    $product->setPrice('19.99');
    $product->setDescription('Lorem ipsum dolor');
    $product->setCategory($category);
 
    $em = $this->getDoctrine()->getManager();
    $em->persist($category);
    $em->persist($product);
    $em->flush();
    //...
}

//...

Obteniendo Datos Relacionados

// src/Acme/StoreBundle/Controller/DefaultController.php
 
// ...
use Acme\StoreBundle\Entity\Product;
use Symfony\Component\HttpFoundation\Response;
// ...

public function showAction()
{
    $product = $this->getDoctrine()
        ->getRepository('AcmeStoreBundle:Product')
        ->find($id);
    $category = $product->getCategory();
 
    $categoryName = $category->getName();
    //...
}

//...
// src/Acme/StoreBundle/Entity/ProductRepository.php
public function findOneByIdJoinedToCategory($id)
{
    $query = $this->getEntityManager()
        ->createQuery(
            'SELECT p, c FROM AcmeStoreBundle:Product p
            JOIN p.category c
            WHERE p.id = :id'
        )->setParameter('id', $id);
 
    try {
        return $query->getSingleResult();
    } catch (\Doctrine\ORM\NoResultException $e) {
        return null;
    }
}
// src/Acme/StoreBundle/Entity/ProductRepository.php

public function findOneByIdJoinedToCategory($id)
{
    $qb = $this->createQueryBuilder('p')
        ->select('p, c')
        ->innerJoin('p.category', 'c')
        ->where('p.id = :id')
        ->setParameter('id', $id)
    ;
    $query = $qb->getQuery();
    try {
        return $query->getSingleResult();
    } catch (\Doctrine\ORM\NoResultException $e) {
        return null;
    }
}

Preguntas?

Muchas Gracias

Made with Slides.com