Answer
I was able to do the query using IS NOT EMPTY in the where clause.
/**
* Finds all developments having at least one image.
*
* #param string
* #return array
*/
public function withImages()
{
return $this->query->createQueryBuilder('d')
->where('d.images IS NOT EMPTY')
->getQuery()
->getResult();
}
Question
I am using the Doctrine ORM. I would like to be able to get all developments which have at least one image, such that for every Development selected in the query, the following property would be true. $development->getImages()->count()) > 0.
I have a Development entity which has a One to Many relationship with an Image entity.
/**
* The Development class provides an abstraction of a development.
*
* #Entity
* #HasLifecycleCallbacks
* #Table(name="developments")
**/
class Development extends BaseEntitiy {
/** #OneToMany(targetEntity="Exquisite\Entity\Image", mappedBy="development") **/
protected $images;
I have a DevelopmentRepository which has an instance of a EntityManager and an instance of the Repository for the Entity. I have made an attempt to do this in my withImages() method in the DevelopmentRepository class, but not having much luck.
class DevelopmentRepository implements DevelopmentRepositoryInterface {
/**
* Class Constructor
*
* #param Doctinre\ORM\EntityManager The EntityManager instance.
*/
public function __construct(EntityManager $em)
{
$this->em = $em;
$this->query = $this->em->getRepository('Exquisite\Entity\Development');
}
/**
* Finds all developments having at least one image.
*
* #param string
* #return array
*/
public function withImages()
{
$qb = $this->query->createQueryBuilder('d');
$qb2 = $this->em->createQueryBuilder();
return $qb->where($qb->expr()->exists($qb2->select('i.development')->from('Exquisite\Entity\Image', 'i')->getDql()))
->getQuery()
->getResult();
}
Any help would be much appreciated!
I had the same problem, and this worked for me.
I fixed just doing a join (would return only the orders with items):
return $this->createQueryBuilder('so')
->select('so')
->join('so.orderItems', 'soi')
->getQuery()
->getResult();
Or doing a sub query with DQL
SELECT so
FROM Entity\\SalesOrder so
WHERE (SELECT count(soi.id) FROM Entity\\SalesOrderItem soi WHERE soi.salesOrder = so.id ) > 0
I hope this can be helpful
Related
I would like to build a query that brings me all the games for a logged in user that he has not yet joined. For this I have built these 2 Entities. They are connected by many to many.
class Game
{
public function __construct()
{
$this->users = new ArrayCollection();
}
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
*
* #var Users[]
*
* #ORM\ManyToMany(
* targetEntity="Domain\Entity\User",
* inversedBy="user",
* fetch="EAGER"
* )
*/
private $users;
/**
* #return array
*/
public function getUsers() : array
{
return $this->users->getValues();
}
/**
* #param User $users
*/
public function setUser($users)
{
if(is_array($users)){
/** #var User $user */
foreach ($users as $user){
$this->users->add($user);
}
} else {
$this->users->add($users);
}
}
}
And the User Entity
class User implements AdvancedUserInterface
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
}
The Entities has more attributes but i think they are not important.
Also I tried these Query, but it doesn't work.
/**
* #param User $user
* #return array
*/
public function fetchAllNonActivatedWhereYouNotJoin(User $user): array
{
$qb = $this->createQueryBuilder('g');
$qb->select('g')
->innerJoin('g.users', 'u')
->where('u.id != :user')
->andWhere('g.activate = 0')
->setParameter('user', $user->getId())
->getQuery()->getResult();
return $qb->getQuery()->getResult();
}
Does anyone know a solution? Its Symfony 3 and Doctrine in PHP 7.1
One way to do it is left join the 2 entities starting from the game repository as you do, with a join condition to the logged in user and have a condition that users is empty:
$qb->leftJoin('g.users', 'u', Join::WITH, 'u.id = :user')
->andWhere('g.activate = 0')
->having('COUNT(u) = 0')
->groupby('g')
->setParameter('user', $user->getId())
->getQuery()->getResult();
This works because of doctrine hydration, which hydrates the users property on the limited joined query(in this case each game will either have the logged in user or not in the users collection).
There are also other ways to achieve this
Be careful with this if you are doing consecutive queries with the query builder, as the entity manager keeps references to the already hydrated relations. Reference of the issue
I want to know which professional diseases are included in all the medical records of a company production unit. The entity MedicalRecord has a many to many relationship with DiseaseTypology as follows:
/**
* AppBundle\Entity\HealthData\MedicalRecord
*
* #ORM\Table(name="medical_record")
* #ORM\Entity(repositoryClass="MedicalRecordRepository")
* #ORM\HasLifecycleCallbacks
*/
class MedicalRecord
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string $companyProductionUnitId
*
* #ORM\Column(name="company_production_unit_id", type="integer",nullable=true)
*/
protected $companyProductionUnitId;
/**
* #var ArrayCollection $professionalDiseases
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\HealthData\Core\DiseaseTypology")
* #ORM\JoinTable(name="medical_record_professional_disease",
* joinColumns={#ORM\JoinColumn(name="medical_record_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="professional_disease_id", referencedColumnName="id")}
* )
*
*/
protected $professionalDiseases;
In the MedicalRecordReposity class I created the following method:
public function getProfessionalDiseasesByProductionUnit($productionUnitId)
{
$em = $this->getEntityManager();
$repository = $em->getRepository('AppBundle:MedicalRecord');
return $repository->createQueryBuilder('m')
->select('m.professionalDiseases')
->where('m.companyProductionUnitId = :productionUnitId')
->setParameter('productionUnitId', $productionUnitId)
->getQuery()
->getArrayResult();
}
But I get the error:
[Semantical Error] line 0, col 9 near 'professionalDiseases': Error: Invalid PathExpression. Must be a StateFieldPathExpression.
How to query the inverse side of a many to many relationship? Thank you!
I don't know if I can understand what you want, but here's my try:
class MedicalRecordRepository extends \Doctrine\ORM\EntityRepository
{
public function getProfessionalDiseasesByProductionUnit($productionUnitId)
{
$qb = $this->createQueryBuilder('m');
$qb
->select('m, pd')
->innerJoin('m.professionalDiseases', 'pd')
->where('m.companyProductionUnitId = :productionUnitId')
->setParameter('productionUnitId', $productionUnitId)
;
return $qb->getQuery()->getArrayResult();
}
}
Explanation: i think you need a join between MedicalRecord and DiseaseTypology, and for that, if you have this setup (in both your entities):
#Entity/MedicalRecord.php
private $companyProductionIUnitId;
/**
* #var \AppBundle\Entity\DiseaseTypology
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\DiseaseTypology", mappedBy="medicalRecords")
*/
private $professionalDiseases;
First, you have to have that mappedBy option, to tell doctrine the inverse side of the relationship.
And
# Entity/DiseaseTypology.php
/**
* #var \AppBundle\Entity\MedicalRecord
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\MedicalRecord", inversedBy="professionalDiseases")
*/
private $medicalRecords;
You have to have that inversedBy option to tell doctrine the owning side of the relationship.
Once we've clarified that, to let doctrine do its things related to joins, you just need to tell it on which field to make the join. And as in my example, the relation between MedicalRecord and DiseaseTypology is made through $professionalDiseases fields. So this one will be the field to make the join with:
->innerJoin('m.professionalDiseases', 'pd') // this professionalDiseases is the $professionalDiseses from MedicalRecord entity
Ok, I've did all those explanations, because I saw how you've did your query, and I feel is not the right approach.
And my results after running the getProfessionalDiseasesByProductionUnit() method was like this:
Note: Use getResult() instead of getArrayResult(), because you fetch entities (DiseaseTypology), not set of fields
There are 2 options here:
Make relation MedicalRecord <=> DiseaseTypology bidirectional See documentation. Then your method would look very simple:
public function getProfessionalDiseasesByProductionUnit($productionUnitId)
{
$em = $this->getEntityManager();
$repository = $em->getRepository(DiseaseTypology::class);
return $repository->createQueryBuilder('dt')
->select('dt')
->join('dt.medicalRecords', 'm')
->where('m.companyProductionUnitId = :productionUnitId')
->setParameter('productionUnitId', $productionUnitId)
->getQuery()
->getResult();
}
Keep existing DB structure and add some logic after query
public function getProfessionalDiseasesByProductionUnit($productionUnitId)
{
$em = $this->getEntityManager();
$repository = $em->getRepository(MedicalRecord::class);
$mediaRecords = $repository->createQueryBuilder('m')
->select('m, dt')
//note: with this join all professionalDiseases will be loaded within one query for all MedicalRecords
->join('m.professionalDiseases', 'dt')
->where('m.companyProductionUnitId = :productionUnitId')
->setParameter('productionUnitId', $productionUnitId)
->getQuery()
->getResult();
$professionalDiseases = [];
foreach($mediaRecords as $mediaRecord) {
foreach($mediaRecord->professionalDiseases as $professionalDisease) {
$professionalDiseases[professionalDisease->id] = $professionalDisease;
}
}
return $professionalDiseases;
}
I have 2 entities connected with many to many relation into a 3th table, and I want to get every color for an id of product:
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Color", inversedBy="products")
* #ORM\JoinTable(name="products_colors",
* joinColumns={#ORM\JoinColumn(name="product_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="color_id", referencedColumnName="id")}
* )
*/
private $colors;
And in my second entity:
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Product", mappedBy="colors")
*/
private $products;
This is my query: (I was trying to make some joins but cant make it work)
class ProductRepository extends \Doctrine\ORM\EntityRepository
{
public function getColors($id)
{
$query = $this->createQueryBuilder('p')
->join('AppBundle\Entity\Color', 'c')
->where('c.product_id = :id')
->setParameter('id', $id)
->getQuery()
->getResult();
return $query;
}
}
I got this error:
[Semantical Error] line 0, col 85 near 'product_id =': Error: Class
AppBundle\Entity\Color has no field or association named product_id
which I understand, but can't think of a way to make this work.
Symfony expects you to reference the entity (not id) of an object when mapped by ManyToMany relationship. Try:
class ProductRepository extends \Doctrine\ORM\EntityRepository
{
public function getColors(Product $product)
{
$query = $this->createQueryBuilder('p')
->join('AppBundle\Entity\Color', 'c')
->where('c.product = :product')
->setParameter('product', $product)
->getQuery()
->getResult();
return $query;
}
}
Of course you'll have to modify your call to this function accordingly.
You can also skip the query creation altogether with the getter function in your entity; symfony automagically will do the query for you.
// class Product
private $colors; // as you have it set up already
/**
* #return ArrayCollection|Color[]
*/
public function getColors()
{
return $this->colors;
}
I have an entity which store the 3D objects what I printed.
private $id;
/**
* #ORM\Column(type="array", nullable=true)
*/
private $images;
/**
* #ORM\Column(type="datetime")
*/
private $date_created;
/**
* #ORM\Column(type="datetime")
*/
private $date_modified;
/**
* #ORM\ManyToOne(targetEntity="App\UserBundle\Entity\User")
*/
private $user;
/**
* #ORM\ManyToOne(targetEntity="App\ThreedBundle\Entity\Threedobject", cascade={"all"})
*/
private $threedobject;
/**
* #ORM\Column(type="text", nullable=true)
*/
private $description;
There is a SQL query which looks like this:
select threedobject_id from threed_print where user_id = {RANDOM_NUMBER} group by threedobject_id;
I have to get the $threedobject all instance which (I mean the App\ThreedBundle\Entity\Threedobject instances) which represent the following sql query through Doctrine.
I tried the following querybuilder, but it have returned the array representation of the values, but most of the cases I have to use the methods of the elements, so I want to get the instances.
$query = $this->em->createQueryBuilder();
$result = $query
->select('tp')
->addSelect('to')
->from('ThreedBundle:ThreedPrint', 'tp')
->join('tp.threedobject', 'to')
->join('tp.user', 'u')
->where('u.id = :userID')
->groupby('to.id')
->setParameter('userID', $userID)
->getQuery();
return $result->getResult();
I read about the repository find method, but in this is not what I want, or I'm not totally understand how it is working.
UPDATE:
Basically what I need:
$query = $this->em->createQueryBuilder();
$result = $query
->select('to')
->from('ThreedBundle:ThreedPrint', 'tp')
->join('tp.threedobject', 'to')
->join('tp.user', 'u')
->where('u.id = :userID')
->groupby('to.id')
->setParameter('userID', $userID)
->getQuery();
return $result->getResult();
But I got the following error for that:
'SELECT to FROM': Error: Cannot select entity through identification variables without choosing at least one root entity alias.
You should implement the inverse relationship oneToMany from Threedobject to Threedprint, adding the $threedprints field in Threedobject.
Then you could write this
$threedobjects=$this->em->createQueryBuilder()
->select('to')
->from('ThreedBundle:Threedobject')
->join('to.threedprints', 'tp')
->join('tp.user', 'u')
->where('u.id = :userID')
->setParameter('userID', $userID)
->getQuery()->getResult();
If you want to use the repository, you should create a folder in your project under src (which normally you call it Repository) and create a new class with a name (for example: ThreedobjectRepository).
then you put the following in that class:
namespace NameProject\NameBundle\Repository;
use Doctrine\ORM\EntityRepository;
class ThreedobjectRepository extends EntityRepository
{
function findById3D($a)
{
$query = $this->getEntityManager()
->createQuery("Select th.threedobject_id AS id
FROM NameprojectNameBundle:threed_print th
Where th.user_id=:user_id
GROUP BY threedobject_id")
set parameter (user_id, $a);
return $query->getResult();
}
}
don't forget to put on your threed_print.php this line:
/**
* #ORM\Entity(repositoryClass="Nameproject\NameBundle\Repository\ThreedobjectRepository")
* #ORM\Table
*/
then you can go to the controller you are working on and instead of using findall() or findoneby(), you just use the function that you already create findById3D($a) and use easily the instance you wanted to use.
I hope I helped you.
i'm trying to load an entity passing a parameter to a foreign key which is the inverse side
i have this two entities
Ad
/**
* Ad
*
* #ORM\Table(name="Ad")
* #ORM\Entity(repositoryClass="Symarket\MarketBundle\Repository\AdRepository")
*/
class Ad
{
/**
* #var AdImage
*
* #ORM\OneToMany(targetEntity="AdImage", mappedBy="ad", cascade={"persist", "merge"})
*/
private $images;
//...
}
AdImage
/**
* AdImage
*
* #ORM\Table(name="AdImage")
* #ORM\Entity(repositoryClass="Symarket\MarketBundle\Repository\AdImageRepository")
*/
class AdImage {
/**
* #var boolean
*
* #ORM\Column(name="adi_is_visible", type="boolean")
*/
protected $isVisible;
/**
* #var Ad
*
* #ORM\ManyToOne(targetEntity="Ad", inversedBy="images", cascade={"persist"})
* #ORM\JoinColumn(name="adi_ad_id", referencedColumnName="ad_id")
*/
protected $ad;
}
now, by getting a Ad from the database i want to get ONLY the images which are "isVisibile" => true
how is this possible with queryBuilder?
what i tried so far with the findBy function is this
public function findById($adId) {
$res = $this->findBy(array("id" => $adId, "isVisible" => true, "images" => array("isVisible" => true)));
$ad = reset($res);
return $ad;
}
and i got this error
You cannot search for the association field 'Symarket\MarketBundle\Entity\Ad#images', because it is the inverse side of an association. Find methods only work on owning side associations.
then i tried this way with the querybuilder
public function findById($adId) {
$res = $query = $this->createQueryBuilder('ad')
->leftJoin('ad.images', 'img')
->where('img.isVisible = :adVisible')
->andWhere('ad.id = :id')
->setParameter('adId', $adId)
->setParameter('imgVisible', true)
->getQuery();
$ad = reset($res);
return $ad;
}
and i get NULL
Thanks in advance.
for those who may encounter this problem here goes my solution:
public function findById($adId) {
$ad = $this->getEntityManager()
->createQuery("SELECT a, i from MarketBundle:Ad a LEFT JOIN a.images i with i.isVisible = :visible where a.id = :adId")
->setParameter("adId", $adId)
->setParameter("visible", true)->getSingleResult();
return $ad;
}
You can do this with the default repository for your AdImage entity. Consider this example:
$entityManager = $this->getEntityManager();
// You said you already have the Ad entity instance you care about so just grabbing by id for demonstration purposes
$ad = $entityManager->getRepository('Ad')->findOneById(5);
$adImagesVisible = $entityManager->getRepository('AdImage')->findBy(array('isVisible' => true, 'ad' => $ad->getId()));
If you approach this from the entity on the "Many" side of the relationship you should be able to get what you want without having to write a custom DQL query.