Doctrine OneToOne relationship - unique constraints - doctrine-orm

I have this scenario:
The entity MealListDay is one day, which has six meals (entity Meal). Now I use OneToOne relationship. But there is problem, because more days can not have same entity Meal - error: unique constraints. I know, that the entity Meal must be unique in OneToOne relationship, but is there any solution with using only these two tables?
Any idea? Thanks.
Entity MealListDay
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
* #var integer
*/
protected $id;
/**
* #ORM\OneToOne(targetEntity="Meal", cascade={"persist"})
* #ORM\JoinColumn(onDelete="SET NULL")
* #var Meal
*/
protected $elevenses;
/**
* #ORM\OneToOne(targetEntity="Meal", cascade={"persist"})
* #ORM\JoinColumn(onDelete="SET NULL")
* #var Meal
*/
protected $soup;
/**
* #ORM\OneToOne(targetEntity="Meal", cascade={"persist"})
* #ORM\JoinColumn(onDelete="SET NULL")
* #var Meal
*/
protected $mainMeal;
/**
* #ORM\OneToOne(targetEntity="Meal", cascade={"persist"})
* #ORM\JoinColumn(onDelete="SET NULL")
* #var Meal
*/
protected $sideDish;
/**
* #ORM\OneToOne(targetEntity="Meal", cascade={"persist"})
* #ORM\JoinColumn(onDelete="SET NULL")
* #var Meal
*/
protected $drink;
/**
* #ORM\OneToOne(targetEntity="Meal", cascade={"persist"})
* #ORM\JoinColumn(onDelete="SET NULL")
* #var Meal
*/
protected $nosh;
Entity meal:
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
* #var integer
*/
private $id;
/**
* #ORM\Column(type="string", length=150)
*/
protected $name;
/**
* #ORM\Column(type="string", length=20)
*/
protected $type;
/**
* #ORM\Column(type="simple_array", nullable=true)
*/
protected $allergens;

Your mmodel has a first normal form problem.
Try the following:
MealListDay
- id
- dateOffered
MealOffering
- MealListDayID (FK to MealListDay)
- MealId (FK to Meal)
Meal
- id
- name
BTW including allergens in meal will probably result in a 1NF problem also

I must implement relation M:N, there is result.
I know, that property allergens is not in 1NF, but these are only numbers, which links to specific allergen name and description which are defined statically in class.

Related

Doctrine 2 Composite Primary Keys as Foreign Key

I have ClassA mapped to the entity ClassB with ManyToOne relation (simple so far).
class ClassA{
/**
* #var string
* #ORM\Id
* #ORM\Column(name="keyA", type="string", length=255)
*/
private $keyA;
/**
* #var ClassB $classB
* #ORM\ManyToOne(targetEntity="ClassB", inversedBy="classAs")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="ClassB_keyB", referencedColumnName="keyB")
* })
*/
private $classB;
}
And this is the ClassB:
class ClassB{
/**
* #var string
* #ORM\Id
* #ORM\Column(name="keyB", type="string", length=255)
*/
private $keyB;
/**
*
* #var ClassC $classC
* #ORM\Id
* #ORM\ManyToOne(targetEntity="ClassC", inversedBy="classBs")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="ClassC_keyC", referencedColumnName="keyC")
* })
*/
private $classC;
/**
* #var ArrayCollection $classAs
* #ORM\OneToMany(targetEntity="ClassA", mappedBy="classB")
*/
private $classAs;
}
As you can notice the ClassB contains a composite primary key (2 entities and on column).
And this is the ClassC:
class ClassC{
/**
* #var string
* #ORM\Id
* #ORM\Column(name="keyC", type="string", length=255)
*/
private $keyC;
/**
* #var ArrayCollection $classBs
* #ORM\OneToMany(targetEntity="ClassB", mappedBy="classC")
*/
private $classBs;
}
Whene i try to display all entities of ClassA (using findAll()) I get this exception Missing value for primary key classC on ERP\................\ClassB
What am I missing here ?!
you have to create your own method at ClassARepository and add your join columns

Doctrine 2 ManyToOne multipy JoinColumns

I have next entities:
/**
* Customer
*
* #ORM\Table(name="customer")
* #ORM\Entity()
*/
class Customer
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var Account[]|ArrayCollection
*
* #ORM\OneToMany(targetEntity="Account", mappedBy="customer")
*/
private $accounts;
/**
* #var Account
*
* #ORM\OneToOne(targetEntity="Account")
*
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="id",referencedColumnName="customer_id"),
* #ORM\JoinColumn(name="default_account_id", referencedColumnName="id")
* })
*/
private $defaultAccount;
}
/**
* Account
*
* #ORM\Table(name="account")
*/
class Account
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var Customer
*
* #ORM\ManyToOne(targetEntity="Customer", inversedBy="accounts")
* #ORM\JoinColumn(nullable=false)
*/
private $customer;
}
Idea that customer has multiply accounts, onr of the account should be default for customer. I would like to use foreign key with multiply fields to ensure that default customer account belongs to this customer, but I'm getting next error:
Column name `customer_id` referenced for relation from MyBundle\Entity\Customer towards MyBundle\Entity\Account does not exist.
Actually there is no "customer_id" field on ORM level, because it is "customer.id" but I dont know how to reference it.
It seems that line #ORM\JoinColumn(name="id",referencedColumnName="customer_id"), is redundant. Try to configure this field as following:
/**
* #var Account
*
* #ORM\OneToOne(targetEntity="Account")
*
* #ORM\JoinColumn(name="default_account_id", referencedColumnName="id")
*/
private $defaultAccount;
By the way, I think it would be better if you just added a boolean column is_default into your Account entity.

Create an entity that will be used by many other entities (Doctrine 2)

How can I do this?
My Entities:
Product entity
/**
* #ORM\Entity
* #ORM\Table(name="products")
*/
class Product
{
/**
* #ORM\Id()
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
* #var int
*/
private $id;
/**
* #ORM\Column(type="string", length=512)
* #var string
*/
private $name;
/**
* (??)
* #var ArrayCollection
*/
private $images;
}
Article entity
/**
* #ORM\Entity
* #ORM\Table(name="articles")
*/
class Article
{
/**
* #ORM\Id()
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
* #var int
*/
private $id;
/**
* #ORM\Column(type="string", length=512)
* #var string
*/
private $title;
/**
* (??)
* #var ArrayCollection
*/
private $images;
}
Image entity
/**
* #ORM\Entity
* #ORM\Table(name="images")
*/
class Image
{
/**
* #ORM\Id()
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
* #var int
*/
private $id;
/**
* #ORM\Column(type="string", length=512)
* #var string
*/
private $name;
/**
* #ORM\Column(type="string", length=1024)
* #var string
*/
private $path;
}
I don't know how to create a link tables with additional fields like the picture. Which association should I use? How to manage these relations in entities?
Usually when you need a many to many approach Doctrine let you define such behaviour with a simple annotation.
#ORM\ManyToMany(targetEntity="full_qualified_namespace")
#ORM\JoinTable(
name="game_schemas_players",
joinColumns={#ORM\JoinColumn(name="this_field_name", referencedColumnName="id")},
inverseJoinColumns={#ORM\JoinColumn(name="that_field_anem", referencedColumnName="id")}
)
This will instruct Doctrine to create a relation the current entity and the target entity.
But that's not your case. For what I can see from your model you need to add some field on the 'middle' entity.
Here what you may want to do:
class Product
{
[...]
/**
* #var ArrayCollection | ProductImage[]
*
* #ORM\OneToMany(targetEntity="ProductImage", mappedBy="product")
*/
private $productImages;
}
class Image
{
[...]
/**
* #var ArrayCollection | ProductImage[]
*
* #ORM\OneToMany(targetEntity="ProductImage", mappedBy="image")
*/
private $productImages;
}
as you can see as defined here either Product and Image have a oneToMany relation with a middle entity which will be named ProductImage**.
The last step will be to implement such entity:
class ProductImage
{
[...]
/**
* #var Image
*
* #ORM\ManyToOne(targetEntity="Image", mappedBy="image")
* #ORM\JoinColumn(name="image_id", referencedColumnName="id")
*/
private $image;
/**
* #var Product
*
* #ORM\ManyToOne(targetEntity="Product", mappedBy="product")
* #ORM\JoinColumn(name="product_id", referencedColumnName="id")
*/
private $product;
/**
* #ORM\Column(type="string", length=1024)
* #var string
*/
private $position;
}
The owning side of the relation both for Product and Image is still ProductImage.
As a side note in your entity is common practice to implement an add method in this fashion:
public function __contructor(){
$this->productImages = new ArrayCollection();
}
/**
* Add ProductImage
*
* #param ProductImage $productImage
* #return $this
*/
public function addDocument(ProductImage $productImage)
{
$productImage->addProductImage($productImage);
$this->documents->add($document);
return $this;
}
and then you can use such method with the following approach:
$product = new Product();
$image = new Image();
$productImage = new ProductImage($product,$image);
$product->addProductImage($productImage);
Don't forget to provide the usual setter method as Doctrine need them to initialize the entity.
Hope it helps, Regards.

Doctrine Single Table Inheritance and bidirectional OneToMany: columns not being generated

The data model:
The entities:
Pet:
/**
* #ORM\Entity
* #ORM\Table(name="pet")
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="pet_type", type="string")
*/
abstract class Pet
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
*/
protected $id;
/**
* #ORM\OneToMany(
* targetEntity="Collar",
* mappedBy="pet",
* cascade={"persist", "remove"},
* orphanRemoval=TRUE
* )
* #ORM\JoinColumn(name="collars", referencedColumnName="id")
*/
protected $collars;
/**
* #ORM\Column(type="integer")
*/
protected $age;
}
Cat:
/**
* #ORM\Entity
* #ORM\Table(name="cat")
*/
class Cat extends Pet
{
/**
* #ORM\Column(type="decimal")
*/
private $purringDecibels;
}
Collar:
/**
* #ORM\Entity
* #ORM\Table(name="collar")
*/
class Collar
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Pet", inversedBy="collars")
* #ORM\JoinColumn(name="pet", referencedColumnName="id", nullable=false)
*/
private $pet;
/**
* #ORM\Column(type="string", length="255")
*/
private $color;
}
The problem:
The generated tables are fine except pet: It lacks the collars column, therefore the bidirectionality is broken. While the pet ids do get stored on the collar table, I cannot fetch collars from a pet (i.e. $pet->getCollars()) as it always returns an empty collection.
What am I missing out here?
PS:
The validate console helper says all (mapping & db) is OK.
Yes, I have my getters and setters (i.e. adders and removers)
I know about the infamous performance impact of the combination between a CTI and this kind of relationship.
You do not need to have a "collars" column on the pet table. It is a one-to-many relationship, so the only table that need to "know" about pets are the collar table.
A bidirectional relationship does not mean you need two columns on two tables.
Your map seems correct.

Unwanted cascade delete by Doctrine2

I am working on a product catalog, and have two entities, PcatSalesItem and PcatCategory with a many-to-many relationship between them. If I delete a category, and there are still sales items associated with it, I want an exception to be thrown, I do NOT want cascading delete. On the RDBMS level (PostgreSQL), in the join table, I have set the foreign keys to "ON DELETE RESTRICT". However, when I delete a category that has sales items, Doctrine does a cascading delete. Nowhere have I specified cascade=remove to Doctrine!
Here are the entities:
/**
* PcatSalesItem
*
* #ORM\Table(name="pcat_sales_item")
* #ORM\Entity
* #Gedmo\Loggable(logEntryClass="Qi\Bss\BaseBundle\Entity\Business\LogEntryBusiness")
*/
class PcatSalesItem
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="SEQUENCE")
* #ORM\SequenceGenerator(sequenceName="pcat_sales_item_id_seq", allocationSize=1, initialValue=1)
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=64, nullable=false)
* #Gedmo\Versioned
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="description", type="text", nullable=true)
* #Gedmo\Versioned
*/
private $description;
/**
* #var array $categories
*
* #ORM\ManyToMany(targetEntity="PcatCategory")
* #ORM\JoinTable(name="pcat_category_x_sales_item",
* joinColumns={#ORM\JoinColumn(name="sales_item_id", referencedColumnName="id", onDelete="RESTRICT")},
* inverseJoinColumns={#ORM\JoinColumn(name="category_id", referencedColumnName="id", onDelete="RESTRICT")}
* )
*/
private $categories;
...
}
/**
* PcatCategory
*
* #ORM\Table(name="pcat_category")
* #ORM\Entity
* #Gedmo\Loggable(logEntryClass="Qi\Bss\BaseBundle\Entity\Business\LogEntryBusiness")
*/
class PcatCategory
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="SEQUENCE")
* #ORM\SequenceGenerator(sequenceName="pcat_category_id_seq", allocationSize=1, initialValue=1)
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=64, nullable=false)
* #Gedmo\Versioned
*/
private $name;
/**
* #var array $salesItems
*
* #ORM\ManyToMany(targetEntity="PcatSalesItem")
* #ORM\JoinTable(name="pcat_category_x_sales_item",
* joinColumns={#ORM\JoinColumn(name="category_id", referencedColumnName="id", onDelete="RESTRICT")},
* inverseJoinColumns={#ORM\JoinColumn(name="sales_item_id", referencedColumnName="id", onDelete="RESTRICT")}
* )
*/
private $salesItems;
....
}
Here is the code I use to delete a category:
$em = $this->getDoctrine()->getManager();
$cat = $em->getRepository('QiBssBaseBundle:PcatCategory')->find(15);
$em->remove($cat);
$em->flush();
Any help will be greatly appreciated!