Symfony4 extending entity does not add Entity column to database schema - symfony

I am developing symfony 4.2 application. I have entity Meal and OrderItem. OrderItem should have all Meal entity properties + few of it's own. The problem is with ManyToOne relationship column. It is not added to order_item table.
https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/inheritance-mapping.html
I tried following "Class Table Inheritance" and "Mapped Superclasses". Example from "Mapped Superclasses" does not add $mappedRelated1 to EntitySubClass . And example from "Class Table Inheritance" removes every other extended field from Meal class and adds some kind of "dtype" column to Meal table.
/**
* #ORM\Entity(repositoryClass="App\Repository\OrderItemRepository")
*/
class OrderItem extends Meal
{
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Order", inversedBy="orderItems")
*/
private $order;
/**
* #ORM\Column(type="integer")
* #Assert\NotBlank
* #Assert\Type("string")
*/
private $amount;
}
/**
* #ORM\Entity(repositoryClass="App\Repository\MealRepository")
*/
class Meal
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
protected $id;
/**
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank
* #Assert\Type("string")
*/
protected $name;
/**
* #ORM\Column(type="float")
* #Assert\NotBlank
* #Assert\Type("float")
*/
protected $price;
/**
* #ORM\Column(type="string", length=255)
* #Assert\Image(
* mimeTypes={"image/jpeg", "image/png"}
* )
*/
protected $image;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Menu", inversedBy="meals")
*/
protected $menu;
}
I expect to have "menu_id" column in "order_item" table, which would have relation to Menu entity.
I know that I could copy all the properties from Meal to OrderItem, but that does not sound right.
EDIT:
Both Meal and OrderItem should be able to have it's instance.

menu_id column is already exists in meal
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Menu", inversedBy="meals")
*/
protected $menu;
OrderItem class doesnt need to have just because it is already subclass of meal.
Also you dont need to copy all propertites from Meal to OrderItem.
u can check php extends here

I had the same issue. I used Trait instead of inheritance and the problem was solved.
Your class will be like this
trait MealInfoTrait
{
/**
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank
* #Assert\Type("string")
*/
protected $name;
/**
* #ORM\Column(type="float")
* #Assert\NotBlank
* #Assert\Type("float")
*/
protected $price;
/**
* #ORM\Column(type="string", length=255)
* #Assert\Image(
* mimeTypes={"image/jpeg", "image/png"}
* )
*/
protected $image;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Menu", inversedBy="meals")
*/
protected $menu;
}
Then, change you Meal class
/**
* #ORM\Entity(repositoryClass="App\Repository\MealRepository")
*/
class Meal
{
use MealInfoTrait;
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
protected $id;
}
And do the same with your OrderItem class. Don't forget to add the id for the database.
/**
* #ORM\Entity(repositoryClass="App\Repository\OrderItemRepository")
*/
class OrderItem
{
use MealInfoTrait;
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Order", inversedBy="orderItems")
*/
private $order;
/**
* #ORM\Column(type="integer")
* #Assert\NotBlank
* #Assert\Type("string")
*/
private $amount;
}

Related

How to update OneToMany ressource

I am a beginner in API platform. I am working with two entities Author and Book with a OneToMany relationship:
a user can have several books
a book can belong to only one Author.
I've tried POST, GET and DELETE methods, they work. However, the PUT method does not work and returns this error:
"hydra:title": "An error occurred",
"hydra:description": **"An exception occurred while executing 'UPDATE Book SET author_id = ? WHERE id = ?' with params [null, 1]:\n\nSQLSTATE[23000]:
Integrity constraint violation: 1048 Column 'author_id' cannot be null"
Here's my code:
/**
* #ORM\Entity(repositoryClass="App\Repository\AuthorRepository")
* #ApiResource(
* normalizationContext={"groups"={"book:output"}},
* denormalizationContext={"groups"={"book:input"}}
* )
*/
class Author
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
* #Groups({"book:input"})
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
* #Groups({"book:output", "book:input"})
*/
private $firstname;
/**
* #ORM\Column(type="string", length=255)
* #Groups({"book:output", "book:input"})
*/
private $lastname;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Book", mappedBy="author", cascade={"all"})
* #Groups({"book:output", "book:input"})
*/
private $books;
/**
* #ORM\Entity(repositoryClass="App\Repository\BookRepository")
* #ApiResource()
*/
class Book
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
* #Groups({"book:output", "book:input"})
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
* #Groups({"book:output", "book:input"})
*/
private $name;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Author", inversedBy="books")
* #ORM\JoinColumn(nullable=false)
*/
private $author;
You seem like passing a null id for an updated entity which is essential for the ORM to know.

Delete an entity with two OneToOne relations

I have a probleme for a job in Symfony 4.2.
I'm a beginner on Symfony and I need help for understand cascade deletion, and what I have a foreign key error
I have 3 entity like this :
The first is call Image
class Image
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\OneToOne(
* targetEntity="App\Entity\Thumbnail",
* mappedBy="Image",
* cascade={"persist"},
* orphanRemoval=true
* )
*/
private $Thumbnail;
}
the second is the Thumbnail of this image :
class Thumbnail
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\OneToOne(
* targetEntity="App\Entity\Image",
* inversedBy="Thumbnail",
* cascade={"persist", "remove"}
* )
* #ORM\JoinColumn(nullable=false)
*/
private $image;
}
and a Div which contain a Thumbnail :
class Div
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\OneToOne(targetEntity="App\Entity\Thumbnail", cascade={"persist", "remove"})
*/
private $Thumbnail;
}
this is my three entities, and my problem is when I try to delete one of this three entities because :
When I delete an Image I need to delete his Thumbnail, but the Div can live without Thumbnail so I mustn't delete the Div.
When I delete a Thumbnail, I mustn't delete the Image, and I mustn't delete the Div
When I delete the Div I mustn't delete the Thumbnail.
I would like to know the best practice for do this job.
Thank for help and sorry for my bad english.
Try below code in your Div entity:
/**
* #ORM\OneToOne(targetEntity="App\Entity\Thumbnail", cascade={"persist", "remove"})
* #ORM\JoinColumn(name="thumbnail_id", referencedColumnName="id", onDelete="SET NULL")
*/
private $Thumbnail;

Relationships with #Embedded column

I have #Embeddable class for id:
/**
* #ORM\Embeddable()
*/
final class Id
{
/**
* #ORM\Id()
* #ORM\Column(type="string")
*/
private $id;
//getters, setters, etc
}
And two classes that have #ManyToOne relationship:
/**
* #ORM\Entity()
*/
final class Gist
{
/**
* #var Id
*
* #ORM\Id()
* #ORM\Embedded(class="App\Entity\Id")
*/
private $id;
/**
* #var User
*
* #ORM\ManyToOne(targetEntity="App\Entity\User")
*/
private $user;
//getters, setters, etc
}
And User class:
/**
* #ORM\Entity()
*/
final class User implements UserInterface
{
/**
* #var Id
*
* #ORM\Embedded(class="App\Entity\Id")
*
*/
private $id;
//getters, setters, etc
}
When I trying to create schema I got error: No mapping found for field 'id' on class 'App\Entity\User'.. What I'm doing wrong?
There no solution. https://github.com/doctrine/doctrine2/issues/7094.
Support of identifiers within #Embeddable is not really there yet, sorry.
I suggest not to use #Embeddable on id field. Cause it won't work. Declare your id field normally and it should work.
/**
* #ORM\Entity()
*/
class Gist
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var User
*
* #ORM\ManyToOne(targetEntity="App\Entity\User")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
//getters, setters, etc
}
/**
* #ORM\Entity()
*/
class User implements UserInterface
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
//getters, setters, etc
}
Also your Many-To-One relation is bad, see: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#many-to-one-unidirectional
And never declare entities as final classes, see: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/architecture.html#entities

Entity inheritance in Doctrine doesn't include OneToOne-relationships

Trying to utilize inheritance, I've created the following entities:
/**
* #ORM\Table(name="persons")
* #ORM\Entity()
*/
class Person
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
protected $name;
/**
* #ORM\OneToOne(targetEntity="Image", cascade={"persist"})
* #ORM\JoinColumn(name="image_id", referencedColumnName="id")
*/
protected $image;
}
/**
* #ORM\Table(name="actors")
* #ORM\Entity()
*/
class Actor extends Person
{
/**
* #ORM\Column(name="character", type="string", length=255)
*/
private $character;
}
/**
* #ORM\Table(name="images")
* #ORM\Entity()
*/
class Image
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="path", type="string", length=255)
*/
private $path;
}
Which almost works perfectly. The generated actors-table contains all the persons-fields, except for the image-relation. I've tried to change the relation to a ManyToOne, which didn't help.
How to make the Actor-entity also inherit all joined fields? I'm open to other solutions, if the above isn't ideal.
You need a parent construct in your Actor class:
public function __construct()
{
parent::__construct();
// your own logic
}
It is advised that you add an ID aswell.

Doctrine2 cascade remove with multiple parents

I have a series of classes with a slightly complicated set of references between the properties of those classes. I am trying to remove an entity and have that remove be cascaded to its children, but I'm running into foreign key constraint errors. Here is an example of my class structure:
<?php
/**
* #ORM\Entity
* #ORM\Table(name="student_tests")
*/
class StudentTest implements IEntityAccess {
/**
*
* #var int
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var StudentTestItem[]
* #ORM\OneToMany(targetEntity="StudentTestItem", mappedBy="studentTest", cascade{"remove","persist"})
*/
protected $studentTestItems;
/**
* #var Test
* #ORM\ManyToOne(targetEntity="Test", inversedBy="studentTests")
*/
protected $test;
/**
* #var \DateTime
* #ORM\Column(type="datetime", nullable=true)
*/
protected $created;
/**
* #var User
* #ORM\ManyToOne(targetEntity="User", inversedBy="studentTests")
*/
protected $student;
}
//...
<?php
/**
* #ORM\Entity
* #ORM\Table(name="student_test_items")
*/
class StudentTestItem {
/**
*
* #var int
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var StudentTest
* #ORM\ManyToOne(targetEntity="StudentTest", inversedBy="studentTestItems")
*/
protected $studentTest;
/**
* #var User
* #ORM\ManyToOne(targetEntity="User", inversedBy="studentTestItems", cascade={"persist"})
*/
protected $student;
/**
* #var TestItem
* #ORM\ManyToOne(targetEntity="TestItem", inversedBy="studentTestItems", cascade{"persist"})
*/
protected $testItem;
}
//...
/**
*
* #ORM\Table(name="tests")
* #ORM\Entity
*
* #ORM\HasLifecycleCallbacks
*/
class Test implements IEntityAccess {
/**
*
* #var int
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var \DateTime
* #ORM\Column(type="datetime", nullable=true)
*/
protected $startDate;
/**
* #var StudentTest[]
* #ORM\OneToMany(targetEntity="StudentTest", mappedBy="test" )
*/
protected $studentTests;
/**
* #var TestItem[]
* #ORM\OneToMany(targetEntity="TestItem", mappedBy="test", cascade={"all"})
*/
protected $items;
}
//...
/**
*
* #ORM\Table(name="test_items")
* #ORM\Entity
*/
abstract class TestItem {
/**
*
* #var int
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var Test
* #ORM\ManyToOne(targetEntity="Test", inversedBy="items")
*/
/**
* #var StudentTestItem[]
* #ORM\OneToMany(targetEntity="StudentTestItem", mappedBy="testItem")
*/
protected $studentTestItems;
}
/**
* This is the primary user object. Used for login and all the other
* good stuff.
*
* #ORM\Table(name="users")
* #ORM\HasLifecycleCallbacks
class User implements AdvancedUserInterface, \Serializable, IEntityAccess
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #var int
*/
private $id;
/**
* #var StudentTest[]
* #ORM\OneToMany(targetEntity="StudentTest", mappedBy="student", cascade={"persist", "remove"})
*/
protected $studentTests;
/**
* #var StudentTestItem[]
* #ORM\OneToMany(targetEntity="StudentTestItem", mappedBy="student", cascade={"persist", "remove"})
*/
protected $studentTestItems;
}
Let's say I want to delete a student test, and have that delete cascaded to its StudentTestItem children. To do so, I run the following code inside of a controller.
//... blah blah class definition
/**
* Delete a student test
*
* #return \Symfony\Component\HttpFoundation\Response
* #Route("/studenttest/delete", name="student_test_delete")
*/
public function DeleteStudentTestAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$studentTest = $em->getRepository("MyAcmeBundle:StudentTest")->findOneBy(array("id" => 3));
$em->remove($studentTest);
$em->flush();
return $this->redirect($this->generateUrl('student_delete_success'));
}
When I try to run that code, I get the following error message:
An exception occurred while executing 'DELETE FROM student_tests WHERE id = ?' with params [3]:
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (`my_acme_bundle/student_test_items`, CONSTRAINT `FK_71FA2A7F36BB1A1` FOREIGN KEY (`student_test_id`) REFERENCES `student_tests` (`id`))
500 Internal Server Error - DBALException
NOW, if I remove all references to studentTestItems from the classes, i.e. I comment out $studentTestItems from the TestItem and User classes, it deletes fine without that issue. Why is this happening? Does Doctrine keep track of the parent references through associations or something?
Looks like you forgot to add ON DELETE CASCADE to the foreign key constraint. Try changing the following association in class StudentTestItem:
/**
* #var StudentTest
* #ORM\ManyToOne(targetEntity="StudentTest", inversedBy="studentTestItems")
*/
protected $studentTest;
To this:
/**
* #var StudentTest
* #ORM\ManyToOne(targetEntity="StudentTest", inversedBy="studentTestItems")
* #ORM\JoinColumn(name="student_test_id", referencedColumnName="id", onDelete="CASCADE")
*/
protected $studentTest;

Resources