join in Doctrine Entity - symfony

I made 2 table product and category in Symfony.
I made entity and repository for them.
In product, I have: id,name,category,discount,price
In category, I have: id, name, discount
I have 2 question here, please help me to solve my problem :
in my product table my category is int, I use category id from category table, I do not know how use join in Symfony , I do many way but every time I failed. Please help me to make join for this tables.
I try to read product by Repository but I received category as a number of category table, how can I receive category name instead of category id?
Please help me to solve them.
Entity/Product.php
class Product implements \JsonSerializable
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\Column(type="integer", length=10)
*/
private $category;
Entity/Category.php
class Category implements \JsonSerializable
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;

Related

Symfony Doctrine Query Builder. How to select value of many-to-one field without join?

I have two entities:
class Products
{
/**
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
*/
private $id;
/**
* #ORM\Column(name="name", type="string", length=255, nullable=true)
*/
private $name;
/**
* #var CatalogSections|null $catalogSection
* #ORM\ManyToOne(targetEntity="App\Entity\CatalogSections")
* #ORM\JoinColumn(name="catalog_section", referencedColumnName="id")
*/
private $catalogSection;
...
}
And catalog sections
class CatalogSections
{
/**
* #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=true)
*/
private $name;
...
}
I want to get catalog_section ID of Product via Doctrine Query Builder. Is there a way to get it without join? I.E. i want to get a catalog_section column value which store in products table
$qb = $this->createQueryBuilder('products');
$qb->select('products.id', 'products.name',
'products.catalogSection' // catalog_section
);
The only valid reason to select the IDs without a Join is to gain performance.
And when performance is concerned it is best to use the DBAL Connection directly instead of the query builder.
$this->entityManager->getConnection()->executeQuery('SELECT id, name, catalog_section FROM products')

Doctrine attributes for 3 linked entities

I want to make a website that list albums, each albums have tracks, and in order to handle various artists albums, each track is made by an artist.
I'm new with symfony and I'm fighting with doctrine to find the best way to handle those 3 entities (album, track and artist).
Album (id, title, release_date, cover, etc...)
Track (id, id_album, id_artist, title, position, duration)
Artist (id, name, country, photo,...)
I was thinking about doing one-to-many/many-to-one relations or many-to-many with attributes, but I'm really not sure anymore what's the best way to link those 3 entities in doctrine.
At the end, I would like to make a homepage that looks like that :
[album_cover]
"album title"
artist
[album_cover]
"album title"
artist1, artist2
So from the album entity I need to get all the artists from all the album's tracks, I don't know yet how to do that.
So far I've done that, but I'm really not sure if it's the best way (with AlbumArtist = Track):
/**
* Album
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="AppBundle\Entity\AlbumRepository")
*/
class Album
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="AlbumArtist", mappedBy="album", cascade={"persist"})
*/
private $albumArtists;
/**
* #var string
*
* #ORM\Column(name="title", type="text")
*/
private $title;
/**
* Artist
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="AppBundle\Entity\ArtistRepository")
*/
class Artist
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="AlbumArtist", mappedBy="artist", cascade={"persist"})
*/
private $albumArtists;
/**
* #var string
*
* #ORM\Column(name="name", type="text")
*/
private $name;
/**
* AlbumArtist
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="AppBundle\Entity\AlbumArtistRepository")
*/
class AlbumArtist
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Album", inversedBy="albumArtists")
* #ORM\JoinColumn(name="album_id", referencedColumnName="id")
*/
private $album;
/**
* #ORM\ManyToOne(targetEntity="Artist", inversedBy="albumArtists")
* #ORM\JoinColumn(name="artist_id", referencedColumnName="id")
*/
private $artist;
/**
* #var string
*
* #ORM\Column(name="title", type="text")
*/
private $title;
Thanks for your help !
Your sequence is a bit strange in my opinion.
Lets make a few statements in human language:
An Album exist out of multiple tracks.
One Track exist out of one Song.
One Song can be composed by one or more Artists.
I use the entity Song because a song can be placed in multiple Albums and on a different track-number.
Gives us the those relations:
Album - oneToMany - Tracks (an Album have multiple tracks)
Tracks - ManyToOne - Song (a Song can be placed on multiple tracks)
Song - oneToMany - Artists (some songs are made by multiple artists)

symfony 2 Doctrine leftJoin NOT IN

How can I solve a query where I want to get all data from the left table which isn't existing in the right table?
left table: ID | NAME | DATE
right table: ID | ID_left_table | NAME | DATE
It is confusing me a bit since I haven't got that experience with doctrine.
My entitys look like:
class NameData
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, nullable=false)
*/
private $name;
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="datetime")
*/
private $date;
.
.
.
and
class ValueData
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="TestBundle\Entity\NameData")
*/
private $nameid;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, nullable=false)
*/
private $name;
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="datetime")
*/
private $date;
.
.
.
1) First of all, fix $nameid property annotation in ValueData entity to this:
/**
* #ORM\ManyToOne(targetEntity="NameData")
* #ORM\JoinColumn(name="name_id", referencedColumnName="id")
**/
private $nameData;
This is basic Doctrine annotation mapping for relationships, in this case ManyToOne.
2) Modify valueData table to add foreign key, by typing php app/console doctrine:schema:update --force or with migrations
3) let Symfony generate the right setters and getters for ValueData entity, by running command in your console php app/console doctrine:generate:entities TestBundle:ValueData.
4) And then, if you need to get data in controller:
$valueData = $this->getDoctrine()->getRepository('TestBundle:ValueData')->find(1);//Find by ID 1 OR ->findAll() to get all records
$nameData = $valueData->getNameData(); //This line of code behind the scenes will
//join the valueData table with nameData, and get associated data
Add a comment if you will need help along the way.
Edit:
Below is the query builder to select all NameDatas which dont have any ValueData:
$nameDataRepo = $this->getDoctrine()->getRepository('TestBundle:NameData');
$nameDatasWithoutDatavalues = $nameDataRepo->createQueryBuilder('nameData')
->leftJoin('nameData.dataValues', 'dataValue')
->where('dataValue.id IS NULL')
->getQuery()
->getResult
Also, make sure to write bi-directional part of doctrine relationships, to be able to access children from parent entity NameData.php:
/**
* #var \Doctrine\Common\Collections\ArrayCollection
*
* #ORM\OneToMany(targetEntity="ValueData", mappedBy="nameData")
*/
private $valueDatas;
/**
* Constructor
*/
public function __construct()
{
$this->valueDatas = new \Doctrine\Common\Collections\ArrayCollection();
}
And in ValueData.php, edit $nameData property annotation to this:
/**
* #ORM\ManyToOne(targetEntity="NameData", inversedBy="valueDatas")
* #ORM\JoinColumn(name="name_id", referencedColumnName="id")
**/
private $nameData;

Doctrine query crashing

Very very weird. I have used this method from doctrine hundreds of times. I have a simple controller that takes an id as parameter. The query that Doctrine generates is wrong and crash.
/**
* #Security("has_role('ROLE_ADMIN')")
* #return Response
*/
public function editSellerAction($id)
{
$em = $this->getDoctrine()->getManager();
$seller = $em->getRepository('SiteUserBundle:Seller')->find($id);
// ...
$form = $this->createForm(new SellerType(), $seller, array(
'method' => 'POST'
));
// ...
}
The query generated is the following
[2/2] DBALException: An exception occurred while executing 'SELECT t1.id AS id2, t1.username AS username3, t1.password AS password4, t1.firstname AS firstname5, t1.lastname AS lastname6 FROM seller t1 WHERE t0.id = ? LIMIT 1' with params ["2"]:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 't0.id' in 'where clause' +
The error thrown makes sense because it's looking at "WHERE t0.id" when it should be looking at "WHERE t1.id". I tried the query with t1 using phpmyadmin and it works.
Any idea what might cause this issue?
/**
* Seller have access to their customer and are able to RW access to the customers
*
* #ORM\Table("seller")
* #ORM\Entity
* #author Michael Villeneuve
*/
class Seller extends User
{
/**
* #var array
*
* #ORM\OneToMany(targetEntity="Customer", mappedBy="seller", cascade={"persist", "remove"})
* #ORM\JoinColumn(name="seller_id", referencedColumnName="id")
**/
protected $customers;
/**
* #var string
*
* #ORM\Column(name="firstname", type="string", length=255, nullable=false)
*/
protected $firstname;
/**
* #var string
*
* #ORM\Column(name="lastname", type="string", length=255, nullable=false)
*/
protected $lastname;
// Other attributes and only getters/setter
/**
*
* #ORM\Entity
*/
class User implements UserInterface
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=255, unique=true)
*/
private $username;
/**
* #ORM\Column(type="string", length=64)
*/
private $password;
I have 3 entities that extends the User (customer, admin and seller).
Updated link: https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/inheritance-mapping.html
Read up a bit on mapped super classes: http://docs.doctrine-project.org/en/latest/reference/inheritance-mapping.html. Basically, your abstract base user class cannot itself be an entity.
So take the #ORM\Entity line out of your User class. That is where the table 0 (t0) is coming from.
You have 2 options:
The first one is to create an abstract User entity and inherit all values from it. This is useful if you have many entities with the same behaviour. I e.g. like to create a BaseEntity with a ID field and some basic methods. All entities can extend this one and automatically have an ID. Cerad explained in his answer how this is done.
The second option are so called discriminator fields. Basically they allow you to have one User table and sub-tables for every extended entity. You can read about them in the official docs.
Which one you end up using is probably case dependent.
Try to add id field to the Seller entity instead of User
/**
* Seller have access to their customer and are able to RW access to the customers
*
* #ORM\Table("seller")
* #ORM\Entity
*/
class Seller extends User
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var array
*
* #ORM\OneToMany(targetEntity="Customer", mappedBy="seller", cascade={"persist", "remove"})
* #ORM\JoinColumn(name="seller_id", referencedColumnName="id")
**/
protected $customers;
/**
* #var string
*
* #ORM\Column(name="firstname", type="string", length=255, nullable=false)
*/
protected $firstname;
/**
* #var string
*
* #ORM\Column(name="lastname", type="string", length=255, nullable=false)
*/
protected $lastname;
// Other attributes and only getters/setter
/**
*
* #ORM\Entity
* #author Michael Villeneuve<michael#panierdachat.com>
*/
class User implements UserInterface
{
/**
* #ORM\Column(type="string", length=255, unique=true)
*/
private $username;
/**
* #ORM\Column(type="string", length=64)
*/
private $password;

Class for manage users groups

I'm creating a class to manage user groups. A group can contain either users or groups of users
I wonder if there is already a symfony class to implement that handle such relationships.
The best way that occurred to me is something like this:
class Group
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* #ORM\ManyToOne(targetEntity="User")
* #ORM\JoinColumn(name="coordinator_id", referencedColumnName="id", nullable=false)
*/
private $coordinator;
/**
* #ORM\ManyToOne(targetEntity="Group")
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=true)
*/
private $parent;
/**
* #ORM\ManyToMany(targetEntity="User", inversedBy="users")
* #ORM\JoinTable(name="groups_users")
*/
private $users;
}
There is currently ( as of 2013-06-16 ) no bundle available which directly provides this special implementation.
FOSUserBundle introduces groups for roles ... but the implementation is pretty basic and needs manual hands-on to get it working fully. Maybe it's something you could look into for inspiration though.
Otherwise your approach looks okay to me for this special use-case.
You might be able to improve by using nested sets with Gedmo's Tree doctrine extension to handle the group-nesting.

Resources