I am using Doctrine2 in my project and I have defined following entities:
namespace Model;
/**
* #Entity()
* #Table(name="author")
**/
class Author {
/**
* #Id
* #GeneratedValue
* #Column(type="integer")
**/
private $id;
/** #Column(type="string") **/
private $firstName;
/** #Column(type="string") **/
private $lastName;
/** #Column(type="string", nullable=true) **/
private $titleBefore;
/** #Column(type="string", nullable=true) **/
private $titleAfter;
/** #ManyToMany(targetEntity="Article", mappedBy="authors") **/
private $articles;
public function __construct() {
$this->articles = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getId() {
return $this->id;
}
public function getTitleBefore() {
return $this->titleBefore;
}
public function setTitleBefore($titleBefore) {
$this->titleBefore = $titleBefore;
}
public function getTitleAfter() {
return $this->titleAfter;
}
public function setTitleAfter($titleAfter) {
$this->titleAfter = $titleAfter;
}
public function getLastName() {
return $this->lastName;
}
public function setLastName($lastName) {
$this->lastName = $lastName;
}
public function getFirstName() {
return $this->firstName;
}
public function setFirstName($firstName) {
$this->firstName = $firstName;
}
public function getArticles() {
return $this->articles;
}
public function addArticle($article) {
$this->articles->add($article);
}
}
and
namespace Model;
/**
* #Entity()
* #Table(name="article")
**/
class Article {
/**
* #Id
* #GeneratedValue
* #Column(type="integer")
**/
private $id;
/** #Column(type="string") **/
private $name;
/** #OneToOne(targetEntity="Publication") **/
private $publication;
/**
* #ManyToMany(targetEntity="Author", mappedBy="articles")
*/
private $authors;
public function __construct() {
$this->authors = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getId() {
return $this->id;
}
public function getName() {
return $this->name;
}
public function setName($name) {
$this->name = $name;
}
public function getPublication() {
return $this->publication;
}
public function setPublication($publication) {
$this->publication = $publication;
}
public function getAuthors() {
return $this->authors;
}
public function addAuthor($author) {
$this->authors->add($author);
$author->addArticle($this);
}
public function setAuthors($authors) {
$this->authors = $authors;
}
}
It looks like, that relation author<->article works nicely. Although I encountered a problem. When I try to acces authors in Smarty template like this: {foreach from=$article->getAuthors() item=author}, following exception is thrown:
Fatal error: Uncaught PDOException: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ON' at line 1 in /code/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:104 Stack trace:
#0 /code/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php(104): PDO->query('SELECT t0.id AS...')
#1 /code/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php(852): Doctrine\DBAL\Driver\PDOConnection->query('SELECT t0.id AS...')
#2 /code/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php(1030): Doctrine\DBAL\Connection->executeQuery('SELECT t0.id AS...', Array, Array)
#3 /code/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php(954): Doctrine\ORM\Persisters\Entity\BasicEntityPersister->getManyToManyStatement(Array, Object(Model\Article))
#4 /code/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php(2839): Doctrine\ORM\Persiste in /code/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php on line 90
I already spent a day on this, trying to find out what went wrong. I was suspicious, that I might use a reserved MySQL word, but I didn't find any in my variables.
I finally managed to obtain complete query log until I got the exception. It looks like, the most interesting query is not present
mysqld, Version: 5.7.20 (MySQL Community Server (GPL)). started with:
Tcp port: 3306 Unix socket: /var/run/mysqld/mysqld.sock
Time Id Command Argument
2017-11-25T23:46:52.327597Z 10 Connect user#articlerepository_php_1.articlerepository_default on article_repository using TCP/IP
2017-11-25T23:46:52.334083Z 10 Query SELECT t0.id AS id_1, t0.firstName AS firstName_2, t0.lastName AS lastName_3, t0.titleBefore AS titleBefore_4, t0.titleAfter AS titleAfter_5 FROM author t0
2017-11-25T23:46:52.342077Z 10 Query SELECT t0.id AS id_1, t0.name AS name_2, t0.publication_id AS publication_id_3 FROM article t0
2017-11-25T23:46:52.348058Z 10 Quit
Looks like I had a mismatch in my ManyToMany relationship. Here are the steps I took to fix this:
I dropped current schema by running bin/doctrine orm:schema-tool:drop --force
Validated current entities with running bin/doctrine orm:validate several times and made changes in entities, until it went through without errors
Generated new schema: bin/doctrine orm:schema-tool:update --force --dump-sql
Correct relationship looks like this:
class Author {
/**
* #ManyToMany(targetEntity="Article", inversedBy="authors")
* #JoinTable(name="authors_articles")
**/
private $articles;
}
class Article {
/**
* #ManyToMany(targetEntity="Author", mappedBy="articles")
*/
private $authors;
}
`
Related
Hello there have something i dont understand about SQL i guess. I linked below the code and the image about my db shema for help you to understand.
So my problem with this request is that it return an empty array and i dont understand why its not working as from my understanding it should return an array containing all the "r.messages" that have the specified ID related to the fields relation. What do i do wrong ?
PS: for some context helping your understanding, im trying to build a messagery system from a user to another. The SQL request i try to do should return the conversation between two users.
public function listOfMessages($value, $value2)
{
return $this->createQueryBuilder('user')
->select('r.message')
->innerJoin('user.sender', 's')
->innerJoin('user.receiver', 'r')
->where('s.sender = :value')
->andWhere('r.recipient = :value2')
->setParameter(':value', $value)
->setParameter(':value2', $value2)
->getQuery()
->getResult()
;
}
DB shema
Entity User
use App\Repository\PrivateMessageRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=PrivateMessageRepository::class)
*/
class PrivateMessage
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="text", nullable=true)
*/
private $message;
/**
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="sender")
*/
private $sender;
/**
* #ORM\ManyToOne(targetEntity=user::class, inversedBy="receiver")
*/
private $recipient;
/**
* #ORM\Column(type="boolean", nullable=true)
*/
private $isRead = 0;
/**
* #ORM\Column(type="datetime_immutable", nullable=true)
*/
private $sentAt;
public function getId(): ?int
{
return $this->id;
}
public function getMessage(): ?string
{
return $this->message;
}
public function setMessage(?string $message): self
{
$this->message = $message;
return $this;
}
public function getSender(): ?User
{
return $this->sender;
}
public function setSender(?User $sender): self
{
$this->sender = $sender;
return $this;
}
public function getRecipient(): ?user
{
return $this->recipient;
}
public function setRecipient(?user $recipient): self
{
$this->recipient = $recipient;
return $this;
}
public function getIsRead(): ?bool
{
return $this->isRead;
}
public function setIsRead(?bool $isRead): self
{
$this->isRead = $isRead;
return $this;
}
public function getSentAt(): ?\DateTimeImmutable
{
return $this->sentAt;
}
public function setSentAt(?\DateTimeImmutable $sentAt): self
{
$this->sentAt = $sentAt;
return $this;
}
}
Entity privateMessage
use App\Repository\PrivateMessageRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=PrivateMessageRepository::class)
*/
class PrivateMessage
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="text", nullable=true)
*/
private $message;
/**
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="sender")
*/
private $sender;
/**
* #ORM\ManyToOne(targetEntity=user::class, inversedBy="receiver")
*/
private $recipient;
/**
* #ORM\Column(type="boolean", nullable=true)
*/
private $isRead = 0;
/**
* #ORM\Column(type="datetime_immutable", nullable=true)
*/
private $sentAt;
public function getId(): ?int
{
return $this->id;
}
public function getMessage(): ?string
{
return $this->message;
}
public function setMessage(?string $message): self
{
$this->message = $message;
return $this;
}
public function getSender(): ?User
{
return $this->sender;
}
public function setSender(?User $sender): self
{
$this->sender = $sender;
return $this;
}
public function getRecipient(): ?user
{
return $this->recipient;
}
public function setRecipient(?user $recipient): self
{
$this->recipient = $recipient;
return $this;
}
public function getIsRead(): ?bool
{
return $this->isRead;
}
public function setIsRead(?bool $isRead): self
{
$this->isRead = $isRead;
return $this;
}
public function getSentAt(): ?\DateTimeImmutable
{
return $this->sentAt;
}
public function setSentAt(?\DateTimeImmutable $sentAt): self
{
$this->sentAt = $sentAt;
return $this;
}
}
Could you please use the web debug toolbar to extract the readable query and fire it agains your sql server.
I think your repository dql is wrong - hope i figure it out of the head correct.
You have to go over the message repository, not over the user repo.
You have to select the messages for user a and user b visaverce
$qb = $this->createQueryBuilder('m');
$qb->where(
$qb->expr()->orX(
$qb->expr()->andX(
$qb->expr()->eq('m.sender', ':sender'),
$qb->expr()->eq('m.recipient', ':recipient')
),
$qb->expr()->andX(
$qb->expr()->eq('m.sender', ':recipient'),
$qb->expr()->eq('m.recipient', ':sender')
)
)
)
->setParameter(':sender', $sender)
->setParameter(':recipient', $recipient)
->getQuery()
->getResult()
You say you want the conversation between the two users. Architectural i think, there is a missing table named conversation. Think about that:
A user can have a conversation to one-or-many users
A conversation can have one-or-many message(s)
A message can have a sender and a recipient
Maybe a better solution if you want to have more than one conversation ...
Thank you for your time and tips, i tried your dql request and it return "NULL".
About doing it into the message repo instead of user repo i did it like this because in my entity user i have two methods that can retrieve messages sent and received.
But anyways even trying it inside message repo return an empty array.
I tryed with rawsql and got some good result:
public function stack($id)
{
$rawSql =
"SELECT pm.sent_at as date, pm.message, user.login as login FROM private_message as pm
INNER JOIN user on user.id = pm.sender_id
WHERE pm.sender_id = $id
UNION
SELECT pm.sent_at as date, pm.message, user.login as login FROM private_message as pm
INNER JOIN user on user.id = pm.recipient_id
WHERE pm.recipient_id = $id
ORDER BY date DESC"
;
$conn = $this->getEntityManager()->getConnection()->prepare($rawSql);
$stmt = $conn->executeQuery([$rawSql]);
return $stmt->fetchAll();
}
However this request have some weird behavior specially when i set two different $id variable if i manually set the user id and recipient id it start to duplicate some messages and also the user.login returned is the same for all messages sent and recieved. But still a kind of progress as i have the conversation with this sql request but its not a good solution for long term as the ->fetchAll() method is depreciated and going to be removed from doctrine api in 2023.
By the way the clause UNION in my sql request seem to not have its equal version with DQL do you have any tips to make a UNION with a DQL request ?
I think you are right about adding a conversation table ill have to think back about my db architecture maybe it will make the process more simple and intuitive
I am trying to get composite and foreign keys as primary keys working in Doctrine ORM. I know what I'm trying to do is possible because it is described here: https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/tutorials/composite-primary-keys.html#composite-and-foreign-keys-as-primary-key. This is exactly my use-case: I have some products, an order and an order item.
However, doctrine orm is unable to map this relation unto the database. The current problem is that only one of the annotated \Id primary keys is reflected on the mysql database. So $producto is translated unto the database correctly as producto_id and is both a primary key and a foreign key. However, the $orden property which is annotated in the same way doesn't appear whatsoever on my database.
This seems odd because when I was first testing this feature I tried only with one of the two properties and it worked fine, however, when both properties are annotated only one seems to be parsed by the metadata parser. Furthermore, I tried to revert my project to a usable state by forgetting about the foreign keys and just have a composite primary key (like I had it before), but now the parser doesn't seem to even recognize the primary key. For example, for:
class ProductoOrden
{
/**
* #ORM\Id()
* #ORM\Column(type="integer")
*/
private $idOrden;
/**
* #ORM\Id()
* #ORM\Column(type="integer")
*/
private $idProducto;
I get:
bash-3.2$ php bin/console make:migration
In MappingException.php line 52:
No identifier/primary key specified for Entity "App\Entity\ProductoOrden". Every Entity must
have an identifier/primary key.
So I'm unable to set it up properly or to revert it to the previous state (which is the strangest of all).
I'm about to restart my whole project from scratch, because I cannot make sense of how the metadata parsing works. I am worried I have screwed up the process because I have manually erased the files at 'src\Migrations' because of similar issues before and php bin/console doctrine:migrations:version --delete --all didn't seem to work or I haven't understood the proper use of it.
In conclusion: ¿Could anyone assert if what I am trying to do with ProducoOrden is possible (maybe I'm not understanding the documentation example)? Is there any way to completely wipe out previous cache about the annotations/ schema metadata?
I've looked unto the orm:schema-tool but I don't really get how to configure it properly or why I have to configure it at all of I already have the bin/console tool on my project.
I will show all three involved classes for completeness sake, but the main problem is within ProductoOrden (Order-items).
<?php
//Products
namespace App\Entity;
use App\Repository\ProductosRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity(repositoryClass=ProductosRepository::class)
* #UniqueEntity("idProducto", message=" {producto {{ value }}}: llave primaria violada ")
*/
class Productos
{
/**
* #ORM\Id()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $nombreProducto;
/**
* #ORM\Column(type="string", length=255)
*/
private $descripcionProducto;
/**
* #ORM\Column(type="string", length=255)
*/
private $urlImagen;
/**
* #ORM\Column(type="integer")
*/
private $puntosProducto;
public function getIdProducto(): ?int
{
return $this->idProducto;
}
public function getCodProducto(): ?int
{
return $this->idProducto;
}
public function setIdProducto(int $codProducto): self
{
$this->idProducto = $codProducto;
return $this;
}
public function getNombreProducto(): ?string
{
return $this->nombreProducto;
}
public function setNombreProducto(string $nombreProducto): self
{
$this->nombreProducto = $nombreProducto;
return $this;
}
public function getDescripcionProducto(): ?string
{
return $this->descripcionProducto;
}
public function setDescripcionProducto(string $descripcionProducto): self
{
$this->descripcionProducto = $descripcionProducto;
return $this;
}
public function getUrlImagen(): ?string
{
return $this->urlImagen;
}
public function setUrlImagen(string $urlImagen): self
{
$this->urlImagen = $urlImagen;
return $this;
}
public function getPuntosProducto(): ?int
{
return $this->puntosProducto;
}
public function setPuntosProducto(int $puntosProducto): self
{
$this->puntosProducto = $puntosProducto;
return $this;
}
public function __toString(){
$str = '{producto:'.$this->getIdProducto().', nombre: '.$this->getNombreProducto().'}';
return $str;
}
}
<?php
\\Orders
namespace App\Entity;
use App\Repository\OrdenesRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity(repositoryClass=OrdenesRepository::class)
* #UniqueEntity("idOrden", message="{orden {{ value }}}: llave primaria violada")
*/
class Ordenes
{
/**
* #ORM\Id()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="integer")
*/
private $totalOrden;
/**
* #ORM\Column(type="string", length=255)
*/
private $estado;
/**
* #ORM\OneToMany(targetEntity=ProductoOrden::class, mappedBy="orden", orphanRemoval=true)
*/
private $productosOrden;
public function __construct()
{
$this->productosOrden = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getIdOrden(): ?int
{
return $this->idOrden;
}
public function setIdOrden(int $idOrden): self
{
$this->idOrden = $idOrden;
return $this;
}
public function getTotalOrden(): ?int
{
return $this->totalOrden;
}
public function setTotalOrden(int $totalOrden): self
{
$this->totalOrden = $totalOrden;
return $this;
}
public function getEstado(): ?string
{
return $this->estado;
}
public function setEstado(string $estado): self
{
$this->estado = $estado;
return $this;
}
public function __toString(){
$str = '{orden:'.$this->getIdOrden().'}';
return $str;
}
/**
* #return Collection|ProductoOrden[]
*/
public function getProductosOrden(): Collection
{
return $this->productosOrden;
}
public function addProductosOrden(ProductoOrden $productosOrden): self
{
if (!$this->productosOrden->contains($productosOrden)) {
$this->productosOrden[] = $productosOrden;
$productosOrden->setOrden($this);
}
return $this;
}
public function removeProductosOrden(ProductoOrden $productosOrden): self
{
if ($this->productosOrden->contains($productosOrden)) {
$this->productosOrden->removeElement($productosOrden);
// set the owning side to null (unless already changed)
if ($productosOrden->getOrden() === $this) {
$productosOrden->setOrden(null);
}
}
return $this;
}
}
<?php
\\Order-items
namespace App\Entity;
use App\Repository\ProductoOrdenRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity(repositoryClass=ProductoOrdenRepository::class)
* #UniqueEntity(fields={"idOrden","idProducto"}, message="{prod. orden {{ value }}}: llave primaria violada")
*/
class ProductoOrden
{
/*
* #ORM\Id
* #ORM\ManyToOne(targetEntity=Ordenes::class, inversedBy="productosOrden")
* #ORM\JoinColumn(nullable=false)
*/
private $orden;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity=Productos::class)
* #ORM\JoinColumn(nullable=false)
*/
private $producto;
/**
* #ORM\Column(type="integer")
*/
private $puntos;
/**
* #ORM\Column(type="integer")
*/
private $cantidad;
public function getId(): ?int
{
return $this->idOrden;
}
public function setIdOrden(int $idOrden): self
{
$this ->idOrden = $idOrden;
return $this;
}
public function getIdProducto(): ?int
{
return $this->idProducto;
}
public function setIdProducto(int $idProducto): self
{
$this->idProducto = $idProducto;
return $this;
}
public function getPuntos(): ?int
{
return $this->puntos;
}
public function setPuntos(int $puntos): self
{
$this->puntos = $puntos;
return $this;
}
public function getCantidad(): ?int
{
return $this->cantidad;
}
public function setCantidad(int $cantidad): self
{
$this->cantidad = $cantidad;
return $this;
}
public function __toString(){
$str = '{productoOrden:'.$this->getId().', '.$this->getIdProducto().'}';
return $str;
}
public function getOrden(): ?Ordenes
{
return $this->orden;
}
public function setOrden(?Ordenes $orden): self
{
$this->orden = $orden;
return $this;
}
}
For the shown classes, the migration it generates is
final class Version20200814210929 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('ALTER TABLE producto_orden ADD puntos INT NOT NULL');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('ALTER TABLE producto_orden DROP puntos');
}
}
As you can see, small changes like changing the type of a property work; but it doesn't seem to take on the id() and the association annotations.
Many thanks
I have a page that displays information about a movie. I recover in GET the id of the film. What I would like to do is retrieve the comments for each film (there is a filmId column in my table linked to the primary id of the film table)
/**
* #Route("/user/film/{id}", name="film")
*/
public function film(FilmRepository $repo, CommentRepository $comRepo, EntityManagerInterface $em, Request $req, $id)
{
$film = $repo->find($id);
$comments = $comRepo->findBy(array('id' => $id));
return $this->render('film/film.html.twig', [
'controller_name' => 'FilmController',
'film' => $film,
'comments' => $comments
]);
}
when I make a $comments = $comRepo->findBy(array('id' => $id)); I get some comments, but based on their id and NOT the film id (the comment with id 1 will be displayed on the film with id 1, but for example a comment with id 4 and the filmId a 1 will not appear on film 1, but on the film with id 4)
I tried to access the filmId field by simply making a $comments = $comRepo->findBy(array ('filmId' => $ id)); but i get the error :
An exception occurred while executing 'SELECT t0.id AS id_1, t0.content AS content_2, t0.created_at AS created_at_3, t0.author_id AS author_id_4 FROM comment t0 WHERE comment_film.film_id = ?' with params ["1"]:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'comment_film.film_id' in 'where clause'
I tried a personalized request with, in my Comment repository:
public function findAllWithFilmId($filmId)
{
$em = $this->getEntityManager();
$query = $em->createQuery(
'SELECT c
FROM App\Entity\Comment c
WHERE c.filmId = :filmId'
)->setParameter('filmId', $filmId);
return $query->getResult();
}
But it doesn't seem to work..
Where do I go to make a request like this ?
How to modify the request, which seems erroneous, from symfony without disorganizing everything? or is there a better method to correct the problem?
This is my Comment Entity
<?php
namespace App\Entity;
use App\Entity\Film;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\CommentRepository")
*/
class Comment
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="comments")
* #ORM\JoinColumn(nullable=false)
*/
private $author;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Film", inversedBy="comments")
* #ORM\JoinColumn(nullable=false)
*/
private $filmId;
/**
* #ORM\Column(type="text")
*/
private $content;
/**
* #ORM\Column(type="datetime")
*/
private $createdAt;
public function getId(): ?int
{
return $this->id;
}
public function getAuthor(): ?User
{
return $this->author;
}
public function setAuthor(?User $author): self
{
$this->author = $author;
return $this;
}
public function getFilmId(): ?Film
{
return $this->filmId;
}
public function setFilmId(?Film $filmId): self
{
$this->filmId = $filmId;
return $this;
}
public function getContent(): ?string
{
return $this->content;
}
public function setContent(string $content): self
{
$this->content = $content;
return $this;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeInterface $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
}
I think it is possible that the error comes from annotations, because starting on symfony during the make: entity, I defined types relations which I corrected later in phpmyadmin, but not the code. For example we can see that filmId is in ManyToMany, but I think it should be in OneToOne (FilmId can only have one id and an id can only correspond to one filmId), but I'm afraid that if I change certain things it breaks everything.
If you have set up your ORM relations correctly, it should be as simple as:
$film = $repo->find($id);
$comments = $film->getComments();
You might be missing a mapping in Film.php.
Here's an XML example, should be easy enough to convert to annotations:
In film:
<one-to-many field="comments" target-entity="App\...\Comments" mapped-by="film"/>
In comments:
<many-to-one field="film" target-entity="App\...\Film" inversed-by="comments"/>
First of all, I advise you to read more about the relations between entities.
Because, the current annotations says that you can have a lot of comments on many films. It's not right. One comment may belong to one film. One movie can have many comments.
Also, I want to note that, as far as I know, #JoinColumn should be in a child entity, that is, where the link to FK is contained.
Therefore, your entities should look like this:
Comment:
<?php
namespace App\Entity;
use App\Entity\Film;
use DateTimeInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\CommentRepository")
*/
class Comment
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="comments")
*/
private $author;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Film", inversedBy="comments")
* Here we set property for our table and property of foreign table to map our comment to the right film
* nullable, because comment couldn't be without film
* #ORM\JoinColumn(name="film_id", referencedColumnName="id", nullable=false)
*/
private $film;
/**
* #ORM\Column(type="text")
*/
private $content;
/**
* #ORM\Column(type="datetime")
*/
private $createdAt;
public function getId(): ?int
{
return $this->id;
}
public function getAuthor(): ?User
{
return $this->author;
}
public function setAuthor(?User $author): self
{
$this->author = $author;
return $this;
}
public function getFilmId(): ?Film
{
return $this->filmId;
}
public function setFilmId(?Film $filmId): self
{
$this->filmId = $filmId;
return $this;
}
public function getContent(): ?string
{
return $this->content;
}
public function setContent(string $content): self
{
$this->content = $content;
return $this;
}
public function getCreatedAt(): ?DateTimeInterface
{
return $this->createdAt;
}
public function setCreatedAt(DateTimeInterface $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
}
Film:
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\FilmRepository")
*/
class Film
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Comment", mappedBy="film")
*/
private $comments;
public function __construct()
{
$this->comments = new ArrayCollection();
}
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
return $this;
}
public function getComments(): Collection
{
return $this->comments;
}
public function setComments(Collection $comments): Film
{
$this->comments = $comments;
return $this;
}
}
So, now, you can retrieve your comments via:
/**
* #Route("/user/film/{id}", name="film")
*/
public function film($id)
{
/** #var null|EntityManager $entityManager */
$entityManager = $this->get('doctrine.orm.entity_manager');
if (null == ($film = $entityManager->getRepository(Film::class)->find($id))){
throw new NotFoundHttpException('Film not found');
}
$comments = $film->getComments();
return $this->render('film/film.html.twig', [
'film' => $film,
'comments' => $comments
]);
}
I am trying to figure out why one of my doctrine finds is running so slow. I don't really know where to start, so please bear with me.
I do a pretty basic find to fetch a user object. This find is taking ~160ms. When I run the query via phpmyadmin, it takes .7ms.
$this->em->find('Entities\User', $userId)
I have already tried adding skip-name-resolve to mysql's my.cnf. The id field in the user table is indexed. I really don't know what else to try. Let me know if there is additional information I can provide.
Below is the entity file:
namespace Entities;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityRepository;
/** #Entity(repositoryClass = "Entities\UserRepository")
* #Table(name="user")
*/
class User extends \Company_Resource_AbstractEntity
{
/** #Id #Column(type="integer") #GeneratedValue */
protected $id;
/** #Column(type="string") */
protected $name;
/** #Column(type="string") */
protected $password;
/** #Column(type="string") */
protected $email;
/** #Column(type="string") */
protected $first_name;
/** #Column(type="string") */
protected $last_name;
/** #Column(type="integer") */
protected $password_reset;
/** #Column(type="string") */
protected $salt;
/** #Column(type="integer") */
protected $active;
/** #Column(type="string") */
protected $cookie_hash;
/**
* #ManyToOne(targetEntity="Company" , inversedBy="user")
*/
protected $company;
/**
* #ManyToOne(targetEntity="Privilege" , inversedBy="user")
*/
protected $privilege;
/**
* #OneToMany(targetEntity="CompanySubscription" , mappedBy="user")
*/
protected $subscription;
/**
* #OneToMany(targetEntity="EquipmentEvent" , mappedBy="check_in_user")
*/
protected $check_in;
/**
* #OneToMany(targetEntity="EquipmentEvent" , mappedBy="check_out_user")
*/
protected $check_out;
/**
* #OneToMany(targetEntity="GroupEvent" , mappedBy="check_in_user")
*/
protected $check_in_group;
/**
* #OneToMany(targetEntity="GroupEvent" , mappedBy="check_out_user")
*/
protected $check_out_group;
/**
* #OneToMany(targetEntity="Maintenance" , mappedBy="submit_user")
*/
protected $maintenance_submit;
/**
* #OneToMany(targetEntity="Maintenance" , mappedBy="completed_user")
*/
protected $maintenance_complete;
/**
* #OneToMany(targetEntity="UserLogin" , mappedBy="user")
*/
protected $login;
}
Abstract entity:
use \Doctrine\Common\Collections\ArrayCollection;
abstract class Company_Resource_AbstractEntity implements ArrayAccess
{
public function offsetExists($offset)
{
return property_exists($this, $offset);
}
// The get/set functions should check to see if an appropriately named function exists before just returning the
// property. This way classes can control how data is returned from the object more completely.
public function offsetGet($offset)
{
$property = new Zend_Filter_Word_UnderscoreToCamelCase();
$method = 'get'. $property->filter($offset);
return $this->{$method}();
}
public function offsetSet($offset, $value)
{
$property = new Zend_Filter_Word_UnderscoreToCamelCase();
$method = 'set'. $property->filter($offset);
return $this->{$method}($value);
}
public function offsetUnset($offset)
{
// can't do this
}
/*==-====-====-====-====-====-====-====-====-====-====-==*/
/*
* Provides magic method access for getFieldName() and setFieldName()
* where field_name is a simple field and not a relation
* A special getData implementation returns all of the current object vars
*/
public function __call($method, $arguments)
{
preg_match('#^([a-z]+)(.*)#', $method, $matches);
$action = $matches[1];
$property = $matches[2];
$underscore = new Zend_Filter_Word_CamelCaseToUnderscore();
$offset = strtolower($underscore->filter($property));
if ($action == 'get')
{
if ($property == 'Data')
return get_object_vars($this);
if ($this->offsetExists($offset))
return $this->{$offset};
else
throw new Zend_Exception(sprintf("'%s' does not have property '%s'", get_class($this), $offset));
}
else if ($action == 'set')
{
if ($this->offsetExists($offset))
return $this->{$offset} = $arguments[0];
else
throw new Zend_Exception(sprintf("'%s' does not have property '%s'", get_class($this), $offset));
}
else
throw new Zend_Exception(sprintf("'%s' does not have method '%s'", get_class($this), $method));
}
}
The SQL that the find produces:
SELECT t0.id AS id1,
t0.name AS name2,
t0.password AS password3,
t0.email AS email4,
t0.first_name AS first_name5,
t0.last_name AS last_name6,
t0.password_reset AS password_reset7,
t0.salt AS salt8,
t0.active AS active9,
t0.cookie_hash AS cookie_hash10,
t0.company_id AS company_id11,
t0.privilege_id AS privilege_id12
FROM user t0 WHERE t0.id = ?
Anyone see anything wrong or know where to go further with this?
Using Doctrine 2.2.2.
The explain I get when I run that query with phpmyadmin: http://i.imgur.com/wWeGO.png
The table schema: http://i.imgur.com/BQsRX.jpg
I believe the problem with my setup was the actual number of lines in the file. Doctrine was reading through those every time. I enabled APC for the meta-cache and load time decreased dramatically after the first load. Without query or result cache, that query ACTUALLY only takes about 6 MS which is what I was aiming for all along. Wish I would have tried that sooner.
I am using both Doctrine2 and Symfony2 but Symfony2 fails me at a task I am able to accomplish in Doctrine2 (the 'standalone' version).
In Doctrine2:
I have 2 classes, Outcome, Ticket as such:
namespace Entity;
/** #Entity #Table(name="tickets") */
class Ticket {
/** #Id #Column(type="integer") #GeneratedValue(strategy="AUTO") */
private $id;
/** #Column(type="string", length=100) */
private $title;
/** #OneToMany(targetEntity="Outcome", mappedBy="ticket") */
private $outcomes;
/* with setters and getters for title and outcomes */
public function __toString() {
return $this->getTitle().' ('.$this->getId().')';
}
public function __construct () {
$this->outcomes=new \Doctrine\Common\Collections\ArrayCollection();
}
}
and
namespace Entity;
/** #Entity #Table(name="outcomes") */
class Outcome {
/** #Id #Column(type="integer") #GeneratedValue(strategy="AUTO") */
private $id;
/** #Column(type="string", length=100) */
private $text;
/** #ManyToOne(targetEntity="Ticket", inversedBy="outcomes") #JoinColumn(nullable=false) */
private $ticket;
public function __toString() {
return $this->getText().' ' .' ('.$this->getId().')';
}
}
in my index.php I use
$query = $em->createQuery ("SELECT t,o FROM Entity\Ticket t LEFT JOIN t.outcomes o WHERE t.id=1");
$query->useResultCache(true);
$tickets_2 = $query->execute();
$ticket_2 = $tickets_2[0];
$amount=count($ticket_2->getOutcomes());
echo $ticket_2." --ticket\n";
echo $amount." --outcomes\n";
and I get the correct output
now in Symfony2 I use the following definitions for the entities:
namespace test\DemoBundle\Entity;
/** #orm:Entity #orm:Table(name="tickets") */
class Ticket {
/** #orm:Id #orm:Column(type="integer")
#orm:GeneratedValue(strategy="AUTO") */
private $id;
/** #orm:Column(type="string", length=100) */
private $title;
/** #orm:OneToMany(targetEntity="Outcome", mappedBy="response") */
private $outcomes;
...
}
namespace test\DemoBundle\Entity;
/** #orm:Entity #orm:Table(name="outcomes") */
class Outcome {
/** #orm:Id #orm:Column(type="integer") #orm:GeneratedValue(strategy="AUTO") */
private $id;
/** #orm:Column(type="string", length=100) */
private $text;
/** #orm:ManyToOne(targetEntity="Ticket", inversedBy="outcomes") #orm:JoinColumn(nullable=false) */
private $ticket;
....
}
now, I've set up a controller with a test function:
namespace test\DemoBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use test\DemoBundle\Entity\Ticket as Ticket;
use test\DemoBundle\Entity\Outcome as Outcome;
class TicketController extends Controller {
...
public function testAction() {
$em = $this->get('doctrine.orm.entity_manager');
$query = $em->createQuery ("SELECT t, o
FROM test\DemoBundle\Entity\Ticket t
LEFT JOIN t.outcomes o
WHERE t.id=1");
$query->useResultCache(true);
$tickets_2 = $query->execute();
$ticket_2 = $tickets_2[0];
$amount=count($ticket_2->getOutcomes());
$prepared_result=$ticket_2." --ticket ".$amount." --outcomes ";
$response = new Response($prepared_result);
return $response;
}
}
at this point I get an error while trying to execute the last query like so:
SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'WHERE t0_.id = 1' at line 1
from the webdebugger I get the following syntax from PDO:
SELECT t0_.id AS id0,
t0_.title AS title1,
o1_.id AS id2,
o1_.text AS text3,
o1_.ticket_id AS ticket_id4
FROM tickets t0_
LEFT JOIN WHERE t0_.id = 1
so I guess I am missing something after the LEFT JOIN so that's why I am gettint the error in the first place?
What am I doing wrong?/What should I do?