Symfony & Doctrine 2 / Speed up query - doctrine-orm

I have a problem with query speed after I joined 3 tables. Before speed was 120-500ms. Now it's 1500-5000ms. It's possible to speed up my query?
Here is my query builder:
public function findByCategory($category)
{
$qb = $this->createQueryBuilder('p');
$qb->select('p');
$qb->leftJoin('p.details', 'd');
$qb->leftJoin('p.model', 'm');
$qb->leftJoin('m.category', 'c');
$qb->where('c.id = :category');
$qb->andWhere('d.quantity > 0');
$qb->setParameter('category', $category);
$qb->addOrderBy('p.id', 'DESC');
$qb->setMaxResults(10);
return $qb->getQuery()->useQueryCache(true)->useResultCache(true, 31536000, uniqid())->getResult();
}
and here is formatted query:
SELECT
p0_.id AS id_0,
p0_.title AS title_1,
p0_.url AS url_2,
p0_.description AS description_3,
p0_.views AS views_4,
p0_.price AS price_5,
p0_.is_active AS is_active_6,
p0_.created_at AS created_at_7,
p0_.updated_at AS updated_at_8,
p0_.model_id AS model_id_9,
p0_.details_id AS details_id_10
FROM
products p0_
LEFT JOIN product_details p1_ ON p0_.details_id = p1_.id
LEFT JOIN models m2_ ON p0_.model_id = m2_.id
LEFT JOIN categories c3_ ON m2_.category_id = c3_.id
WHERE
c3_.id = ?
AND p1_.`quantity` > 0
ORDER BY
p0_.id DESC
LIMIT
10
Thanks
EDIT:
Products model:
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* #var string
*
* #ORM\Column(name="url", type="string", length=255)
*/
private $url;
/**
* #var string
*
* #ORM\Column(name="description", type="text", nullable=true)
*/
private $description;
/**
* #var int
*
* #ORM\Column(name="views", type="bigint", nullable=true)
*/
private $views;
/**
* #ORM\ManyToOne(targetEntity="Models")
* #ORM\JoinColumn(name="model_id", referencedColumnName="id")
*/
private $model;
/**
* #ORM\OneToOne(targetEntity="ProductDetails", cascade={"persist"})
* #ORM\JoinColumn(name="details_id", referencedColumnName="id")
*/
private $details;
/**
* #var File
*
* #ORM\OneToMany(targetEntity="ProductImages", mappedBy="product", cascade={"persist"})
*
*/
private $images;
/**
* #var File
*
* #ORM\OneToMany(targetEntity="Cart", mappedBy="productId", cascade={"persist"})
*
*/
private $cart;
/**
* #var string
*
* #ORM\Column(name="price", type="integer", length=255)
*/
private $price;
/**
* #var bool
*
* #ORM\Column(name="is_active", type="boolean")
*/
private $isActive;
/**
* created Time/Date
*
* #var \DateTime
*
* #ORM\Column(name="created_at", type="datetime", nullable=false)
*/
protected $createdAt;
/**
* updated Time/Date
*
* #var \DateTime
*
* #ORM\Column(name="updated_at", type="datetime", nullable=false)
*/
protected $updatedAt;
ProductDetail model:
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="`processor`", type="string", length=255, nullable=true)
*/
private $processor;
/**
* #var string
*
* #ORM\Column(name="`graphic_card`", type="string", length=255, nullable=true)
*/
private $graphicCard;
/**
* #var string
*
* #ORM\Column(name="`release_year`", type="string", length=4, nullable=true)
*/
private $releaseYear;
/**
* #var string
*
* #ORM\Column(name="`ram`", type="string", length=255, nullable=true)
*/
private $ram;
/**
* #var string
*
* #ORM\Column(name="`ssd`", type="string", length=255, nullable=true)
*/
private $ssd;
/**
* #var string
*
* #ORM\Column(name="`hdd`", type="string", length=255, nullable=true)
*/
private $hdd;
/**
* #var string
*
* #ORM\Column(name="`battery`", type="string", length=255, nullable=true)
*/
private $battery;
/**
* #var string
*
* #ORM\Column(name="`color`", type="string", length=255, nullable=true)
*/
private $color;
/**
* #var string
*
* #ORM\Column(name="`accessories`", type="string", length=255, nullable=true)
*/
private $accessories;
/**
* #var string
*
* #ORM\Column(name="`guarantee`", type="string", length=255, nullable=true)
*/
private $guarantee;
/**
* #var string
*
* #ORM\Column(name="`condition`", type="string", length=255)
*/
private $condition;
/**
* #var string
*
* #ORM\Column(name="`quantity`", type="string", length=255)
*/
private $quantity;
Models model:
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Categories")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
private $category;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="url", type="string", length=255)
*/
private $url;
/**
* #var string
*
* #ORM\Column(name="image", type="string", length=255)
*/
private $image;
Categories Model:
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=50)
*/
private $title;
/**
* #var string
*
* #ORM\Column(name="url", type="string", length=255)
*/
private $url;
/**
* #var string
*
* #ORM\Column(name="description", type="string", length=140)
*/
private $description;
/**
* #var string
*
* #ORM\Column(name="image", type="string", length=100)
*/
private $image;
/**
* #var bool
*
* #ORM\Column(name="is_active", type="boolean")
*/
private $isActive;
/**
* created Time/Date
*
* #var \DateTime
*
* #ORM\Column(name="created_at", type="datetime", nullable=false)
*/
protected $createdAt;
/**
* updated Time/Date
*
* #var \DateTime
*
* #ORM\Column(name="updated_at", type="datetime", nullable=false)
*/
protected $updatedAt;

One Join is not necessary. You are joining the category table just for filter the id, but you can do that with IDENTITY, which is really helpful to reference the FK column. Your query should be faster like this:
public function findByCategory($category)
{
$qb = $this->createQueryBuilder('p');
$qb->select('p');
$qb->leftJoin('p.details', 'd');
$qb->leftJoin('p.model', 'm');
// $qb->leftJoin('m.category', 'c'); // this don't go anymore
$qb->where('IDENTITY(m.category) = :category');
$qb->andWhere('d.quantity > 0');
$qb->setParameter('category', $category);
$qb->addOrderBy('p.id', 'DESC');
$qb->setMaxResults(10);
return $qb->getQuery()->useQueryCache(true)->useResultCache(true, 31536000, uniqid())->getResult();
}

Related

Doctrine 2 Composite Primary Keys as Foreign Key

I have ClassA mapped to the entity ClassB with ManyToOne relation (simple so far).
class ClassA{
/**
* #var string
* #ORM\Id
* #ORM\Column(name="keyA", type="string", length=255)
*/
private $keyA;
/**
* #var ClassB $classB
* #ORM\ManyToOne(targetEntity="ClassB", inversedBy="classAs")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="ClassB_keyB", referencedColumnName="keyB")
* })
*/
private $classB;
}
And this is the ClassB:
class ClassB{
/**
* #var string
* #ORM\Id
* #ORM\Column(name="keyB", type="string", length=255)
*/
private $keyB;
/**
*
* #var ClassC $classC
* #ORM\Id
* #ORM\ManyToOne(targetEntity="ClassC", inversedBy="classBs")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="ClassC_keyC", referencedColumnName="keyC")
* })
*/
private $classC;
/**
* #var ArrayCollection $classAs
* #ORM\OneToMany(targetEntity="ClassA", mappedBy="classB")
*/
private $classAs;
}
As you can notice the ClassB contains a composite primary key (2 entities and on column).
And this is the ClassC:
class ClassC{
/**
* #var string
* #ORM\Id
* #ORM\Column(name="keyC", type="string", length=255)
*/
private $keyC;
/**
* #var ArrayCollection $classBs
* #ORM\OneToMany(targetEntity="ClassB", mappedBy="classC")
*/
private $classBs;
}
Whene i try to display all entities of ClassA (using findAll()) I get this exception Missing value for primary key classC on ERP\................\ClassB
What am I missing here ?!
you have to create your own method at ClassARepository and add your join columns

zf2 + Doctrine2 Integrity constraint violation: 1048 Column '' cannot be null

I have a problem with Doctrine2 it throws me this exception:
An exception occurred while executing 'INSERT INTO sesion (Contrasena, Estado, UsuarioIdentificacion) VALUES (?, ?, ?)' with params ["j15474874654j", "1", null]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'UsuarioIdentificacion' cannot be null
I am a beginner with doctrine 2 and I could not resolve this error, I think the error is in relationships. I've read other similar problems, but I have not succeeded.
I would greatly appreciate the help. this is my code:
In my controller:
$usuario = new Usuario();
$data = $request->getPost();
$usuario->setNombre($data['nombre']);
$usuario->setApellidos($data['apellidos']);
$usuario->setIdentificacion($data['identificacion']);
$usuario->setTitulo($data['titulo']);
$usuario->setContacto($data['contacto']);
$usuario->setCorreo($data['correo'] . "#correounivalle.edu.co");
$sesion = new Sesion();
$sesion->setUsuarioidentificacion($usuario);
$sesion->setEstado("1");
$sesion->setContrasena($data['nombre'][0].$data['identificacion'].$data['apellidos'][0]);
$em->persist($usuario);
$em->persist($sesion);
$em->flush();
class usuario:
<?php
namespace DBAL\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Usuario
*
* #ORM\Table(name="usuario", uniqueConstraints={#ORM\UniqueConstraint(name="Identificacion", columns={"Identificacion"}), #ORM\UniqueConstraint(name="Correo", columns={"Correo"})})
* #ORM\Entity
* #ORM\Entity(repositoryClass="DBAL\Repository\UsuarioRepository")
*/
class Usuario
{
/**
* #var integer
*
* #ORM\Column(name="Id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="Identificacion", type="integer", nullable=false)
*/
private $identificacion;
/**
* #var string
*
* #ORM\Column(name="Nombre", type="string", length=50, nullable=false)
*/
private $nombre;
/**
* #var string
*
* #ORM\Column(name="Apellidos", type="string", length=50, nullable=false)
*/
private $apellidos;
/**
* #var string
*
* #ORM\Column(name="Correo", type="string", length=100, nullable=false)
*/
private $correo;
/**
* #var integer
*
* #ORM\Column(name="Contacto", type="integer", nullable=true)
*/
private $contacto;
/**
* #var string
*
* #ORM\Column(name="Titulo", type="string", length=20, nullable=true)
*/
private $titulo;
/*with their respective setter and getter*/
class sesion:
<?php
namespace DBAL\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Sesion
*
* #ORM\Table(name="sesion", indexes={#ORM\Index(name="UsuarioIdentificacion", columns={"UsuarioIdentificacion"}), #ORM\Index(name="sesion", columns={"UsuarioIdentificacion"})})
* #ORM\Entity
*/
class Sesion
{
/**
* #var integer
*
* #ORM\Column(name="Id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="Contrasena", type="string", length=50, nullable=false)
*/
private $contrasena;
/**
* #var boolean
*
* #ORM\Column(name="Estado", type="boolean", nullable=false)
*/
private $estado;
/**
* #var \Usuario
*
*
* #ORM\OneToOne(targetEntity="Usuario")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="UsuarioIdentificacion", referencedColumnName="Correo")
* })
*/
private $usuarioidentificacion;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Roles", mappedBy="sesionusuarioidentificacion")
*/
private $rolesid;
/**
* Constructor
*/
public function __construct()
{
$this->rolesid = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set contrasena
*
* #param string $contrasena
*
* #return Sesion
*/
public function setContrasena($contrasena)
{
$this->contrasena = $contrasena;
return $this;
}
/**
* Get contrasena
*
* #return string
*/
public function getContrasena()
{
return $this->contrasena;
}
/**
* Set estado
*
* #param boolean $estado
*
* #return Sesion
*/
public function setEstado($estado)
{
$this->estado = $estado;
return $this;
}
/**
* Get estado
*
* #return boolean
*/
public function getEstado()
{
return $this->estado;
}
/**
* Set usuarioidentificacion
*
* #param \DBAL\Entity\Usuario $usuarioidentificacion
*
* #return Sesion
*/
public function setUsuarioidentificacion(\DBAL\Entity\Usuario $usuarioidentificacion)
{
$this->usuarioidentificacion = $usuarioidentificacion;
return $this;
}
/**
* Get usuarioidentificacion
*
* #return \Usuario
*/
public function getUsuarioidentificacion()
{
return $this->usuarioidentificacion;
}
/**
* Add rolesid
*
* #param \Roles $rolesid
*
* #return Sesion
*/
public function addRolesid(\Roles $rolesid)
{
$this->rolesid[] = $rolesid;
return $this;
}
/**
* Remove rolesid
*
* #param \Roles $rolesid
*/
public function removeRolesid(\Roles $rolesid)
{
$this->rolesid->removeElement($rolesid);
}
/**
* Get rolesid
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getRolesid()
{
return $this->rolesid;
}
If someone could help me, I really need it and I have already searched a lot and attempted in many ways, but really not fix it.

Unwanted cascade delete by Doctrine2

I am working on a product catalog, and have two entities, PcatSalesItem and PcatCategory with a many-to-many relationship between them. If I delete a category, and there are still sales items associated with it, I want an exception to be thrown, I do NOT want cascading delete. On the RDBMS level (PostgreSQL), in the join table, I have set the foreign keys to "ON DELETE RESTRICT". However, when I delete a category that has sales items, Doctrine does a cascading delete. Nowhere have I specified cascade=remove to Doctrine!
Here are the entities:
/**
* PcatSalesItem
*
* #ORM\Table(name="pcat_sales_item")
* #ORM\Entity
* #Gedmo\Loggable(logEntryClass="Qi\Bss\BaseBundle\Entity\Business\LogEntryBusiness")
*/
class PcatSalesItem
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="SEQUENCE")
* #ORM\SequenceGenerator(sequenceName="pcat_sales_item_id_seq", allocationSize=1, initialValue=1)
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=64, nullable=false)
* #Gedmo\Versioned
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="description", type="text", nullable=true)
* #Gedmo\Versioned
*/
private $description;
/**
* #var array $categories
*
* #ORM\ManyToMany(targetEntity="PcatCategory")
* #ORM\JoinTable(name="pcat_category_x_sales_item",
* joinColumns={#ORM\JoinColumn(name="sales_item_id", referencedColumnName="id", onDelete="RESTRICT")},
* inverseJoinColumns={#ORM\JoinColumn(name="category_id", referencedColumnName="id", onDelete="RESTRICT")}
* )
*/
private $categories;
...
}
/**
* PcatCategory
*
* #ORM\Table(name="pcat_category")
* #ORM\Entity
* #Gedmo\Loggable(logEntryClass="Qi\Bss\BaseBundle\Entity\Business\LogEntryBusiness")
*/
class PcatCategory
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="SEQUENCE")
* #ORM\SequenceGenerator(sequenceName="pcat_category_id_seq", allocationSize=1, initialValue=1)
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=64, nullable=false)
* #Gedmo\Versioned
*/
private $name;
/**
* #var array $salesItems
*
* #ORM\ManyToMany(targetEntity="PcatSalesItem")
* #ORM\JoinTable(name="pcat_category_x_sales_item",
* joinColumns={#ORM\JoinColumn(name="category_id", referencedColumnName="id", onDelete="RESTRICT")},
* inverseJoinColumns={#ORM\JoinColumn(name="sales_item_id", referencedColumnName="id", onDelete="RESTRICT")}
* )
*/
private $salesItems;
....
}
Here is the code I use to delete a category:
$em = $this->getDoctrine()->getManager();
$cat = $em->getRepository('QiBssBaseBundle:PcatCategory')->find(15);
$em->remove($cat);
$em->flush();
Any help will be greatly appreciated!

Doctrine2 join error

Classes Entity
**
* classes
*
* #ORM\Table(name="classes")
* #ORM\Entity
* #Annotation\Name("classes")
* #Annotation\Hydrator("Zend\Stdlib\Hydrator\ClassMethods")
*/
class Classes {
/**
* #var integer
*
* #ORM\Column(name="class_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
* #Annotation\Exclude()
*/
protected $classId;
/**
* #var string
*
* #ORM\Column(name="class_name", type="string", length=100, nullable=false)
* #Annotation\Filter({"name":"StringTrim"})
* #Annotation\Validator({"name":"StringLength", "options":{"min":1, "max":30}})
* #Annotation\Validator({"name":"Regex", "options":{"pattern":"/^[a-zA-Z][a-zA-Z0-9_-]{0,24}$/"}})
* #Annotation\Attributes({"type":"text"})
* #Annotation\Options({"label":"class name:"})
*/
private $className;
}
Entity Maptechclass
/** #ORM\Entity */
class Maptechclass {
/**
* #var integer
*
* #ORM\Column(name="map_tech_class_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
* #Annotation\Exclude()
*/
protected $mapTechClassID;
/**
* #var integer
*
* #ORM\Column(name="map_sch_id", type="integer", nullable=true)
*/
protected $mapSchID;
/**
* #var integer
*
* #ORM\Column(name="map_tech_id", type="integer", nullable=true)
*/
protected $mapTechID;
/**
* #ORM\OneToMany(targetEntity="SchoolAdmin\Entity\Classes")
* #ORM\JoinColumn(name="map_class_id", referencedColumnName="classId")
* */
protected $mapClassID;
}
Following Error will show , how to fix it.
OneToMany mapping on field 'mapClassID' requires the 'mappedBy' attribute.
Find:
#ORM\OneToMany(targetEntity="SchoolAdmin\Entity\Classes")
Replace it by:
#ORM\OneToMany(targetEntity="SchoolAdmin\Entity\Classes", mappedBy="classId")

OneToMany Relationship Only Returns 1 Row

I am looking for some help as to why my OneToMany relationship in doctrine only returns one value. My data model has three tables. Users, Authorization, and Applications. The Authorization table is the glue that maps users to applications and also contains an accesslevel field to indicate their level of access for that application.
I have a user that has three entries in authorization for three different applications, but for some reason, doctrine is only loading 1 of those authorization records. I've included my full data model below. The attribute in question is "authorization" in the Webusers table.
Anyone know what I am doing wrong?
class Webusers {
/**
* #var integer
*
* #ORM\Column(name="userid", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $userid;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Applications", mappedBy="userid")
*/
private $applications;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\OneToMany(targetEntity="Authorization", mappedBy="user")
*/
private $authorization;
/**
* Constructor
*/
public function __construct() {
$this->applications = new \Doctrine\Common\Collections\ArrayCollection();
$this->authorization = new \Doctrine\Common\Collections\ArrayCollection();
}
class Authorization {
/**
* #var integer
*
* #ORM\Column(name="accesslevel", type="integer", nullable=false)
*/
private $accesslevel;
/**
* #var integer
*
* #ORM\Column(name="applicationid", type="integer", nullable=false)
* $ORM\Id
*/
private $applicationid;
/**
* #var integer
*
* #ORM\Column(name="userid", type="integer", nullable=false)
* #ORM\Id
*/
private $userid;
/**
* #var \Webusers
*
* #ORM\ManyToOne(targetEntity="Webusers")
* #ORM\JoinColumn(name="userid", referencedColumnName="userid")
*/
private $user;
}
class Applications {
/**
* #var integer
*
* #ORM\Column(name="applicationid", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $applicationid;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=50, nullable=false)
*/
private $name;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Webusers", inversedBy="applicationid")
* #ORM\JoinTable(name="authorization",
* joinColumns={
* #ORM\JoinColumn(name="applicationid", referencedColumnName="applicationid")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="userid", referencedColumnName="userid")
* }
* )
*/
private $userid;
/**
* Constructor
*/
public function __construct()
{
$this->userid = new \Doctrine\Common\Collections\ArrayCollection();
}
}
I was able to fix this by making sure I specified both the webusers and application relationship as ManyToOne AND giving them the #ORM\Id property since they make up a composite primary key.
I think the main reason this was failing was because I had not associated authorization to applications.
The key points of my new model are as follows:
class Webusers {
/**
* #var \Doctrine\Common\Collections\Collection
* #ORM\OneToMany(targetEntity="Authorization", mappedBy="user")
*/
private $authorization;
}
class Authorization {
/**
*
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Applications")
* #ORM\JoinColumn(name="applicationid", referencedColumnName="applicationid")
*/
private $application;
/**
*
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Webusers")
* #ORM\JoinColumn(name="userid", referencedColumnName="userid")
*/
private $user;
}
class Applications {
/**
* #ORM\OneToMany(targetEntity="Authorization", mappedBy="application")
*/
private $authorization;
}