Table partinioning (horizontal) with doctrine - mysql

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

Related

In Symfony, find() method return null on entity with one to one unidirectional relation

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;

Symfony doctrine join related entities to discriminator columns

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.

Doctrine add existing foreign keys when schema is updated (mysql)

I have the following entities:
A.php
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="a")
* #ORM\Entity()
*/
class A
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* One Cart has One Customer.
* #ORM\OneToOne(targetEntity="B", inversedBy="a")
* #ORM\JoinColumn(name="b_id", referencedColumnName="id")
*/
private $b;
/**
* #ORM\OneToOne(targetEntity="C", inversedBy="a")
* #ORM\JoinColumn(name="c_id", referencedColumnName="id")
*/
private $c;
/**
* #ORM\ManyToOne(targetEntity="D", inversedBy="a")
* #ORM\JoinColumn(name="d_id", referencedColumnName="id")
*/
private $d;
}
B.php
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="b")
* #ORM\Entity()
*/
class B
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToOne(targetEntity="A", mappedBy="b")
*/
private $a;
}
C.php
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="c")
* #ORM\Entity()
*/
class C
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToOne(targetEntity="A", mappedBy="c")
*/
private $a;
}
D.php
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="d")
* #ORM\Entity()
*/
class D
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="A", mappedBy="d")
*/
private $a;
}
I executing commands:
php bin/console doctrine:database:create
php bin/console doctrine:schema:update --force
php bin/console doctrine:schema:update --dump-sql
Last command prints:
ALTER TABLE a ADD CONSTRAINT FK_E8B7BE43296BFCB6 FOREIGN KEY (b_id) REFERENCES b (id);
But this constraint is added before, so I can't to use
php bin/console doctrine:schema:update --force
Two times without error.
This behavior occurs only when I am using MySQL. When I removed MySQL and installed MariaDB problem vanished.
Should I introduce changes in my entities, or is it bug of Doctrine?
It seems a bug. Try using DoctrineMigrations bundle so that you can control migration classes before applying them.

Symfony update Entity value + 1

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

Symfony2 - Doctrine - Join 2 tables

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`
}