Virtual many-to-many relationship - symfony

let's imagine two distinct entities A and B is it possible to create a kind of virtual many-to-many relation.
I want A able to get all the B by id and B to get all A without using a join table.
I want to do this trick because my entities are not related. So, I can only access B from A using:
->join('App:entityB','b')
But this is not working using the normalizer.
I want my normalizer came from A to B and using a doctrine criteria in A to filter my B collection
/**
* #return mixed
*/
public function getEntitiesA()
{
$criteria = Criteria::create();
$criteria->where(Criteria::expr()->eq('foo', 'foo'));
return $this->entitiesA->matching($criteria);
}
I have found resolve_target_entity on the symfony documentation can I use this for my purpose?
resolve_target_entity

Related

How to get the join ID when using Doctrine?

I am working on a project of my company. However, I am not familiar with Doctrine. I am the old-styled query-guy.
Table-A and Table-B is in one-to-many relation, linking up by "a_id" on Table-B. In the Entity-B, $a_name is specified.
Table-A
a_id
a_name
a_attr
Table-B
b_id
a_id
b_name
b_attr
Entity-B
/**
* #ORM\ManyToOne(targetEntity="EntityA")
* #ORM\JoinColumn(name="a_id", referencedColumnName="a_id")
* #var Timezone
*/
protected $a_name;
Now I am writing a method to get a set of records using IN()
/**
* #param array $ids
*
* #return array
*/
public function getByIds($ids) {
$query = $this->getEntityManager()->createQuery('SELECT t FROM Entity-B t INDEX BY t.id WHERE t.id IN (:ids)');
}
The above line "INDEX" and "WHERE" with the Entity-B ID. How can I "INDEX" and "WHERE" with Entity-A's ID (a_id on Table-B)?
Thanks.
Try this or similar for your DQL:
SELECT a.a_id, t.b_id, t.b_name, t.b_attr FROM Entity-B t LEFT JOIN t.a_name a INDEX BY a.id WHERE a.a_id IN (:ids)
We just add a join to Entity-A, include a.id in the select and then index by a.id.
As we working with entities in doctrine, we need to join the entity to get at its id to then index by it. Also, the naming of your properties within each entity could be simpler and more intuitive. So rather than Entity-A (a) having properties a_id, a_attr, etc, just use id, attribute, etc. I assume you just generalised your code for the question and you probably have nicer property names in your project.
Let me know how that DQL works out for you.

Symfony Doctrine - In a many to many relation why is the patch working in one direction but not the other

I'm on a project where I have a many to many relationship between team and agent. Because my teams can have multiple agents and my agents can have multiple teams.
I'm in a situation where I'm doing a patch so I can add multiple agents to a team (which is working) but I cannot do a working patch to add multiple teams to an agent.
Is it because of mapped by and inversed by?
UPDATE
In my TEAM entity here is the relation
/**
* #ORM\ManyToMany(targetEntity="MyBundle\Entity\Agent", inversedBy="teams")
*/
private $agents;
Here is the relation in my AGENT entity
/**
* #ORM\ManyToMany(targetEntity="MyBundle\Entity\Team", mappedBy="agents")
*/
private $teams;
In my team controller, when I want to give my team some new agents I'm using this piece of code and it works. I can see all the agents associated to the team in the database.
$team->setAgents($theAgents);
But when I want to do the opposite in my agent controller (assigning some teams to a new agent) the agent is created in the database but it's not assigned to any team in the association table. Even if I'm using this:
$agent->setTeams($theTeams);
Hence, is it maybe because it's not possible with Doctrine? Or perhaps I'm missing something.
This is the expected behavior.
For your ManyToMany relation you have the owning side:
/**
* #ORM\ManyToMany(targetEntity="MyBundle\Entity\Agent", inversedBy="teams")
*/
private $agents;
and the inverse side:
/**
* #ORM\ManyToMany(targetEntity="MyBundle\Entity\Team", mappedBy="agents")
*/
private $teams;
which are defined by the settings inversedBy and mappedBy respectively.
For a ManyToMany relation, you can chose which entity is the owning and which the inverse side, either of them can be defined.
Doctrine only checks the owning side for association changes. Check Working with associations, which means on your case, only $agents of Teams is checked for any changes to be persisted in the database.
On Agents, any changes in teams are ignored.
Changes made only to the inverse side of an association are ignored. Make sure to update both sides of a bidirectional association (or at least the owning side, from Doctrine's point of view)
It is your responsibility to include these changes on the owning side also.
So in setTeams, do something like:
public function setTeams($teams) {
$this->teams = $teams;
foreach($teams as $team) {
$team->addAgent($this);
}
}
Note that in addAgent you have to check if the agent already exists in the collection.
In the end the solution I found is similar to Jannes'.
In my Agent entity I added this function:
/**
* #param mixed $team
*/
public function addTeam(Team $team)
{
$this->teams[] = $team;
$team->addAgent($this);
}
and in my Agent controller:
$teams = $request->get('teams');
foreach ($teams as $team){
$myTeam = $em->getRepository('MyBundle:Team')
->find($team["id"]);
$agent->addTeam($myTeam);
}
By doing so, I was I able to have a working post on both sides!
Thank you all again for your help!

Symfony - Read-only entities hydrated from YAML

Is it possible to have simple read-only entities, that can have an association with an other doctrine entity, but their data is stored in a text ( YAML ) file ?
Let's say I have a product entity, and I want to set a category for each product. But for now, I only have very few categories ( and don't need to edit or add ), so I don't want/need to create a full doctrine entity with it's own table in the DB.
So I create a very simple entity:
class ProductCategory
{
private $id;
private $name;
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
}
Now I would like to create a simple YAML file where the data is stored, like so:
0: Cheese
1: Meat
2: Dairy Products
....
Then I would like to set a ManyToOne relation from the product entity to the ProductCategory entity.
Is that possible ?
And, how to "query" the categories ? ( let's say I want to list all categories that start with a certain letter )
'Why' you ask ?
Well for now, as I said, I only have very few categories, but maybe some day I want to add many more, and even have a CRUD editor for them and so on, then I could easily convert it to a full doctrine entity.
Or any other suggestions on how to approach this ?
There is already a library that provides what you are looking for that's called Alice:
https://github.com/nelmio/alice
https://github.com/hautelook/AliceBundle
https://github.com/fzaninotto/Faker
https://github.com/h4cc/AliceFixturesBundle
This way you can create random test data en masse and can still work with Doctrine as usual.
If you want to do this manually it will be a pain to solve the problem of connecting the entities. Your best bet is to keep all of them in arrays with id's being used as keys but even then you will probably end up writing lots of glue code to connect the entities.

Doctrine ManyToMany, find related and no related results

I have an entity called tournament to which users can register to participate in.
The relationship between the two entities is ManyToMany and need to create a view of Symfony2 in which list all tournaments, with or without registered users so that they can join.
This is my DoctrineQueryBuilder
$em->createQueryBuilder('d')
->select('d, i, u')
->leftJoin('d.item','i')
->leftJoin('d.users','u')
->where('d.active = 1')
->andWhere('d.state = 1')
->orderBy('d.dateStart', 'ASC');
I also need to get the number of users who have joined the tournament.
Preamble
There are various ways to achieve what you want. You can create a sub-query to do the count, however a simpler solution is to let doctrine handle this for you.
The solution described below is based on Doctrine lazy/eager loading capability. When doctrine loads an entity, it will also populate it's associations, either lazily or eagerly (default is lazy).
Solution
Assuming your Tournament entity maps the users association as a ManyToMany relation. You can create a new method which counts your Tournament->users.
ManyToMany associations would populate the entity property (in this cases $users) with an ArrayCollection.
A method called countUsers would do the trick, example implementation below:
...
class Tournament {
...
/**
* #ManyToMany(targetEntity="User")
* #JoinTable(name="tournament_users",
* joinColumns={#JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="tournament_id", referencedColumnName="id"}
* )
**/
private $users;
...
public function countUsers(){
return $this->users->count();
}
In the view when iterating over the collection of tournaments, simply call the $tournament->countUsers() method to display the count.
References:
ArrayCollection: http://www.doctrine-project.org/api/common/2.1/class-Doctrine.Common.Collections.ArrayCollection.html

Definitions of one-to-many, many-to-many etc

I'm building a database application using Doctrine2. I'm getting somewhat confused by the foreign key mappings. I'm wondering, have I got these examples correct:
One-To-One: An X has exactly one Y.
One-To-Many: An X can have multiple Ys.
Many-To-One: Multiple Xs can have the same Y.
Many-To-Many: Multiple Xs can have multiple Ys.
This is the specific situation that got me confused:
A User has exactly one HomeTown. Many users can belong to the same home town, so the link for the User is:
/**
* #ORM\ManyToOne(targetEntity="HomeTown", inversedBy="localUsers")
*/
$homeTown;
And, the corresponding HomeTown link is:
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="homeTown")
*/
$localUsers;
OR is it:
/**
* #ORM\OneToMany(targetEntity="User", mappedBy="homeTown")
*/
$localUsers;
Some clarification would be much appreciated!
I've been looking at http://doctrine-orm.readthedocs.org/en/latest/reference/association-mapping.html
When you have OneToMany association, the inverted has to be ManyToOne. Saying that, your second option is correct.
TIP: Using Doctrine CLI command orm:validate-schema might also help to identify this issue.
The full path in Symfony app: php app/console doctrine:schema:validate
If you want one city to have many users the mapping should be as it follows
Entity City
/**
* #ORM\OneToMany(targetEntity="User", mappedBy="homeTown")
*/
private $users;
...
public function __construct()
{
$this->users = new ArrayCollection();
}
...
Entity User
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="users")
* #ORm\JoinColumn(name="home_town", referencedColumnName="id")
*/
private $homeTown;
This mapping shows us that the City which is owning side has On-To-Many relation with User(target Entity). Respectively the User which is inversed side has to be annotated with ManyToOne relation because many users have same city. Of course, here the target entity should be City. Its important to specify which column is pointing the foreignkey with the referencedColumnName attribute in JoinColumn annotation. It shows what column of other table points this key. In this example in table User there is column named "home_town" which is a foreign key pointing to column id of table City
In ManyToOne relation you shod use JoinColumn annotation
This mapping is also Bidirectional.
You can make id Unidirectional as in the User Entity do not use "inversedBy=" attribute and remove OneToMany annotation with $user property from the City entity. It is something like when you have to know the city of a particular user, but you do not need to know all users for a specific city

Resources