I would like to know if exist a way to add fields on the fly to any entity on Symfony2. I'm searching on the big internet and I didn't find anything. When I said "a way", I mean if exist a Doctrine Extension with that behavior, a bundle that implement it, design pattern, etc.
My idea is something similar to Translatable behavior of Doctrine Extensions. Supouse I have a Address entity, so I would like to add some attributes on the fly like street, number, intersections, and others but at the begining I didn't know what fields could exist.
I'm thinking something as 2 entities: Address and AddressFieldValues. Address will have specifics attributes like id, foreing keys of relationships with others classess and will be used to inject the dynamic attributes (a collections of field-values). AddressFieldValue will have the reals fields-values of Address, with the following attributes: id, address_id, field_name, field_value.
So, entity Address could be like this:
/**
* Address
*
* @ORM\Entity(repositoryClass="AddressRepository")
* @ORM\Table(name="address")
*/
class Address
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\OneToMany(
* targetEntity="AddressFieldValues",
* mappedBy="object",
* cascade={"persist", "remove"}
* )
*/
private $field_value;
public function __construct()
{
$this->field_value = new ArrayCollection();
}
public function getFieldValue()
{
return $this->field_value;
}
public function addFieldValue(AddressFieldValues $fv)
{
if (!$this->field_value->contains($fv)) {
$this->field_value[] = $fv;
$fv->setObject($this);
}
}
public function getId()
{
return $this->id;
}
}
and AddressFieldValues entity could be like this:
/**
* @ORM\Entity
* @ORM\Table(name="address_field_values",
* uniqueConstraints={@ORM\UniqueConstraint(name="lookup_unique_idx", columns={
* "object_id", "field"
* })}
* )
*/
class AddressFieldValues
{
/**
* @var integer $id
*
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue
*/
protected $id;
/**
* @var string $field
*
* @ORM\Column(type="string", length=32)
*/
protected $field;
/**
* @ORM\ManyToOne(targetEntity="Address", inversedBy="field_value")
* @ORM\JoinColumn(name="object_id", referencedColumnName="id", onDelete="CASCADE")
*/
protected $object;
/**
* @var string $content
*
* @ORM\Column(type="text", nullable=true)
*/
protected $content;
/**
* Convenient constructor
*
* @param string $field
* @param string $value
*/
public function __construct($field, $value)
{
$this->setField($field);
$this->setContent($value);
}
/**
* Get id
*
* @return integer $id
*/
public function getId()
{
return $this->id;
}
/**
* Set field
*
* @param string $field
*/
public function setField($field)
{
$this->field = $field;
return $this;
}
/**
* Get field
*
* @return string $field
*/
public function getField()
{
return $this->field;
}
/**
* Set object related
*
* @param string $object
*/
public function setObject($object)
{
$this->object = $object;
return $this;
}
/**
* Get related object
*
* @return object $object
*/
public function getObject()
{
return $this->object;
}
/**
* Set content
*
* @param string $content
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content
*
* @return string $content
*/
public function getContent()
{
return $this->content;
}
}
So, if I have the following values on table: address_field_values
id | object | field | content
1 | 1 | street | 1st Ave
2 | 1 | number | 12345
3 | 1 | intersections | 2sd Ave and 4th Ave
4 | 2 | street | 1st Ave
5 | 2 | number | 12347
6 | 2 | intersections | 2sd Ave and 4th Ave
7 | 3 | street | 1st Ave
8 | 3 | number | 12349
9 | 3 | intersections | 2sd Ave and 4th Ave
For now address table only have the following values:
| id |
| 1 |
| 2 |
| 3 |
I could like to inject those fields-values to a Address object on the fly, to do something like this:
// if I need get de Address with id = 2
$addressRepository = $em->getRepository('Address');
$address = $addressRepository->find(2);
sprintf('The address is: "%s", #"%s" between "%s".', $address->getStreet(), $address->getNumber(), $address->getIntersections());
// then it should show: The address is 1st Ave, #12347 between 2sd Ave and 4th Ave.
//
// or if I need add a new Address, do something like this:
$address = new Address();
$address->setStreet('1st Ave');
$address->setNumber('12351');
$address->setIntersections('2sd Ave and 4th Ave');
$em->persist($address);
$em->flush();
then it save the address and address_field_values, and the tables have the following values:
// address
| id |
| 1 |
| 2 |
| 3 |
| 4 |
// address_field_values
id | object | field | content
1 | 1 | street | 1st Ave
2 | 1 | number | 12345
3 | 1 | intersections | 2sd Ave and 4th Ave
4 | 2 | street | 1st Ave
5 | 2 | number | 12347
6 | 2 | intersections | 2sd Ave and 4th Ave
7 | 3 | street | 1st Ave
8 | 3 | number | 12349
9 | 3 | intersections | 2sd Ave and 4th Ave
10 | 4 | street | 1st Ave
11 | 4 | number | 12351
12 | 4 | intersections | 2sd Ave and 4th Ave
So, any ideas how can I do that?
Remember, I have as requirement in my bussiness logic that I didn't know what fields could have a Address at beginig so I need to inject the fields on the fly. I use Address as example but this behavior can be used for any entity.
Thanks in advance