I am implementing horizontal sharding of my database in my Symfony application. I have a method to create a table based on the "template" Entity I've made. For example, my Entity "AnswerData" will be used to create tables such as "AnswerData_sourceA", "AnswerData_sourceB"... and so on that are exact same schemas as the entity-based "AnswerData". While I have the database tables created already, I'm not quite sure how to get Symfony and Doctrine to designate which table I want the Entity to be created/saved to.
For example:
$em = $this->getDoctrine()->getManager();
$answerData= new AnswerData();
$answerData->setData();
//Set other properties...
...
$em->persist($answerData);
$em->flush();
$em->clear();
The above would have Symfony/Doctrine save the Entity to "AnswerData" table, but I am not sure where/how to tell it to save it to the "AnswerData_sourceA" table. Should I be writing custom repository classes that handle this or can the above snippet be modified to manually set the table? Thank you for any advice!
If I understood you want to save to specified table. you need
/**
* #Entity
* #Table(name="AnswerData_sourceA")
*/
to specify that name like this.
Related
I am currently searching for a way to share users between multiple Symfony2 applications for one of our customers. In the past 3 years, we made a few applications (4) for them that have different purposes, but always use the "same" user model and data.
Currently, we have 4 separate databases, where the "users" table is kinda the same on all the applications, except for the many-to-many relationships. At first, I was thinking about adding a second entity manager (and connection), and putting the users in a separate database. All the applications would be able to use this and all the users would have the same credentials. But how do I handle the many-to-many relationships?
To give an example, on application A you have a many-to-many relation from "Users" to "Clients", but the "Clients" table doesn't exist in application B/C/D. On application B, you have a many-to-many relation from "Users" to "Suppliers", but the "Suppliers" table doesn't exist in application A/C/D and so on. Moving the "Clients" or "Suppliers" table to the shared database isn't really an option either, because other entities (which are not shared) are also having relations to those tables.
I basically need to find a way to map many-to-many relationships on the "shared user" model/database which are unique for each application. Is there a way to achieve this with multiple databases? Should I go for some other approach?
All info is welcome. Thanks in advance.
Option 1
Using different connections, this doesn't seem to be possible with Doctrine out of the box .
Similar questions have been answered already:
Using Relationships with Multiple Entity Managers
Entities associations across different managers
As stated in the first answer, you could do the following:
keep the object graphs disconnected by saving the identifiers of the related objects (old style) instead of a reference to them, then manually get the objects through services.
But if your want Doctrine to actually be aware of the associations, you need to persist the associated entities in the same database, or your mapping will just generate errors. That means you would need to duplicate the User entity.
Option 2
In the very specific case where you can use the same connection (that is, with multiple databases of the same DBMS on the same host and with the same user), there seems to be a way, but I haven't tested it:
https://techpunch.co.uk/development/using-multiple-databases-with-symfony2-and-doctrine2
The idea is to prefix each table with the database name, as if it were a schema name, like this:
This entity is mapped to the «User» table in the database «users»:
<?php
namespace Demo\UserBundle\Entity;
use DoctrineORMMapping as ORM;
/**
* #ORMTable(name="users.User")
*/
class User
{
/* ... */
}
This one is mapped to the «Post» table in the database «posts»:
<?php
namespace Demo\PostBundle\Entity;
use DoctrineORMMapping as ORM;
/**
* #ORMTable(name="posts.Post")
*/
class Post
{
/* ... */
}
Then you can make associations as usual:
class Post
{
/**
* #ORM\ManyToOne(targetEntity="\Demo\UserBundle\Entity\User")
**/
private $user;
/* ... */
}
The author also links to an example project on github:
https://github.com/lobsterdore/symfony2-multiple-db-example
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'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 have a story table and user table. The column userid in story table is a foreign key which refers the id in user table.
I have set the relationship is that a user may have many stories which is stored in story table. I have created the entities of both table.
But if try to persist operation only to story table it is asking the details for new user entry.
My objective is to add a new story with existing userId.
Am posting the error here:
A new entity was found through the relationship 'Story#_userId' that
was not configured to cascade persist operations for entity:
User#0000000038960c50000000008ea93852. To solve this issue: Either
explicitly call EntityManager#persist() on this unknown entity or
configure cascade persist this association in the mapping for example
#ManyToOne(..,cascade={\"persist\"}).
I set ManyToOne relationship in Story entity:
/**
* #ManyToOne(targetEntity="User", inversedBy = "_story" )
* #JoinColumns({
* #JoinColumn(name="user_id", referencedColumnName="id")
* })
*/
private $_userId;
I checked the database schema and it shows relationship is set correctly. So I have done the story insertion process.
$user = new User();
$user->setUserId($id);
$story = new Story();
$story->setContent("....");
$story->setUserid($user);
$this->em->persist($story);
$this->em->flush();
You are probably persisting the story entity but not the user. If you have something like this:
$story = new Story();
$user = new User();
$story->setUser($user);
$em->persist($story);
$em->flush();
This will result in a fatal error, since you are persisting one entity, but through its relations, Doctrine finds another new entity. You have two options:
Call persist on both entities:
$story = new Story();
$user = new User();
$story->setUser($user);
$em->persist($story);
$em->persist($user);
$em->flush();
Or, set up cascading persist for the Story entity. Eg. if you are using annotation mapping, you would do something like
/**
* #ManyToOne(targetEntity="User", inversedBy="stories", cascade={"persist"})
*/
private $author;
The chapter 8. Working with associations details this.
K. Norbert's answer hits the spot, but there is something that might be unclear. At least it was unclear for me, who came to doctrine from SQL.
The thing is doctrine seem to remember which objects are already persisted. Whenever it finds new (not persisted yet) objects related to entity you want to persist - it yells with 'A new entity was found through the relationship ...' error. When you want to save new object (only one entity, without persisting related entities) you just need to make sure related entities are persisted already.
So:
$story = new Story();
$entityManager->find('User', $id);
$story->setUser($user);
$em->persist($story);
$em->flush();
does the trick. Because doctrine knows the user is persisted already and it doesn't need to do it anymore. Doctrine knows because we got the user from the database.
Dont you think you should set User rather than Userid for story
$user = new User();
$user->setUserId($id);
$story = new Story();
$story->setContent("....");
$story->setUser($user); //change here
$this->em->persist($story);
$this->em->flush();
I'm struggling to understand what you need, so I'm not sure if this will respond to your question.
As the story.user_id id a foreign key of user.id (which is a primary key), this can't be empty/null. So if, let's say, you have a php session, you should probably store your user id (or user name) in your session variables, and when you create a new record in the story table, use the user id from your session to populate the story.user_id attribute.
Hope it helps.
I would be helpful if you attach your entites.
But I suppose that it is simmilar problem to mine. You cannot define the #Id and #ManyToOne together in one field. You have to use separate fields for an #Id and for relation #ManyToOne.
If you have a new (not persisted yet) User entity, you have to persist User which have cascade={"persist"} or cascade={"all"} in field correspond to Story entity. You can store the Story entity only if User already exists and it is attached to Doctrine.
Hope it help.
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