Let's say I have a Post entity, and a Comment entity. A comment can be approved or not by an admin (which is a flag in the db). The post entity has:
/**
* #ORM\OneToMany(targetEntity="Comment", mappedBy="post")
*/
protected $comments;
And I also want a second attribute which will look like:
/**
* #ORM\OneToMany(targetEntity="Comment", mappedBy="post")
*/
protected $approvedComments;
How is it possible to load only the approved comments here?
Idea #1
You could use Inheritance mapping: https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/inheritance-mapping.html
The idea would to have separate classes for each type (approved and non-approved), but to store everything in a single table (SINGLE_TABLE inheritance).
You will need to have additional column which will store class type discriminator.
Then, you would have:
/**
* #ORM\OneToMany(targetEntity="ApprovedComment", mappedBy="post")
*/
protected $approvedComments;
/**
* #ORM\OneToMany(targetEntity="NonApprovedComment", mappedBy="post")
*/
protected $nonApprovedComments;
The obvious downside is creation of additional classes.
Idea #2
You could just tweak you Query/QueryBuilder like:
`SELECT p, c FROM AcmeDemoBundle:Post p LEFT JOIN p.comments c WITH c.approved = FALSE`
This idea seems more reasonable.
This can not be achieved through relationships as you describe it. 2 tables can not be related "conditionally" as the relations are based on primary keys.
You have at least solutions here
Leave the "comments" field on the entity with the annotations, remove the annotations from the approved comments field as it is the same as comments as understood by the ORM. You would then have the ->getComments() function to get all comments and you could add a function "getApprovedCommentsForPost($post)" in your repostitory class to retrieve those approved ones.
You could distinguish between comments with single inheritance, so you would have a Comment class and an ApprovedComment class in one table for example, then you could make 2 relations on your entity (read here doctrine-orm.readthedocs.org/en/latest/reference/inheritance-mapping.html#single-table-inheritance)
You could use doctrine filters to filter out unapproved comments by default when retrieving data from Comments repository
You cannot define that contraint in your entity. Here is the related documentation:
http://doctrine-orm.readthedocs.org/en/latest/reference/annotations-reference.html#annref-onetomany
As you can see there is no option which is related to conditions. You have to define this condition using QueryBuilder.
Related
I have a rather big number of classes related to each other via various relations (OneToOne, OneToMany, ManyToOne as well as Class- and Joined Inheritance which in turn again have multiple relations.
When I want to select all of a parent class via a simple ->findAll(), I get around 10 extra queries per Entity. I can reduce this number by a limited extend by unsing the a QueryBuilder with ->leftJoin() and ->addSelect(). Some relations do not join because reasons. Still, it gets a pretty nasty long list of addSelect()s. Is there a way to join and select all relations automatically?
Try using "JoinTable" or "JoinColumn"
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html#jointable
The automatical join can be achieved by setting the fetch-mode of the associations to eager (e.g. on ManyToOne:
/**
* #ManyToOne(targetEntity="Cart", cascade={"all"}, fetch="EAGER")
*/
, see the docs)
To set the fetch mode programatically, refer to another StackOverflow question.
I am using symfony2 with the Doctrine entities and I have a problem with the next:
I know I could solve the problem putting an ID to the "club_has_torneo" and turning it into an entity, but to me creating an entity for that looks like something that should not be done. So I wanted to know if there is a way to solve this issue or if I have to do what I think I have to.
Thank you in advance.
I guess I'll submit my own two cents worth.
ORM stands for Object Relational Mapper. The basic idea is to figure out how to map your objects without worrying too much about the database schema. So you have three domain model entities: torneo, club and nadador. Great. Figure out how your application will use these entities. Don't worry about how the relations will end up being stored.
Once you have a working domain model then worry about persistence. The three domain entities clearly map to three doctrine entities. As far as the relations go, I personally am not a big fan of composite primary keys. I think they just complicate things while adding little value. So I would make Doctrine entities for your two has tables and just given them their own primary database id.
Note that these are Doctrine entities not domain entities. Your application code should never need to deal with these relational doctrine entities at all. So in my opinion
creating an entity for that looks like something that should not be
done
does not apply here. It is just a persistence detail.
I think the best solution is indeed to make a entity for your club_has_torneo table. This ClubHasTorneo entity has club_id and torneo_id as composite keys and holds the owning side of a many-to-many relationship between your ClubHasTorneo entity and Nadador entity. This relationship can be done with a join table using the 3 keys. Check the code below on how to do that.
Your database scheme will look exactly like you drew it.
Your ClubHasTorneo entity would look something like this:
<?php
namespace Application\Entity;
use Application\Entity\Club;
use Application\Entity\Torneo;
use Application\Entity\Nadador;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="club_has_torneo")
*/
class ClubHasTorneo
{
/** MANY-TO-ONE BIDIRECTIONAL, OWNING SIDE
* #var Club
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Application\Entity\Club", inversedBy="clubHasTorneos", cascade={"persist"})
* #ORM\JoinColumn(name="club_id", referencedColumnName="id")
*/
protected $club;
/** MANY-TO-ONE BIDIRECTIONAL, OWNING SIDE
* #var Torneo
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Application\Entity\Torneo", inversedBy="clubHasTorneos")
* #ORM\JoinColumn(name="torneo_id", referencedColumnName="id")
*/
protected $torneo;
/** MANY-TO-MANY BIDIRECTIONAL, OWNING SIDE
* #var Collection
* #ORM\ManyToMany(targetEntity="Application\Entity\Nadador", inversedBy="clubHasTorneos")
* #ORM\JoinTable(name="club_has_torneo_has_nadador",
* joinColumns={
* #ORM\JoinColumn(name="club_id", referencedColumnName="club_id"),
* #ORM\JoinColumn(name="torneo_id", referencedColumnName="torneo_id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="nadador_id", referencedColumnName="id")
* }
* )
*/
protected $natadors;
public function __construct()
{
$this->natadors = new ArrayCollection();
}
// setters and getters
}
my 5 cents
If you want your implementation to match the drawn table structure, then (in my opinion) you need create an entity out of the 'club_has_torneo' table (for 'club_has_torneo_has_matador' you don't need to).
The rationale being that if you try to achieve this without creating the entity, you would need to create the entity associations so, that the 'natador' table references the 'club' and 'torneo' directly - in which case the actual database relations wouldn't match with your drawn table relationship anymore (i.e. the natador wouldn't have relationship to the 'club_has_torneo' table).
If you have a set of association mapped entities in Doctrine, sometimes you might want to retrieve those entities without its mapped associations being fetched and slowing the query down.
For example I have a set of entities which are association mapped in a chain of linked database tables. They are all OnetoMany associations and act as a hierarchy of prices in matrices on product pages. They can be represented as so:
SitePage->SiteMatrix->SiteItems->SiteItemPrices.
The associated mapping works perfectly, and when I use the findBy method to get the root SitePage object it contains arrays which represent the mapped entities down the chain. In other words the SitePage object contains all matrices, which contains all items which contains all prices. So far so good.
My problem is that every time I get a list of pages on my site, doctrine is walking the entire association mapping tree and returning me with the entire datatabase which is very slow. Sometime I want to just get my SitePage entity by ID and not contain all the mapped associations.
I have looked into lazy and extra lazy loading of associations but they only seem to affect certain functions, and not findBy etc. The official documentation is far from helpful:
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html
Other similar questions on Stack Overflow have gone unanswered:
Doctrine 2 Association Mapping Overhead?
Is there an easy way to fetch an entity without its mapped associations? The easiest way I can see currently is to create two entities for each database table, one with association mapping and one without for use in the separate situations where they are required. It seems odd to me that you cannot simply fetch an entity and specify whether you want to link to it to other entities or fetch it by itself.
Thanks for any information on the subject.
The JMSSerializer exclusion strategies can help you.
First, exclude all properties by default :
// ...
use JMS\Serializer\Annotation as JMS;
/**
* #ORM\Entity
* #JMS\ExclusionPolicy("all")
*/
class Foo
Then, choose to exclude or expose your properties :
/**
* #ORM\Column(type="string")
* #JMS\Expose
*/
protected $name;
Also, for your associations, you can use the MaxDepth to restrict the associated entries of your results. Example :
// Entity
/** #MaxDepth(1) */
private $selfAssociatedEntries;
// Controller
use JMS\Serializer\SerializationContext;
$serializer->serialize($data, 'json', SerializationContext::create()->enableMaxDepthChecks());
Like this, your entity will contains the $selfAssociatedEntries serialised, but the $selfAssociatedEntries doesn't have the $selfAssociationEntries property.
The serialisation is restricted to the first parent object.
Groups are a powerful feature that allows you to expose properties for some actions and exclude them for another :
/**
* #ORM\Column(type="string", length=255, nullable=true, unique=false)
* #JMS\Groups({"default"}) // Set the group to expose the property
*/
protected $name;
In your controller, set the group used for serialisation :
// Property 'name' is exposed
$serializer->serialize($data, 'json', SerializationContext::create()->setGroups(array('default')));
For more informations, look at Exclusion Strategies chapter of the documentation.
I was planning on creating my application and use an ORM for the models, but the thing is, there's a part of the database which uses Entity-Attribute-Value Tables.
I pretty liked Doctrine ORM but I don't know if it is possible to maybe create classes that would look like any ordinary doctrine entity, when the table actually hooked up to is of EAV style.
Would it be possible to use Doctrine on this, and if so, how?
definitely possible:
Have relationships like this:
Object (one to many) -> AttributeValue -> Many to One -> AttributeType
In view of EAV it seems to be obvious how to build a relation between entity and attribute using doctrine. In the most complicated case we deal with a Many to Many relation.
So lets say we want to map an attribute Name to an entity User. Assuming a user has exactly one name and each name belongs to exactly one user this link can be archived using One to One relation
But how to model the relation between attribute and value? The problem is that values can be of different types or even need different numbers of fields in order to save their information.
Consider the attributes name and phone_number. While a name might be represented by a string, an integer could be needed for the phone number. Or it is even necessary to not only the number but also the area code in a separate filed.
Because EAV requires very flexible value representation, it is not possible to store all of them within the same field in a database table (disregard blobs, data serialization an the like). Therefore most EAV implementations using different tables representing different value types.
In order to reach such flexibility, doctrine features Inheritance Mapping. It basically allows you to extend doctrine entities. Doing so you specify a discriminator for each sub-type of your entity:
/**
* #Entity
* #InheritanceType("JOINED")
* #DiscriminatorColumn(name="value_type", type="string")
* #DiscriminatorMap({"name" = "Name", "phone" = "PhoneNumber"})
*/
class Value
{
// ...
}
/** #Entity */
class Name extends Value
{
// ...
}
/** #Entity */
class PhoneNumber extends Value
{
// ...
}
The Value class provides common implementation for all values, i.e. an id. Each subclass (i.e. Name and PhoneNumber) extend those common values by their specific ones, for example additional fields.
The #DiscriminatorColumn defines a column in the parent relation which stores the type of the value.
The #DiscriminatorMap is used by doctrine to map the type from the #DiscriminatorColumn to one of those classes.
The relation between attribute and value can be specified to the parent class. Calling values from the attribute then will fetch all types of values which can be filtered (and dealt with) during runtime using for example instanceof.
A friend of mine and me are working on a multilingual web-application for storing recipes. We use Doctrine 2.1.1 for persistence. At the moment I stuck generating models for an entity and hope someone can give me a hint, or show up a best practice for our case.
I'll try to explain our ERD broadly. Feel free to ask, if there is something unintelligible. Our application has the Entities recipe, ingredients, units (measure units for ingredients) and categories. Each of these entities is stored in an own table. Each entity can be translated in multiple languages. Storage of the translations is intended in a table called (you name it) translations.
Now it will be a bit tricky… we have an extra table called mnemonic. We use it for identifying recipes, ingredients, categories and units of measure in a global context… best analogy to this is a GUID I think. The mnemonic also helps us to map between recipes, ingredients etc. and their translations.
The mnemonic table consists of five rows: id (the primary key) and four additional meta-data rows: ingredient_id, recipe_id, category_id and unit_id referencing a 1:1 relationships to primary key of their relatives.
I'm asking myself how cam I map the relationship between the mnemonic entity and recipes, ingredients an so on.
Or (to be more specific) how can the category_id in the mnemonic table be automatically populated with the value of the the primary key of the category table?
I tried first this kind of association mapping in my Category model:
class Category
{
/**
* #var integer $id
*
* #Column(name="id", type="bigint", nullable=false)
* #Id
* #GeneratedValue(strategy="IDENTITY")
* #OneToOne(targetEntity="Mnemonic", mappedBy="category")
* #JoinColumn(name="id", referencedColumnName="category_id")
*/
private $id;
and in the Mnemonic model:
class Mnemonic
{
/**
*
* #OneToOne(targetEntity="Category", inversedBy="mnemonic")
* #JoinColumn(name="category_id", referencedColumnName="id")
*/
private $categoryId;
That didn't work out — Doctrine dont' produce any errors, but populates only the category table.
So I thought I could write some code in the __construct method of the Category model, where I would create a Mnemonic object, set his categoryId according to the ID of Category model and bind it to doctrine's entity manager. But that's kind of ugly — I think model classes should not be responsible for persistence.
During the writing of my question I thought, that the solution could be a kind of a Factory class. It would take all the persistence logic out of the model and could handle special cases.
What do you think? What is the best solution?
Best regards in advice
Paul
Have you thought about adding an event listener for onFlush? so essentially on flush, you check what object you are persisting, then execute some code such as creating the Mnemonic object.
Info about registering onFlush events can be found on the doctrine website at the following link:
http://docs.doctrine-project.org/projects/doctrine-orm/en/2.0.x/reference/events.html#onflush