I wonder if anyone can help with this Doctrine query.
Basically, My query does not return rows where the foreign key is not set or NULL. And I would like to return all rows.
Here are 2 schemas
Items
class Items{
/**
* #var integer $id
*
* #Column(name="id", type="integer", nullable=false)
* #Id
* #GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $name
*
* #Column(name="name", type="string", length=255, nullable=false)
*/
private $name;
/**
* #var integer $type
*
* #ManyToOne(targetEntity="Types")
*
*/
private $type;
}
Types
class Types{
/**
* #var integer $id
*
* #Column(name="id", type="integer", nullable=false)
* #Id
* #GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $name
*
* #Column(name="name", type="string", length=255, nullable=false)
*/
private $name;
}
And the following DQL query
SELECT i.id, i.name, t.name as type FROM entity\Items i, entity\Types t WHERE i.type=t.id (OTHER CONDITIONS...)
That above query does not return the rows that does not have a value in the type foreign key.
Is it possible to return those rows?
Thank you in advance...
Try a LEFT JOIN:
SELECT i.id, i.name, t.name as type FROM entity\Items i LEFT JOIN i.type t
That will return everything on the left (Items) regardless if there is a matching Type.
Is very simple, you use IS EMPTY
In your entity you have defined a inverse key, then, you call your entity principal where inverseKey IS EMPTY.
SELECT a FROM items a WHERE a.types IS EMPTY;
Then i have a field curriculums is defined like type in the items
/**
* inversed key on principal entity (item)
* #ORM\OneToMany(targetEntity="entity\types", mappedBy="id")
*/
private $types;
Sounds like you do not want this condition in the query;
i.type=t.id
Have you tried removing it or alternative something along the lines
(i.type=t.id OR i.type IS NULL)
Related
Hy,
I have problem with validation and schema creation
I am creating an api via the api platform, (this is my 1st project under symfony)
I have a constraint, the database exists and I cannot touch it.
I have an headerOrder entity and an LineOrder entity.
But the column of join are not a key.
class enteteCommande
{
/**
* #var int
*
* #ORM\Column(name="I_ID", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $IId;
/**
* #var string
*
* #ORM\Column(name="C_CDE_NUMERO", type="string", length=50, nullable=true)
*
*/
#[Groups(['write:commande'])]
private $CCdeNumero;
/**
*
* #ORM\ManyToMany(targetEntity="lignesCommande", mappedBy="enteteLigne")
*
*/
private $detailLigne;
class lignesCommande
{
/**
* #varint
*
* #ORM\Column(name="I_IDL", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $IIdL;
/**
* #varstring|null
*
* #ORM\Column(name="LIGNE_C_CDE_NUMERO", type="string", length=50, nullable=true)
*/
private $ligneCCdeNumero;
/**
*
*
* #ORM\ManyToMany(targetEntity="enteteCommande", inversedBy="detailLigne")
* #ORM\JoinColumn(name="LIGNE_C_CDE_NUMERO", referencedColumnName="C_CDE_NUMERO")
*
*/
private $enteteLigne;
My schema :
enteteCommande
I_ID
C_CDE_NUMERO
lignesCommande
I_IDL
LIGNE_C_CDE_NUMERO
And error log :
The referenced column name 'id' has to be a primary key column on the target entity class 'App\Entity\OrderLignes'.
The referenced column name 'id' has to be a primary key column on the target entity class 'App\Entity\OrderEntete'.
How to make manytomany without key ?
thank !
You need to configure the JoinTable on the owning side of the relationship (lignesCommande). Not just a JoinColumn. If the JoinTable configuration is missing, Doctrine will fall back to its default ManyToMany configuration and try to connect id primary key columns, which in your case won't work.
Here's an example (but untested) JoinTable annotation:
/**
* #ORM\ManyToMany(
* targetEntity="enteteCommande",
* inversedBy="detailLigne"
* )
* #ORM\JoinTable(
* name="foobar",
* joinColumns={
* #ORM\JoinColumn(
* name="LIGNE_C_CDE_NUMERO",
* referencedColumnName="LIGNE_C_CDE_NUMERO"
* ),
* },
* inverseJoinColumns={
* #ORM\JoinColumn(
* name="C_CDE_NUMERO",
* referencedColumnName="C_CDE_NUMERO"
* ),
* }
* )
*/
private $enteteLigne;
I have this structure of Entities
class Group
{
/**
* #var int
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private int $id;
/**
* #var string
* #ORM\Column(name="name", type="string", length=50, nullable=false)
*/
private string $name;
}
class GroupUser
{
/**
* #var int
*
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private int $id;
/**
* #var Group
* #ORM\ManyToOne(targetEntity="Group", inversedBy="GroupUser")
* #ORM\JoinColumn(name="group_id", referencedColumnName="id", nullable=false)
*/
private Group $group;
/**
* #var string
* #ORM\Column(type="string", length=50, nullable=false)
*/
private string $type;
/**
* #var int
* #ORM\Column(type="integer", nullable=false)
*/
private int $user;
}
And there are two types of users. Admins and Clients. There's a ManyToMany relationship between Group and User. And the property in GroupUser $type is saving the Class of either Admin or Client and the property $user is saving the id.
id
group_id
user
type
1
1
1
Entity\Admin
2
2
1
Entity\Client
How do I join it using doctrine from the Admin and Client-side? Or maybe someone could point out to some resources how this kind of relationship works on doctrine? As I'm having a hard time googling anything out.
I imagine it could be like a conditional leftJoin, but I can't seem to figure it out.
Normaly, you cannot do that on database, because it not safe.
Why is it not safe? because you can have an id in the user column of userGroup table that refer to nothing as it is not linked.
I will write what you should have done, and how you can achieve what you want using your own method:
What you should have done:
In your UserGroup entities, have 2 columns (admin and client) which are linked to the related entities. They can be null (Client is null and admin contain the id of admin entity if it is an admin and vice versa). Then you can delete the type column
How to achieve what you using your method:
As it cannot be done in the database, you will have to do it in some manager. Have a method getUser which will check on your type attribute and return the associated entity from the current id stored in $user
example:
public function getUserFromGroupUser(GroupUser $groupUser){
if('Entity\Admin' ===$groupUser->getType()){
return $this->adminRepository->find($groupUser->getUser());
}
if('Entity\Client' ===$groupUser->getType()){
return $this->ClientRepository->find($groupUser->getUser());
}
throw new \RuntimeException('the type does not exist');
}
Firstly sorry for my English)
I have following tables:
1) rejection_reasons
rejection_reason_id
locale_code
title
Primary key: rejection_reason_id, locale_code
2) order_rejection_reasons
order_id
rejection_reason_id
Primary key: order_id
Foreign key: rejection_reason_id (Note! without field locale_code)
Entities:
class RejectionReason
{
/**
* #var int
*
* #ORM\Id
* #ORM\Column(name="rejection_reason_id", type="smallint", length=1, nullable=false)
*/
private $id;
/**
* #var string
*
* #ORM\Id
* #ORM\Column(name="locale_code", type="string", length=2, nullable=false, options={"fixed"=true})
*/
private $localeCode;
/**
* #ORM\OneToMany(targetEntity="OrderRejectionReason", mappedBy="rejectionReason", cascade={"remove", "persist"}, orphanRemoval=true)
*/
private $orderRejectionReasons;
/**
* #param int $id
* #param string $localeCode
*/
public function __construct($id, $localeCode)
{
$this->id = $id;
$this->localeCode = $localeCode;
$this->orderRejectionReasons = new ArrayCollection();
}
}
class OrderRejectionReason
{
/**
* #var int
*
* #ORM\Column(name="order_id", type="integer", nullable=false, options={"unsigned"=true})
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var RejectionReason
*
* #ORM\ManyToOne(targetEntity="RejectionReason", inversedBy="orderRejectionReasons")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="rejection_reason_id", referencedColumnName="rejection_reason_id", nullable=false, onDelete="CASCADE")
* })
*/
private $rejectionReason;
}
Doctrine returns error:
The join columns of the association 'rejectionReason' have to match to ALL identifier columns of the target entity 'App\Entity\RejectionReason', however 'locale_code' are missing.
Could you help me set relation between these tables?
The join columns of the association 'rejectionReason' have to match to ALL identifier columns, so you should take a look at the identifier columns. As you can see localcode is marked as an id (#ORM\Id) aswell as id, this means that you created a composite primary key.
Take a look at: https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/tutorials/composite-primary-keys.html
Because it is a composite primary key you cannot relate to only one of the two identifiers (JoinColumn, referencedColumnName="id").
You may consider making localeCode unique instead of an id which should solve you problem.(so you have to decide if localCode should be an id) You could also try to add localCode to the JoinColumn annotation.
Try without the extra curly braces and JoinColumn statement
class OrderRejectionReason
{
/**
* #var RejectionReason
*
* #ORM\ManyToOne(targetEntity="RejectionReason", inversedBy="orderRejectionReasons")
* #ORM\JoinColumn(name="rejection_reason_id", referencedColumnName="id", nullable=false, onDelete="CASCADE")
*/
private $rejectionReason;
}
EDIT I:
Added name="rejection_reason_id", referencedColumnName="rejection_reason_id"
EDIT II:
Changed referencedColumnName="rejection_reason_id" to referencedColumnName="id"
I have 2 entities related ManyToMany
class Product
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=100, unique=true)
*/
private $name;
/**
* #ORM\ManyToMany(targetEntity="ProductTransfer", inversedBy="product")
* #ORM\JoinTable(name="products_transfers")
*/
private $transfers;
and second one
class ProductTransfer
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var int
*
* #ORM\Column(name="stock_from_id", type="integer")
*/
private $stockFromId;
/**
* #var int
*
* #ORM\Column(name="stock_to_id", type="integer")
*/
private $stockToId;
/**
* #var int
*
* #ORM\Column(name="product_id", type="integer")
*/
private $productId;
/**
* #ORM\ManyToMany(targetEntity="Product",mappedBy="transfers")
*/
private $product;
All works great, additional table were created by Doctrine.
But when I'm trying to get all rows from ProductTransfers I see in profiler that each row need one single query with 2 joins.
So for 5000 products it will be really many queries.
Is there any way to get them all in one query like in "clean SQL"?
If there is no way to do it with Doctrine, what is best way to achieve that?
Like:
SELECT * FROM product_transfer pt
LEFT JOIN products_transfers pts ON pt.product_id=pts.product_id
LEFT JOIN product p ON pts.product_id=p.id;
edit:
created method in repository
public function loadTransfersByStock($stockId)
{
$q = $this->getEntityManager()
->createQuery(
'SELECT pt,p FROM AppBundle:ProductTransfer pt
LEFT JOIN AppBundle:Product p WITH pt.productId=p.id
WHERE pt.stockToId = :stockId'
);
return $q->setParameter('stockId', $stockId)->getResult();
}
and then result is
ProductsController.php on line 495:
array:3 [▼
0 => ProductTransfer {#1071 ▶}
1 => Product {#1084 ▶}
2 => ProductTransfer {#1099 ▶}
]
By default Doctrine uses lazy loading in object relations - the rows for the relationships are only loaded when you try to access them. If you know you're going to be needing all the related rows, try changing to eager fetching.
/**
* #ORM\ManyToMany(targetEntity="ProductTransfer", inversedBy="product", fetch="EAGER")
* #ORM\JoinTable(name="products_transfers")
*/
When you are writing the query like that in the repository, you are skipping the way doctrine "wants" you to write it. So instead of that, I would do something like this:
//ProductTransferRepository.php
$qb = $this->createQueryBuilder('pt');
$qb
->select('pt, p') // here you can choose the columns you want if you don't need everything from both tables
->leftJoin('pt.product', 'p')
->where('pt.stockToId = :stockId')
->setParameter('stockId', $stockId)
;
return $qb
->getQuery()
->getArrayResult()
;
Use getArrayResult to fetch results as array rather than objects, especially because you have many queries.
I am trying to do a complex query in Doctrine but had no luck so far.
I had an Entity called Category.
Category has reports (1 to many)
Category has subcategories (1 to many self referencing)
class Category extends
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #Expose
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
* #Expose
*/
private $name;
/**
* #ORM\OneToMany(targetEntity=".....\Category", mappedBy="parentCategory", cascade={"persist"})
*
* #MaxDepth(1)
*
* #Expose
*
**/
private $subcategories;
/**
* #ORM\ManyToOne(targetEntity="....\Category", inversedBy="subcategories", cascade={"persist"})
* #ORM\JoinColumn(name="idparent", referencedColumnName="id", nullable=true)
*
* #Expose
*/
private $parentCategory;
/**
* #ORM\OneToMany(targetEntity="....\Report", mappedBy="category")
*/
private $reports;
I use these entities in a rest API set up in symfony with FosRestBundle and HATEOASBundle.
So my objective is to return the following:
All parent categories (categories with idparent=null, meaning they are roots) with total_count reports and total_sum_reports of their subcategpories, with their subcategories with total_count reports and total_sum_reports.
Something like that:
name: parentCategory1
total_reports: 20
total_amount 100
subcategories: [
[name: child 1, total_reports: 15, total_amount 80],
[name: child 2, total_reports: 5, total_amount 20],
]
Unfortunately haven't figured this out.
How to take all info in need into a single json object?
Is it possible with Doctrine take all this into a big object or need to create this manually?