I'm having issues setting up my Doctrine request properly.
I have two tables (PROPRIETE and PHOTO), one PROPRIETE can have many PHOTO
Therefore, I'd like to make a SELECT that will return an array of PROPRIETE where which one includes an array of it's own PHOTOs (not sure if I'm clear though...)
This is what my Popriete class looks like
class Propriete
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="libelle", type="string", length=255)
*/
private $libelle;
/**
* #ORM\ManyToOne(targetEntity="VillaPrivee\UserBundle\Entity\User")
* #ORM\JoinColumn(onDelete="CASCADE")
*/
private $proprietaire;
/**
* #ORM\OneToMany(targetEntity="VillaPrivee\MainBundle\Entity\Photo", mappedBy="propriete")
*/
private $photo;
And then the Photo class
class Photo
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="path", type="string", length=255)
*/
private $path;
/**
* #ORM\ManyToOne(targetEntity="VillaPrivee\MainBundle\Entity\Propriete")
* #ORM\JoinColumn(onDelete="CASCADE")
*/
private $propriete;
And finally, my Doctrine request (that successfully returns a list of Propriete, but nothing about their photos)
public function getProprietesByCriteria($ville, $rooms) {
$qb = $this->createQueryBuilder('p');
$qb->where('p.commune = :ville AND p.nbChambres >= :rooms')
->setParameter('ville', $ville)
->setParameter('rooms', $rooms);
return $qb->getQuery()->getResult();
}
I've tried with a leftJoin, but it seems that I don't know how to use that stuff...
Thanks guys for your help
I thing you're mixing different concepts in your code. Using Doctrine you should forget about trying to get multiple arrays with a query.
You only need to invoke the getPhoto() method on any propriete object.
So, you can use a code similar to:
$props = $this->getProprietesByCriteria($ville, $rooms);
foreach($props as $prop)
{
$prop->getPhoto(); // <--will return an array of the `PHOTO` related to this `PROPIETE`
}
Related
The title may not be so clear so I'll explain in detail here (I must miss something obvious but I can't figure out what).
I'm using the Vich uploader bundle to store pictures in my project. I have two entity linked with an unidirectional one to one relation, the first is the owner and contain the annotation pointing to the second entity containing the file.
This is the code part from the first entity :
...
/**
* #ORM\OneToOne(targetEntity="Cartong\MyBundle\Entity\Mysql\EntityContainingTheFile")
* #ORM\JoinColumn(name="photo_id", referencedColumnName="id")
*/
private $photo;
...
And the one containing the file :
/**
* #ORM\Entity
* #ORM\HasLifecycleCallbacks()
* #Vich\Uploadable
*/
class EntityContainingTheFile extends FileUpload
{
/**
* #var UploadedFile
* #Vich\UploadableField(mapping="my_pictures", fileNameProperty="filename")
*/
protected $file;
/**
* #return UploadedFile
*/
public function getFile()
{
return parent::getFile();
}
/**
* #param UploadedFile $file
*/
public function setFile(File $file)
{
return parent::setFile($file);
}
}
The FileUpload code is here too. It basically containing the file description (I'm using other entity that are extending this class) :
/**
* #ORM\MappedSuperclass
* #Vich\Uploadable
*/
class FileUpload
{
/**
* #var integer : stock the unique id of the file
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string : stock the format of the file
*
* #ORM\Column(name="format", type="string", length=255)
*/
private $format;
/**
* var string : stock the original name of the file
*
* #ORM\Column(name="alt", type="string", length=255)
*/
private $alt;
/**
* #var integer : stock the size of the file (ko)
*
* #ORM\Column(name="size", type="integer")
*/
private $size;
/**
* #var \DateTime
*
* #ORM\Column(name="updated_at", type="datetime", nullable=true)
*/
private $updatedAt;
/**
* #var string $filename
*
* #ORM\Column(name="filename", type="string", length=255)
*/
protected $filename;
protected $file;
The file upload is working well, everything is stored at the right place in my project and the DB. The problem occur when I try to retrieve what I just store trough the first entity.
This is the kind of code I have in my controller :
$repo = $this->container->get('doctrine')->getRepository('CartongMSFBundle:MyFirstEntity');
$test = $repo->find($theEntityWithAFile);
The object returned containing all the expected information except the photo, where all the fields are null.
So if I'm trying to get the specific file trough a findById in the "file" repo it's working but when I'm trying to get it trough my first entity it's not.
Any idea ? (maybe a mistake in the annotations ?)
It seems like a typical doctrine hydration issue. In case of associations, doctrine by default doesn't load from database associated entities, until it is needed (e.g. you call $myFirstEntity->getPhoto()->getFormat()). This is called lazy loading.
If you want your associated entity to be loaded along with your first entity you should set doctrine fetch option to EAGER:
/**
* #ORM\OneToOne(targetEntity="EntityContainingTheFile", fetch="EAGER")
* #ORM\JoinColumn(name="photo_id", referencedColumnName="id")
*/
private $photo;
i have 2 entities.
the simple logic is that a user has many delivery address, so in the future he will be able to choose one of them for their deliverys.
first entity, Direccion (address).
<?php
use Doctrine\ORM\Mapping as ORM;
/**
* Direccion
*
* #ORM\Table(name="direccion", indexes={#ORM\Index(name="id_usuario",
columns={"id_usuario"})})
* #ORM\Entity
*/
class Direccion
{
/**
* #var string
*
* #ORM\Column(name="Calle", type="string", length=100, nullable=false)
*/
private $calle;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \Usuario
*
* #ORM\ManyToOne(targetEntity="Usuario", inversedBy="direcciones", fetch="EAGER")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="id_usuario", referencedColumnName="id" )
* })
*/
private $idUsuario;
/**
* Set calle
*
* #param string $calle
* #return Direccion
*/
public function setCalle($calle)
{
$this->calle = $calle;
return $this;
}
/**
* Get calle
*
* #return string
*/
public function getCalle()
{
return $this->calle;
}
/**
* Set idUsuario
*
* #param \Usuario $idUsuario
* #return Direccion
*/
public function setIdUsuario(\Usuario $idUsuario = null)
{
$this->idUsuario = $idUsuario;
return $this;
}
/**
* Get idUsuario
*
* #return \Usuario
*/
public function getIdUsuario()
{
return $this->idUsuario;
}
}
and the second entity is, Usuario (User)
<?php
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Usuario
*
* #ORM\Table(name="usuario")
* #ORM\Entity
*/
class Usuario
{
/**
* #var string
*
* #ORM\Column(name="nombre", type="string", length=255, nullable=true)
*/
private $nombre;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="Direccion", mappedBy="id_usuario", cascade={"persist"})
*/
private $direcciones;
public function __construct()
{
$this->direcciones = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Set nombre
*
* #param string $nombre
* #return Usuario
*/
public function setNombre($nombre)
{
$this->nombre = $nombre;
return $this;
}
/**
* Get nombre
*
* #return string
*/
public function getNombre()
{
return $this->nombre;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
public function getDirecciones()
{
return $this->direcciones;
}
public function setDirecciones($direcciones)
{
$this->direcciones = $direcciones;
return $this;
}
}
i have already readed many blogs and similar questions , but i can't get the property $direcciones hydrated or filled with the associated data, when i try to achieve that on the inverse side of the relation (OneToMany), with $usuario->getDirecciones();
i'm working in with this code in a non MVC architecture, to this point everything works like charm in getting creating and updating through a DAO wich uses the Doctrine sentences,so i know that in the ending points (updating, creating,persist and flush,retrieving data with find, findBy,etc), everything works fine.
when i try to fill the ArrayCollection calling from the Service layer where i use both classes (Usuario-InverseSide and Direccion-OwnerSide), nothing happens, the $direcciones arrayCollection property on Usuario instance, doesn't fetch anything, even when i tried establishing fetch="EAGER"on the annotations.
for the rest of the data or properties, everything works fine, all the other attributes of the user get filled just fine.
For some reason, the annotations are not being considered, donĀ“t know why.
i've spend a few days trying to figure out how to acomplish this way of accessing the associated data, didn't wanted to use DQL, but at this point i think i will take that road.
I tried something hard-coded to test and, the result is the same, $direcciones doesn't get his data.Obvious are already discarted, that exact ID for a Usuario(user) has his related direcciones(address) setted.
$usuario = $this->usuarioDAO->find(20);
$direcciones = $usuario->getDirecciones();
var_dump($direcciones);
return $usuario;
the relation was only defined with a FK called id_usuario on Direccion on a mysql InnoDb table.
don't know if i should be setting something else on the Usuario table.
or if there is something wrong in the way i store the entities on my project.
please help me, any recommendation will be appreciated.
doctrine 2, hwo do get data from the inverse side (many to one)
Provided that your bidirectional mapping between Direccion and Usuaro is correct, you need to use setter methods on both sides.
And the other thing, when using ArrayCollection, an easier (perhaps the only way) is to add elements one by one.
Therefore, instead of setDirecciones() you add this method to Usuario class:
public function addDireccion(Direccion $direccion) {
$direccion->setIdUsuario($this);
$this->direcciones->add($direccion);
}
Hope that helps.
And, it would be better to name it $usuario and setUsuario instead od setIdUsuario because you work with objects and PHP should not be concerned about actual database field names (to put it that way).
i resolved it.
happens that for some reason, the php annotations were not being actually readed, or on a different way to say it, it was listening to the XML annotations first, so i put the xml sentence on the inverse side of the relation (usuario) inside the tags
<one-to-many field="direcciones" target-entity="Direccion" mapped-by="idUsuario" />
and it worked, now i got polish the data result of the consult so i can send a cleaner/clearer object to the front-end.
RoundMatch.php
/**
* #ORM\Entity(repositoryClass="MyApp\MyBundle\Repository\RoundMatchRepository")
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="type", type="string")
* #ORM\DiscriminatorMap({"team_round_match" = "TeamRoundMatch", "player_round_match" = "PlayerRoundMatch"})
* #ORM\Table("my_round_match")
*/
abstract class RoundMatch
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var \DateTime
*
* #ORM\Column(name="match_date", type="datetime")
*/
private $matchDate;
How can I join related entities to discriminated entities?
I cannot get direct access to discriminated table columns to create joins.
I cannot get access to discriminated table columns to create joins.
How can I join children entities to discriminator entities?
I created joins like this:
RoundMatchRepository.php
public function getMatchesWithNoResultsSubmitted()
{
$qb = $this->createQueryBuilder("rm");
$qb->leftJoin("rm.round", "rnd" )
->leftJoin("rnd.group", "sg")
->leftJoin("sg.server", "ss")
->leftJoin("ss.stage", "ts")
->leftJoin("ts.tournament", "t")
->leftJoin("MyAppMyBundle:PlayerRoundMatch", "prm", "WITH", "rm.id = prm.id")
->leftJoin("prm.player1", "p1")
->leftJoin("prm.player2", "p2")
->leftJoin('p1.gameProfiles',"gp1")
->leftJoin('p2.gameProfiles',"gp2")
->leftJoin('p1.gameProfiles', 'gp1', "WITH", $qb->expr()->andX(
$qb->expr()->eq('t.game', 'gp1.game'),
$qb->expr()->eq('prm.player1', 'gp1.player')
))
->leftJoin('p1.gameProfiles', 'gp2', "WITH", $qb->expr()->andX(
$qb->expr()->eq('t.game', 'gp2.game'),
$qb->expr()->eq('prm.player2', 'gp2.player')
));
return $qb->getQuery()->getResult();
}
I want to use result object in a twig and I cannot get joined entities in returned object because they are not joined via object relation.
I don't have object relation created because they are joined one to one via discriminator.
I have achieved dynamic relationships using Single Table Inheritance. This will allow you to write DQL against the relation or against the discriminating class.
You CANNOT "query" the discriminator using DQL. You must INSTANCE OF the entity name, see possible duplicate as per #LBA's comment
In this resulting schema, group_id will mean a different relation depending on the discriminator.
Obviously, this has performance implications on the database, though.
/**
* #ORM\Entity
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="type", type="string", length=255)
* #ORM\DiscriminatorMap(
* {"parents" = "Parents", "children" = "Child"}
* )
* #ORM\Table("people")
*/
class Person
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var mixed
*/
private $group;
}
/*
* #ORM\Table("parents")
*/
class Parent extends Person
{
/**
* Many parents have one group.
*
* #ManyToOne(targetEntity="ParentGroups", inversedBy="parents")
* #JoinColumn(name="group_id", referencedColumnName="id")
*/
private $group;
}
/*
* #ORM\Table("children")
*/
class Child extends Person
{
/**
* Many children have one group.
*
* #ManyToOne(targetEntity="ChildGroups", inversedBy="children")
* #JoinColumn(name="group_id", referencedColumnName="id")
*/
private $group;
}
/*
* #ORM\Table("parent_groups")
*/
class ParentGroups
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* One group has many parents.
*
* #OneToMany(targetEntity="Parent", mappedBy="group")
*/
private $parents;
public function __construct() {
$this->parents = new ArrayCollection();
}
}
/*
* #ORM\Table("child_groups")
*/
class ChildGroups
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* One group has many parents.
*
* #OneToMany(targetEntity="Child", mappedBy="group")
*/
private $children;
public function __construct() {
$this->children = new ArrayCollection();
}
}
Effectively the extending tables can override the annotations (if any) of the parent.
It work's a dream and we have multiple uses of this on our monolith :)
Untested code, but it should give you an idea of how I achieved it. If you struggle from here I'll go and make sure it runs.
I am using Doctrine 2.4 and I want to split the table one of my entity is using into several tables to improve querying that table - Horizontal partitioning, if I am not mistaken. The table has a lot of data (> 20 Mio. rows) and queries are always done on a subset (Component.Type).
Here are my entities:
Station:
/**
* #ORM\Id
* #ORM\Column(type="bigint")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
Component:
/**
* #ORM\Id
* #ORM\Column(type="bigint")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $type;
Data
/**
* #ORM\Id
* #ORM\Column(type="bigint")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="datetime")
*/
private $datetime;
/**
* #ORM\Column(type="float", nullable=true)
*/
private $value;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Station")
* #ORM\JoinColumn(name="station_id", referencedColumnName="id")
*
* #var Station $station
*/
private $station;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Component")
* #ORM\JoinColumn(name="component_id", referencedColumnName="id")
*
* #var Component $component
*/
private $component;
At the moment I solved this manually by having one Entity Data[TYPE] for every Component.Type (e.g. DataPollutionNo). All entities Data[TYPE] extent a MappedSuperclass (Data). To query for data, I first get the corresponding Data Entity from the Component.Type and query that Repository (DataPollutionNoRepository). This works out fine but I was wondering if there is a more sophisticated/generic approach to solve this.
Thanks, Hannes
I would like to summate or add one up while I update an existing db entry.
For the Reason that I will have to work with MagicCalls, I just wonder how I can handle this.
In raw sql, I would do it like:
UPDATE table SET value= value + 1 WHERE ....
But in this case, I have absolutely no idea how to work it out.
My code looks like:
Entity:
class Properties
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="Sport", type="string", length=11, nullable=true)
*/
private $sport;
/**
* #var string
*
* #ORM\Column(name="Entertainment", type="string", length=11, nullable=true)
*/
private $entertainment;
/**
* #var string
*
* #ORM\Column(name="Wellness", type="string", length=11, nullable=true)
*/
private $wellness;
Now, I get those column names by
$metadata = $em->getClassMetadata($className);
$columnNames=$metadata->getColumnNames();
I recieve an array, which I can foreach and add values to each of them which I will have to write back by using an accessor:
$properties= new Properties();
$accessor = PropertyAccess::createPropertyAccessorBuilder()
->enableMagicCall()
->getPropertyAccessor();
foreach($columnNames as $merkmale) {
$accessor->setValue($properties, $merkmale, 1);
}
So how can I handle an update by counting one up ? I missed something I guess
Why don't you use Lifecycle callbacks? This way you can choose doctrine event which suits your case best and increment all values you need in one method.
/**
* #ORM\Entity()
* #ORM\HasLifecycleCallbacks()
*/
class Properties
{
/**
* #var string
*
* #ORM\Column(name="Sport", type="string", length=11, nullable=true)
*/
private $sport;
/**
* #var string
*
* #ORM\Column(name="Entertainment", type="string", length=11, nullable=true)
*/
private $entertainment;
/**
* #var string
*
* #ORM\Column(name="Wellness", type="string", length=11, nullable=true)
*/
private $wellness;
/**
* #ORM\PrePersist
*/
public function incrementValues()
{
$this->entertainment = 1; //you don't need any magic any more
$this->wellness = $this->wellness + 5; //you can access your entity values directly
}
}
All you need to do is annotate your entity with #ORM\HasLifecycleCallbacks() and add method which you also need to annotate with given event, like #ORM\PrePersist and this method will be called by doctrine each time this event occurs