I have a problem with entity setup in Doctrine 2.3 (using Symfony 2).
There is a class 'Product' that has a field 'producer'.
Now this producer can either be a single person (class 'User') or a group (class 'Group').
What I wanted to do, is now have the User- and Group-class implement an interface 'Producer' and then have my $producer-Field map to the Producer-interface.
/**
* #var Producer
* #ORM\OneToOne(targetEntity="..\..\Entity\Producer")
*/
private $producer;
This however fails with the message:
"Column name `id` referenced for relation from ..\..\Entity\Product towards
..\..\Entity\Producer does not exist.
After giving it a second thought, this seems obvious - but now I'm wondering, how I can solve the problem. Because if I hadn't to use any persistence and just use PHP/Java/whatever, this approach would work - but when mapping to a DB, life is more complicated :-)
Inheritance is not a solution because 'User' already extends class 'Person' and has no similarity to 'Group' and as there is no such thing as multiple inheritance in PHP...
So, what would be a clean solution for this problem?
http://symfony.com/doc/current/cookbook/doctrine/resolve_target_entity.html
this documentation should help you. Not sure it is the correct answer to your problem
Related
Problem:
In my Symfony 4.4 app there is a Notification entity class (managed via Doctrine ORM 2.7). In real life every Notification object can be related to one "subject". This "subject" may be a Task, a Project, a Document, an Invoice, a Contract, etc. all of which are entity classes themself.
Ideally it would be possible to have a Notification::getSubject and Notification::setSubject method if that makes sense. And if possible a NotificationRepository::findBy('subject' => $subject) would also work – if that makes sense.
Question:
What is a clean/good way to design the realation between the Notification entity class and all those different "subject" entity classes using Symfony and Doctrine ORM? Is there an OOP Design Pattern that should be used?
Since your question is rather vague, I think you should begin defining the entity.
A goood way to start is to make an an Entity Relationship Diagram.
This is a good thing to begin by thinking about how Entity will interract between them.
A Diagram is verry helpfull when it is to develop a new feature.
Then, when you have a clear idea about what you wanna do, all you have to do is code it.
For the thing you ask, I try to make such a Diagram, but it may be incomplete, because I don't know your project as you are :
Of course I haven't represent every entity that you describe, but you got the idea.
The good thing with this inheritance is that you'll be able to call Notification::getSubject and then you know which kind of subject it is
You'll create entity Subject, and then you extends Task, a Project, a Document, an Invoice, a Contract, etc.
To create inheritance in doctrine :
#src\Entity\Subject
/**
* #ORM\Table(name="subject")
* #ORM\Entity(repositoryClass="App\Repository\SubjectRepository")
* #ORM\InheritanceType(value="JOINED")
* #ORM\DiscriminatorColumn(name="subject_type", )
* #ORM\DiscriminatorMap(value={SUB_TYPE_DOCUMENT=Document::class, SUB_TYPE_PROJECT=Project::class, SUB_TYPE_TASK=Task::class})
*/
abstract class Subject{
const SUB_TYPE_DOCUMENT = 'DOCUMENT';
const SUB_TYPE_PROJECT = 'PROJECT';
const SUB_TYPE_TASK = 'TASK';
...
}
#src\Entity\Document
class Document extends Subject{
...
}
#src\Entity\Project
class Project extends Subject{
...
}
#src\Entity\Task
class Task extends Subject{
...
}
Read more at : https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/inheritance-mapping.html
That's a very classic way to proceed. Now you asking if there is a Pattern To be used.
The one that come in mind could be the Decorator. I recommend you to read about it.
If none of this help you, please edit your question and add more details about what you need...
I need some help with doctrine 2 that uses "polymorphic associations". Let me clarify myself. Entitys can support file attachments using a subset of the polymorphic relationship. the File entity is used to safekeep this relationship where reference to the files are stored as records in the files table and have a polymorphic relation to the parent model. I want to create the same functionality as https://octobercms.com/docs/database/attachments
But do not know how to make the relationship, and how, for example, put the attachment_type dynamic like attachment_id;
/**
* #var \Doctrine\Common\Collections\ArrayCollection
*
* #ORM\OneToOne(targetEntity="App\Domain\FileAttachment\Entity\FileAttachment", attachment_type="news_thumbnail")
*/
private $thumbnail;
/**
* #var \Doctrine\Common\Collections\ArrayCollection
*
* #ORM\OneToOne(targetEntity="App\Domain\FileAttachment\Entity\FileAttachment", attachment_type="news_image")
*/
private $image;
/**
* #var \Doctrine\Common\Collections\ArrayCollection
*
* #ORM\OneToMany(targetEntity="App\Domain\FileAttachment\Entity\FileAttachment", attachment_type="news_files")
*/
private $files;
An example of the files table.
I have some experience in trying to make polymorphism work (including polymorphic files) in symfony and by this time I think I can share a few of my insights with you in hopes that they would provide you with some useful information about this subject.
Firstly, I would suggest reading up on inheritance mapping in doctrine link. With doctrine inheritance mapping you would simply create one main File class and then make every other attachment extend it. Then, say you want to add a picture attachment to the user. You would simply create a oneToOne relationship between the user and the main File class. If the attachment you persist would be an instance of one of the attachment classes, Doctrine is smart enough to return you an object of that class, not the main File class.
So to answer you question, I will give you a specific example. Case:
ImageAttachment extends FileAttachment
User has a property called photo
Property photo is a OneToOne relationship to the FileAttachment entity
Code:
$image = new ImageAttachment();
$user->setPhoto($image);
$em->persist($user);
$em->flush();
Result:
Now in the database in the User table, in a column called something like photo_id the referenced ID would be the one in the FileAttachment table. When you would do $user->getPhoto(); it would return an object of class ImageAttachment since doctrine knows that you have persisted an ImageAttachment, not just a FileAttachment.
When it comes to collections, things would also be pretty simple. In this case, you would probably need to create an ManyToMany relationship between the file and the entity that you want to relate to the file. Say that a user can have many different types of attachments saved in the database. If you want to use this filesystem application wide it would probably make no sense for a file to know about the user it belongs to, because soon file would have to hold information on all different types of the relationships and that is just not a smart architecture choice if you want to have any type of modular system in place. Thats why my suggestion is to use ManyToMany relationships between some entity and the attachments. This way only user would know about the files in the database and filesystem would be agnostic and decoupled.
A third important point to be made when talking about polymorphism in doctrine is symfony support for this feature. Generally polymorphism is considered to be somewhat of a bad practice in certain cases, and especially in data persistence does not have much support in the community. So an important thing to consider is that symfony CollectionType HAS NO SUPPORT FOR POLYMORPHISM what so ever. Basically you will have to write your own Type if you were planning on using polymorphic form collections. But if you don't mind using a bit of ajax, this is not really a problem, you can simply avoid using SF forms for this purpose alone.
I have two different bundle:
first bundle, OrderBundle , has core logic and functionalities and contains the Order entity.
second bundle, CustomerBundle depends on OrderBundle and contains the Customer entity.
I need to create a oneToMany relation between Customers and Orders (obviously one Customer do many Orders) but I need to keep the first bundle OrderBundle decoupled from the second, because OrderBundle is intended to be reused for other stuff.
I think the correct way could be something like this http://symfony.com/doc/current/cookbook/doctrine/resolve_target_entity.html but I can't figure out how to have a concrete implementation.
How to implement the relation between Order and Customer, if I can't specifically use Customer like targetEntity in the ManyToOne doctrine mapping?
Many thanks, in advance.
UPDATE
I write down the involved code, for better explanation.
\\ Order\Bundle\Entity\Order.php
class Order {
/**
* #ORM\ManyToOne(targetEntity="Order\Bundle\Model\OrderSubjectInterface", inversedBy="orders")
* #var SourceSubjectInterface
*/
protected $subject; // How to define getter ans setter for $subject ? Do I
have to use php app/console doctrine:generate:entities command?
...
\\ Customer\Bundle\Entity\Customer.php
use Order\Bundle\Model\OrderSubjectInterface;
class Customer implements OrderSubjectInterface{
/**
* #ORM\OneToMany(targetEntity="Order\Bundle\Entity\Order", mappedBy="subject")
*/
private $orders;
How to define getters, setters and the interface?
Yes, this is the right way.
As shown in the documentation you mentioned, you can specify something like OrderSubjectInterface as a targetEntity in the ManyToOne mapping.
This way, you know that your Order is related to subjects. Those subjects are, in your case, the Customers, as defined in app/config/config.yml.
It's quite hard to entirely decouple the bundles if you have entity definitions all over the place. The problem is, the Doctrine doesn't allow by default for a related entity to be missing. Let's start from the beginning.
If you want to decouple only Order entity, what you have to do is create interfaces for all its related entities (they need to implement them) and then use the ResolveTargetEntity. So instead of referencing the full entities you reference the interfaces (when you define the relations in your entities). Lastly, you can then set which interface maps to which entity in the configuration.
What this does is it allows you to pick up the Order bundle and put it in an environment which has entirely different entity structure. The important thing is that the entities related to Order must not be missing in the new environment (They can be entirely different, but they must implement the same interfaces as the originals). Then you change the settings so that the interfaces point to the entities from the new environment.
So as you see, this is not "entirely decoupled" code. I can't help you much more without some details. Is the relation bidirectional or unidirectional? What do you exactly mean by "reused for other stuff", can you be more detailed?
I'm working on a Symfony 2.3 Project that utilizes the Doctrine 2 ORM. As is to be expected functionality is split and grouped into mostly independent bundles to allow for code-reuse in other projects.
I have a UserBundle and a ContactInfoBundle. The contact info is split off because other entities could have contact information associated, however it is not inconcievable that a system may be built where users do not require said contact information. As such I'd very much prefer these two do not share any hard links.
However, creating the association mapping from the User entity to the ContactInfo entity creates a hard dependency on the ContactInfoBundle, as soon as the bundle is disabled Doctrine throws errors that ContactInfo is not within any of its registered namespaces.
My investigations have uncovered several strategies that are supposed to counter this, but none of them seem fully functional:
Doctrine 2's ResolveTargetEntityListener
This works, as long as the interface is actually replaced at runtime. Because the bundle dependency is supposed to be optional, it could very well be that there is NO concrete implementation available (i.e. contactInfoBundle is not loaded)
If there is no target entity, the entire configuration collapses onto itself because the placeholder object is not an entity (and is not within the /Entity namespace), one could theoretically link them to a Mock entity that doesn't really do anything. But this entity then gets its own table (and it gets queried), opening up a whole new can of worms.
Inverse the relation
For the ContactInfo it makes the most sense for User to be the owning side, making ContactInfo the owning side successfully sidesteps the optional part of the dependency as long as only two bundles are involved. However, as soon as a third (also optional) bundle desires an (optional) link with ContactInfo, making ContactInfo the owning side creates a hard dependency from ContactInfo on the third bundle.
Making User the owning side being logical is a specific situation. The issue however is universal where entity A contains B, and C contains B.
Use single-table inheritance
As long as the optional bundles are the only one that interacts with the newly added association, giving each bundle their own User entity that extends UserBundle\Entities\User could work. However having multiple bundles that extend a single entity rapidly causes this to become a bit of a mess. You can never be completely sure what functions are available where, and having controllers somehow respond to bundles being on and/or off (as is supported by Symfony 2's DependencyInjection mechanics) becomes largely impossible.
Any ideas or insights in how to circumvent this problem are welcome. After a couple of days of running into brick walls I'm fresh out of ideas. One would expect Symfony to have some method of doing this, but the documentation only comes up with the ResolveTargetEntityListener, which is sub-optimal.
I have finally managed to rig up a solution to this problem which would be suited for my project. As an introduction, I should say that the bundles in my architecture are laid out "star-like". By that I mean that I have one core or base bundle which serves as the base dependency module and is present in all the projects. All other bundles can rely on it and only it. There are no direct dependencies between my other bundles. I'm quite certain that this proposed solution would work in this case because of the simplicity in the architecture. I should also say that I fear there could be debugging issues involved with this method, but it could be made so that it is easily switched on or off, depending on a configuration setting, for instance.
The basic idea is to rig up my own ResolveTargetEntityListener, which would skip relating the entities if the related entity is missing. This would allow the process of execution to continue if there is a class bound to the interface missing. There's probably no need to emphasize the implication of the typo in the configuration - the class won't be found and this can produce a hard-to-debug error. That's why I'd advise to turn it off during the development phase and then turn it back on in the production. This way, all the possible errors will be pointed out by the Doctrine.
Implementation
The implementation consists of reusing the ResolveTargetEntityListener's code and putting some additional code inside the remapAssociation method. This is my final implementation:
<?php
namespace Name\MyBundle\Core;
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
use Doctrine\ORM\Mapping\ClassMetadata;
class ResolveTargetEntityListener
{
/**
* #var array
*/
private $resolveTargetEntities = array();
/**
* Add a target-entity class name to resolve to a new class name.
*
* #param string $originalEntity
* #param string $newEntity
* #param array $mapping
* #return void
*/
public function addResolveTargetEntity($originalEntity, $newEntity, array $mapping)
{
$mapping['targetEntity'] = ltrim($newEntity, "\\");
$this->resolveTargetEntities[ltrim($originalEntity, "\\")] = $mapping;
}
/**
* Process event and resolve new target entity names.
*
* #param LoadClassMetadataEventArgs $args
* #return void
*/
public function loadClassMetadata(LoadClassMetadataEventArgs $args)
{
$cm = $args->getClassMetadata();
foreach ($cm->associationMappings as $mapping) {
if (isset($this->resolveTargetEntities[$mapping['targetEntity']])) {
$this->remapAssociation($cm, $mapping);
}
}
}
private function remapAssociation($classMetadata, $mapping)
{
$newMapping = $this->resolveTargetEntities[$mapping['targetEntity']];
$newMapping = array_replace_recursive($mapping, $newMapping);
$newMapping['fieldName'] = $mapping['fieldName'];
unset($classMetadata->associationMappings[$mapping['fieldName']]);
// Silently skip mapping the association if the related entity is missing
if (class_exists($newMapping['targetEntity']) === false)
{
return;
}
switch ($mapping['type'])
{
case ClassMetadata::MANY_TO_MANY:
$classMetadata->mapManyToMany($newMapping);
break;
case ClassMetadata::MANY_TO_ONE:
$classMetadata->mapManyToOne($newMapping);
break;
case ClassMetadata::ONE_TO_MANY:
$classMetadata->mapOneToMany($newMapping);
break;
case ClassMetadata::ONE_TO_ONE:
$classMetadata->mapOneToOne($newMapping);
break;
}
}
}
Note the silent return before the switch statement which is used to map the entity relations. If the related entity's class does not exist, the method just returns, rather than executing faulty mapping and producing the error. This also has the implication of a field missing (if it's not a many-to-many relation). The foreign key in that case will just be missing inside the database, but as it exists in the entity class, all the code is still valid (you won't get a missing method error if accidentally calling the foreign key's getter or setter).
Putting it to use
To be able to use this code, you just have to change one parameter. You should put this updated parameter to a services file which will always be loaded or some other similar place. The goal is to have it at a place that will always be used, no matter what bundles you are going to use. I've put it in my base bundle services file:
doctrine.orm.listeners.resolve_target_entity.class: Name\MyBundle\Core\ResolveTargetEntityListener
This will redirect the original ResolveTargetEntityListener to your version. You should also clear and warm your cache after putting it in place, just in case.
Testing
I have done only a couple of simple tests which have proven that this approach might work as expected. I intend to use this method frequently in the next couple of weeks and will be following up on it if the need arises. I also hope to get some useful feedback from other people who decide to give it a go.
You could create loose dependencies between ContactInfo and any other entities by having an extra field in ContactInfo to differentiate entities (e.g. $entityName). Another required field would be $objectId to point to objects of specific entities. So in order to link User with ContactInfo, you don't need any actual relational mappings.
If you want to create a ContactInfo for a $user object, you need to manually instantiate it and simply setEntityName(get_class($user)), setObjectId($user->getId()). To retrieve user ContactInfo, or that of any object, you can create a generic function that accepts $object. It could simply just return ...findBy(array('entityName' => get_class($user), 'objectId' => $object->getId());
With this approach, you could still create User form with ContactInfo (embed ContactInfo into User). Though after you process the form, you will need to persist User first and flush, and then persist ContactInfo. Of course this is only necessary for newly created User objects, just so to get user id. Put all persist/flush in a transaction if you're concerned about data integrity.
I have also posted this question before and I am posting it again as it is not resolved yet.
I am using Symfony2 for my application and I need to create two database connections i.e Read and Write for this I search and found easily that we can create different entity managers and i have created like that :
Making object in controller
$emWrite = $this->getDoctrine()->getEntityManager('write');
$em = $this->getDoctrine()->getEntityManager();
when I persist the entity It gives me the following error:
A new entity was found through the relationship '
AppBundle\Entity\Follower#user' that was not configured to cascade persist
operations for entity: adeel. Explicitly persist the new entity or configure
cascading persist operations on the relationship. If you cannot find out which
entity causes the problem implement AppBundle\Entity\User#__toString()
to get a clue. (500 Internal Server Error)
I have already tried many things like giving persist property in both sides of entities involved.
Your problem isn't related to multiple entity managers but to options that you haven't setted onto your class, fore relationship purpose.
I haven't much details about your entities, but I could suggest you to modify relationship in that way
<?php
class User
{
//...
/**
* Bidirectional - One-To-Many (INVERSE SIDE)
*
* #OneToMany(targetEntity="Comment", mappedBy="author",
cascade={"persist","remove"})
*/
private $commentsAuthored;
//...
}
Where cascade={"persist","remove"} is what you're looking for.