Trying to use the following code to join my 2 tables:
$query = $em->createQuery('SELECT a, c FROM AppBundle:Auctions a JOIN a.catalog c');
$auctions = $query->getResult();
And getting the error:
[Semantical Error] line 0, col 53 near 'c': Error: Class AppBundle\Entity\Auctions has no association named catalog
My tables are defined like this:
Auctions.php:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Auctions
*
* #ORM\Table(name="auctions")
* #ORM\Entity
*/
class Auctions
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \Doctrine\Common\Collections\ArrayCollection
*
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Catalogs", mappedBy="auction")
*/
private $catalog;
}
And Catalogs.php:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Catalogs
*
* #ORM\Table(name="catalogs", indexes={#ORM\Index(name="catalogs_FI_1", columns={"treasurer_info_id"}), #ORM\Index(name="catalogs_FI_2", columns={"auction_id"}), #ORM\Index(name="unique_stripped_name", columns={"stripped_name"})})
* #ORM\Entity
*/
class Catalogs
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \AppBundle\Entity\Auctions
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Auctions", inversedBy="catalog")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="auction_id", referencedColumnName="id")
* })
*/
private $auction;
Not really sure what I'm doing wrong here, it seems like all of the doctrine examples for OneToMany work like this? Any help is appreciated.
UPDATE: updated the schema files and query being used.
You don't want your OneToMany association on your primary key. What you want is another variable in your Auctions entity that relates to Catalogs:
/**
* #var \Doctrine\Common\Collections\ArrayCollection
*
* #ORM\OneToMany(targetEntity="Catalogs", mappedBy="auction")
*/
private $catalogs;
Then update your Catalogs entity:
/**
* #var \AppBundle\Entity\Auctions
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Auctions", inversedBy="catalogs")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="auction_id", referencedColumnName="id")
* })
*/
private $auction;
Then your join would look like this:
$query = $em->createQuery('SELECT a, c FROM AppBundle:Auctions a JOIN a.catalogs c');
$auctions = $query->getResult();
What you want to remember when dealing with Doctrine is that you are thinking of your entities in terms of how they relate to each other, not how they would relate in a SQL query. You're defining the id mappings inside your entity so that when you write DQL you can join based on your association rather than the actual column names.
Related
I have a list of news ordered by the published date and I want to paginate it. I am using Doctrine 2.5 in a Zend Framework 2 project. Here's my entity:
<?php
namespace BuscadorJuridico\Entity;
use Doctrine\ORM\Mapping as ORM;
use Zend\Form\Annotation as Form;
/**
* Class News
* #package BuscadorJuridico\Entity
*
* #ORM\Table(name="news")
* #ORM\Entity
*/
class News
{
/**
* #var int
*
* #ORM\Column(name="news_id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="news_title", type="string", length=200, nullable=false)
*/
public $title;
/**
* #var string
*
* #ORM\Column(name="news_body", type="blob", nullable=false)
*
*/
public $body;
/**
* #var \DateTime
*
* #ORM\Column(name="news_date_published", type="date", nullable=true)
*
*/
protected $datePublished;
/**
* #var bool
*
* #ORM\Column(name="news_status", type="boolean", nullable=false)
*
*/
public $published;
}
I am using the Doctrine\DBAL\Driver\SQLSrv\Driver and the Paginator componenr of ZF2. Here is the pagination code:
$query = $this
->entityManager
->getRepository('BuscadorJuridico\Entity\News')
->createQueryBuilder('news')
->select()
->orderBy('news.datePublished', 'desc')
->andWhere('news.published = true');
$paginator = new Paginator(new DoctrineAdapter(new ORMPaginator($query)));
$paginator->setCurrentPageNumber($page);
$paginator->setItemCountPerPage($count);
But when I go to the list, I get the error:
SQLSTATE [42000, 8120]: [Microsoft][ODBC Driver 11 for SQL Server][SQL Server]Column 'dctrn_result.news_date_published_3' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
When I delete the ->orderBy('news.datePublished', 'desc') line I get the list, but naturally not ordered. This is part of the SQL that Doctrine is generating:
SELECT DISTINCT
news_id_0,
MIN(sclr_5) AS dctrn_minrownum,
ROW_NUMBER() OVER (ORDER BY news_date_published_3 DESC) AS doctrine_rownum
FROM (
SELECT n0_.news_id AS news_id_0,
n0_.news_title AS news_title_1,
n0_.news_body AS news_body_2,
n0_.news_date_published AS news_date_published_3,
n0_.news_status AS news_status_4,
ROW_NUMBER() OVER(ORDER BY n0_.news_date_published DESC) AS sclr_5
FROM news n0_
WHERE n0_.news_status = 1)
dctrn_result
GROUP BY news_id_0
With MySQL the code works. Any help appreciated.
I actually have a PR open to fix this. I'll see if I can poke that along.
https://github.com/doctrine/dbal/pull/818
I used the PR from wschalle and created my own Mssql Driver included the fix, maybe it help you.
https://github.com/kokspflanze/GameBackend/tree/master/src/GameBackend/DBAL
Let's say I have a simple entity EstablishmentEntity, with a ManyToOne relationship on $employee that looks like this :
namespace Msm\CeopBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Msm\CeopBundle\Entity\Establishment;
/**
* Establishment
*
* #ORM\Table(name="establishment")
* #ORM\Entity()
*/
class School
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
*
* #ORM\ManyToOne(targetEntity="Professionnal", cascade={"persist"})
* #ORM\JoinColumn(name="teacher_id", referencedColumnName="id", nullable=true)
*/
private $employee;
My problem : that entity is used for different kinds of establishments and has a lot more useful attributes in my code (I kept it simple for my question). One kind of establishement won't have any employees. Is it possible to tell Doctrine/symfony NOT to cascade persist the employee in that perticular case after class instanciation for example ?
So far it automatically persists an empty employee entity when creating an establishment, which is ok for most establishments but not all...
hi i'm trying to link a class test with 2 entities, the Administrator that post the test and the competence (subject of the test ), but whatever i do i only get 1 index FK on my database after schema update
namespace Admin\AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Test
*
* #ORM\Table(name="test")
* #ORM\Entity
*/
class Test
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="type", type="string", length=50, nullable=false)
*/
private $type;
/**
* #var integer
* #ORM\ManyToOne(targetEntity="ProjetCompetenceListe")
* #ORM\JoinColumn(name="id_competence", referencedColumnName="id")
*/
private $idCompetence;
/**
* #var \Administrateur
*
* #ORM\ManyToOne(targetEntity="Administrateur")
* #ORM\JoinColumn(name="id_administrateur", referencedColumnName="id")
*/
private $idAdministrateur;
please can any one tell me why ?
Have you tried creating indexes with #index annotation, maybe you should give it a try. Ref
add index with #index annotation and then run schema update command
You may need to clear doctrine meta data :
php app/console doctrine:cache:clear-metadata
Otherwise do a :
php app/console doctrine:schema:validate
to check if the relations are correct.
I have this Entity in Symfony2 :
<?php
namespace Project\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Users
*
* #ORM\Table(name="users")
* #ORM\Entity
*/
class Users
{
/**
* #var integer
*
* #ORM\Column(name="user_id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $userId;
/**
* #var integer
*
* #ORM\Column(name="test", type="integer", nullable=false)
*/
private $test;
}
I add the following line between {{userId}} and {{test}} :
/**
* #var integer
*
* #ORM\Column(name="superbanana", type="integer", nullable=false)
*/
private $superbanana;
Then I execute in console :
php app/console doctrine:schema:update --dump-sql
It give me the response :
ALTER TABLE users ADD superbanana INT NOT NULL
**How can I do to have instead ? **
ALTER TABLE users ADD superbanana INT NOT NULL AFTER user_id
If you don't want to drop/create the table, you can use #columnDefinition attribute and define the column definition yourself.
/**
* #var integer
*
* #ORM\Column(type="integer", columnDefinition="INT NOT NULL AFTER `user_id`")
*/
private $superbanana;
I don't think this is possible because using Doctrine means that you don't care about how the Table is managed anymore (apparently someone tried it before).
And since you never use MySQL directly, I think there is no utility to specify column orders for Doctrine.
But you can always delete your table so Doctrine will completely rebuild the table, respecting your order.
How can I select all items from one specific author ? Its possible this way ? Or how can I edit entities if I want many item types and item packages (item has many items) too ?
Item
/**
* #ORM\Table()
* #ORM\Entity
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="discr", type="string")
* #ORM\DiscriminatorMap({
* "cd" = "ItemCD",
* "dvd" = "ItemDVD",
* "pack" = "ItemPack",
* })
*/
class Item
{
/**
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #ORM\Column(name="name", type="string", length=250, nullable=false)
*/
private $name;
}
ItemCD
/**
* #ORM\Table()
* #ORM\Entity
*/
class ItemCD extends Item
{
/**
* #ORM\ManyToOne(targetEntity="Author", inversedBy="item")
* #ORM\JoinColumn(name="author_id", referencedColumnName="id")
*/
private $author;
}
ItemDVD
/**
* #ORM\Table()
* #ORM\Entity
*/
class ItemDVD extends Item
{
/**
* #ORM\ManyToOne(targetEntity="Author", inversedBy="item")
* #ORM\JoinColumn(name="author_id", referencedColumnName="id")
*/
private $author;
}
ItemPack
/**
* #ORM\Table()
* #ORM\Entity
*/
class ItemPack extends Item
{
/**
* #ORM\ManyToMany(targetEntity="Item", inversedBy="item")
* #ORM\JoinTable()
*/
private $items;
}
Author
/**
* #ORM\Table()
* #ORM\Entity
*/
class Author
{
/**
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*
*/
private $id;
/**
* #ORM\Column(name="name", type="string", length=250, nullable=false)
*/
private $name;
}
You will have to query for specific elements. This is a known (and wanted) limitation, since DQL is a static typed language: see http://www.doctrine-project.org/jira/browse/DDC-16
Related: how to access fields in inherited table in doctrine2 / dql query
A way of handling this with a workaround is using 2 subqueries in your DQL:
SELECT
i
FROM
Item i
WHERE
i.id IN(
SELECT
i2.id
FROM
ItemDvd i2
WHERE
i2.author = :author
)
OR
i.id IN(
SELECT
i3.id
FROM
ItemCd i3
WHERE
i3.author = :author
)
As you can see you have to extract the identifiers for each possible subtype manually.
Edit: to get all the packs from a given author (along with single DVDs or CDs), the query becomes even worse:
SELECT
i
FROM
Item i
WHERE
i.id IN(
SELECT
i2.id
FROM
ItemDvd i2
WHERE
i2.author = :author
)
OR
i.id IN(
SELECT
i3.id
FROM
ItemCd i3
WHERE
i3.author = :author
)
OR
i.id IN(
SELECT
i4.id
FROM
ItemPack i4
JOIN
i4.items i5
WHERE
i5.id IN (
SELECT
i6.id
FROM
Item i6
WHERE
i6.id IN(
SELECT
i7.id
FROM
ItemDvd i7
WHERE
i7.author = :author
)
OR
i6.id IN(
SELECT
i8.id
FROM
ItemCd i8
WHERE
i8.author = :author
)
)
)
Make $author in Item and have ItemPacks $author value always be null. Then you can do:
$em->findBy("Item", array("author" => $author));
And you always get instances of ItemDVD or ItemCD.
It's tricky and lengthy the answer.
I think the Entities aproach is ok, and by querying the item entity you would get what you want.
Now for forms you'll probably need one FormType per sub-item and then user the aproach for Form Collections (http://symfony.com/doc/2.1/cookbook/form/form_collections.html) and I'm certain that you will need to hook into the pre-bind event to prepare the data.
This is a quick thought, may be it can help you.