Doctrine 2 - How to get Categories without specific Article - php

Iam learning Doctrine.
I have two entities Article and Category in many to many relationship and iam trying to get all categories where isnt specific article.
ArticleEntity:
class Article extends BaseEntity
{
use Identifier;
/**
* #ORM\Column(type="string", nullable = false, unique=TRUE)
* #var string
*/
private $title;
/**
* #ORM\ManyToMany(targetEntity="Category", inversedBy="articles")
* #ORM\JoinTable(name="article_categories")
*/
private $categories;
public function getCategories()
{
return $this->categories;
}
public function __construct()
{
$this->categories = new \Doctrine\Common\Collections\ArrayCollection();
}
And CategoryEntity:
class Category extends BaseEntity
{
use Identifier;
/**
* #ORM\Column(type="string", nullable = false, unique=true)
* #var string
*/
private $title;
/**
* #ORM\Column(type="string",nullable=false,unique=true)
* #var sting
*/
private $slug;
/**
* #ORM\ManyToMany(targetEntity="Article", mappedBy="categories")
*/
private $articles;
public function __construct()
{
$this->articles = new \Doctrine\Common\Collections\ArrayCollection();
}
And i am trying to get all categories without specific article. In pure MySQL id would be something like this:
SELECT * FROM category LEFT JOIN article_categories ON category.id = article_categories.category_id WHERE article_categories.article_id <> 1(for example) AND article_id IS NOT NULL
And the only solution i could create in my CategoryRepository is this one.
public function findWithoutArticle($article_id)
{
$articleCat = $this->em->find(Article::getClassName(), $article_id);
$qb = $this->em->createQueryBuilder();
$qb->select('c')
->from(Category::getClassName(), 'c')
->where('c.id NOT IN (:article_id)')
->setParameter('article_id', $articleCat->getCategories()->toArray());
return $qb->getQuery()->getResult();
}
And this doesnt look right. Is there any better "Doctrine way" practice?

How about this one?
$qb = $this->em->createQueryBuilder();
$qb->select('c');
$qb->from('Category', 'c');
$qb->leftJoin('c.articles', 'a');
$qb->where($qb->expr()->neq('a.article_id', '?1'));
$qb->setParameter(1, $article_id);
$categories = $qb->getQuery()->getResult();

How about this:
public function findWithoutArticle($article_id)
{
$qb = $this->em->createQueryBuilder()
->select('c')
->from('Category', 'c')
->leftJoin('c.article', 'a')
->where('a.article_id <> :articleId')
->setParameter('articleId', $article_id);
return $qb->getQuery()->getResult();
}

Related

"NOT EXISTS" Query with Many to Many Relation Doctrine Symfony3

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

join tables in doctrine2

I am trying join the tables, in Doctrine2 Query Builder. This was my query to join the two tables and get the result.
Initially i have two tables to be joined:
--> employees
--> org_employees
Other tables:
--> users
Here in my Doctrine2 Query builder code, i have joined 2 tables to fetch the results of particular Organization employees by passing the Organization id.
SELECT
*
FROM org_branch_employees oe
LEFT JOIN employees e ON oe.employee_id = e.id
WHERE
oe.org_id = 1;
By using the Query Builder the above sql code has been changed like below.
$qb = $this->entityManager->createQueryBuilder();
$qb->select('oe', 'e', 'o')
->from('Employee\Entity\OrgEmployee', 'oe')
->leftJoin('oe.organization', 'o')
->leftJoin('oe.employee', 'e')
->where('oe.organization = :organizationId')
->setParameter('organizationId', $orgId);
$query = $qb->getQuery();
$orgEmployees = $query->getResult();
return $orgEmployees;
This is my Employee Entity:
<?php
namespace Employee\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Library\Entity\BaseEntity;
use Users\Entity\User;
use Organization\Entity\Organization;
//use Organization\Entity\OrgEmployee;
/**
* Description of Employee
*
* #author Macwin
*/
/**
* #ORM\Entity
* #ORM\Table(name="employees")
*/
class Employee extends BaseEntity {
/**
* #ORM\OneToOne(
* targetEntity="Users\Entity\User"
* )
* #ORM\JoinColumn(
* name="user_id",
* referencedColumnName="id",
* nullable=false
* )
*/
private $user;
/**
* #ORM\Column(name="employee_code", type="string", nullable=true)
* #var string
*/
protected $empCode;
/**
* #ORM\OneToOne(
* targetEntity="Organization\Entity\Organization"
* )
* #ORM\JoinColumn(
* name="org_id",
* referencedColumnName="id",
* nullable=false
* )
*/
private $organization;
/**
* #ORM\OneToMany(targetEntity="Employee\Entity\OrgEmployee", mappedBy="employee")
*/
protected $orgEmployee;
/**
* #ORM\OneToMany(targetEntity="Employee\Entity\OrgEmployeeDesignation", mappedBy="employee")
*/
protected $orgEmployeeDesignation;
public function __construct() {
$this->organizations = new \Doctrine\Common\Collections\ArrayCollection();
parent::__construct();
}
public function getOrganizations() {
return $this->organizations;
}
public function addOrganization(Organization $organization = null) {
$this->organizations->add($organization);
}
public function setUser(User $user = null) {
$this->user = $user;
}
public function getUser() {
return $this->user;
}
public function getEmpCode() {
return $this->empCode;
}
public function setEmpCode($empCode) {
$this->empCode = $empCode;
}
public function setOrganization(Organization $organization = null) {
$this->organization = $organization;
}
public function getOrganization() {
return $this->organization;
}
function getOrgEmployeeDesignation() {
return $this->orgEmployeeDesignation;
}
function setOrgEmployeeDesignation($orgEmployeeDesignation) {
$this->orgEmployeeDesignation = $orgEmployeeDesignation;
}
public function getOrgEmployee() {
return $this->orgEmployee;
}
public function __toString() {
return __CLASS__ . ": [id: {$this->id}, name: {$this->name}]";
}
}
Here is my OrgEmployee Entity Which maps the Organization table and Enity table, to get the Organization details and Employee Details.
<?php
namespace Employee\Entity;
use Doctrine\ORM\Mapping as ORM;
use Library\Entity\BaseEntity;
use Employee\Entity\Employee;
use Organization\Entity\Organization;
/**
* Description of Org Employees
*
* #author Macwin
*/
/**
* #ORM\Entity
* #ORM\Table(name="org_branch_employees")
*/
class OrgEmployee extends BaseEntity{
/**
* #ORM\ManyToOne(targetEntity="Employee\Entity\Employee", inversedBy="orgEmployee")
* #ORM\JoinColumn(name="employee_id",referencedColumnName="id",nullable=false)
*/
protected $employee;
/**
* #ORM\ManyToOne(targetEntity="Organization\Entity\Organization", inversedBy="orgEmployee")
* #ORM\JoinColumn(name="org_branch_id", referencedColumnName="id", nullable=false)
*/
protected $organization;
public function setEmployee(Employee $employee = null)
{
$this->employee = $employee;
return $this;
}
public function getEmployee()
{
return $this->employee;
}
public function setOrganization(Organization $organization = null)
{
$this->organization = $organization;
return $this;
}
public function getOrganization()
{
return $this->organization;
}
}
Here is how i am getting the Organization details and Employee Details:
'employeeCode' => $orgEmp->getEmployee()->getEmpCode(),
userFirstName = $orgEmp->getEmployee()->getUser()->getFirstName(),
Being, employees table has mapping of users table, i can fetch the users information,
so far so good, but while i am trying to join more tables here, i couldnt bring the exact result.
But when i need to make the filter in the above functionality, i am not sure, how can bring the exact result.
Like filter by employee_code, user first name.
Can anyone guide me to bring the result. I am working on the REST API side to give the result to the client. Pagination was also there in the functionality.
I am trying the following if i am rightly said:
SELECT
*
FROM org_branch_employees oe
LEFT JOIN employees e ON oe.employee_id = e.id
LEFT JOIN users u ON e.user_id = u.id
WHERE
oe.org_id = 1 AND
u.user_first_name = "John" and
e.employee_code = "EMP777"
So the first query is working as needed? You just need to join the user entity and add some where conditions? Or maybe I misunderstood the problem.
$qb = $this->entityManager->createQueryBuilder();
$qb->select('oe', 'e', 'o', 'user')
->from('Employee\Entity\OrgEmployee', 'oe')
->leftJoin('oe.organization', 'o')
->leftJoin('oe.employee', 'e')
->leftJoin('e.user','user')
->andWhere('oe.organization = :organizationId')
->setParameter('organizationId', $orgId),
->andWhere('user.user_first_name = :userFirstName')
->setParameter('userFirstName', 'John'),
->andWhere('e.employee_code = :employeeCode')
->setParameter('employeeCode', 'Emp666');
return $qb->getQuery()->getResult();

Symfony 2, QueryBuilder, multiple andWhere with same parameter

I have two entities - Lawyer and Category. They are connected with Many to Many association. Assume that example lawyer has 3 categories. I want to create function to search lawyers by a array of categories, and it should return only lawyers who have all categories from array.
class Lawyer {
//...
/**
* #ORM\ManyToMany(targetEntity="Dees\KancelariaBundle\Entity\Category")
* #ORM\JoinTable(name="lawyers_has_categories",
* joinColumns={#ORM\JoinColumn(name="lawyer_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="category_id", referencedColumnName="id")}
* )
*
* #var ArrayCollection
*/
protected $categories = null;
//...
}
class Category {
//...
/**
* #ORM\Column(length=255, nullable=true)
*
* #var string
*/
protected $name;
//...
}
public function searchLawyers(array $categories) {
$queryBuilder = $this->createQueryBuilder('lawyer')
->join('lawyer.categories', 'category');
$queryBuilder->andWhere("category.name = :category1");
$queryBuilder->setParameter("category1", "First category");
$queryBuilder->andWhere("category.name = :category2");
$queryBuilder->setParameter("category2", "Second category");
//...
//won't work, return null, but lawyer with these categories exists.
}
How can I achieve something like that?
I figured it out:
public function searchLawyers(array $categories) {
$queryBuilder = $this->createQueryBuilder('lawyer')
->join('lawyer.categories', 'category');
$queryBuilder->andWhere("category.name in (:categories)");
$queryBuilder->setParameter("categories", $categories);
$queryBuilder->addGroupBy("lawyer.id");
$queryBuilder->andHaving("COUNT(DISTINCT category.name) = :count");
$queryBuilder->setParameter("count", sizeof($categories));
return $queryBuilder->getQuery()->getResult();
}

Doctrine: Get one object of my collection (relation ManyToMany bidireccional)

I need to get a specific object from my collection Doctrine.
Currently, I have two entity (with ManyToMany bidirectional relationship):
Categoy
User
And in my User entity, I have a board property defined as an ArrayCollection:
/**
* #ORM\ManyToMany(targetEntity="\MyNamespace\WebsiteBundle\Entity\Category", inversedBy="users", cascade={"remove"})
* #ORM\JoinTable(name="user_categories")
*/
private $categories;
public function __construct()
{
parent::__construct();
$this->categories = new ArrayCollection();
}
/**
* get Categories
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getCategories()
{
return $this->categories;
}
And in my Categories Entity, I have this:
/**
* #ORM\ManyToMany(targetEntity="\MyNamespace\UserBundle\Entity\User", mappedBy="categories")
* #Exclude
*/
private $users;
public function __construct()
{
$this->users = new ArrayCollection();
}
/**
* get Users
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getUsers()
{
return $this->users;
}
When I need to get all the categories in my database I process like this:
$user = $this->getUser();
$categories = $user->getCategories()
\Doctrine\Common\Util\Debug::dump($categories) // OK: Result is the categories belonging to the user
But now I want to retrieve only the category of the user with the category name "Sport".
How do I do this? I have to use the QueryBuilder and does not pass directly through my object ?
Finaly, I just want to add a condition to my $user->getBoards()
Thank's for your help !
I will definitely go for the query builder, but i think you can achieve wat you want with filters, it will be kind of tricky but here is the doc hope it helps http://doctrine-orm.readthedocs.org/en/latest/reference/filters.html
Finaly, I have create a method in my categoryRepositor, like this:
public function findOneUserCategoryById($user_id, $board_id)
{
$query = $this->getEntityManager()
->createQuery('
SELECT c FROM WebsiteBundle:Category c
JOIN c.users u
WHERE u.id = :user_id AND c.id = :category_id'
)
->setParameters(array(
'user_id' => $user_id,
'category_id' => $category_id)
);
try {
return $query->getSingleResult();
} catch (\Doctrine\ORM\NoResultException $e) {
return null;
}
}
That work's fine and I use this method like this:
$em = $this->getDoctrine();
$category = $em->getRepository("WebsiteBundle:Category")
->findOneUserCategoryById(1, 5);

Doctrine 2 - ManyToMany + IN clause

I've got this model:
/** #Entity #Table(name="articles") */
class Article {
/** #Id #GeneratedValue #Column(type="integer") */
protected $id;
/** #Column(type="string", length=100, nullable=true) */
protected $title;
/** #ManyToOne(targetEntity="User", inversedBy="articles") */
protected $author;
/** #Column(type="datetime") */
protected $datetime;
/**
* #ManyToMany(targetEntity="Game", inversedBy="articles")
* #JoinTable(name="articles_games",
* joinColumns={#JoinColumn(name="article_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="game_id", referencedColumnName="id")}
* )
*/
protected $games;
# Constructor
public function __construct() {
$this->datetime = new DateTime();
$this->games = new \Doctrine\Common\Collections\ArrayCollection();
}
# ID
public function getId() { return $this->id; }
# TITLE
public function setTitle($v) { $this->title = $v; }
public function getTitle() {
if(empty($this->title)) {
$game = $this->getFirstGame();
return ($game instanceof Game) ? $game->getTitle() : NULL;
} else
return $this->title;
}
# AUTHOR
public function setAuthor($v) { $this->author = $v; }
public function getAuthor() { return $this->author; }
# DATE & TIME
public function getDateTime() { return $this->datetime; }
public function setDateTime($v) { $this->datetime = $v; }
# GAMES
public function setGames($value) {
$except_txt = 'Jedna z przesłanych wartości nie jest instancją klasy Game!';
if(is_array($value)) {
foreach($value as $v) {
if($v instanceof Game) $this->games->add($v);
else throw new Exception($except_txt);
}
} else {
if($value instanceof Game) $this->games->add($value);
else throw new Exception($except_txt);
}
}
public function getGames() { return $this->games; }
}
How to make query looking like this
SELECT a FROM Article a WHERE :game_id IN a.games
I have this (the $game->getId() is an integer)
$articles = $db->createQuery("SELECT a.type FROM Article a WHERE :game_id IN a.games GROUP BY a.type")->setParameter('game_id', $game->getId())->getResult();
But it's returning me an syntax error
[Syntax Error] line 0, col 47: Error: Expected Doctrine\ORM\Query\Lexer::T_OPEN_PARENTHESIS, got 'a'
This question was linked from a more recent question that I just answered, and I feel it would also be beneficial to put it here as it is a much more correct solution:
The Doctrine IN function expects a format of (1, 2, 3, 4, ...) after the IN statement. Unfortunately, it is not meant for column conditionals to prove membership.
However, I believe you're looking for the MEMBER OF Doctrine function:
SELECT a FROM Article a WHERE :game_id MEMBER OF a.games
You can pass a valid Doctrine object or the identifier into game_id using this functionality.
The example for this is hidden deep in the Doctrine docs:
$query = $em->createQuery('SELECT u.id FROM CmsUser u WHERE :groupId MEMBER OF u.groups');
$query->setParameter('groupId', $group);
$ids = $query->getResult();
If you are looking for articles related to one game:
$articles = $db->createQuery("SELECT a FROM Article a JOIN a.games game WHERE game.id = :game_id")
->setParameter('game_id', $game->getId())
->getResult();
or multiple:
$articles = $db->createQuery("SELECT a FROM Article a JOIN a.games game WHERE game.id IN (?,?, ... ?)")
->setParameters(array($game1->getId(), $game2->getId() ... $gameN->getId()))
->getResult();
I guess you need to create a custom repository for that. I have just solved such problem.
use Doctrine\ORM\EntityRepository;
class Company extends EntityRepository
{
/**
* Load by product.
* #param int $productId
* #return array
*/
public function getByProduct($productId)
{
$dql = "SELECT i FROM Domain\Model\Company i JOIN i.products p WHERE p.id = :id";
return $this->_em->createQuery($dql)->setParameter(':id', $productId)->getResult();
}
}

Categories