I want to ultimately have an abstract base class that defines a relationship to another entity for all inheriting Entity-classes. So have this test-setup that was created with symfony's make:entity-command.
The only thing I changed was switching the MappedSuperClass's property-visibility from private to protected to make them visible to the extenders.
This is the MappedSuperClass:
[...]
/**
* Class TestMappedSuperclass
*
* #package App\Entity
* #ORM\MappedSuperclass()
*/
class TestMappedSuperclass
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity=TestTarget::class, mappedBy="testMappedSuperclass")
*/
protected $targets;
public function __construct()
{
$this->targets = new ArrayCollection();
}
//..getters/setters,add/remove...
}
This my target-entity for the mapping:
[...]
/**
* #ORM\Entity(repositoryClass=TestTargetRepository::class)
*/
class TestTarget
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity=TestMappedSuperclass::class, inversedBy="targets")
*/
private $testMappedSuperclass;
// ..getters,setters..
}
This is the extending entity class:
[...]
/**
* #ORM\Entity(repositoryClass=TestEntityRepository::class)
*/
class TestEntity extends TestMappedSuperclass
{
}
So when I try to do a make:migration it produces the following error:
Column name `id` referenced for relation from App\Entity\TestTarget towards
App\Entity\TestMappedSuperclass does not exist.
Using: Symfony 5.1, Doctrine 2.7
AFAIK you need to have repeat id as protected in TestEntity :
/**
* #ORM\Entity(repositoryClass=TestEntityRepository::class)
*/
class TestEntity extends TestMappedSuperclass
{
/**
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
}
I took this from a project where i´m using mappedSuperClass like this, i´m not relate entities in it though
Related
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
I have a "many-to-many" relation with an association table. The function calculDesCouts(), from the parent table is calculating the total cost of ingredients, and persist this info in a field. Actually, because of the owning and inversed sides of the relations, I'm not able to cascade persist the parent table when an ingredient price has changed.
What is the proper way to do this? I mean, when a child row is updated, how to trigger the persist callback from the parent table? I know I can do it in my controller, with a loop on all recipes using this ingredient, but I would really like to do it on the ORM layer...
This is my parent entity, with a lifecycle callback on the calculDesCouts :
<?php
/**
* #ORM\Table(name="recette")
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class Recette
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="\My\AcmeBundle\Entity\RecetteIngredientAssociation", mappedBy="recette", cascade={"persist"})
*/
protected $recette_ingredient_associations;
/**
* #ORM\Column(type="decimal", scale=2, nullable=true)
*/
private $cout_nourriture;
/**
* #ORM\PrePersist
* #ORM\PreUpdate
*/
public function calculDesCouts()
{
$this->cout_nourriture = 0;
foreach ($this->recette_ingredient_associations as $ria) {
$this->cout_nourriture += $ria->getIngredient()->getPrix() * $ria->getQuantite();
}
}
}
This is my association entity:
<?php
/**
* #ORM\Table(name="recette_ingredient_association")
* #ORM\Entity
*/
class RecetteIngredientAssociation
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="\My\AcmeBundle\Entity\Recette", inversedBy="recette_ingredient_association")
* #ORM\JoinColumn(name="recette_id", referencedColumnName="id")
*
*/
private $recette;
/**
* #ORM\ManyToOne(targetEntity="\My\AcmeBundle\Entity\Ingredient", inversedBy="recette_ingredient_association")
* #ORM\JoinColumn(name="ingredient_id", referencedColumnName="id")
*/
private $ingredient;
/**
* #ORM\Column(type="decimal", scale=2, nullable=false)
*/
private $quantite;
}
And this is my child entity:
<?php
/**
* #ORM\Table(name="ingredient")
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class Ingredient
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="decimal", scale=2, nullable=true)
*/
private $prix;
/**
* #ORM\OneToMany(targetEntity="\My\AcmeBundle\Entity\RecetteIngredientAssociation", mappedBy="ingredient")
*/
protected $recette_ingredient_associations;
}
I don't know how to make it on ORM level, but I would do it on Symfony2 layer.
To make this what you need is to create an EventListener which will in case of Ingredient's update call Recette's calculDesCouts.
Like declare a service:
app.ingredient_listener_update:
class: AppBundle\Listener\IngredientPriceListener
tags:
- { name: doctrine.event_listener, event: postUpdate }
and in this AppBundle\Listener\IngredientPriceListener make a method
public function postUpdate(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$em = $args->getEntityManager();
if ($entity instanceof Ingredient) {
foreach ($entity->get_recette_ingredient_associations() as $association)
foreach ($association->get_recettes() as $recette) {
$recette->calculDesCouts();
$em->persist($recette);
}
}
}
There is a problem if I use $id in the MappedSuperclass?
I have the following schema, and I need to know if there is some problem use $id in the MappedSuperclass.
Thanks.
Example:
/**
* #ORM\MappedSuperclass
*/
abstract class AbstractBase
{
/**
* #var integer $id
* #ORM\Column(name="id", type="bigint", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
}
-
/**
* #ORM\MappedSuperclass
*/
abstract class AbstractAddress extends AbstractBase
{
}
-
/**
* #ORM\Table(name="customer_address")
*/
class CustomerAddress extends AbstractAddress
{
}
-
/**
* #ORM\Table(name="order_address")
*/
class OrderAddress extends AbstractAddress
{
}
I have many relations of this type, but I can't see why this one is not working.
I have a Delegation and a Promotion entities:
Delegation
Promotion
/**
* Company\CBundle\Entity\Promotion
*
* #ORM\Entity
* #DoctrineAssert\UniqueEntity("promotionCode")
*/
class Promotion
{
const REGISTER_DAYS = 30;
const REGISTER_DISCOUNT = 100;
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(name="promotionCode", type="string", unique=true, nullable=true)
*/
protected $promotionCode;
/**
* #ORM\Column(name="name", type="string")
*/
protected $name;
/**
* #ORM\Column(name="description", type="string", nullable=true)
*/
protected $description;
/**
* #ORM\Column(name="days", type="integer")
*/
protected $days;
/**
* #ORM\Column(name="discount", type="float")
*/
protected $discount;
/**
* #ORM\ManyToOne(targetEntity="Delegation", inversedBy="promotions")
* #ORM\JoinColumn(name="delegation_id", referencedColumnName="id")
*/
private $delegation;
/**
* #ORM\ManyToOne(targetEntity="Product", inversedBy="promotions")
*/
private $product;
/**
* #var date $adquiredDate
*
* #ORM\Column(name="adquiredDate", type="date", nullable=true)
*/
private $adquiredDate;
When in a controller I create a promotion, the table Promotion has the new object related to the delegation one
private function createPromotion($delegation)
{
$em = $this->getDoctrine()->getEntityManager();
$promotion = Promotion::createPromotion($delegacion, Promotion::REGISTER_DAYS, Promotion::REGISTER_DISCOUNT);
$em->persist($promotion);
$em->persist($delegation);
$em->flush();
}
Database
*************************** 15. row ***************************
id: 32
delegation_id: 19
days: 20
discount: 50
adquiredDate: 2013-01-10
*************************** 16. row ***************************
id: 33
delegation_id: 19
days: 25
discount: 50
adquiredDate: 2013-01-10
*************************** 17. row ***************************
id: 34
delegation_id: 19
days: 30
discount: 50
adquiredDate: 2013-01-10
But when I call the $delegation->getPromotions() in another controller/action there is no promotions, returns a Doctrine\ORM\PersistentCollection with no data.
Can anyone help, please?
Edit with more information.
$delegation->getPromotions() is empty, but looking for a promotion of that delegation and calling $promotion->getDelegation() is returning the delegation correctly :?
Have you tried defining your $delegation property like
/**
* #ORM\ManyToOne(targetEntity="Delegation", inversedBy="promotions")
* #ORM\JoinColumn(name="delegation_id", referencedColumnName="id")
*/
private $delegation;
See Doctrine2 Docs: Association Mapping->Many-To-One
Also there are a lot of typos in your code. For example
/**
* #ORM\OneToMany(targetEntity="Promotion", mappedBy="delegacion", cascade={"all"}, orphanRemoval=true)
*/
protected $promotions;
mappedBy="delegacion" should be mappedBy="delegation".
Or
public function getDeleTacion()
{
return $this->deleTacion;
}
Should be
public function getDelegation()
{
return $this->delegation;
}
Edit
Okay, I created a minimalistic version for you that worked for me. You can built it up from there or watch for differences with your code:
Promotion.php
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Promotion
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Delegation", inversedBy="promotions", cascade={"persist"})
* #ORM\JoinColumn(name="delegation_id", referencedColumnName="id")
*/
public $delegation;
/**
* #ORM\ManyToOne(targetEntity="Product", inversedBy="promotions", cascade={"persist"})
* #ORM\JoinColumn(name="product_id", referencedColumnName="id")
*/
public $product;
}
Delegation.php
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Delegation
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="Promotion", mappedBy="delegation", cascade={"all"}, orphanRemoval=true)
*/
public $promotions;
public function __construct() {
$this->promotions = new \Doctrine\Common\Collections\ArrayCollection();
}
}
Product.php
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Product
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="Promotion", mappedBy="product", cascade={"all"}, orphanRemoval=true)
*/
public $promotions;
public function __construct() {
$this->promotions = new \Doctrine\Common\Collections\ArrayCollection();
}
}
If you now do something like
$delegation = new Delegation();
$product = new Product();
$promotion = new Promotion();
$promotion->delegation = $delegation;
$promotion->product = $product;
$em->persist($promotion);
$em->flush();
$products = $em->createQuery('select p from BundleName\Entity\Product p')->execute();
$delegations = $em->createQuery('select d from BundleName\Entity\Delegation d')->execute();
var_dump(count($products[0]->promotions), count($delegations[0]->promotions));
You should end up with
int(1)
int(1)
So the refrence is in fact saved and can be read. Phew. Good luck with that! :-)
I had a similar error where my many-to-one relationship on some freshly created entities contained entities, and an inverse one-to-many didn't, although database clearly had the corresponding rows in it.
I did persist and flush entities on the one-to-many side, but I had to also do
$entityManager->clear();
before getting those entities from a repository again for the one-to-many relationship to be able to access those entities.
I'm trying to learn read/write data to/from database, and I have a huge problem :
My entities looks :
Kategorie:
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="kategorie")
*/
class Kategorie
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $idkategorii;
/**
* #ORM\Column(type="string", length=50)
*/
protected $nazwa;
/**
* #ORM\OneToMany(targetEntity="Ogloszenia", mappedBy="ogloszenia")
*/
protected $ogloszenia;
Ogloszenia:
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="ogloszenia")
*/
class Ogloszenia
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string",length=100)
*/
protected $tytul;
/**
* #ORM\Column(type="string", length=120)
*/
protected $tytul_seo; //tytul bez polskich znaków etc.
/**
* #ORM\Column(type="text")
*/
protected $tresc; //tresc ogloszenia
/**
* #ORM\Column(type="string",length=50)
*/
protected $dodal; //imie osoby ktora dodala ogloszenie
/**
* #ORM\Column(type="string", length=50)
*/
protected $kontakt; //nr tel lub mail
/**
* #ORM\ManyToOne(targetEntity="Kategorie", inversedBy="kategoria")
* #ORM\JoinColumn(name="kategoria", referencedColumnName="idkategorii")
*/
protected $kategoria;
Now, in my controller i'm trying to read all values :
public function odczytajAction()
{
$id = 1;
$kategoria = $this->getDoctrine()
->getRepository('FrontendOgloszeniaBundle:Kategorie')
->find($id);
$ogl = $kategoria->getOgloszenia();
foreach($ogl as $o)
{
print_r($o);
}
return new Response('test odczytu');
}
And unfortunately symfony2 gives me an following error
Notice: Undefined index: ogloszenia in /home/sl4sh/public_html/Projekt1/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php line 1574
500 Internal Server Error - ErrorException
So, please tell me what's wrong with mine code ?
You have invalid mappedBy annotation:
/**
* #ORM\OneToMany(targetEntity="Ogloszenia", mappedBy="kategoria")
*/
protected $ogloszenia;
And also inverse side:
/**
* #ORM\ManyToOne(targetEntity="Kategorie", inversedBy="ogloszenia")
* #ORM\JoinColumn(name="kategoria", referencedColumnName="idkategorii")
*/
protected $kategoria;