Is there a way to have something like this in doctrine:
class Entity {
/**
* #Column(name="related_entity_id")
*/
private $relatedEntityId;
/**
* #ManyToOne(targetEntity="RelatedEntitiy")
* #JoinColumn(name="related_entity_id", referencedColumnName="id")
*/
private $relatedEntity;
}
What I want to do I do something like this:
call Entity::setRelatedEntityId($someId), and persist the entity,
and have the entity return the related entity by calling Entity::getRelatedEntity().
The related entity is selected from a table which will be strictly limited and it will never dynamically grow at runtime, so there is a finite number of related entity ids.
At the time of creating a new Entity, I'd like to set the related entity id, but without having to fetch the whole related entity from the database.
As far as I could test this, it does not work, because if I set the relatedEntityId but not the relatedEntity, Doctrine automatically sets the related_entity_id column to null, since basically no relationship has been established.
I've tried to do something like this also:
remove the relatedEntityId property, and use
Entity::setRelatedEntity(new RelatedEntity($relEntId))
the constructor of the RelatedEntity will set the id, but not other values.
I do not want to persist the RelatedEntity (it's values are already set in the DB for the given $relEntId), but this time Doctrine signals an error at flush, because it has an unpersisted entity.
Basically, what I want to do is create a relationship without knowing anyhing but the Id of the related entity. If there is some other way this can be done, please share.
Thanks in advance
EDIT:
I've found a workaround. Since the RelatedEntities will be a limited set of immutable objects, I've done the following:
use the entityManager to find all RelatedEntities;
inject the list to the object that will be creating new Entities
when creating a new Entity, select one of the RelatedEntities from the list as its RelatedEntity
I'll leave the question open for a day or two, just in case somebody comes up with something better.
Use the entity proxy:
Entity::setRelatedEntity($entityManager->getReference('RelatedEntity', $relEntId))
I don't think this is supposed to work like how you described :)
The entity you add must be a Doctrine managed object, so that means you have to load it yourself first using the find() family of methods.
Based on my experience with Doctrine 2 further elaborated here http://ssmusoke.wordpress.com/2012/03/25/doctrine2-day-3-proxies-associations-relationships/
My approach is as follows:
a) Have only the $relatedEntity property
b) Add a getRelatedEntityId() function which returns the id value from $relatedEntity
c) Add a setRelatedEntityId() which sets the $relatedEntity object - you may need to load it from the database, saves you from polluting other layers when u only have the id of the related entity
d) Add getRelatedEntity() and setRelatedEntity() functions
BOTTOM LINE: You cannot have a property for the foreign key column and the mapped property as Doctrine gets confused
Related
I'm struggling for hours about this one...
I have a Buddy object (Entity) which holds (should hold) a Preference object. Inside this object I have several collections of sub-objects. For simplicity I'll just explain it with one single sub-object. Let's call it University. It's also an entity of its own.
Currently, Buddy and Preference have a One-to-One association with Buddy having a database column preference_id and Preference just an id and nothing else.
Preference and University have a Many-to-Many association, mapped by Doctrine with a buddy_preferences_universities table with preference_id and univeristy_id columns.
My goal is to establish an association between Buddy and University keeping the described class hierarchy but without the need of a useless preferences table, i.e. I still want to be able to issue $unis = $buddy->getPreference()->getUniversities and $unis being a University[] collection.
So, is it possible to "skip" the Preference entity and map the associations directly with the buddy_id?
Here's an image of the tables created by Doctrine's schema-tool:
If necessary I can also post the classes and/or XML mapping files.
Thank you!
Can you not make your buddy_preferences table like this:
id: char(36)
university_id: char(36)
country_id: char(36)
interest_id: char(36)
Like that you can drop your 3 join tables.
Now you can do $buddy->getPreference()->getUniversity();
How about remove the Preference entity of your model, then link with a Many2Many relation the Buddy and the University entities.
If you really need to keep the $buddy->getPreference()->getUniversities() function call to get all the Universities linked with a Buddy object, instead of $buddy->getUniversities()(which sound more logical in my mind, but however..), you can add a function in the Buddy class like this :
public function getUniversities()
{
return $this->universities;
}
// Now you'll be able to call $buddy->getPreference()->getUniversities()
public function getPreference()
{
return $this;
}
Althought it's a solution for your needs, I don't really think it's a good solution.
Why the $buddy->getPreference() is so important if you don't need a Preference object ?
I have 2 Doctrine Entities with many-to-many relations. When I edit the first entity I want to be able to select the checkboxes that have the data from the 2nd entity to establish the joins for particular entry.
It works fine on creating a new Entry (using Array Collection), but when I want to edit an Entry - it adds the ones that I have selected without removing the previous choice (unchecking).
Which way would be the correct way to do that and how?
Remove all the Join table data for the Entry that is being updated,
then set the new data. (How can I remove it from the join table that
is not an Entity?)
Pass all the data from the 2nd Entity and remove
those that aren't checked (seems super-clumsy?)
Some other way I am not aware of?
I am not using Symfony, just Doctrine.
Doctrine makes working with the many-to-many associations quite easy. Your associations are stored into an ArrayCollection class that has some methods that can help you. First of all, check all the available methods for the ArrayCollection here (Doctrine API - ArrayCollection)
In your case, I'd use this approach: use the clear method on your ArrayCollection that contains the relationship with the 2nd entity and populate it again with the checked elements. After this, call the flush method on the entitymanager.
Another approach consists in filtering your collection (with the filter method) for getting a brand new ArrayCollection that contains only the elements that are checked. Like the first approach, associate this new collection to the relationship's ArrayCollection and call the flush method on the entitymanager.
I have a Entity in my database (say Member) which has many relationships with other tables (6 relationships to be exact). Some of them I don't want mapped with the ORM (I mean linked to this Entity) because they may have many records (like MemberAccessLogs for example) and some other load many other entities.
Now I want this Member Entity to have an isDeletable method so I can disable exclude button in administration page.
If I where to do this the traditional way, I would have to declare the associations with all the other tables in the entity class, including MemberAccessLogs and I would put the method in it so I could test if these associations are empty.
But AFAIU, I would have to make a fetch (or at least a count) to the association's tables in order check for empty.
Another way would be to fetch the Members I want shown and then make a separate query to check for empty with a low cost exists(select * from table limit 1) in these sub-tables and then populate the isDeletable method in Member programmatically before pass it to Twig.
But I found this solution cumbersome. Anyone has a better way to do this ?
Just for the record: Some people may think this is "premature optimization". I maintain (contrary to some), that you should think ahead when you are programming and don't this this is bad. But I really think this isn't the place to discuss it. Please let's focus on the question asked ok ? :)
Edit
To easily prove that limit 1 is increadibly faster than count, I did a small test in a table in my database that has more than 20 million lines. Here are the results:
select count(*) from loga [20 million+ table]
20678473
1 row(s) fetched - 27023ms
select exists(select null from loga limit 1)
true
1 row(s) fetched - 2ms
I guess 13511,5 times faster is conclusive enough. :D
Extra lazy
You could look into extra-lazy associations.
Basically you map all associations as you normally would, and add fetch="EXTRA_LAZY":
/**
* #Entity
*/
class CmsGroup
{
/**
* #ManyToMany(targetEntity="CmsUser", mappedBy="groups", fetch="EXTRA_LAZY")
*/
public $users;
}
Now Doctrine will not load the complete collection into memory the first time it's accessed, but performs specialized queries to load the parts you actually need at that moment.
So $users->count() (or count($users)) on the collection would trigger a simple count-query in stead of loading the complete collection into memory.
PostLoad
You could use an postLoad event to determine if such an entity is deletable. This postLoad event is called after an entity is constructed by the EntityManager, so when the entity is loaded.
Add an unmapped property ($isDeletable) to the entity that stores whether the entity can be deleted or not.
Create an entity listener that listens to the postLoad event. The listener can have the EntityManager, DBAL Connection, or anything else injected. With that dependency you could perform whatever query you want and use the result to set $isDeletable.
The result is a single additional query when the entity is loaded, after which the entity "knows" whether it's deletable or not.
An example of using the postLoad event can found in a Cookbook entry on the Strategy Pattern
Do note that when the conditions that determine whether it's deletable or not change, the value of $isDeletable could become incorrect. To resolve this issue, you could keep track of those conditions:
Keep track
Add a mapped property ($isDeletable) to the entity that stores whether the entity can be deleted or not. It would probably start with true.
When something is added to an association which would mean that the entity is no longer deletable, set $isDeletable to false.
When something is removed from an association that which would mean that the entity is deletable again, set $isDeletable to true.
In other words: with every change you keep track of whether the entity is deletable or not.
This way you won't need any additional queries at all.
There's a Cookbook entry on aggregate fields that explains this concept very well.
I've been thrown a 'can it be done in Doctrine' type question by my supervisor, and while I've been able to do some of it with external 'Mapper' objects, I'm wondering if there might be any way to fake-out the ORM to do it within Doctrine itself.
Essentially, the thing we are interested in doing is cutting down database clutter. We have a number of tables containing distinct properties or item sets in various categories and then a whole bunch of link-tables tying them to other properties or item sets. For example
We have a couple of tables such as 'Materials' and 'PaperTypes' which describe various Material and Paper options for our products. We then group these into 'MaterialCollections' and 'PaperFamilies' respectively. Then there has to be a one-to-many link table between MaterialCollection/Materials and PaperFamilies/PaperTypes respectively. Repeat these types of relationships a couple dozen more times and you see where our DB is starting to clutter up.
The link tables themselves are nothing more than a number of entries including multiple records with the PrimaryId from the parent table (collections/families) and unique-per-parent PrimaryId's from the sub-table (materials/papertypes). Link tables could be named something like MaterialCollectionsMaterials and PaperFamilyPaperTypes for example.
The idea was to get rid of this slew of link tables by using an abstract 3-table structure as follows:
Lists (consisting of a unique ListId, a ListName and a TypeId)
Types (consisting of a unique TypeId, a TypeName and an EntityName)
ListXref (consisting of ListIds and memberId which points to the primaryId from the Entity designated in the list type)
Multiple lists can exist for a given type, but the Xref pairs are unique. Different Types can also point to the same Entity. (e.g. there may be more than one type of Material list defined)
The 'ListName' would be the equivalent of the parent TableNames above and would allow for eliminating two of the tables in those relationships. So the records in 'MaterialCollections' would now instead be records in the List table of TypeName "MaterialCollection". The records that would have been in a link table (such as MaterialCollectionsMaterials) would now instead be pointed to from ListXRef.
As stated, I've gotten a basic mapper to make this work for rather basic list creation. But what I'm wondering is if there is any way to create Entities or things that behave like Doctrine Entities to establish the abstract relationships between a given list/listtype and the table referenced by EntityName and the corresponding memberIds?
In other words, it would be really nice if I had some means by which I could produce something that had the bulk of functionality of a Doctrine Entity which could be retrieved from the Service Manager or something like it that would behave (have similar properties/functions) like a Doctrine Entity.
I tried adding a wrapper object in the Entity tree that my mappers could try to retrieve which was basically an inherited version of the Xref entity with a few alias functions, but it can't be retrieved with the Entity manager.
Any help is appreciated.
P.S. While it is not a priority at the moment, longer term I also want to really throw a wrench into the works by trying to have some lists be capable of pointing back to the records produced by other lists. So, for example, a List "ProductXMaterials" of type "ProductMaterials" might point to some but not all results of "MaterialCollections". But I'll worry about this one later.
Well, Doctrine can't generate "pseudo entity classes" but you can.
First you need to hook into the Doctrine "loadClassMetadata" event.
Inside, you generate your entity classes and configuration from code on the fly and dump them into a specified directory (in the cache for example) maintaining the doctrine convention naming but with a custom namespace. For example : "AutoGeneratedNamespace\Entities"
So let's say you need to have a dynamic entity generated for an entity with name: "TmpUser", and you're using "yml" instead of annotations
<?php
namespace Example;
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
class DoctrineEventListener
{
/**
* Invoked on Doctrine loadClassMetadata event
*
* #var Doctrine\ORM\Event\LoadClassMetadataEventArgs
**/
public function loadClassMetadata(LoadClassMetadataEventArgs $args)
{
$em = $args->getEntityManager();
$metadata = $args->getClassMetadata();
$factory = $em->getMetadataFactory();
$name = 'AutoGeneratedNamespace\Entities\TmpUser';
$tmpDirectory = '/path/to/cache/generated-classes';
// current class metadata is the same as the one we need. this means we already generated it
if($metadata->getName() == $name || $factory->hasClassMetadata($name)) {
return;
}
/**
Generate your class here and dump it into the cache directory with the name: TmpUser.php
(If you're using "yml" instead of annotations also create the TmpUser.orm.yml configuration and dump it in the same directory)
**/
// create a metadata for the newly created class
$metadata = $factory->newClassMetadataInstance($name);
// Register metadata so doctrine knows about it
$factory->setMetadataFor($name, $metadata);
}
}
Finally, you tell Doctrine to look for entities inside the cache directory as well using the configuration: (This is taken from the Symfony documentation so if you're not using Symfony it may vary a bit but you can find the precise configuration in the Doctrine docs)
doctrine:
orm:
mappings:
MyGeneratedEntitiesUniqueMappingsKey:
type: yml
dir: '/path/to/cache/generated-classes'
is_bundle: false
prefix: "AutoGeneratedNamespace\Entities"
alias: GeneratedEntities
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.