I'm trying to make topic / comment system with symfony and I have some question about doctrine's querybuilder.
if I have two entities connected:
/**
* #ORM\ManyToOne(targetEntity="Topicit", inversedBy="comments")
* #ORM\JoinColumn(name="topic_id", referencedColumnName="id")
*/
protected $topicit;
and
/**
* #ORM\OneToMany(targetEntity="Comment", mappedBy="topicit")
*/
protected $comments;
Currently I have builder (getting every column):
$repository = $this -> getDoctrine() -> getRepository('FTFairyBundle:Comment');
$query = $repository->createQueryBuilder('p')
->orderBy('p.topicit', 'ASC')
->getQuery();
$comment = $query ->getResult();
How I make it get only wanted topic_id? Like if I open page with id #1 I wanna get comments connected to that id.
Thanks for your time
You don't need to use query builder for that kind of tasks, you can simply use a getter in your Topicit entity (that of course you need to retrieve from DB)
Something like ->getComments() *(of course that method needs to be defined in Topic class)
Doctrine will take care of load only "consistent" data from your Topic object
Method definition
public getComments()
{
return $this->comments;
}
If you want to query directly you can modify your code as follows
$topicit_id = ... //code to retrieve topicit_id
$repository = $this->getDoctrine()-> getRepository('FTFairyBundle:Comment');
$query = $repository->createQueryBuilder('p')
->where('p.topicit = :topicit_id')
->setParameter('topicit_id', $topicit_id)
->orderBy('p.topicit', 'ASC')
->getQuery();
$comment = $query ->getResult();
Related
I have 2 entities, MyItem and MyItemCategory. When I try to create a query with query builder I get the following error:
Error: Class App\Entity\MyItem has no
field or association named myitem_category_id (500 Internal Server Error)
This is the where part of my query builder:
$queryBuilder = $this->getDoctrine()
->getRepository('App\Entity\MyItem')
->createQueryBuilder('m');
// adds where for category_id:
$queryBuilder->where('m.myitem_category_id = :category_id')->setParameter('category_id',$category_id);
Here're first entities:
/**
* #ManyToOne(targetEntity="MyItemCategory")
* #JoinColumn(name="myitem_category_id", referencedColumnName="id")
*/
private $myItemCategory;
...and my category entity:
/**
* #ORM\OneToMany(targetEntity="MyItem", mappedBy="myItemCategory")
*/
private $myItemCategories;
The querybuilder actually doesn't care about database fields, but instead uses the object mapper, so there is no field my_item_category_id on your Entity, but instead a field myItemCategory
So, you can either do:
$querybuilder
->where('m.myItemCategory = :category')
->setParameter('category', $category) // <-- an actual MyItemCategory object
or you can join it in and check for the id:
$querybuilder
->leftJoin('m.myItemCategory', 'mic')
->where('mic.id = :micid')
->setParameter('micid', $category_id)
(I actually don't know if m.myItemCategory.id = :micid might work ........ you could try ;o))
I have an entity called Cycle, with a OneToMany association to CycleActeur (see code below).
I'd like to be able to fetch all Cycle objets in database with no CycleActeur objects associated, using a simple doctrine findBy* method from my controller.
That is to say something like this :
$manager = $this->getContainer()->get('doctrine.orm.entity_manager');
$cycleManager = $manager->getRepository('ESI67Zen2Bundle:Cycle');
$cyclesWithNoCycleActeur = $cycleManager->findBy('acteurs', null);
Is there a way to do this without having to write a specific method in the CycleRepository ?
Extract from the Cycle class code
class Cycle {
/**
* #ORM\OneToMany(
* targetEntity="CycleActeur",
* mappedBy="cycle",
* orphanRemoval=true)
*/
private $acteurs;
}
Extract from the Cycle class code
class CycleActeur {
/**
* #var Cycle Le cycle concerné
*
* #ORM\ManyToOne(targetEntity="Cycle", inversedBy="acteurs")
* #ORM\JoinColumn(name="cycle_id", referencedColumnName="id")
*
*/
private $cycle;
}
Your Cycle entity is the inverse side of relationship and it's table in database has no 'acteurs' column, so you cannot use findBy(['acteurs'=>null]) or findByActeurs(null). But you can do something anyway:
$manager = $this->getContainer()->get('doctrine.orm.entity_manager');
$cycleManager = $manager->getRepository('ESI67Zen2Bundle:Cycle');
$allCycles = $cycleManager->findAll();
$cyclesWithNoCycleActeur = [];
foreach($allCycles as $cycle)
{
if($cycle->getActeurs()->isEmpty())
{
$cyclesWithNoCycleActeur[] = $cycle;
}
}
In this case (to my mind) the best way is to use DQL's condition IS EMPTY:
$manager
->createQueryBuilder()
->from(Cycle::class, 'cycle')
->select('cycle')
->andWhere('cycle.acteurs IS EMPTY')
->getQuery()
->getResult()
;
You can use this code in the EntityRepository or anywhere you have access to the EntityManager.
Source: Doctrine documentation.
There's a DQL function SIZE(), which according to Doctrine documentation:
SIZE(collection) - Return the number of elements in the specified collection
So you can use it as a condition like:
SIZE(acteurs) = 0
I'm not sure if it will work with a findBy method, but I would recommend to create a custom method in ESI67Zen2Bundle:Cycle's repository, to make it explicit what the code is doing. Its will work both for DQL Query and Query Builder.
$cyclesWithNoCycleActeur = $cycleManager->findBy(array('SIZE(acteurs)' => 0));
My 2 cents
I have searched for this specific issue for a long time and not sure if I have a misunderstanding in using Criteria in queries or if it is just not possible (yet?)
This is an example with only the minimum fields and conditions.
I have two Entities:
Product
field: visible
Brand
field: active
Within the Brand entity, I have a method
"getVisibleProducts()" where I want to get all Products with
product.visible = 1
And in my ProductRepository I have a method findAllVisibleForBrand(Brand $brand) where I want to get all Products with product.visible = 1 AND brand.active = 1
So my idea for best reusability was to make a criteria for the product.visible value within my ProductRepository and use it for query within the repository
/**
* #param Brand $brand
*
* #return Product[]
*/
public function findAllVisibleForBrand(Brand $brand)
{
return $this->createQueryBuilder('product')
->leftJoin('product.brand', 'brand')
->addCriteria(ProductRepository::createVisibleCriteria())
->andWhere('product.brand = :brand')
->setParameter('brand', $brand)
->getQuery()
->execute();
;
}
/**
* #return Criteria
*/
public static function createVisibleCriteria()
{
return Criteria::create()
->andWhere(Criteria::expr()->eq('visible', '1'))
;
}
and within my Brand entity:
/**
* #return Product[]|ArrayCollection
*/
public function getVisibleProducts()
{
return $this->getProducts()->matching(ProductRepository::createVisibleCriteria());
}
This is working fine and without problems.
The same idea for the BrandRepository
/**
* #return Brand[]
*/
public function findAllActiveBrands()
{
return $this->createQueryBuilder('brand')
->addCriteria(BrandRepository::createActiveCriteria())
->getQuery()
->execute()
;
}
/**
* #return Criteria
*/
public static function createActiveCriteria()
{
return Criteria::create()
->andWhere(Criteria::expr()->eq('active', '1'))
;
}
Now my idea was to use the Brand Active Criteria within my ProductRepository to get only Products that are visible but only IF the brand is active. So my method now looks like this:
/**
* #param Brand $brand
*
* #return Product[]
*/
public function findAllVisibleForBrand(Brand $brand)
{
return $this->createQueryBuilder('product')
->leftJoin('product.brand', 'brand')
->addCriteria(BrandRepository::createActiveCriteria()) // added this line
->addCriteria(ProductRepository::createVisibleCriteria())
->andWhere('product.brand = :brand')
->setParameter('brand', $brand)
->getQuery()
->execute();
;
}
by adding the brandActiveCriteria this way, I get the following error:
[Semantical Error] line 0, col 97 near 'active = :active': Error: Class AppBundle\Entity\Product has no field or association named active
The Query doctrine is building looks like this:
SELECT product FROM
AppBundle\Entity\Product product
LEFT JOIN product.brand brand
WHERE
product.active= :active
AND product.visible = :visible
AND product.brand = :brand
adding the table name into the criterias work for the Query within the repository but not for the query within the entity since the table alias is not the same.
Is there any chance to have the rules as criteria or in any other place without duplicating it over multiple classes?
I got the idea of using static function to create criterias from
https://knpuniversity.com/screencast/collections/criteria-collection-filtering
You can add the alias in your criteria:
public static function createActiveCriteria($alias = null)
{
return Criteria::create()
->andWhere(Criteria::expr()->eq(($alias ? $alias.'.' : '').'active', '1'))
;
}
And then you call your criteria like this:
// "brand" is the name of the given alias in leftJoin
BrandRepository::createActiveCriteria('brand');
So your findAllVisibleForBrand() method will look like this:
public function findAllVisibleForBrand(Brand $brand)
{
return $this->createQueryBuilder('product')
->leftJoin('product.brand', 'brand')
->addCriteria(BrandRepository::createActiveCriteria('brand')) // Notice the alias parameter here
->addCriteria(ProductRepository::createVisibleCriteria())
->andWhere('product.brand = :brand')
->setParameter('brand', $brand)
->getQuery()
->execute();
;
}
And your DQL should be like this:
SELECT product FROM
AppBundle\Entity\Product product
LEFT JOIN product.brand brand
WHERE
brand.active= :active
AND product.visible = :visible
AND product.brand = :brand
In my sf2 project I access entity collection by calling:
$user_payment_info_datas = $user->getUserPaymentInfoDatas();
In the User entity there is:
/**
* #ORM\OneToMany(targetEntity="UserPaymentInfoData", mappedBy="user")
* #ORM\OrderBy({"payment_info" = "ASC", "payment_info_data" = "ASC"})
*/
private $user_payment_info_datas;
So it's 1:n relation and user has many UserPaymentInfoData's. However, there is another entity called PaymentInfoData that contains the actually values for UserPaymentInfoData's. So the relation is
User -> UserPaymentInfoData -> PaymentInfoData.
So in terms of annotations in UserPaymentInfoData it is:
/**
* #ORM\ManyToOne(targetEntity="PaymentInfoData", inversedBy="user_payment_info_datas")
* #ORM\JoinColumn(name="payment_info_id", referencedColumnName="id")
* #ORM\OrderBy({"title"="ASC"})
*/
private $payment_info_data;
I need to sort the collection returned by
$user_payment_info_datas = $user->getUserPaymentInfoDatas();
ascending by a field from PaymentInfoData (let's call it 'title') and NOT UserPaymentInfoData.
Can I do this with Doctrine annotations? Or without writing DQL?
I know I can do it with:
$user_payment_info_datas = $em->getRepository('STMainBundle:UserPaymentInfoData')
->createQueryBuilder('upid')
->innerJoin ('upid.payment_info_data', 'pid')
->where('upid.user = :user')
->addOrderBy('upid.payment_info', 'ASC')
->addOrderBy('pid.title', 'ASC')
->setParameter('user', $user)
->getQuery()
->getResult();
but the question is, whether it's possible to stay with only annotations as I need to fix it in a few places and it would be convenient to just change annotations and not create query builder in two places.
I have entity for Doctrine:
<?php
/**
* #Entity
* #Table(name="orders")
*/
class Orders {
/** #Id #Column(name="OID",type="integer") #GeneratedValue */
private $id;
/** #Column(name="Product",type="string")*/
private $product;
/** #Column(name="RegCode",type="string")*/
private $reg_code;
/** #Column(name="OrderEmail",type="string")*/
private $email;
}
I need make query like this:
select * from `orders` where `OrderEmail`='some#mail.com' and `Product` LIKE 'My Products%'
I try handle query without like:
$em->getRepository("Orders")->findByEmailAndProduct($uname,$product);
But it make error. Why?
Can I do this query without DQL? I want make this query use magic methods findBy**
This is not possible with the magic find methods. Try using the query builder:
$result = $em->getRepository("Orders")->createQueryBuilder('o')
->where('o.OrderEmail = :email')
->andWhere('o.Product LIKE :product')
->setParameter('email', 'some#mail.com')
->setParameter('product', 'My Products%')
->getQuery()
->getResult();
You can use the createQuery method (direct in the controller) :
$query = $em->createQuery("SELECT o FROM AcmeCodeBundle:Orders o WHERE o.OrderMail = :ordermail and o.Product like :searchterm")
->setParameter('searchterm', '%'.$searchterm.'%')
->setParameter('ordermail', 'some#email.com');
You need to change AcmeCodeBundle to match your bundle name
Or even better - create a repository class for the entity and create a method in there - this will make it reusable
This is not possible with the magic methods, however you can achieve this using DQL (Doctrine Query Language). In your example, assuming you have entity named Orders with Product property, just go ahead and do the following:
$dql_query = $em->createQuery("
SELECT o FROM AcmeCodeBundle:Orders o
WHERE
o.OrderEmail = 'some#mail.com' AND
o.Product LIKE 'My Products%'
");
$orders = $dql_query->getResult();
Should do exactly what you need.
You can also consider using DBAL:
$qb = $em->getRepository("Orders")->createQueryBuilder('o');
$result = $qb->where('o.OrderEmail = :email')
->andWhere($qb->expr()->like('o.Product', ':product'))
->setParameter('email', 'some#mail.com')
->setParameter('product', 'My Products%')
->getQuery()
->getResult();
Actually you just need to tell doctrine who's your repository class, if you don't, doctrine uses default repo instead of yours.
#ORM\Entity(repositoryClass="Company\NameOfBundle\Repository\NameOfRepository")
you can also do it like that :
$ver = $em->getRepository('GedDocumentBundle:version')->search($val);
$tail = sizeof($ver);