Doctrine NOT EXIST condition with ManyToMany relation - sql

I have 2 entities with a many-to-many relation as this:
User
{
/**
* #var Group[]
*
* #ORM\ManyToMany(targetEntity="Group", inversedBy="users")
* #ORM\JoinTable(
* name="user_group",
* joinColumns={
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="group_id", referencedColumnName="id")
* }
* )
*/
protect $groups
}
Group
{
/**
* #var User[]
*
* #ORM\ManyToMany(targetEntity="User", mappedBy="groups")
*/
private $users;
}
This creates 3 tables on DB:
user
group
user_group
Is possible to manipulate user_group in a DQL? I'm trying something like this:
SELECT *
FROM user u
WHERE EXISTS
(SELECT ug.user_id FROM user_group ug WHERE ug.user_id = u.id AND ug.group_id = 3)
This doesn't work since it should be written as Entities. I could run this with Native SQL but I wanted to be sure that there's no better solution for this

The solution from Jonathan Martinez worked but I found this to be more straightforward (specially because I could use NOT MEMBER OF as well):
$query = $em->createQuery('SELECT u.id FROM User u WHERE :groupId MEMBER OF u.groups');
$query->setParameter('groupId', $group);
More on this, here:
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#dql-select-examples

You can try this:
$em = $this->getDoctrine()->getManager();
$query = $em->createQueryBuilder('u')
->select('u')
->from('AppBundle:User', 'u')
->innerJoin('u.groups', 'g', 'WITH', 'g.user_id = u.id')

Related

Doctrine ORM, many to many, lazy loading. Retrieve an Entity and fetch join all the feautures

I have Zend Framework 3 + Doctrine ORM application.
Class Goods have link "characters":
/**
* Goods
*
* #ORM\Entity
* #ORM\Table(name="goods")
* #property int $id
*/
class Goods implements InputFilterAwareInterface
{
/**
* #ORM\ManyToMany(targetEntity="\Application\Entity\CharacterValue", inversedBy="goods")
* #ORM\JoinTable(name="character_value_item",
* joinColumns={#ORM\JoinColumn(name="good_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="value_id", referencedColumnName="id")})
**/
protected $characters;
public function getCharacters()
{
return $this->characters;
}
}
I trying to use this method to get characters by method for lazy loading, but it returns just one character. Not all characters for the product.
$dql = 'SELECT u, ch FROM Goods u LEFT JOIN u.characters ch';
This method from here:
$query = $em->createQuery('SELECT u, p FROM CmsUser u JOIN u.phonenumbers p');
$users = $query->getResult(); // array of CmsUser objects with the phonenumbers association loaded
$phonenumbers = $users[0]->getPhonenumbers();
https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/dql-doctrine-query-language.html#dql-select-examples
I do not understand why documentation's method working wrong. What is the right way to deside my issue?

Doctrine- one of associations doesn't work proper

I have problem with associations in Doctrine 2
I have two associations which look the same. One works fine, the second one work in weird way. I'll explain it a bit later.
Here is my main entity- Product:
**
* Product
*
* #ORM\Table(name="product", indexes={
* #ORM\Index(name="category_index", columns={"category_id"}),
* #ORM\Index(name="partner_index", columns={"partner_id"})
* })
* #ORM\Entity
*/
class Product
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, nullable=false)
*/
private $name;
/**
* #var Category
*
* #ORM\ManyToOne(targetEntity="Category")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
private $categoryId;
/**
* #var Partner
*
* #ORM\ManyToOne(targetEntity="Partner")
* #ORM\JoinColumn(name="partner_id", referencedColumnName="id")
*/
private $partnerId;
}
Category Entity:
/**
* Category
*
* #ORM\Table(name="category")
* #ORM\Entity
*/
class Category
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="category_name", type="string", length=100, nullable=false)
*/
private $categoryName;
}
Partner Entity:
/**
* Partner
*
* #ORM\Table(name="partner")
* #ORM\Entity
*/
class Partner
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=150, nullable=false)
*/
private $name;
}
As you can see I have two identical associations:
Product#categoryId
Product#partnerId
The first one- categoryId works as it should, but the second one- partnerId doesn't:
First thing I noticed is that Doctrine create proxy class with Partner Entity,
while Category Entity doesn't need proxy class to work, so one of
this doesn't work proper.I just started with Doctrine so I might be
wrong.
I have 4 records in Partner table. When I try to modify Product
record manually, in partner_id field I can't set 1-4, I can set
something like this:
(empty)
1 - 1
2 - 2
3 - 3
4 - 4
(empty)
1 - 1
2 - 2
3 - 3
4 - 4
First time I see something like this.
And the last thing I noticed:
$query = $em->createQuery('
SELECT pp.name, p.id
FROM Application\Entity\Product AS p
LEFT JOIN Application\Entity\Partner AS pp WITH p.partner_id = pp.id GROUP by p.partner_id
');
$items = $query->getResult();
Shows me error:
Doctrine\ORM\Query\QueryException: [Semantical Error] line 0, col 148 near 'partner_id =': Error: Class Application\Entity\Product has no field or association named partner_id in
Thanks for any help and tips.
Edit
So, the question is. Why does Doctrine create proxy class for Partner Entity, but not for Category?
As far as I know Doctrine will only create proxies when functionality for the entity it belongs to is used.
You can also manually generate the proxies using the command line tools.
About the error:
You should use partnerId instead of partner_id. Partner_id is the name of your database column, not of the attribute defined in your Product entity.
$query = $em->createQuery('
SELECT pp.name, p.id
FROM Application\Entity\Product AS p
LEFT JOIN Application\Entity\Partner AS pp WITH p.partnerId = pp.id GROUP by p.partnerId
');
$items = $query->getResult();
Have a good look at the DQL documentation:
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html
Maybe it will give you some extra insights about the difference between SQL and DQL.

Doctrine ManyToOne with join table - filter results

I have a Customer entity and User entity with a JOIN relationship table customers_users.
In the Customer entity I have this.
/**
* #var User
*
* #ORM\ManyToMany(targetEntity="Entity\User")
* #ORM\JoinTable(name="customers_users",
* joinColumns={#ORM\JoinColumn(name="customer_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id", unique=true)}
* )
*
*/
private $users;
I want that when a customer is created I can assign the users to it and actually it works well, however I want filter the users to show only those that don't are assigned to none customer.
For example, I have two customers and two users and the first customer has assigned two users; then when I edit the second customer, two users appear to be assigned to it but when I select these and I send the form, it throws the following exception:
An exception occurred while executing 'INSERT INTO customers_users (customer_id, user_id) VALUES (?, ?)' with params [2, 1]:
SQLSTATE[23505]: Unique violation: 7 ERROR: duplicate key value violates unique constraint "uniq_2763c6cca76ed395"
DETAIL: Key (user_id)=(1) already exists.
/**
* #var User
*
* #ORM\ManyToMany(targetEntity="Entity\User", inversedBy="customers")
* #ORM\JoinTable(name="customers_users",
* joinColumns={#ORM\JoinColumn(name="customer_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id", unique=true)}
* )
*
*/
private $users;
and in the User entity, in customers annotation you should put:
* #ORM\ManyToMany(targetEntity="Entity\Customer", inversedBy="users")
(...)
What I actually did in this situation, is:
// UserAdmin.php that extends SonataAdminBundle's User admin
/**
* {#inheritdoc}
*/
public function createQuery($context = 'list')
{
/** #var QueryBuilder $query */
$query = parent::createQuery();
$request = Request::createFromGlobals();
// Could not find another way to detect that admin list is created from
// sonata_type_model_list parent.
// Of course, this applies only to sonata_type_model_list field type.
if ($request->query->get('pcode') == 'demo.admin.customer') {
$alias = $query->getRootAliases()[0];
$query->leftJoin($alias . '.customers', 'c');
// $query-> ... do filtering here
}
return $query;
}
This way I could filter Users in any way I wanted.

Doctrine 2 where two associations exists query

I have two entities related, and I need a query that checks if entityOne have two different relations (extra column as diff field) to entityTwo.
A simple "WHERE entityTwo.diff_field = 1 AND entityTwo.diff_field = 2" don't work.
How can I achive this?
Thanks
UPDATE:
$query = $this->createQueryBuilder('one');
$query->addSelect('two')->leftJoin('one.two', 'two');
$query->where('two.id = :a')->setParameter('a',1);
$query->andWhere('two.id = :b')->setParameter('b',2);
EntityOne
class EntityOne {
/**
* #ORM\Id
* #ORM\Column(type="integer", nullable=false);
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="EntityTwo", mappedBy="one")
*/
protected $two;
}
EntityTwo
class EntityTwo {
/**
* #ORM\Id
* #ORM\Column(type="integer", nullable=false);
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToMany(targetEntity="EntityOne", inversedBy="two", cascade={"persist"})
* #ORM\JoinTable(name="one_two_xref")
*/
protected $one;
}
I think you need two EXISTS subqueries.
You want (I think) to select entityOnes which have
a linked entityTwo with diffType 1 and also a linked entityTwo with difftype 2.
If you scroll down to 'EXISTS in WHERE clause with correlated Subquery' in this documentation:
http://docs.doctrine-project.org/en/2.1/reference/dql-doctrine-query-language.html
I think what you need is a similar query but with two separate EXISTS subqueries, something like :
WHERE EXISTS (SELECT EntityTwo ... DiffType = 1)
AND EXISTS (SELECT EntityTwo ... DiffType = 2)

Doctrine 2.1 DQL - Many-to-many query multiple values - Item in multiple categories?

This may seem like a rudimentary request, but I can't seem to get it to work, so I'm either missing something stupid, or am not understanding how it should be done. Thanks in advance.
I have two doctrine entities with a many-to-many relationship: Items and Categories. Which are joined by items_has_categories.
/**
* Item
*
* #Table(name="items")
* #Entity(repositoryClass="Entity\Repository\Item")
*/
class Item
{
....
/**
* #var Categories
*
* #ManyToMany(targetEntity="Categorie", inversedBy="items", cascade={"persist"})
* #JoinTable(name="items_has_categories",
* joinColumns={
* #JoinColumn(name="items_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #JoinColumn(name="categories_id", referencedColumnName="id")
* }
* )
*/
private $categories;
....
}
/**
* Categorie
*
* #Table(name="categories")
* #Entity(repositoryClass="Entity\Repository\Categorie")
*/
class Categorie
{
.....
/**
* #var Items
*
* #ManyToMany(targetEntity="Item", mappedBy="categories")
*/
private $items;
....
}
And what I'm trying to do is run a query to return all items that are in all of "x" number of categories - which I think/thought should be a SELECT with and AND clause:
class Item extends EntityRepository
{
public function findItemsByCategories($categories)
{
$qString = 'SELECT j, t, c FROM Technique\Entity\Item j LEFT JOIN j.itemImages t JOIN j.categories c WHERE';
$i = 0;
foreach ($categories as $c)
{
$qString .= ' c.name = ?' . $i;
if ($i < (count($categories)-1))
{
$qString .= ' AND';
}
$i++;
}
$query = $this->_em->createQuery($qString);
$query->setParameters($categories);
return $query->getResult();
}
That little bit of code has no errors and spits out the following DQL SELECT query (when 2 categories are sent in the array: $categories):
SELECT j, t, c FROM Technique\Entity\Item j LEFT JOIN j.itemImages t JOIN j.categories c WHERE c.name = ?0 AND c.name = ?1
This is always returning an empty array, i.e., no results. Even though in my DB, there are more than 20 items fitting the criteria: are in both categories.
Anyone see what I'm doing wrong here? Is this supposed to be an AND selection...? Basically I'd just like to know how to query a many-to-many relationship in Doctrine 2+, where there's more than one value that must be met...
For anyone interested, I figured it out (painfully). Doctrine should really explain this better instead of the the one line they have on the DQL page....
Basically it's not an AND query, it's a MEMBER OF AND query. For each category a MEMBER OF must be created and then added to the whole query with an AND:
SELECT j, t FROM Entity\Item j
LEFT JOIN j.itemImages t
WHERE ?0 MEMBER OF j.categories AND ?1 MEMBER OF j.categories
AND ?2 MEMBER OF j.categories, etc.
That will return all items that are in all categories requested.