I've got a problem setting up the Doctrine mapping correctly.
I have a CashRegister Entity which has a bin location and a return bin location. Both locations are from the same Type (BinLocation Entity).
Outgoing from CashRegister, CashRegister->getBinLocations() and CashRegister->getReturnBinLocations() are working fine, but how can I achieve that BinLocation->getCashRegisters() returns all CashRegister Entities that are referenced (binLocation + returnBinLocation)?
/**
* CashRegister
*
* #ORM\Table(name="cash_registers")
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class CashRegister
{
...
/**
* #var BinLocation
*
* #ORM\ManyToOne(targetEntity="BinLocation", inversedBy="cashRegisters")
* #ORM\JoinColumn(name="bin_location_id", referencedColumnName="id")
*/
private $binLocation;
/**
* #var BinLocation
*
* #ORM\ManyToOne(targetEntity="BinLocation", inversedBy="cashRegisters")
* #ORM\JoinColumn(name="return_bin_location_id", referencedColumnName="id")
*/
private $returnBinLocation;
/**
* #return BinLocation
*/
public function getBinLocation()
{
return $this->binLocation;
}
/**
* #return BinLocation
*/
public function getReturnBinLocation()
{
return $this->returnBinLocation;
}
...
}
/**
* BinLocation
*
* #ORM\Table(name="bin_locations")
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class BinLocation
{
...
/**
* #var CashRegister[]
*
* #ORM\OneToMany(targetEntity="CashRegister", mappedBy="binLocation") <= Here is the problem, in this case mappedBy need to be an array [binLocation, returnBinLocation]
*/
private $cashRegisters;
/**
* #return CashRegister[]
*/
public function getCashRegisters()
{
return $this->cashRegisters;
}
...
}
The simple answer is that you cannot. mappedBy accepts only one argument.
The solution to achieve what you want is however simple. Create a second property in BinLocation called: cashRegisters2 as follows:
/**
* #var CashRegister[]
*
* #ORM\OneToMany(targetEntity="CashRegister", mappedBy="binLocation")
*/
private $cashRegisters;
/**
* #var CashRegister[]
*
* #ORM\OneToMany(targetEntity="CashRegister", mappedBy="binLocation")
*/
private $cashRegisters2;
Then merge the Collections in your getCashRegisters method.
/**
* #return CashRegister[]
*/
public function getCashRegisters()
{
return new ArrayCollection(
array_merge($cashRegisters->toArray(), $cashRegisters2->toArray())
);
}
Also change your CashRegister mappings accordingly:
/**
* #var BinLocation
*
* #ORM\ManyToOne(targetEntity="BinLocation", inversedBy="cashRegisters")
* #ORM\JoinColumn(name="bin_location_id", referencedColumnName="id")
*/
private $binLocation;
/**
* #var BinLocation
*
* #ORM\ManyToOne(targetEntity="BinLocation", inversedBy="cashRegisters2")
* #ORM\JoinColumn(name="return_bin_location_id", referencedColumnName="id")
*/
private $returnBinLocation;
Note: I did not test the code. This example is to server guide only.
Note2: The ArrayCollection merge was inspired from here: https://stackoverflow.com/a/16871539/2853903
I have also searched for solutions and made a patch for Doctrine so you can have custom_attributes linked to a variety of entity types.
Doctrine 2.6: https://github.com/danielbeeke/doctrine2/commit/2d8530176b872cb490c5c88b8c8e17d8d0091388
Doctrine 2.7: https://github.com/danielbeeke/doctrine2/commit/5bde696848ea9fe7035fadc4d46baa4c0d51f3a2
/**
* #Entity
* #Table(name="product")
* #HasLifecycleCallbacks
**/
class Product {
/**
* One Product has Many Attributes.
*
* #OneToMany(
* targetEntity="CustomAttribute",
* mappedBy="EntityId",
* mappedByType={
* "field": "EntityType",
* "value": "product"
* }
* )
*
* #var $CustomAttributes ArrayCollection
*/
protected $CustomAttributes;
}
/**
* #Entity
* #Table(name="custom_attribute")
* #HasLifecycleCallbacks
**/
class CustomAttribute_entity {
/** #Id #Column(type="integer") #GeneratedValue */
protected $Id;
/**
* Many Attributes have One Entity of the EntityType provided.
* #ManyToOne(targetEntity="Product", inversedBy="CustomAttributes")
* #JoinColumn(name="EntityId", referencedColumnName="Id")
*/
protected $EntityId;
/** #Column(type="string") */
protected $EntityType;
/** #Column(type="string") */
protected $Type;
/** #Column(type="string") */
protected $Value;
}
Related
I need help for my problem.
When add the relationship ManyToMany to the php script this return when validate the relationship.
This is my scripts:
class Post implements ResourceInterface, TranslatableInterface
{
/**
* #var int
*/
private $id;
/**
* #var bool
*/
private $important;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Tag", inversedBy="posts")
* #ORM\JoinTable(name="posts_tags",
* joinColumns={
* #ORM\JoinColumn(name="post_id", referencedColumnName="id", onDelete="CASCADE")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="tag_id", referencedColumnName="id")
* }
* )
*/
private $tags;
}
And
class Tag implements ResourceInterface, TranslatableInterface
{
use TranslatableTrait {
__construct as private initializeTranslationsCollection;
}
/**
* #var int
*/
private $id;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Post",
mappedBy="tags")
*/
private $posts;
}
The error is:
Mapping
[FAIL] The entity-class AppBundle\Entity\Tag mapping is invalid: *
The field AppBundle\Entity\Tag#posts is on the inverse side of a bi-
directional relationship, but the specified mappedBy association on
the target-entity AppBundle\Entity\Post#tags does not contain the
required 'inversedBy="posts"' attribute.
Hello use symfony maker
. /bin/console make:entity Post
For the field name enter 'tags' and for the type enter 'relation' . Then give the related tags entity name. Shoud be Tag. Choose the manytomany relation and enter. Like this you should never have relation issue :-). Even if the entity exist already.
maybe the mapping of the tags attribute in the post entity should be like this:
class Post implements ResourceInterface, TranslatableInterface
{
/**
* #var int
*/
private $id;
/**
* #var bool
*/
private $important;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Tag", inversedBy="posts", cascade={"persist"})
* #ORM\JoinTable(name="posts_tags",
* joinColumns={
* #ORM\JoinColumn(name="post_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="tag_id", referencedColumnName="id")
* }
* )
*/
private $tags;
}
I hope I've helped :)
I have create Formtype UniversitaireType but I have this error:
attribut condidat_id doesn't create in database when I try command php bin/console doctrine:schema:update --force (all attributs create in database except id_condidat) .. I make mapping OneToMany $condidat in entity Universitaire
code entity:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use AppBundle\Entity\Condidat;
/**
* Universitaire.
*
* #ORM\Table(name="universitaires")
* #ORM\Entity(repositoryClass="AppBundle\Entity\UniversitaireEntityRepository")
*/
class Universitaire
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="date_start", type="string", length=255, nullable=true)
*/
private $dateStart;
/**
* #var string
*
* #ORM\Column(name="date_end", type="string", length=255, nullable=true)
*/
private $dateEnd;
/**
* #var string
*
* #ORM\Column(name="establishment", type="string", length=255, nullable=true, unique=true)
*/
private $establishment;
/**
* #var string
*
* #ORM\Column(name="diploma", type="string", length=255, nullable=true, unique=true)
*/
private $diploma;
/**
*#ORM\OneToMany(targetEntity="AppBundle\Entity\Condidat",mappedBy="id_universitaire",cascade={"persist"})
*#ORM\JoinColumn(name="id_condidat", referencedColumnName="id")
*/
private $condidat;
/**
* Get id.
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Add Condidat
*
* #param \AppBundle\Entity\Condidat $condidat
* #return Universitaire
*/
public function addCondidat(\AppBundle\Entity\Condidat $condidat)
{
$this->condidat[] = $condidat;
return $this;
}
/**
* Remove Condidat
*
* #param \AppBundle\Entity\Condidat $condidat
*/
public function removeCondidat(\AppBundle\Entity\Condidat $condidat)
{
$this->Condidat->removeElement($condidat);
}
/**
* Get Condidat
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getCondidat()
{
return $this->condidat;
}
/**
* Set dateStart.
*
* #param string $dateStart
*
* #return Universitaire
*/
public function setdateStart($dateStart)
{
$this->dateStart = $dateStart;
return $this;
}
/**
* Get dateStart.
*
* #return string
*/
public function getdateStart()
{
return $this->dateStart;
}
/**
* Set dateEnd.
*
* #param string $dateEnd
*
* #return Universitaire
*/
public function setDateEnd($dateEnd)
{
$this->dateEnd = $dateEnd;
return $this;
}
/**
* Get dateEnd.
*
* #return string
*/
public function getdateEnd()
{
return $this->dateEnd;
}
/**
* Set establishment.
*
* #param string $establishment
*
* #return Universitaire
*/
public function setEstablishment($establishment)
{
$this->establishment = $establishment;
return $this;
}
/**
* Get establishment.
*
* #return string
*/
public function getEstablishment()
{
return $this->establishment;
}
/**
* Set diploma.
*
* #param string $diploma
*
* #return Universitaire
*/
public function setDiploma($diploma)
{
$this->diploma = $diploma;
return $this;
}
/**
* Get diploma.
*
* #return string
*/
public function getDiploma()
{
return $this->diploma;
}
public function __construct()
{
$this->ResponsableCategory = new \Doctrine\Common\Collections\ArrayCollection();
}
}
code FormType:
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use AppBundle\Entity\Universitaire;
use AppBundle\Entity\Condidat;
class UniversitaireType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('dateStart')->add('dateEnd')->add('establishment')->add('diploma')
->add('condidat',null, array(
'class' => 'AppBundle:Condidat',
'choice_label' => 'condidat',));
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Universitaire',
"csrf_protection" => "false"
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_universitaire';
}
}
You have two mistakes in your "condidat" property.
You used One-To-Many (Bidirectional). If you want to have only one
candidate per "Universitaire" - then you have to use One-To-One (Bidirectional).
class Universitaire
{
## ...
/**
* Bidirectional One-To-One (Owning Side)
*
* #ORM\OneToOne(
* targetEntity="AppBundle\Entity\Condidat",
* inversedBy="universitaire",
* cascade={"persist", "remove"}
* )
* #ORM\JoinColumn(
* name="condiat_id",
* referencedColumnName="id"
* )
*/
private $condidat;
class Condidat
{
## ...
/**
* Bidirectional One-To-One (Inversed Side)
*
* #ORM\OneToOne(
* targetEntity="AppBundle\Entity\Universitaire",
* mappedBy="condidat",
* )
*/
private $universitaire;
If not, and One-To-Many was exactly you wanted to have, than you have to set in your __construct() that "condidat" is an array collection
public function __construct()
{
$this->condidat = new \Doctrine\Common\Collections\ArrayCollection();
}
If I were you I'd rather use "candidates" with "s" - to show, that this an arrayCollection.
As I've already said - your example is Bidirectional One-To-Many. In this case we use mappedBy and inversedBy. Entity, in which you set Join column is Owning side. And of course JoinColumn has to be in the "Candidate" entity. So:
class Universitaire
{
## ...
/**
* Bidirectional One-To-Many (Owning Side)
* Predicate: One Universitaire can have a lot of candidates
*
* #ORM\OneToMany(
* targetEntity="AppBundle\Entity\Condidat",
* mappedBy="universitaire",
* cascade={"persist", "remove"}
* )
*/
private $condidats;
class Condidat
{
## ...
/**
* Bidirectional Many-To-One (Owning Side)
* Predicate: Each Condidat belongs to one Universitaire
*
* #ORM\ManyToOne(
* targetEntity="AppBundle\Entity\Universitaire",
* inversedBy="condidats",
* )
* #ORM\JoinColumn(
* name="universitaire_id",
* referencedColumnName="id",
* onDelete="SET NULL"
* )
*/
private $universitaire;
I added onDelete="SET NULL". If you delete universitaire then in field universitaire of each "condidate" you will see null value.
In our symfony2 project we want to implement elasticsearch with ongr-io bundle, but our whole system is built on doctrine. Is it possible to somehow use ongr-io elasticsearch bundle without totally redoing whole database with documents instead of entities?
Entity that I want to index (fields for search would be: name, ChildCategory and ParentCategory):
/**
* Course
*
* #ORM\Table(name="course")
* #ORM\Entity(repositoryClass="CoreBundle\Repository\CourseRepository")
* #ORM\HasLifecycleCallbacks
*/
class Course
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, unique=false)
*/
private $name;
/**
* #var ParentCategory
*
* #ORM\ManyToOne(targetEntity="ParentCategory")
* #ORM\JoinColumn(name="parentCategory_id", referencedColumnName="id")
*/
private $parentCategory;
/**
* #var ChildCategory
*
* #ORM\ManyToOne(targetEntity="ChildCategory", inversedBy="courses")
* #ORM\JoinColumn(name="childCategory_id", referencedColumnName="id")
*/
private $childCategory;
/**
* #var City
*
* #ORM\ManyToOne(targetEntity="City")
* #ORM\JoinColumn(name="city_id", referencedColumnName="id")
*/
private $city;
/**
* #var Users
*
* #ORM\ManyToOne(targetEntity="UserBundle\Entity\Users", inversedBy="course")
* #ORM\JoinColumn(name="owner_id", referencedColumnName="id")
*/
private $owner;
/**
* #var text
*
* #ORM\Column(name="description", type="text")
*/
private $description;
/**
* #var Picture
*
* #ORM\OneToMany(targetEntity="CoreBundle\Entity\Picture", mappedBy="course", cascade={"persist", "remove"})
*/
private $pictures;
/**
* #var Ratings
*
* #ORM\OneToMany(targetEntity="CoreBundle\Entity\Ratings", mappedBy="courseid")
*/
private $ratings;
public $avgRating;
}
it's very interesting.
The first thing is Entity location. The document for ElasticsearchBundle has to be in Document directory. It's not a big deal. All you need to do is to create an empty class and extend entity with #Document annotation. Next are the fields. With name field there is no problem at all, just add #property annotation and you are good. More interesting is with relations. My suggestion would be to create separate property and in the getter return value from the relation for that field. I hope you got the idea.
Update:
Here's an example of documents:
AppBundle/Entity/Post
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use ONGR\ElasticsearchBundle\Annotation as ES;
/**
* Post
*
* #ORM\Table(name="post")
* #ORM\Entity(repositoryClass="AppBundle\Repository\PostRepository")
*/
class Post
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ES\Property(type="string")
* #ORM\Column(name="title", type="string", length=255, nullable=true)
*/
private $title;
/**
* #var User
*
* #ORM\ManyToOne(targetEntity="User")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
/**
* #var string
*
* #ES\Property(name="user", type="string")
*/
private $esUser;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
*
* #return Post
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set user
*
* #param \AppBundle\Entity\User $user
*
* #return Post
*/
public function setUser(\AppBundle\Entity\User $user = null)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return \AppBundle\Entity\User
*/
public function getUser()
{
return $this->user;
}
/**
* #return string
*/
public function getEsUser()
{
return $this->esUser;
}
/**
* #param string $esUser
*/
public function setEsUser($esUser)
{
$this->esUser = $esUser;
}
}
AppBundle/Document/Post
<?php
namespace AppBundle\Document;
use ONGR\ElasticsearchBundle\Annotation as ES;
use AppBundle\Entity\Post as OldPost;
/**
* #ES\Document()
*/
class Post extends OldPost
{
}
My entities are as follows:
a Portfolio has many Series [OneToMany-ManyToOne]
a Serie has many Assets [OneToMany-ManyToOne]
a Serie can have an Asset (as a thumbnail) [OneToOne]
I also used the cascade={"remove"} property in order to remove the Series and Assets if the corresponding Portfolio has been deleted.
Serie
/**
* Serie
*
* #ORM\Table(name="serie")
* #ORM\HasLifecycleCallbacks
*/
class Serie
{
/**
* #var \Portfolio
*
* #ORM\ManyToOne(targetEntity="Portfolio", inversedBy="series")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="portfolio", referencedColumnName="id")
* })
*/
private $portfolio;
/**
* #var \Image
*
* #ORM\OneToOne(targetEntity="Image")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="preview", referencedColumnName="id", nullable=true, onDelete="SET NULL")
* })
*/
private $preview;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\OneToMany(targetEntity="Asset", mappedBy="serie", cascade={"remove"})
**/
private $assets;
/**
* #ORM\PostRemove
*/
public function removeDirectory(){
rmdir(...);
}
Asset
/**
* #ORM\Table(name="asset")
* #ORM\HasLifecycleCallbacks
*/
class Asset
{
/**
* #var \Serie
*
* #ORM\ManyToOne(targetEntity="Serie", inversedBy="assets")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="serie", referencedColumnName="id")
* })
*/
protected $serie;
/**
* #ORM\PostRemove()
*/
public function removeFile()
{
unlink($this->filename);
}
I put the cascade={"remove"} property in Asset's side of the relationship because the other way around would result in a foreign key constraint error.
The postRemove method in Serie is called but not the one in Asset. Am I doing something wrong, is there a way to fix it?
I have a many to one relation between my entities.
An application can have activities
<?php
namespace AppAcademic\ApplicationBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Activity
*
* #ORM\Table()
* #ORM\Entity
*/
class Activity
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="AppAcademic\ApplicationBundle\Entity\Application", inversedBy="activity")
* #ORM\JoinColumn(name="application_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $application;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
public function setApplication($application)
{
$this->application = $application;
return $this;
}
public function getApplication()
{
return $this->application;
}
/**
* Set title
*
* #param string $title
* #return Activity
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
}
And the application
/**
* Application
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="AppAcademic\ApplicationBundle\Entity\ApplicationRepository")
*/
class Application
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="AppAcademic\ApplicationBundle\Entity\Activity", mappedBy="application")
*/
protected $activities;
...
}
I have this in the profiler:
AppAcademic\ApplicationBundle\Entity\Application
The mappings AppAcademic\ApplicationBundle\Entity\Application#activities and AppAcademic\ApplicationBundle\Entity\Activity#application are inconsistent with each other.
AppAcademic\ApplicationBundle\Entity\Application
The mappings AppAcademic\ApplicationBundle\Entity\Application#activities and AppAcademic\ApplicationBundle\Entity\Activity#application are inconsistent with each other.
AppAcademic\ApplicationBundle\Entity\Activity
The association AppAcademic\ApplicationBundle\Entity\Activity#application refers to the inverse side field AppAcademic\ApplicationBundle\Entity\Application#activity which does not exist.
For the mapping annotation ManyToOne on the Activity::$application property, the attribute
inversedBy="activity"
This should be
inversedBy="activities"