Doctrine many-to-one association keeps pulling all rows from associated table - php

I'm attempting to set up a many-to-one relationship between a series of sales memos and transaction records.
/**
* #var TransactionInterface
*
* #ORM\ManyToOne(targetEntity="Twb\Common\Model\Broker\TransactionInterface")
* #ORM\JoinColumn(name="FormNoSeller", referencedColumnName="Form")
*/
private $formnoseller;
/**
* #var TransactionInterface
*
* #ORM\ManyToOne(targetEntity="Twb\Common\Model\Broker\TransactionInterface")
* #ORM\JoinColumn(name="FormNoBuyer", referencedColumnName="Form")
*/
private $formnobuyer;
They are split between two different bundles at the moment ('SalesBundle' and 'BrokerBundle'), and with that in mind I am using interfaces from the SalesMemo entity in SalesBundle to the Transaction entity in BrokerBundle.
For some reason, when I reference either or both of $formnoseller and $formnobuyer in my forms, I notice in dev.log that, after selecting all Transaction rows matching the $formnoseller and/or $formnobuyer fields in the SalesMemos, Doctrine tries to SELECT all rows in the Transaction table (the entity for which TransactionInterface references). This is a bit of a problem, since there is an innumerable amount of rows in the DB, which takes up a lot of memory.
Is there any way to have Doctrine avoid selecting all rows with associations? Or am I even understanding properly how Doctrine does associations? Many thanks for any help.

My understanding of your problem is that you're using an Entity Field Type for $formnoseller and $formnobuyer (or you don't specify the type). Giving the choice to select any élément from the underlying table is the expected behaviour for the Entity Field Type (Used by default for OneToMany relationships)
If you don't whant a select list of all the elements of your table for those Fields, you should use an other form field type. You should also have a look at data transformers in the documentation.

If it were me, I would write a stored procedure and do an inner or outer join as appropriate.
Once upon a time, they called this "client server" code. About 15 years ago, it created such a mess the whole industry moved to n-tier development. I'd like to know how the table joins got placed back into the presentation tier again? ORMs and LINQ-to-SQL are a return to client/server".
If you have to do it this way, do the join in LINQ on the Models. Do not do it with the ORM language.

Related

How can I select all relations automatically in Doctrine?

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.

Symfony2 - Sharing users between multiple applications

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

One entity to multiple tables with symfony/doctrine

Introduction
I'm working on a symfony3 project and I have a lot of different "types" for certain entities. What I mean by this is that, I have a TypeUser, TypeEvent, TypeMeeting tables.
These tables will only have an id and a label.
Problem:
When using doctrine, I can link 1 entity to 1 table using the anotations, like so:
/**
* #ORM\Entity
* #ORM\Table(name="TypeUser")
*/
However, I would like this part to be completely generic. How can I specify the Table name depending on the type I need?
Is there another alternative when using Doctrine other than the annotations to make this possible?
I would really like to avoid making n entities for n tables, when they are very similar in name and in structure.
Question:
Is it possible to make one generic entity to match a specific TypeXXXX table, to reduce redundancy? If so how would I go about doing it?
Similar Doctrine 2.1 - Map entity to multiple tables
Symfony book on doctrine

Doctrine isDeletable method in entity

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.

Doctrine2: association mapping for a table with multiple foreign keys

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

Categories