Convert SQL with queryBuilder Error: Invalid PathExpression. Must be a StateFieldPathExpression - sql

Hello I am new in Symfony 5, I try to convert SQL query with queryBuilder.
I try to filter blacklisted profils by exclude them with profil entity found in my subquery.
My SQL example, works fine with user_id 66 :
SELECT p.first_name, p.id, i.interest_sender_id, i.accepted
FROM interest as i
INNER JOIN profil p on p.user_id = i.interest_sender_id
WHERE i.interest_receiver_id = 66 AND p.id NOT IN(
SELECT b.blocked_profil_id
FROM blacklist b
WHERE b.profil_id = p.id
)
I try to convert into createQueryBuilder(), but I have an error :
public function findInterestSended($sender_id) {
$main = $this->_em->createQueryBuilder();
$blacklisted = $this->_em->createQueryBuilder()
->select('b.blocked_profil')
->from('App\Entity\Blacklist', 'b',)
->where('b.profil = p');
$filtered = $main->select('p, i.accepted')
->from('App\Entity\Interest', 'i',)
->join( 'i.interestReceiver', 'r')
->join( 'App\Entity\Profil', 'p', Join::WITH, 'p.user = r')
->where('i.interestSender = :userId')
->andWhere(
$main->expr()->notIn('p', $blacklisted->getDQL()),
)
->orderBy('i.last_send', 'DESC')
->setParameter('userId', $sender_id)
->getQuery()
->getResult();
return $filtered;
}
DQL result :
SELECT p, i.accepted FROM App\Entity\Interest i
INNER JOIN i.interestReceiver r
INNER JOIN App\Entity\Profil p WITH p.user = r
WHERE i.interestSender = :userId AND p NOT IN(
SELECT b.blocked_profil FROM App\Entity\Blacklist b WHERE b.profil = p
)
ORDER BY i.last_send DESC
My Blacklist class :
/**
* #ORM\Entity(repositoryClass=BlacklistRepository::class)
*/
class Blacklist
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity=Profil::class, inversedBy="blacklist")
* #ORM\JoinColumn(nullable=false)
*/
private ?Profil $profil;
/**
* #ORM\Column(type="datetime_immutable")
*/
private $createdAt;
/**
* #ORM\ManyToOne(targetEntity=Profil::class, inversedBy="blockedProfil")
* #ORM\JoinColumn(nullable=false)
*/
private ?Profil $blocked_profil;
...}
My Profil class :
/**
* #ORM\Entity(repositoryClass=ProfilRepository::class)
* #ORM\HasLifecycleCallbacks()
* #Vich\Uploadable
*/
class Profil
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\OneToOne(targetEntity=User::class, inversedBy="profil", cascade={"persist", "remove"})
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* User who block other
* #ORM\OneToMany(targetEntity=Blacklist::class, mappedBy="profil")
*/
private Collection $blacklist;
/**
* user profil blocked
* #ORM\OneToMany(targetEntity=Blacklist::class, mappedBy="blocked_profil")
*/
private Collection $blockedProfil;
...
}
I don't uderstand what is missing

Related

Transform SQL to DQL

I tried to transform a SQL function into DQL but i always have errors...
I have 3 tables
/**
* #ORM\Entity(repositoryClass=ArticleRepository::class)
*/
class Article
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $title;
/**
* #Gedmo\Slug(fields={"title"})
* #ORM\Column(type="string", length=255, unique=true, nullable=true)
*/
private $slug;
/**
* #ORM\Column(type="boolean", options={"default":"0"})
*/
private $isItalian = false;
/**
* #ORM\Column(type="text")
*/
private $content;
/**
* #ORM\ManyToMany(targetEntity=Contact::class, inversedBy="viewedArticles")
*/
private $contact;
}
class Contact implements UserInterface, JsonSerializable
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
protected ?int $id = null;
/**
* #ORM\Column(type="string", length=255, unique=true)
*/
protected ?string $email = null;
/**
* #ORM\ManyToMany(targetEntity=Article::class, mappedBy="contact")
*/
private $viewedArticles;
}
And a many to many table
article_contact (a manyToMany table) with article_id and contact_id
i want to get all articles who are not in the article_contact table (it's all the articles who're not viewed by the contact)
I tried this request in SQL and it works, but it return an array and i need to return an object to use it in Symfony
$conn = $entityManager->getConnection();
$sql = 'SELECT *
FROM article
LEFT JOIN article_contact
ON article.id = article_id
AND contact_id = '.$id.'
WHERE article.is_italian = '.$isItalian.'
AND article_id IS NULL';
$stmt = $conn->prepare($sql);
$result = $stmt->executeQuery();
return $result->fetchAllAssociative();
DQL request :
return $this->createQueryBuilder('a')
->leftJoin('a.contact', 'contact')
->where('a.isItalian = 0')
->andWhere('contact = 1')
->andWhere('contact.viewedArticles IS NULL')
->getQuery()
->getResult()
;
it's the ->andWhere('contact.viewedArticles IS NULL') part who's not working, i have this error
[Semantical Error] line 0, col 113 near 'viewedArticles': Error: Invalid PathExpression. StateFieldPathExpression or SingleValuedAssociationField expected.
The answer is
$qb->select('a')
->from(Article::class,'a')
->leftJoin('a.contact', 'contact', 'WITH', 'contact.id = :id')
->setParameter('id', $contactId)
->andWhere('a.isItalian = :isItalian')
->setParameter('isItalian', $isItalian)
->groupBy('a.id')
->having('COUNT(contact.id) = 0')
;
return $qb->getQuery()->getResult();
Thanks to Will B. !

queryBuilder create sql with innerJoin

i can't to do true query from 2 tables.
/**
* Order
*
* #ORM\Table(name="order_work")
* #ORM\Entity(repositoryClass="AppBundle\Repository\OrderWorkRepository")
*/
class OrderWork
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Client", cascade={"persist"})
* #ORM\JoinColumn(name="client_id", referencedColumnName="id")
*/
private $client;
/**
* #var string
*
* #ORM\Column(name="orderNumber", type="string", length=255)
*/
private $orderNumber;
and client entity have id, name, surname parameters:
I want to do search by orders column, and by client parameters how i can which query?)
only for orders work this:
$queryBuilder = $this->createQueryBuilder('c')
->orWhere('c.orderNumber LIKE :term')
->orWhere('c.device LIKE :term')
->setParameter('term', '%'.$term.'%');
You have to make a query with a join, which is possible with the querybuilder but i like to use DQL.
public function findOrdersOnClientName($searchTerm)
{
return $this->getEntityManager()->createQuery(
'SELECT o, c FROM AppBundle:OrderWork o
JOIN o.client c
WHERE c.name LIKE :term'
)->setParameter('term, '%'. $searchTerm . '%')->getResult();
}

Doctrine2 OneToMany relation is null within the same request

So i have the following mapping:
Order entity
/**
* Order
*
* #ORM\Table(name="`order`")
* #ORM\Entity
*/
class Order
{
/**
* #var integer
*
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
// ... //
/**
* #ORM\OneToMany(targetEntity="OrderItem", mappedBy="order")
*/
private $items;
// ... //
OrderItem entity
/**
* OrderItem
*
* #ORM\Table(name="order_item")
* #ORM\Entity
*/
class OrderItem
{
/**
* #var integer
*
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
// ... //
/**
* #ORM\ManyToOne(targetEntity="Order", inversedBy="items")
* #ORM\JoinColumn(referencedColumnName="id")
*/
private $order;
// ... //
The problem I experience is when I try to dump $order->getItems() after flushing it remains NULL. Eveything is inserted fine to the database with the correct ids and when I try to get the order in the NEXT request it does contain items in $order->getItems() so no doubt it does work.
But it does not work (giving NULL) with the same request.
Look at the code below:
$manager = $this->getDoctrine()->getManager();
$order = new Order();
$orderItem = new OrderItem();
$orderItem
->setOrder($order)
;
$manager->persist($order);
$manager->persist($orderItem);
dump($order->getItems()); // returns NULL
// but in the next request it will contain items
So how can I get those items within the same request? I need to generate the order and return the items... any idea?
In class OrderItem, maybe.
public setOrder(Order $order)
{
$order->addItem($this);
$this->ordder = $order;
}

Doctrine insert table using joined table entities as reference

I have spent the last day trying to find a solution to a problem that I am having and now turn to you to see if you can offer some advice.
Ill start with the table and then the entity
I have three tables,
table_a
table_b
table_c
each table row has a unique id
table_c contains a reference to table_a_id and to table_b_id
This reference should be unidirectional as table_a and table_b have no need to ever know of table_c's existence.
Also, there is no cascading, if an id from table_a or table_b does not exist, then table_c should not be able to insert a row.
ok, now for entities. (I am going to cut out irrelevant code to keep it short)
Entity A defines table_a
/**
* A
* #ORM\Entity(repositoryClass="Sample\Bundle\MyBundle\Entity\ARepository")
* #ORM\Table("table_a")
*/
class A
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* Get id
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
* #param string $name
* #return A
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
* #return string
*/
public function getName()
{
return $this->name;
}
}
Entity B defines table_b
/**
* B
*
* #ORM\Table("table_b")
* #ORM\Entity
*/
class B
{
/**
* #var integer
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
* #param string $name
* #return B
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
* #return string
*/
public function getName()
{
return $this->name;
}
}
Entity C maps table_c and has many to one relationships with table_a and table_b
/**
* C
*
* #ORM\Table("table_c")
* #ORM\Entity(repositoryClass="Sample\Bundle\MyBundle\Entity\CRepository")
*/
class PricesAverage
{
/**
* #var integer
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var \Sample\Bundle\MyBundle\Entity\A
* #ORM\ManyToOne(targetEntity="Sample\Bundle\MyBundle\Entity\A", inversedBy="id")
* #ORM\JoinColumn(name="a_id",unique=false)
*/
private $a;
/**
* #var \Sample\Bundle\MyBundle\Entity\B
* #ORM\ManyToOne(targetEntity="Sample\Bundle\MyBundle\Entity\B", inversedBy="id")
* #ORM\JoinColumn(name="b_id",unique=false)
*/
private $b;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* #return A
*/
public function getA() {
return $this->a;
}
/**
* #param A $a
* #return $this
*/
public function setA($a) {
$this->a = $a;
return $this;
}
/**
* #return B
*/
public function getB() {
return $this->b;
}
/**
* #param B $b
* #return $this
*/
public function setB($b) {
$this->b = $b;
return $this;
}
}
No what I do is the following. (this is just simplified logic but it is the same process)
$days = 5;
$aCollection = {get collection of A's)
for($i = 0,$i<$days,$i++){
foreach($aCollection as $a){
$b = {get a single b}
$c = new C();
$c->setA($a)
->setB($b);
{use doctrine to persist and flush $c}
}
}
What is happening is that on the first insert the id for a is correct, but on the second insert, doctrine seems to think that competitor is a new entity and tries to persist and create a new id.
I have dumped my collection without activating the doctrine insert and I see that the id on A is correct, I then do the same thing after doctrine and find that doctrine is somehow clearing away my object.
How can I resolve this?
UPDATE:
I resolved this issue.
The problem that I was having is that the related entity was being removed from the entity manager after I did my batch insert.
The was a result of doing clear after every flush.
I moved clear to the end of the process so I flush as much as I need but only clear once.

Doctrine 2 JOIN error

I try to execute this query
$qb = $this->_em->createQueryBuilder();
$qb->select(array('c', 'ld'))
->from('Model\Entity\Company', 'c')
->leftJoin('c.legaldetails', 'ld')
->where("c.companyid = 1");
$query = $qb->getQuery();
echo($query->getSQL());
having this sql code at the end:
SELECT ... FROM Company c0_ LEFT JOIN WHERE c0_.CompanyID = 1
These are my models:
<?php
namespace Model\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Company
*
* #ORM\Table(name="Company")
* #ORM\Entity(repositoryClass="\Model\Repository\CompanyRepository")
*/
class Company
{
/**
* #var integer $companyid
*
* #ORM\Column(name="CompanyID", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $companyid;
/**
* #var \Model\Entity\LegalDetails $legaldetails
*
* #ORM\OneToOne(targetEntity="\Model\Entity\Legaldetails", mappedBy="companyid")
*/
private $legaldetails;
//other fields
public function __construct()
{
$this->legaldetails = new ArrayCollection();
}
//setters and getters
and legaldetails entity:
<?php
namespace Model\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Legaldetails
*
* #ORM\Table(name="LegalDetails")
* #ORM\Entity
*/
class Legaldetails
{
/**
* #var integer $legalid
*
* #ORM\Column(name="LegalID", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $legalid;
/**
* #var \Model\Entity\Company $company
*
* #ORM\Column(name="CompanyID", type="integer", nullable=false)
* #ORM\OneToOne(targetEntity="\Model\Entity\Company", inversedBy="companyid")
* #ORM\JoinColumn(name="companyid", referencedColumnName="companyid")
*/
private $company;
What is wrong?
P.S.: I understand that having two fields with identical names (companyid) is a bad practice, but it is not my fault
Judging on SQL operator, JOIN ON what? You missed the key part of join operator. Maybe, ON table.companyid=table2.companyid? Using the same names in tables could be even useful and is usual, not bad practice. You could put here the full SQL operator, that would be a better practice. :-)