webkenny
webkenny

Reputation: 221

Class design for multiple profile types using Doctrine

This is more of a best practices in design question. Given how many possible answers there may be, I'll keep it brief and simple.

I am using FOSUserBundle to provide some basic User management to an application I am building. Imagine for a moment that we have Users in my application and Users could have 1 of 2 types of profiles associated with them (1:1).

What would be a best practice to setting this up? i.e. The user object holds common fields for all users (things like first name, last name, email, etc.) but the other profiles will have varying fields in them.

User Profile Type A Profile Type B

I know I need additional classes and some kind of relationship between them so should I...

Thank you so much. It's been a while since I did OO design (missed it though!) and I'm kind of rusty on this sort of thing.

Upvotes: 2

Views: 447

Answers (1)

Matteo
Matteo

Reputation: 39410

In a similar situation (one user with multiple possibile profile active) we modeling the situation with a one2many from user to roles and the roles table is a doctrine2 Mapped Superclasses with Single Table Inheritance (see this doc).

Some example code for describe the situation:

The User Doctrine Class:

<?php
namespace Acme\SecurityBundle\Entity;

/**
 *
 * @ORM\Table(name="acme_user")
 * @ORM\Entity()
 */
class AcmeUser implements AdvancedUserInterface
{

    /**
     * One-To-Many
     *
     * @ORM\OneToMany(targetEntity="Acme\SecurityBundle\Entity\AcmeUserRoles", mappedBy="acmeUser",cascade={"persist"})
     */
    protected $acmeRoles;
    .....

The Role Doctrine Class:

<?php
namespace Acme\SecurityBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\Role\RoleInterface;

/**
 * @ORM\Entity
 * @ORM\Table(name="acme_user_roles")
 * @ORM\InheritanceType("SINGLE_TABLE")
 * @ORM\DiscriminatorColumn(name="discr", type="string")
 * @ORM\DiscriminatorMap({"customerRole" = "ACmeCustomerRole", "user" = "AcmeUserRoles","driver" = "AcmeDriverRole"})
 */
class AcmeUserRoles implements RoleInterface
{


    /**
     * Bidirectional
     *
     * @ORM\ManyToOne(targetEntity="Acme\SecurityBundle\Entity\AcmeUser", inversedBy="acmeRoles",cascade={"persist"})
     * @ORM\JoinColumn(name="acme_user_id", referencedColumnName="id", onDelete="cascade")
     */
    protected $acmeUser;


    /**
     * Bidirectional
     *
     * @ORM\ManyToOne(targetEntity="Acme\SecurityBundle\Entity\AcmeRole", inversedBy="acmeUserRoles",cascade={"persist"})
     * @ORM\JoinColumn(name="acme_role_id", referencedColumnName="id", onDelete="cascade")
     */
    protected $acmeRole;

    /**
     * Implementation of getRole for the RoleInterface.
     *
     * @return string The role.
     */
    public function getRole()
    {
        return $this->getAcmeRole()->getName();
    }
    .....

The Role Description Doctrine Class

<?php
namespace Acme\SecurityBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
 * @ORM\Entity
 * @ORM\Table(name="acme_role")
 **/
class AcmeRole
{
    const USER_ROLE_NAME = "ROLE_ACME_USER";
    const DRIVER_ROLE_NAME = "ROLE_ACME_DRIVER";
    ....

    /**
     * @ORM\ManyToMany(targetEntity="AcmeModule", inversedBy="rolePermissionsTemplate")
     * @ORM\JoinTable(name="acme_role_permission_template",
     *  joinColumns={@ORM\JoinColumn(name="role_id", referencedColumnName="id", onDelete="cascade")},
     *  inverseJoinColumns={@ORM\JoinColumn(name="module_id", referencedColumnName="id", onDelete="cascade")}
     * )
     */
    protected $permissionsTemplate;

    /**
     * One-To-Many
     *
     * @ORM\OneToMany(targetEntity="Acme\SecurityBundle\Entity\AcmeUserRoles", mappedBy="acmeRole",cascade={"persist"})
     */
    protected $acmeUserRoles;
    .....

Hope this help

Upvotes: 3

Related Questions