copy object Inheritance Mapping doctrine2 - symfony

i have the following entity:
/**
* #ORM\Table(name="event")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="eventtype", type="integer")
* #ORM\DiscriminatorMap({1 = "eventClub", 2 = "eventLive", 3 = "eventBar", 4 = "eventGeneric" })
*/
class P1event extends AbstractEntity {
/**
*
* #var List[] $lists
*
* #ORM\OneToMany(targetEntity="List", mappedBy="fkevent", cascade={"persist", "merge"})"
*/
private $lists;
A user should have the possibility to change the eventtype via a form. By changing the evetntype, i must create a new Object becaus of my table inheritance (doctrine doc).
I have no idea how i can change the lists of the copied event to the new event inside one transaction. Has anybody an idea how to handle it correctly? Thank you very much.

In a similar situation, I just passed GET parameter type, then in the controller created an object of the desired type and transferred it to the form. If I understand your question correctly.

After created a new event object, call
$eventY->setLists($eventX->getLists());
doen't work ?

Related

Symfony: Filter ArrayCollection by associated entity id

I have a User entity and a Usecase entity. This 2 entities are associated by a ManyToMany association, but this association also holds another property, called "environment". To implement this relationship I also have an entity called UserUsecase that has a ManyToOne relationship with User, a ManyToOne relationship with Usecase and the extra field "environment". When fetching a user from the database, his usecases are being fetched as well, so the user has an ArrayCollection of objects of type UserUsecase that represent all the usecases a user has. What I want to do is to filter this ArrayCollection by usecase_id. The UserUsecase class has the structure below:
class UserUsecase
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="userUsecases")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
/**
* #ORM\ManyToOne(targetEntity="Usecase", inversedBy="userUsecases")
* #ORM\JoinColumn(name="usecase_id", referencedColumnName="id")
*/
protected $usecase;
/**
* #ORM\Column(type="integer")
*/
protected $environment;
}
So I tried this inside the User class:
public function filterUsecases($usecase_id){
$criteria = Criteria::create();
$criteria->where(Criteria::expr()->eq('usecase', $usecase_id));
return $this->userUsecases->matching($criteria);
}
It makes sense to me that even the field usecase of the class UserUsecase is an object of type Usecase, it should resolve to its id and the equation would hold when the ids matched. Still this doesn't seem to work, and I cannot find how to implement this filtering. Isn't it possible to be done this way? I found a relevant article that seems to do exactly what I want but this is not working in my case. Here is the article! Am I doing something wrong?
Thanks in advance!
Unless you have many many use cases per user(thousands) I recommend:
public function filterUsecases(Usecase $useCase){
$criteria = Criteria::create();
$criteria->where(Criteria::expr()->eq('usecase', $useCase));
return $this->userUsecases->matching($criteria);
}
Then:
$user->filterUsecases($useCase);
Or passing reference
$user->filterUsecases($em->getReference(Usecase::class, $id));

Use Doctrine to merge entity with different subclass

I have a mapped superclass AbstractQuestion with single-table-inheritance.
/**
* #ORM\Entity
* #ORM\MappedSuperclass
* #ORM\Table(name="Question")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="dtype", type="string")
* #ORM\DiscriminatorMap({
* "simple": "SimpleQuestion",
* "dropdown": "DropdownQuestion"
* })
*/
abstract class AbstractQuestion
SimpleQuestion and DropdownQuestion inherit from this superclass.
/**
* Class SimpleQuestion.
* #ORM\Entity()
*/
class SimpleQuestion extends AbstractQuestion
I want to modify an existing SimpleQuestion and make it a DropdownQuestion.
When saving a question, I deserialise and merge the question, which contains an ID and the 'dtype' and other properties.
$dquestion = $this->serial->fromJson($request->getContent(), AbstractQuestion::class);
$question = $this->em->merge($dquestion);
$this->em->flush();
So I submit something like:
{ id: 12, dtype: 'dropdown', 'text': 'What is my favourite animal?'}
After the deserialisation, $dquestion is a DropdownQuestion object as I desired, but after the merge $question is a SimpleQuestion object as it was in the database previously, so any unique properties of DropdownQuestion are lost and the question is saved as a SimpleQuestion. Is there any way to work around this?
You will first have to delete the existing record (SimpleQuestion) and then insert the new record (DropdownQuestion). Type casting is not supported in Doctrine 2.
Note.
You can probably change the discriminator column with a pure SQL query, but this is absolutely not recommended and will for sure give you problems...
Check also the answers here and here since they might be interesting for you.

Doctrine Associations Mapping

I'm trying Doctrine Associations with Symfony2 for the first time and it's giving me headache.
I have an Admin interface that can, among other things, upload images. I want to know which administrator uploaded what image so i've putted a foreign key administrator to my images table. To gather data, a simple JOIN is necessary to collect the data but with Doctrine, I'm stuck, altough it seems simple.
So, I have an Administrator object that reflects the table. In that object, I have this statement...
#ORM\OneToMany(targetEntity="ImageBundleNamespace\ImageEntity", mappedBy="administrator")
It's simple. In my ImageEntity object (that reflects Images table) is a foreigh key column administrator.
In the ImageEntity object, I use this statement...
#ORM\ManyToOne(targetEntity="AdministratorNamespace\Administrator", inversedBy="imageEntity")
#ORM\JoinColumn(name="administrator", referencedColumnName="id")
There is a administrator field in ImageEntity and a imageEntity field in Administrator that the formentioned statements are mapping.
It doesn't work.
I've run the SchemaValidator on the EntityManger and it says the the administrator field on the ImageEntity object is not defined as an association. The second message says that the administrator field does not exist.
If it helps, this is my DQL for all of it...
'SELECT i.id,
i.imeSlike,
i.velicina,
i.ekstenzija,
i.paths,
a.username,
a.ime,
a.prezime FROM ImageBundle:ImageEntity i
JOIN a.administrator a'
Thank you in advance for all the help.
EDIT
I had a mistake in DQL. Corrected it.
EDIT
I forgot to add the source code.
Association part of the Administrator...
**
* #ORM\OneToMany(targetEntity="Icoo\Administracija\GalerijaBundle\Entity\ImageEntity", mappedBy="administrator")
*/
protected $imageEntity;
public function __construct() {
$this->imageEntity = new ArrayCollection();
}
Association part of the ImageEntity
/**
* #ORM\Column(type="smallint")
*
* #ORM\ManyToOne(targetEntity="Icoo\LoginBundle\Entity\Administrator", inversedBy="imageEntity")
* #ORM\JoinColumn(name="administrator", referencedColumnName="id")
*
*/
protected $administrator;
In the administrator class, you have:
/**
* #ORM\Column(type="smallint")
*
* #ORM\ManyToOne(targetEntity="Icoo\LoginBundle\Entity\Administrator", inversedBy="imageEntity")
* #ORM\JoinColumn(name="administrator", referencedColumnName="id")
*
*/
you need to remove the #ORM\Column(type="smallint")
This must be all. Let me know.
You can separate field mapping from association mapping:
/**
* #ORM\Column(name="administrator", type="smallint", nullable=false, options={"unsigned"=true})
*/
protected $administratorId;
/**
* #ORM\ManyToOne(targetEntity="Icoo\LoginBundle\Entity\Administrator", inversedBy="imageEntity")
* #ORM\JoinColumn(name="administrator", referencedColumnName="id")
**/
protected $administrator;
If you go further you can put exception into $administratorId getter/setter to avoid usage of it.
As i have tested, doctrine ignores value into $administratorId property when you persist/flush your entity (For confirmation you can look at prepareUpdateData() in Doctrine\ORM\Persisters\BasicEntityPersister)
EDIT
I think, my previous variant is possible but wrong. Because, doctrine gets field mapping from definitions in referencedColumn, you can add some more definitions using
* #ORM\JoinColumn(name="administrator", referencedColumnName="id", unique, nullable, onDelete, columnDefinition, fieldName)
This means that your image_entity.administrator field in db will be the same as your administrator.id field in db (except of additional definitions)

Symfony2 - One ManyToOne relation on one field referencing two entities

I have an entity which stores "removal requests" to either studios or models. An object (Studio or model can have many requests).
Entity RemovalRequest has a field named : object.
I would like to know if it's possible to do something like this in RemovalRequest entity:
/**
* #ORM\ManyToOne(targetEntity="Project\GestionBundle\Entity\Studio", inversedBy="requests")
* #ORM\ManyToOne(targetEntity="Project\GestionBundle\Entity\Model", inversedBy="requests")
*/
private $object;
I can't find anything about this special case over Internet..
If it's not possible, I'm open to any suggestions you might have !
Do you realy need a new entity to store information about removal? Maybe just add a flag to Studio and Model:
/**
* #ORM\Column(name="is_to_remove", type="boolean")
*/
$isToRemove = false;
If you need RemovalRequest entity you should add two properties fore each type like this:
/**
* #ORM\ManyToOne(targetEntity="Project\GestionBundle\Entity\Model", inversedBy="requests")
*/
$model;
/**
* #ORM\ManyToOne(targetEntity="Project\GestionBundle\Entity\Studio", inversedBy="requests")
*/
$studio;
It is bed idea to store two different classes in one property

Doctrine2 doesn't load Collection until a method is called

When does Doctrine2 loads the ArrayCollection?
Until I call a method, like count or getValues, I have no data
Here is my case. I have a Delegation entity with OneToMany (bidirectional) relation to a Promotion Entity, like this:
Promotion.php
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Promotion
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Delegation", inversedBy="promotions", cascade={"persist"})
* #ORM\JoinColumn(name="delegation_id", referencedColumnName="id")
*/
protected $delegation;
}
Delegation.php
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Delegation
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="Promotion", mappedBy="delegation", cascade={"all"}, orphanRemoval=true)
*/
public $promotions;
public function __construct() {
$this->promotions = new \Doctrine\Common\Collections\ArrayCollection();
}
}
Now I do something like the following (with a given delegation)
$promotion = new Promotion();
$promotion = new Promotion();
$promotion->setDelegation($delegation);
$delegation->addPromotion($promotion);
$em->persist($promotion);
$em->flush();
Looking for the relation into the database is ok. I have my promotion row with the delegation_id set correctly.
And now my problem comes: if I ask for $delegation->getPromotions() I get an empty PersistenCollection, but if I ask for a method of the collection, like $delegation->getPromotions()->count(), everything is ok from here. I get the number correctly. Asking now for $delegation->getPromotions() after that I get the PersistenCollection correctly as well.Why is this happening? When does Doctrine2 loads the Collection?
Example:
$delegation = $em->getRepository('Bundle:Delegation')->findOneById(1);
var_dump($delegation->getPromotions()); //empty
var_dump($delegation->getPromotions()->count()); //1
var_dump($delegation->getPromotions()); //collection with 1 promotion
I could ask directly for promotions->getValues(), and get it ok, but I'd like to know what is happening and how to fix it.
As flu explains here Doctrine2 uses Proxy classes for lazy loading almost everywhere. But acessing $delegation->getPromotions() should automatically invoke the corresponding fetch. A var_dump get an empty collection, but using it- in a foreach statement, for example- it is working ok.
Calling $delegation->getPromotions() only retrieves the un-initialized Doctrine\ORM\PersistentCollection object. That object is not part of the proxy (if the loaded entity is a proxy).
Please refer to the API of Doctrine\ORM\PersistentCollection to see how this works.
Basically, the collection itself is again a proxy (value holder in this case) of a real wrapped ArrayCollection that remains empty until any method on the PersistentCollection is called. Also, the ORM tries to optimize cases where your collection is marked as EXTRA_LAZY so that it is not loaded even when you apply some particular operations to it (like removing or adding an item).

Resources