I have an Alert Class with some data in it. I then have an Availability class. In my Availability class I have
/**
* #var \Nick\AlertBundle\Entity\Alert
*
* #ORM\ManyToOne(targetEntity="Nick\AlertBundle\Entity\Alert")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="availability_alert_id", referencedColumnName="id")
* })
*/
private $availabilityAlert;
So this is a foreign key back to my Alert class, linked to my Alerts class id.
I am now doing some work on the Availability data, so I have the DQL query
public function getAlertAvailability($id)
{
return $this->getEntityManager()
->createQuery(
'SELECT a.id, a.classLetter, a.flightNumber, a.alertPseudo, a.availability, a.lastUpdated
FROM NickAlertBundle:Availability a
WHERE a.availabilityAlert = :id
ORDER by a.classLetter, a.lastUpdated'
)
->setParameter('id', $id)
->getResult();
}
The way I call that is like this
public function getAvailabilityData(){
$alerts = $this->em->getRepository('NickAlertBundle:Alert')->getActiveAlertIds();
if (!$alerts) {
echo "No Availability";
}
foreach($alerts as $alert){
$alertId = (int)$alert['id'];
$allAvailability = $this->em->getRepository('NickAlertBundle:Availability')->getAlertAvailability($alertId);
}
}
So I essentially get all my active Alerts Ids, and then pass this to get my Availability for these individual Alerts.
Now I have a couple of problems.
Firstly, in the DQL query I make, I need to also get something from my Alert table (a field called command). How would I do a join in this query to get this piece of data?
Secondly, with the data that is returned, how do I access availabilityAlert in my Twig file?
UPDATE
Attempt at join
public function getAlertAvailability()
{
return $this->getEntityManager()
->createQuery(
'SELECT a.id, a.classLetter, a.flightNumber, a.alertPseudo, a.availability, a.lastUpdated, u.searchCommand
FROM NickAlertBundle:Availability a
JOIN a.availabilityAlert u
ORDER BY a.classLetter, a.lastUpdated'
)
->getResult();
}
Doctrine will load that entity as a proxy (for lazy loading) when the Availability entity is loaded.
You can access these via a normal getter / property access, but they will typically be lazy loaded by Doctrine. You can have them joined via a DQL query and the property will be hydrated with all the linked entities already loaded, see Improving Performance with a Join.
You can then access those associated entities in Twig as any other property.
Related
I used to get my data using this command so the first array is for find variable and the second array is for the order.
So I need to order with specific column:name of test(that is an object that has many columns: id/name ) But I dont know how !! when I enter _test.name I get unrecognized field
->findBy(
array('_id' => $id),
array('_test' =>'ASC')
);
class model1
{
private $_idModel1;
/**
*
* #ORM\ManyToOne()
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="id1",
* referencedColumnName="id2")
* })
*/
private $_test;
}
You cannot order using a relation with the findBy, but you can do it creating a query builder:
$model1Repository->createQueryBuilder('model1')
->leftJoin('model1._test', 't')
->where('model1._id = :id')
->setParameter('id', $id)
->orderBy('t.name', 'ASC');
The code has not been tested, you should adapt it to your code.
It creates a query builder over the model1 class , then joins it with your ManyToOne relation, filters via _id and then orders the result by the _test relation using the name field.
Aslo you need to declare in the #ManyToOne the target entity (https://www.doctrine-project.org/projects/doctrine-orm/en/2.8/reference/association-mapping.html#many-to-one-unidirectional)
#ManyToOne(targetEntity=TheNameOfTheTestClass)
Think of this class:
class Person {
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="Person", fetch="EXTRA_LAZY")
* #ORM\OrderBy({"name" = "ASC"})
*/
public $friends;
/**
*
* #var Person
* #ORM\ManyToOne(targetEntity="Person")
*/
public $bestFriend;
}
I have to iterate a lot over all Persons, so I'd like to fetch join them all at once.
To save memory, I have to do this partially.
So what I can do is:
$this->em->createQuery('SELECT partial p.{id, name, bestFriend} FROM Person p')->getResult();
This is cool, after this query, all persons are in the UoW, and I can traverse the graph via $aPersion->bestFriend->bestFriend without creating an additional query to the DB, since all Persons are in memory.
However, this does not work with the ToMany association. Adding friends to the partial select gives an error. If I want to iterate over all friends, this will first create a query to the join table...
How can I realise the full hydration of the friends-ToMany-assotiation with one query? Maybe a second query could help? Or a clever join clause?
Thanks in advance!
I would create a query in PersonRepository.php with a leftJoin and a addSelect like so:
$qb = $this->em->getRepository('App:Person')
->createQueryBuilder('p')
->leftJoin('p.friends', 'friends')
->select('partial p.{id, name, bestFriend}'}
->addSelect('partial friends.{id, name}') // Retrieve what you want here
->getQuery()->getResult();
return $qb;
I have not tested this, but believe it should work.
#DirkJFaber your answer was right,
in terms of DQL here is my solution:
$this->em->createQuery('
SELECT partial p.{id, name, bestFriend}, f FROM Person p JOIN f.friends f')->getResult();
I got a method on my repository class to get me Article with their join (category, image ...) and not every Article have categories or image, what don't give me back all expected result, cause my current repository function return only Article who have Categories and Image not null and just ignore what having null value.
My Article entity have the following relationship.
/**
* #ORM\ManyToMany(targetEntity="App\ArticleBundle\Entity\Category", cascade={"persist"})
*/
private $categories;
/**
* #ORM\ManyToOne(targetEntity="App\ArticleBundle\Entity\Image", cascade={"persist"})
*
*/
private $image;
And this is my repository function
public function getArticle($id)
{
$qb = $this->createQueryBuilder('a')
->where('a.id = :theId')
->setParameter('theId', $id)
->join('a.author', 'auth')
->addSelect('auth')
->join('a.categories', 'cat')
->addSelect('cat')
->join('a.image', 'img')
->addSelect('img');
return $qb->getQuery()->getOneOrNullResult();
}
Now I want to know if I can get Article which have categories, image or not in one query with the join. I want to say when using the Doctrine lazy loading (by avoid the join in the query) I get the expected result.
Use ->leftJoin() to get Article which have categories, image or not in one query:
public function getArticle($id)
{
$qb = $this
->createQueryBuilder('a')
->addSelect('auth', 'cat', 'img')
->join('a.author', 'auth')
->leftJoin('a.categories', 'cat')
->leftJoin('a.image', 'img')
->where('a.id = :theId')
->setParameter('theId', $id)
;
return $qb->getQuery()->getOneOrNullResult();
}
Thus, avoids extra queries when Doctrine try to load the related properties in a lazy way.
Explanation:
Using ->join() or ->innerJoin():
This is the simplest, most understood Join and is the most common. This query will return all of the records in the left table (table A) that have a matching record in the right table (table B).
Using ->leftJoin():
This query will return all of the records in the left table (table A) regardless if any of those records have a match in the right table (table B). It will also return any matching records from the right table.
Source: Visual-Representation-of-SQL-Joins explained in detail by C.L. Moffatt
Have a problem with subquery with symfony.
What I try to do - I have a table with users and a table with posts.
Posts Users
id|author|content id|username
I want create subquery to get user name by id.
/**
* #return array
*/
public function findAll()
{
return $this->getEntityManager()->createQuery(
'SELECT a, (SELECT u.username
FROM BackendBundle:User u WHERE u.id = a.author) as authorName
FROM BackendBundle:Article a'
)->getResult();
}
Result:
What am I doing wrong? What is the best way to join column from other table by id? Maybe i can use annotations?
Thx for any help.
You don't need a subquery here, what you need is a simple (INNER) JOIN to join Users with their Articles.
$em->createQuery("SELECT a FROM Article JOIN a.author'");
You don't even need an on clause in your join, because Doctrine should already know (through annotations on your entities or a separate yaml file), that the article.author field relates to user.id.
Edit:
I assume you have a User entity that is One-To-Many related to the Article entity.
class User
{
// ...
/**
* #OneToMany(targetEntity="Article", mappedBy="author")
*/
private $articles;
// ...
}
class Article
{
// ...
/**
* #var User
* #ManyToOne(targetEntity="User", inversedBy="articles")
*/
private $author;
// ...
}
Please refer to doctrines association mapping documentation: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html
I have a Car entity with a many-to-one relationship with an entity Owner. If I select all cars, Doctrine does one query on the Car table, and subsequently one query on the Owner table for each car. So fetching N cars becomes N+1 queries instead of a single JOIN query between the Car and Owner tables.
My entities are as follows:
/** #Entity */
class Car {
/** #Id #Column(type="smallint") */
private $id;
/** #ManyToOne(targetEntity="Owner", fetch="EAGER")
#JoinColumn(name="owner", referencedColumnName="id") */
private $owner;
public function getId() { return $this->id; }
public function getOwner() { return $this->owner; }
}
/** #Entity */
class Owner {
/** #Id #Column(type="smallint") */
private $id;
/** #Column(type="string") */
private $name;
public function getName() { return $this->name; }
}
If I want to list the cars with their owners, I do:
$repo = $em->getRepository('Car');
$cars = $repo->findAll();
foreach($cars as $car)
echo 'Car no. ' . $car->getId() .
' owned by ' . $car->getOwner()->getName() . '\n';
Now this all works very well, apart from the fact that Doctrine issues a query for each car.
SELECT * FROM Car;
SELECT * FROM Owner WHERE id = 1;
SELECT * FROM Owner WHERE id = 2;
SELECT * FROM Owner WHERE id = 3;
....
Of course I'd want my query log to look like this:
SELECT * FROM Car JOIN Owner ON Car.owner = Owner.id;
Whether I have fetch="EAGER" or fetch="LAZY" doesn't matter, and even if I make a custom DQL query with JOIN between the two entities, $car->getOwner() still causes Doctrine to query the database (unless I use EAGER, in which case $repo->findAll() causes all of them).
Am I just too tired here, and this is the way it is supposed to work - or is there a clever way to force Doctrine to do the JOIN query instead?
At least in 1.x Doctrine if you wanted to query for the related objects, you had to use DQL. For your case, the DQL query would look something like this:
//Assuming $em is EntityManager
$query = $em->createQuery('SELECT c, o FROM Car c JOIN c.owner o');
$cars = $query->execute();
Run first a DQL query where you select all the cars joined (DQL JOIN) with the owner. Put the owner in the select().
// preload cars
$qb = $em->createQueryBuilder()
->select('car, owner')
->from('\Entity\Car', 'car')
->leftJoin('c.owner', 'owner');
$query = $qb->getQuery();
// the following seems not needed, but I think it depends on the conf
$query->setFetchMode("\Entity\Car", "owner", "EAGER");
$query->execute(); //you don't have to use this result here, Doctrine will keep it
Doctrine 2 will then perform a JOIN (normally faster as it requires less db queries depending on the number of records).
Now launch your foreach, Doctrine will find the entities internally and it won't run single queries when you need the owner.
Monitor the number of queries first/after each change (eg. mysql general log)
Your query...
$car->getOwner() // "go and fetch this car's owner"
... is in a foreach loop so it will certainly issue the query several times.
If you're writing custom DQL to deal with this, $car->getOwner() shouldn't feature in this at all. This is a function of the Car class. The custom DQL you would write would mimick the exact SQL query you point out and get your join done efficiently.