Doctrine Query Builder Where Count of ManyToMany is greater than - symfony

Im using the Doctrine Query Builder, and have a very specific requirement that came through.
I am using the ManyToMany field in my entity, related to User entity association (Array of User account entities).
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="User", cascade={"persist"})
* #ORM\JoinTable(name="post_user_list")
*/
protected $userList;
Amongst the requirements of displaying "public posts" requires that the Entity have a published boolean set to true, a published date less than the current date, and two users associated with entity.
In my query builder, I have setup this:
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select($select)->from($this->getEntityName(), 'p');
$criteria = $qb->expr()->andX();
$criteria->add($qb->expr()->eq('p.editor_published', 1))
->add($qb->expr()->lte('p.datePublished', ':now'));
and that only handles the first two requirements, now I need a criteria entry for counting the amount of user entities in userList, and the where clause specifically for greater than or equal to two users.
Not exactly sure where to proceed..

Try this. The query uses HAVING to only display entities that are associated with 2 or more users.
$qb->select($select)
->from($this->getEntityName(), 'p')
->innerJoin('p.userList','u')
->where('p.editor_published = 1')
->andWhere('p.datePublished <= :now')
->groupBy($select) //not sure what's in $select may need to change this
->having('count(u.id) > 1'); //assuming user has an id column otherwise change it
->setParameter('now',new \DateTime());

Related

Symfony: Ordered one-to-many relationship

I have two separate entities that I want to link by a one-to-many relationship. But I want this relationship to be ordered, meaning every time I call on the first entity, the elements of the second entity come in a pre-ordered way. I cannot use 'order by' calls because this order has nothing to do with the fields of the second entity. I thought about having one field of the first entity be an array of entities, but I'm not sure how to accomplish that either..
EDIT
So far I have something like this:
/**
* #ORM\OneToMany(targetEntity="FusionDesign\BlogBundle\Entity\Element", mappedBy="page")
*/
private $elements;
and
/**
* #ORM\ManyToOne(targetEntity="FusionDesign\BlogBundle\Entity\Page", inversedBy="elements")
* #ORM\JoinColumn(name="page_id", referencedColumnName="id")
*/
private $page;
I'm aware that I can put "ORDER BY whatever ASC" somewhere in there but that orders according to a column in Element, and that's not what I need, because Element entities and Page entities would never be persisted at the same time, nor by the same process. What I want to do is constructing a basic CMS where the user could generate new pages. First choose the kind of elements a page could potentially have (like header image, banner, title, and so on) and persist Element entities with fields describing the html, routing and controller content according to those choices. Then, when a new page is created, give the user the choice to order those potential elements at will, and bind Element entities following an order that reflects the layout desired.
I thought about having something like this
/**
* #var array
*
* #ORM\Column(name="structure", type="array")
*/
private $structure;
Where the array stores Element entities but I have no idea how to do that.
You just need to define the orderBy attribute in doctrine's mapping configuration for the relation.
YAML Mapping Example:
'Example\Entity\Article':
# [..]
oneToMany:
images:
targetEntity: 'Example\Entity\Article\Image\ArticleImage'
mappedBy: 'article'
orderBy: # <--- here
position: 'ASC'

Doctrine get all entities from collection

I don't know if what I am trying is really possible. So I thought to ask it to you guys.
What I am trying to do:
get a set of companies
get all the users associated with the given companies
In code:
$companyIds = array(1,2,3);
$companies = $this->em->getRepository('AppBundle:Company')->findById($companyIds);
dump($companies->getUsers()); // this will not work, but I like it to work
Where they are associated as follows:
class User implements AdvancedUserInterface, \Serializable
{
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Company", inversedBy="users")
* #ORM\JoinColumn(name="company_id", referencedColumnName="id", nullable=false)
*/
private $company;
}
class Company
{
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\User", mappedBy="company")
*/
private $users;
}
Repository returns you an ArrayCollection of entities, not a single entity, therefore you need to access each of them separately.
This should work:
foreach($companies as $company) {
$company->getUsers();
}
The problem with the above is that by default it will fetch (lazy load) users from database for each company in separate query (on demand when calling getUsers), which would be very inefficient on larger scale.
There are couple possible solution depending on your needs.
You could configure doctrine to always fetch users with companies, which is called eager fetch.
Having fetched users, you can merge ArrayCollections (and remove duplicates if needed), to achieve single collection containing all users.
Other way could be to fetch companies with users by creating sufficient DQL query in a custom method of you company's repository. If you need only users and don't need companies, then it could be a query that only fetches users without companies.
Try something like this in your User-Repository:
public function getAllUsersFromCompanies($ids)
{
$qb = $this->createQueryBuilder('u');
$qb->leftJoin('u.company', 'c')
->where('c.id IN :ids')
->setParameter('ids', $ids)
->getQuery();
return $query->getResult();
}
We are joining the user table with the company table here, which gives us the company for each user. Then, we filter out every user, that has the wrong company.
You can e.g. fetch all Comapany entities with users with one query:
$companies = $em->createQueryBuilder()
->select('c, u')
->from('AppBundle:Company', 'c')
// or left join based on you needs
->innerJoin('c.users', 'u')
->getQuery()
->getResult();
This will not result in queries when fetching company users.

limit columns returned in relationnal entity symfony2

Is it possible to filter an entity and display only few columns in symfony2?
I think I can do a custom query for this, but it seems a bit dirty and I am sure there is a better solution.
For example I have my variable $createdBy below, and it contains few data that shouldnt be displayed in this parent entity such as password etc...
/**
* #var Customer
*
* #ORM\ManyToOne(targetEntity="MyCompany\Bundle\CustomerBundle\Entity\Customer")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="created_by", referencedColumnName="id", nullable=false)
* })
*/
protected $createdBy;
So I need to display my Customer entity, but only containing fields like id and name for example.
EDIT :
I already have an instance of Project, the entity with my createdBy field, and I want to grab my customer data 'formatted' for this entity and not returning too much fields like password ...
Thanks
It sounds like expected behavior to me. The doctrine documentation seems to imply that eager fetching is only one level deep.
According to the docs:
Whenever you query for an entity that has persistent associations and
these associations are mapped as EAGER, they will automatically be
loaded together with the entity being queried and is thus immediately
available to your application.
http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-objects.html#by-eager-loading
The entity being queried has eager on createdBy so it will be populated.
to bypass you can create a method in your entity repository as following :
// join entities and load wanted fields
public function findCustom()
{
return $this->getEntityManager()
->createQuery(
'SELECT p FROM AppBundle:Product p ORDER BY p.name ASC'
)
->getResult();
}
hope this helps you
try this and let me know if it works, you should fill the right repository name
'XXXXBundle:CustomerYYYY', 'c'
public function findUser($user_id){
$qb = $this->_em->createQueryBuilder('c')
->select(array('c', 'cb.id', 'cb.name'))
->from('XXXXBundle:Customer', 'c')
->where('c.id <> :id')
->leftJoin('c.createdBy', 'cb')
->setParameter('id', $user_id)->getQuery();
if ($qb != null)
return $qb->getOneOrNullResult();
return null;
}

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

Doctrine one-to-many situation: how to easily fetch related entities

To simplify, two entities are defined: User and Comment. User can post many comments and every comment has only one user assigned, thus Comment entity has:
/**
* #var \Frontuser
*
* #ORM\ManyToOne(targetEntity="Frontuser")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="ownerUserID", referencedColumnName="id")
* })
*/
private $owneruserid;
However, when in action:
$orm = $this->getDoctrine()->getManager();
$repo = $orm->getRepository('CompDBBundle:Comment');
$repo->findBy(array('owneruserid' => $uid);
Error occured, that there's no such field like owneruserid.
How can I fetch all the user's comments then? The same happens to similar relations in my DB - looks likes you cannot run find() with foreign keys as parameters. I believe a function $user->getComments() should be automatically generated/recognised by Doctrine to allow efficient, quick access to related entities.
The example's simple but, what if there are more entities related to my User in the same way? Do I have to declare repositories for each and try to fetch them by it's owneruserid foreign keys?
Using doctrine, when you define a related entity it's type is the entity class (in this case FrontUser). Therefore firstly your related entity variable name is misleading. It should be e.g.
private $ownerUser;
Then, in order to do a findBy on a related entity field you must supply an entity instance e.g.
$orm = $this->getDoctrine()->getManager();
$userRepo = $orm->getRepository('CompDBBundle:FrontUser');
$user = $userRepo->findById($uid);
$commentRepo = $orm->getRepository('CompDBBundle:Comment');
$userComments = $commentRepo->findByOwnerUser($user);
If you don't have or want to retrieve the user entity you could use a DQL query with the 'uid' as a parameter instead.

Resources