Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Doctrine - decoupled model from entity

Here is the deal. Using Doctrine ORM for PHP and I need to "decouple" model from entity persistent layer. Say we have UserEntity which holds all the pretty stuff for db mapping, as: annotations, properties, setters/getters and so on. From the other hand i'd like to have a separate User class which only holds business related logic, for instance: User::getFullName(). Furthermore i want User to extend UserEntity so User inherits all the access methods.

Possible solutions i've checked through don't work for me:

  • just extending model from entity and then specifying model in DQL does not work
  • make UserEntity /** @MappedSuperclass */ does not work since in this case UserEntity "is not itself an entity"
  • InheritanceType / DiscriminatorColumn / DiscriminatorMap does not work as well cause model is not an entity

any ideas ?

like image 456
Sergey Poskachey Avatar asked Oct 21 '22 01:10

Sergey Poskachey


1 Answers

Gotcha ! (>= PHP 5.4 solution)

Short: make entity a trait, use entity trait in model class. Checkout this doc page: http://doctrine-orm.readthedocs.org/en/latest/tutorials/override-field-association-mappings-in-subclasses.html.

Example: Suppose we have user model. First create user entity:

/**
 * @ORM\Table(name="user")
 */
trait UserEntity {

     /**
      * @var integer
      *
      * @ORM\Column(name="id", type="integer", nullable=false)
      * @ORM\Id
      * @ORM\GeneratedValue(strategy="IDENTITY")
      */
      protected $id;

     /**
      * @var string
      *
      * @ORM\Column(name="first_name", type="string", length=100, nullable=true)
      */
      protected $firstName;

     /**
      * @var string
      *
      * @ORM\Column(name="last_name", type="string", length=100, nullable=true)
      */
      protected $lastName;

      // other mapping here...
}

Then create a model and use an entity trait in it:

/**
 * @ORM\Entity
 */
class User {
    use UserEntity;

    public function getFullName() {
        return $this->firstName . ' ' . $this->lastName;
    }
}

Mind @Entity annotation in User model. This is required in order to use model directly in entity manager.

Now suppose we need an Admin model which extends User one. This is a bit tricky. We have to change @Entity to @MappedSuperclass in User so it can be extended. Then create Admin model, declare it as @Entity and also redeclare a table name on it using @Table annotation (otherwise Doctrine will be confused from which table to fetch for some reason). Look like this:

/**
 * @ORM\MappedSuperclass
 */
class User {
    use UserEntity;

    public function getFullName() {
        return $this->firstName . ' ' . $this->lastName;
    }
}

/**
 * @ORM\Entity
 * @ORM\Table(name="user")
 */
class Admin extends User {
    public function getFullName() {
        return parent::getFullName() . ' (admin)';
    }
}

This way both User and Admin models (=entities) can be used in Doctrine.

And no we can do whatever we would normally do with entities: find() them via entity manager, use models directly in queries, etc.

like image 100
Sergey Poskachey Avatar answered Oct 27 '22 08:10

Sergey Poskachey