I struggle organising my code, and I would like to share my “problem” with you by using a simple example: the calculation of the area of a rectangle. I put the code for example, but reading the first intro on each class section explains the situation easily.
Entity Rectangle:
The entity Rectangle contains two inportant properties $length and $width.
// src/Acme/CalculationBundle/Entity/Rectangle.php
/**
* @ORM\Entity(repositoryClass="Acme\CalculationBundle\Repository\RectangleRepository")
* @ORM\Table(name="rectangle")
*/
class Rectangle
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\Column(type="integer")
*/
protected $length;
/**
* @ORM\Column(type="integer")
*/
protected $width;
FORM
Of course, the user can set length and width via a form
CONTROLLER
CreateRectangleAction: renders the form on GET request and work on the data on a POST request.
ViewRectangleAction: shows the rectangle.
RECTANGLE MANAGER
Now, to make sure the controller doesn’t do too much stuff, I use a RectangleManager to realise common operation on Rectangle objects and use it as a service (injecting the appropriate elements).
// src/Acme/CalculationBundle/Entity/RectangleManager.php
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityRepository;
use Acme\EmployerBundle\Entity\Manager;
class rectangleManager
{
/**
* Doctrine entity manager
* @var EntityManager
*/
protected $em;
/**
* @var EntityRepository
*/
protected $repository;
/**
*
* @var string
*/
protected $class;
public function __construct(EntityManager $em, $class)
{
$this->em = $em;
$this->class = $class;
$this->repository = $em->getRepository($class);
}
/**
* @param $id
* @return Rectangle
*/
public function find($id)
{
$rectangle = $this->repository->find($id);
}
THE PROBLEM: WHAT IF?
What if I need to do some calculations on the rectangle? For example, if I need to add an area property so that I can render the area directly in the template without doing the calculation (length*width) in the template?
Not knowing how to do this properly, I went for this pretty bad solution:
I created a RectangleDisplay class (where I inject the rectangle entity) and display that entity instead of the Rectangle entity when calling ViewRectangleAction in the controller.
// src/Acme/CalculationBundle/Entity/
class RectangleDisplay
{
/**
* @var Rectangle $rectangle
*/
protected $rectangle;
/**
* @var Integer
*/
protected $area;
/**
* @param Rectangle $rectangle
*/
public function __construct(rectangle $rectangle){
$this->rectangle = $rectangle;
//calculate are
$area = this->calculateArea($this->rectangle->getLength(),$this->rectangle->getWidth());
$this->area = $area;
}
/**
* @return Integer $area
*/
public function calculateArea($length,$width)
{
return $length * $width;
}
Now the property area is directly accessible in the template. In the case of a rectangle, that is fine, but in the case of a more complex element (cone, …), I might want to use a service that is crazy at doing calculation. I am not gonna inject the container in my RectangleDisplayEntity, am I?
This belongs in your entity.
The area of a rectangle is a property of a rectangle. It’s not some crazy calculation that should be delegated to another service, and it’s especially not something display-related (though you may think that based on it being displayed in your application).
Now, if you were calculating something a lot more complex, it’s probably worth moving to a service / another class. (Services = verbs, Entities = nouns and their properties).