Our Blog

Doctrine and goodies

Viktor
by Viktor on Fri 13 February 2015 No comments

If you have some experience in programming using the .NET framework and are familiar with Entity Framework, you would be pleasantly surprised to find the Doctrine2 object mapper. If you are not, an ‘Object-Relational Mapper’ is a library that puts a layer between your object-oriented business logic and the database layer. Needless to say, that this new layer makes database type mostly irrelevant, as Docrine2 has support for most widely used database engines these days (MySQL, PostgreSQL, MongoDB, CouchDB e.t.c.).The purpose of this is to have something like ‘a virtual database of objects’, which you can use directly in your code.

In a recent project we used a combination of Doctrine2, Symfony2, JMSSerializerBundle (for symfony), FOSRestBundle (for symfony) to make an api for partial data out of very sensitive data. You would probably develop a custom mapper or use View Models to fix that problem, but with combination of Doctrine2 and the JMSSerializer you can do that in a blink.

Let’s say we have the following situation:

diagram

where user_payments.user_id references with a many-to-one relationship the users table.

//Model/User.php
use Doctrine\ORM\Mapping as ORM;
class User
{
/**
 * @var integer
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @var integer
 * @ORM\Column(name="refId", type="integer", unique=true))
 */
private $refId

/**
 * @var string
 * @ORM\Column(name="first_name", type="string", length=255)
 */
private $first_name

/**
 * @var string
 * @ORM\Column(name="last_name", type="string", length=255)
 */
private $last_name

/**
 * @var string
 * @ORM\Column(name="email", type="string", length=255)
 */
private $email

/**
 * @ORM\OneToMany(targetEntity="UserPayments", mappedBy="user")
 */
private $user_payments

 /**
 * Get id
 *
 * @return int
 */
function getId()
{
 return $this->id;
}
 /**
 * Set id
 *
 * @param int $id
 * @return User
 */
function setId($id)
{
 $this->id = $id;
 return $this;
}

.....

 /**
 * Add payment
 *
 * @param UserPayment $payment
 * @return User
 */
function addPayment(UserPayment $payment)
{
 $this->user_payments->add($payment);
 return $this;
}
 /**
 * Remove payment
 *
 * @param UserPayment $payment
 * @return User
 */
function removePayment(UserPayment $payment)
{
 $this->user_payments->removeElement($payment);
 return $this;
}
 /**
 * Get payments
 *
 * @return Collection
 */
function getPayments()
{
 return $this->user_payments;
}
}

and the other model

//Model/UserPayment.php
use Doctrine\ORM\Mapping as ORM;
class UserPayment
{
/**
 * @var integer
 * @ORM\Column(name="refId", type="integer", unique=true))
 */
private $id;

/**
 * @ORM\ManyToOne(targetEntity="User", inversedBy="user_payments")
 * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
 */
private $user

/**
 * @ORM\ManyToOne(targetEntity="Order", inversedBy="user_payments")
 * @ORM\JoinColumn(name="order_id", referencedColumnName="id")
 */
private $order

/**
 * @var string
 * @ORM\Column(name="last_name", type="string", length=255)
 */
private $payment_type

/**
 * @var string
 * @ORM\Column(name="last_name", type="string", length=255)
 */
private $payment_status

 /**
 * Get id
 *
 * @return int
 */
function getId()
{
 return $this->id;
}
 /**
 * Set id
 *
 * @param int $id
 * @return UserPayment
 */
function setId($id)
{
 $this->id = $id;
 return $this;
}

.....

 /**
 * Get user
 *
 * @return User
 */
function getUser()
{
 return $this->user;
}
 /**
 * Set user
 *
 * @param User $user
 * @return UserPayment
 */
function setUser(User $user)
{
 $this->user = $user;
 return $this;
}
}

So now the question arises on how to hide the fields we would not want to share via the API. It is pretty easy, just add:

use JMS\Serializer\Annotation\ExclusionPolicy;
use JMS\Serializer\Annotation\Expose;
use JMS\Serializer\Annotation\Exclude;
/**
 * @ExclusionPolicy("none") //<- Adding this annotation says to the model that by default no property should be hidden
 */
class User

....
/**
 * @var integer
 * @ORM\Column(name="refId", type="integer", unique=true))
 * @Expose         //By this annotation you tell the Serializer that
 *                   this property should exist in the model.
 */
private $refId

/**
 * @var string
 * @ORM\Column(name="first_name", type="string", length=255)
 * @Exclude         //By this annotation you tell the Serializer that
 *                    this property should not exist in the model.
 */
private $first_name

/**
 * @var string
 * @ORM\Column(name="last_name", type="string", length=255)
 * @Exclude
 */
private $last_name

/**
 * @var string
 * @ORM\Column(name="email", type="string", length=255)
 * @Exclude
 */
private $email

On the controller part just add the following code:


use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Request\ParamFetcher;

class ApiController extends FOSRestController
{
    public function getUsersAction(ParamFetcher $paramFetcher)
    {
        $em = $this->getDoctrine()->getManager();
        $entities = $em->getRepository('MySuperAwsomeBundle:User')->findAll();
        $view = $this->view($entities, 200);
        return $this->handleView($view);
    }

Note that FOSRestBundle will take care of the output structure (use it with JSON, XML or HTML (if appropriate View template for the HTML is defined)).

ViktorDoctrine and goodies

Related Posts

Take a look at these posts

Join the conversation