Doctrine and composite unique keys - php

I want to do composite unique key in doctrine.
Those are my fields:
/**
* #var string $videoDimension
*
* #Column(name="video_dimension", type="string", nullable=false)
*/
private $videoDimension;
/**
* #var string $videoBitrate
*
* #Column(name="video_bitrate", type="string", nullable=false)
*/
private $videoBitrate;
How can I show doctrine, that those combined together are composite unique key?

Answer the question:
use Doctrine\ORM\Mapping\UniqueConstraint;
/**
* Common\Model\Entity\VideoSettings
*
* #Table(name="video_settings",
* uniqueConstraints={
* #UniqueConstraint(name="video_unique",
* columns={"video_dimension", "video_bitrate"})
* }
* )
* #Entity
*/
See #UniqueConstraint

In case someone want use PHP 8 Attributes instead of Doctrine annotations:
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
#[ORM\UniqueConstraint(
name: 'video_unique_idx',
columns: ['video_dimension', 'video_bitrate']
)]

I find it more verbose to use only ORM and then prefix ORM in annotations. Also note that you can break annotation to several lines to make it more readable especially if you have several items to mention (index in the example below).
use Doctrine\ORM\Mapping as ORM;
/**
* VideoSettings
*
* #ORM\Cache(usage="NONSTRICT_READ_WRITE")
* #ORM\Entity(repositoryClass="AppBundle\Repository\VideoSettingsRepository")
* #ORM\Table(name="emails", uniqueConstraints={
* #ORM\UniqueConstraint(name="dimension_bitrate", columns={"video_dimension", "video_bitrate"})
* }, indexes={
* #ORM\Index(name="name", columns={"name"})
* })
*/
class VideoSettings

I know this is an old question, but I came across it while looking for a way to create composite PK and thought it could use some update.
Things are actually much simpler if what you need is a Composite Primary Key. (Which, of course, guarantees uniqueness) Doctrine documentation contains some nice examples by this url: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/composite-primary-keys.html
So the original example could look something like this:
/**
* #var string $videoDimension
*
* #ORM\Id #ORM\Column(type="string")
*/
private $videoDimension;
/**
* #var string $videoBitrate
*
* #ORM\Id #ORM\Column(type="string")
*/
private $videoBitrate;
A few notes here:
Column "name" is omitted since Doctrine is able to guess it based on
the property name
Since videoDimension and videoBitrate are both parts of the PK - there is no need to specify nullable = false
If required - the Composite PK may be composed of foreign keys, so feel free to add some relational mappings

XML version :
<unique-constraints>
<unique-constraint columns="column1,column2" name="give_some_explicit_name" />
</unique-constraints>
More details in the docs :
https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/xml-mapping.html#defining-indexes-or-unique-constraints

Related

Error on doctrine:schema:update, table with name already exists

in my entity Class i have added some new attributes and i want to update schema of my database. So when i run php app/console doctrine:schema:update --force im getting this error every time
[Doctrine\DBAL\Schema\SchemaException]
The table with name 'postgres.day' already exists.
and my database won't update. So does anyone know how to solve this problem?
I have read the other question with that problem but it didn't helped me. So can anyone explain me whats going on, or how to solve this? Thanks.
<?php
namespace DashboardBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* RadniNalog
*
* #ORM\Table()
* #ORM\Entity
*/
class RadniNalog
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="time_from", type="string", length=255)
*/
private $time_from;
/**
* #var string
*
* #ORM\Column(name="time_to", type="string", length=255)
*/
private $time_to;
/**
* #var string
*
* #ORM\Column(name="full_date_time", type="string", length=255)
*/
private $full_date_time;
}
it can cause this error message if you want to use a table name which is already used by one of the installed bundles.
In your case the conflicting table name is: day.
#ORM\Table no name provided within your annotation, which is required. See: Doctrine ORM | 21. Annotations Reference
#ORM\Table(name="radniNalog")
Either don't add the annotation if you want the default behaviour of Doctrine or add it correctly with the required options.

Symfony 3.1 Doctrine Entity Cannot alter table add foreign key

Symfony 3.1 - Doctrine: I am trying to add relationships between entities and thereby want to add foreign key to link tables in my example. I tried several settings, utilizing various combination of arguments, but none of them were able to ALTER the tables with Foreign key.
My entities are namely "Trips" and "Airlines" with the content attached below. I would like to use airline_id as a foreign key inside trips table. Here are the extract from my entity scripts:
Entity#1: Trips
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="trips")
*/
class Trips
{
/**
* #ORM\Column(name="trip_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $trip_id;
/**
* #ORM\Column(name="airline_id", type="integer")
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Airlines")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="airline_id", referencedColumnName="airline_id")
* })
*/
private $airline_id;
}
Entity#2: Airlines
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="airlines")
*/
class Airlines
{
/**
* #ORM\Column(name="airline_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $airline_id;
/**
* #ORM\Column(type="string", length=80)
*/
private $name;
}
I tried updating the schema from console , but all it says is
Nothing to update - your database is already in sync with the current entity metadata.
Also tried deleting the table and re-creating them with console; the result did not change much and the foreign key is still missing. :/
Any thoughts, or perhaps you know the solution?
I have a few suggestions, as I've gone through the same process of design.
First off, should I presume that "a trip has one airline" and also "an airline has many trips"? This seems logical, and since you show #ORM\ManyToOne annotation for the Trips class, that would equate to my same presumptions.
If this is so, then these are the suggestions:
First, don't call the classes "Trips" and "Airlines", but rather in the singular, that is "Trip" and "Airline". This is object oriented thinking. So when you create a "Trip" Doctrine object, it represents a trip for example. This also makes you code more readable and also supportable from a future standpoint; as because when people read your code, they will understand that a single instance of a Trip (from the Trip class), represents a trip (that a person takes). This is important when designing code.
Given the above, make these code changes.
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="trip")
*/
class Trip
{
/**
* #ORM\Id
* #ORM\Column(name="trip_id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $trip_id;
/**
* #ORM\ManyToOne(targetEntity="Airline", inversedBy="trips")
* #ORM\JoinColumn(name="trip_airline_id", referencedColumnName="airline_id")
*/
private $airline;
...
/**
* Set airline - Adds this trip to the passed in airline and sets the airline
* property to resultant airline.
*
* #param \AppBundle\Entity\Airline
*/
public function setAirline($airline){
$airline->addTrip($this);
$this->airline = $airline;
}
}
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="airlines")
*/
class Airlines
{
/**
* #ORM\Column(name="airline_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $airline_id;
/**
* #ORM\Column(type="string", length=80)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="Trip", mappedBy="airline")
*/
protected $trips = null;
...
public function __construct(){
// This is an array of trips.
$this->trips = new ArrayCollection();
}
...
/**
* Add trip - Adds a course to the array.
*/
public function addTrip($trip){
$this->trips[] = $trip;
}
}
Now you can get trips from the airline, and also you can do the other way around, get the airline from the trip.
When #ORM\Column is specified along with #ORM\JoinColumn on same column, then JoinColumn's association gets ignored and Foreign Key isn't created on table. so dont use both #ORM\Column and #ORM\JoinColumn in same column. There is no need to specify data type with #ORM\Column for associations, doctrine handles it and assign data type based on database engine. For MySql, it is int.

symfony2 - ManyToMany with duplicate rows

I currently have to Entities in my application:
Page
Block
A Page can have many Blocks, which are shared across many Pages, so it is quite obvious that the relation is a ManyToMany. However, I need to be able to add the same Block twice (or more) to the same Page. Doctrine creates the "page_block" join table automatically, but with page_id and block_id both as Primary Keys, therefore adding a duplicate throws an error.
Is it possible, without adding an additional Entity, to tell doctrine to allow duplicates on the Page--Block relation ?
Well, I'm not sure about that behavior in doctrine, but if that is the case, then you can do something that I almost always do. Represent the ManyToMany relation as two OneToMany-ManyToOne. You must create your own PageBlock entity and configure it's foreign keys.
class Page{
/**
* #var array
*
* #ORM\OneToMany(targetEntity="PageBlock", mappedBy="page", cascade={"all"})
*/
private $pageBlocks;
}
class Block{
/**
* #var array
*
* #ORM\OneToMany(targetEntity="PageBlock", mappedBy="block", cascade={"all"})
*/
private $pageBlocks;
}
class PageBlock{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var \stdClass
*
* #ORM\ManyToOne(targetEntity="Page", inversedBy="pageBlocks")
* #ORM\JoinColumn(name="id_page", referencedColumnName="id")
*/
private $page;
/**
* #var \stdClass
*
* #ORM\ManyToOne(targetEntity="Block", inversedBy="pageBlocks")
* #ORM\JoinColumn(name="id_block", referencedColumnName="id")
*/
private $block;
}
As you can see the primary key remains as ID, so problem resolved. I say almost always do because this is how I do it if I need an extra attribute in the relation(almost always it happens). I suspect that could be a way of do it with the ManyToMany annotation, but there is no difference with this approach.
Hope this help you.

nullable self reference with composite keys using doctrine

I've got a problem with Doctrine ORM 2.5. The issue happens when I try to load instances of an entity that has an optional ManyToOne reference to itself using part of its primary key as a part of the foreign key.
The entity looks like the following:
class Category
{
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Client")
* #ORM\JoinColumn(name="client_id", referencedColumnName="client_id"))
* #var Client
*/
protected $client;
/**
* #ORM\Id
* #ORM\Column(type="string")
* #var string
*/
protected $category_id;
/**
* #ORM\ManyToOne(targetEntity="Category")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="parent_id", referencedColumnName="category_id", nullable=true),
* #ORM\JoinColumn(name="client_id", referencedColumnName="client_id", nullable=false)
* })
* #var Category
*/
protected $parent;
/**
* #ORM\Column(type="translated")
* #SAP\Mapping(fieldName="name")
* #var string
*/
protected $name;
}
As you can see, every child of a category must have the same client_id as its parent.
The problem seems to be that Doctrine expects the parent to exists of the foreigen key isnt empfy ( = at least one field has a non-null value). Thats obviously always true since the FK contains a part of the PK.
Is there a way to tell Doctrine that it shouldn't try to load the parent if parent_id is null?
The only alternatives I see are changing the schema by either using an artifical auto generated pk or adding a new column for the parent_client_id and make sure it equals the client_id in PHP-land.
Is there a better way?
It seems like this is a bug as reported here: https://github.com/doctrine/orm/issues/4384
From the comments on that issue setting fetch="EAGER" resolves the doctrine Missing value for primary key error. Not really a great solution, but it may work for you depending on your use case.

Zend Framework - Doctrine2: ManytoOne Mapping

OK, if anyone can help me with this that would be great, because it appears to be intractable.
I have 2 entities set up in a new zf-boilerplate project as below. I am trying to follow the tutorial on Zendcasts.com - One-to-Many with Doctrine 2, but can't get doctrine to recognise the associations I have mapped. If I run orm:schema-tool:create --dump-sql, it dumps the generated Sql, but NOT the ALTER TABLE statements at the end which should would create the Foreign Key Mapping, I can't get that to work properly.
I've tried everything I can think of, the JOIN statement I need to run obviously doesn't work either, but I figure if I can just get Doctrine to recognise the ALTER statement I can carry it from there.
Any ideas would be great, let me know if you need more info. I thought at first maybe the .ini file might be set up wrong, but I think this is more something to do with the relationship annotation?
Library/Photo/Entity/Gallery.php
<?php
namespace Photo\Entity;
/**
* #Entity(repositoryClass="Photo\Entity\Repository\MyGallery")
* #Table(name="gallery")
*/
class Gallery {
/**
* #Id #GeneratedValue
* #Column(type="smallint",nullable=false)
* #var integer
* #OneToMany(targetEntity="Photo", mappedBy="galleryID")
*/
protected $id;
/**
* #Column(type="string", length=200)
* #var string
*/
protected $gallery;
Library/Photo/Entity/Photo.php
<?php
namespace Photo\Entity;
/**
* #Entity(repositoryClass="Photo\Entity\Repository\MyPhoto")
* #Table(name="photo")
*/
class Photo {
/**
* #Id #GeneratedValue
* #Column(type="smallint",nullable=false)
* #var integer
*/
protected $id;
/**
* #Column(type="smallint",nullable=false)
* #var integer
* #ManyToOne(targetEntity="Gallery")
* #JoinColumns({
* #JoinColumn(name="gallery_id", referencedColumnName="id")
* })
*/
protected $galleryID;
Hmm... I see.. Check you column names, gallery_id vs galleryID looks suspicious.
If it is gallery_id, then you have to change the $galleryID annotation to #Column(type="smallint", nullable=false, name="gallery_id")
Generally, everywhere in the object model you should use the object field names, for example mappedBy="galleryID", but the column itself should be mapped with the appropriate DB name, like I mentioned #Column(name="gallery_id"), or for example #JoinColumns({#JoinColumn(name="gallery_id" referencedColumnName="id")})

Categories