Symfony Entity one-to-one relation - symfony

Can you look at the relationship between entities, unfortunately all the time is not working properly. I will be grateful for the help. This is a relation 1 to 1.
User Entity
/**
*
* #ORM\Column(type="string")
*
* #ORM\OneToOne(targetEntity="Structure", mappedBy="user")
*/
protected $structure;
Structure Entity
/**
* #var string
*
* #ORM\Column(name="user", type="string", length=50, unique=false)
* #ORM\OneToOne(targetEntity="User", inversedBy="structure")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;

Try this one ...
/**
* #ORM\OneToOne(targetEntity="User",
* inversedBy="structure")
*/
private $user;
/**
*#ORM\OneToOne(targetEntity="Structure",mappedBy="user",cascade={"persist","remove"})
*/
private $structure;

As suggested by #Albeis in a comment:
Try to remove #ORM\Column of both sides...relations itself don't
have column..Regards Check
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#one-to-one-bidirectional
This solved my problem. The issue was about to remove #ORM\Column of both sides. Thanks for help.

You can't use mappedBy with #OneToOne. You should use inversedBy on both sides.

/**
* #ORM\OneToOne(targetEntity="Core\UserBundle\Entity\Profile", mappedBy="user")
*/
private $profile;
/**
* #ORM\OneToOne(targetEntity="Core\UserBundle\Entity\User", inversedBy="profile")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id",onDelete="CASCADE")
*/
private $user;
user_id inside profile entity. if you set onDelete="CASCADE", if you delete user, profile will deleting automatically by mysql.

Related

Symfony OneToMany with associative array : new row inserted instead of update

I have to internationalize an app and particularly an entity called Program. To do so, I created an other entity ProgramIntl which contains a "locale" attribute (en_GB, fr_FR, etc) and strings which must be internationalized. I want the programIntl attribute in Program to be an associative array (with locale as key).
We have an API to read/write programs. GET and POST works fine but when I want to update data (PUT), the programIntl is not updated: an insert query is launched (and fails because of the unique constraint, but that's not the question).
Here is the code:
In Program.php:
/**
* #var
*
* #ORM\OneToMany(targetEntity="ProgramIntl", mappedBy="program", cascade={"persist", "remove", "merge"}, indexBy="locale", fetch="EAGER")
* #ORM\JoinColumn(nullable=false, onDelete="cascade")
* #Groups({"program_read", "program_write"})
*/
private $programIntl;
public function addProgramIntl($programIntl)
{
$this->programIntl[$programIntl->getLocale()] = $programIntl;
$programIntl->setProgram($this);
return $this;
}
public function setProgramIntl($programIntls)
{
$this->programIntl->clear();
foreach ($programIntls as $locale => $programIntl) {
$programIntl->setLocale($locale);
$this->addProgramIntl($programIntl);
}
}
public function getProgramIntl()
{
return $this->programIntl;
}
In ProgramIntl.php:
/**
* #ORM\Entity(repositoryClass="App\Repository\ProgramIntlRepository")
* #ORM\Table(name="program_intl",uniqueConstraints={#ORM\UniqueConstraint(name="program_intl_unique", columns={"program_id", "locale"})})
*/
class ProgramIntl
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
* #Groups({"program_read", "program_write"})
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Program", inversedBy="programIntl")
* #ORM\JoinColumn(nullable=false)
*/
private $program;
/**
* #ORM\Column(type="string", length=5, options={"fixed" = true})
*/
private $locale;
/**
* #ORM\Column(type="string", length=64)
* #Assert\NotBlank()
* #Groups({"program_read", "program_write"})
*/
private $some_attr;
/* ... */
}
Any idea of what could be the reason of the "insert" instead of "update" ?
Thanks
I forgot to mention that we use api-platform.
But I found the solution myself. In case anyone is interested, adding the following annotation to classes Program and ProgramIntl solved the problem:
/* #ApiResource(attributes={
* "normalization_context"={"groups"={"program_read", "program_write"}},
* "denormalization_context"={"groups"={"program_read", "program_write"}}
* }) */

Symfony 3 - Multiple relationships to the same Data Model

I have the following code in Symfony 3:
A class Appointment
<?php
/**
* Appointment
*
* #ORM\Entity
* #ORM\Table(name="ev_appointment")
*/
class Appointment
{
/**
* #ORM\OneToMany(targetEntity="EmailForward", mappedBy="_appointment")
*/
private $_email_forwards;
/**
* #ORM\OneToMany(targetEntity="ParticipationRequest", mappedBy="_appointment")
*/
private $_participation_requests;
}
A class EmailForward
<?php
/**
* #ORM\Entity
* #ORM\Table(name="ev_email_forward")
*/
class EmailForward
{
/**
* #ORM\ManyToOne(targetEntity="Appointment" , inversedBy="_email_forwards")
* #ORM\JoinColumn(name="ev_appointment_id", referencedColumnName="id")
*/
private $_appointment;
/**
* #ORM\Column(type="string", length=255, name="email", nullable=true)
*/
private $_email;
/**
* #ORM\Column(type="datetime", name="forwarded_at", nullable=true)
*/
private $_forwarded_at;
/**
* #ORM\Column(type="string", length=255, name="source", nullable=true)
*/
private $_source;
}
A class ParticipationRequest
<?php
/**
* #ORM\Entity
* #ORM\Table(name="ev_participation_request")
*/
class ParticipationRequest
{
/**
* #ORM\ManyToOne(targetEntity="Appointment", inversedBy="_participation_requests")
* #ORM\JoinColumn(name="ev_appointment_id", referencedColumnName="id")
*/
private $_appointment;
/**
* #ORM\Column(type="string", length=255, name="email", nullable=true)
*/
private $_email;
/**
* #ORM\Column(type="datetime", name="forwarded_at", nullable=true)
*/
private $_forwarded_at;
/**
* #ORM\Column(type="string", length=255, name="source", nullable=true)
*/
private $_source;
}
Now seems to me like I have 2 relationships with 2 Entities that have the exact same structure. So I am wondering, what is the right way to go?
On the one hand I could leave it as it is, because it does, work. But again, if some information was to be the same in both fields, it seems kinda like a waste to have 2 DB entries with the exact same info, and also harder to mantain afterwards.
Is there a more intelligent approach to solve this issue?
You could use e.g. Single Table Inheritance.
By that you only define the structure for EmailForward and ParticipationRequest once and all data will be persisted in one table in the database. During ORM mapping Doctrine will recognize which type you're using and instantiate the correct Object for you.
I don't see how to solve the 'if data is same in both relations it will be peristed twice' because
if it was always the same you wouldn't need two relations
there is no real way to keep it in one persistence - only option I see would be to create another relation from EmailForward and ParticipationRequest which keeps the data which might be needed twice and is referenced from both Objects then.

Symfony: How to use Assert/Validation for ArrayCollection

In Symfony 3, its not allowed to use cascade_validation anymore. So you have to make an Assert for the types. But it doesn't work, the form is valid even when a field is BLANK but the Assert said NOtBlank. I have a class Participants and I want to check the Adults ArrayCollection when checking my participant Model.
//Participant Model
/**
* #var ArrayCollection
* #Assert\All({
* #Assert\Type(type="My\WebsiteBundle\Model\Adult"),
* })
*/
protected $adults;
//Adult Model
class Adult
{
/**
* #var string
*
* #Assert\NotBlank()
*/
protected $salutation;
/**
* #var string
*
* #Assert\NotBlank()
*/
protected $firstname;
/**
* #var string
*
* #Assert\NotBlank()
*/
protected $lastname;
You should use the Valid assetion as described here http://symfony.com/doc/current/reference/constraints/Valid.html in the doc
As example:
/**
* #var ArrayCollection
*
* #Assert\All({
* #Assert\Type(type="My\WebsiteBundle\Model\Adult"),
* })
* #Assert\Valid
*/
protected $adults;
Hope this help

User Information on entities symfony

I started using symfony not long ago and at the moment I'm struggling with this problem:
I decided to have "who" information at entity level so I have defined these additional 4 prameters for every entity:
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\User")
* #ORM\JoinColumn(name="created_by", referencedColumnName="id")
*/
private $createdBy;
/**
* #var \DateTime
*
* #ORM\Column(name="created_at", type="datetime")
*/
private $createdAt;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\User")
* #ORM\JoinColumn(name="updated_by", referencedColumnName="id")
*/
private $updatedBy;
/**
* #var \DateTime
*
* #ORM\Column(name="updated_at", type="datetime", nullable=true)
*/
private $updatedAt;
My problem is now where and how I should populate createdBy and updatedBy. ATM I do that in my controller before persisting to the database. Thou I encountered a problem when a entity is a property of another entity and lets say I have an entity called Post that has a property images of type Document the entities Post and Document both have "who" information on them and images property inside Post is defined as follows:
/**
* #var array
*
* #ORM\ManyToMany(targetEntity="Nisand\DocumentsBundle\Entity\Document", cascade={"persist"})
* #ORM\JoinTable(name="blog_documents",
* joinColumns={#ORM\JoinColumn(name="post_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="document_id", referencedColumnName="id")}
* )
*/
private $images;
For Post suppose I set createdBy in the controller before persisting but on Document how should that work cause that will be persisted by the cascade rule?
How do you guys handle in your applications the "who" columns?
Try this bundle: StofDoctrineExtensionsBundle and use Blameable extension.
You will need set current user with BlameableListener. And it will cover your use case.
Documentation for Blameable is here: https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/blameable.md

(doctrine2 + symfony2) cascading remove : integrity constraint violation 1451

First, sorry for my poor English...
I got four entities : User, Application, Bundle & Entity. Here are their relations (with cascading persist & remove, see code below) :
User 1-n Application
Application 1-n Bundle
Bundle 1-n Entity
It's working fine. But an User can have two of his entities as default, and I need to access them directly.
So I add on User two fields, entity1 & entity2, with a 1-1 relation. And now my app crashes :
An exception occurred while executing 'DELETE FROM bundle WHERE id = ?' with params {"1":13}:
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (`misc`.`entity`, CONSTRAINT `FK_E284468F1FAD9D3` FOREIGN KEY (`bundle_id`) REFERENCES `bundle` (`id`))
I tried several things, including those founded in this post, but I wasn't able to fix it.
Any help be welcome, thanks in advance.
EDIT : I need to point out that User->Entity relations are optionnal : User's entity1 & entity2 can be null. The error happens even if they are null both.
Here are my entities definitions :
# User :
/**
* #ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Application", mappedBy="user", cascade={"remove"}, orphanRemoval=true)
* #ORM\OrderBy({"name" = "ASC"})
*/
protected $applications;
/**
* #ORM\OneToOne(targetEntity="\sfCommands\ContentBundle\Entity\Entity")
* #ORM\JoinColumn(name="entity1_id", referencedColumnName="id")
*/
private $entity1;
/**
* #ORM\OneToOne(targetEntity="\sfCommands\ContentBundle\Entity\Entity")
* #ORM\JoinColumn(name="entity2_id", referencedColumnName="id")
*/
private $entity2;
#Application :
/**
* #ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Bundle", mappedBy="application", cascade={"remove"}, orphanRemoval=true)
* #ORM\OrderBy({"name" = "ASC"})
*/
protected $bundles;
/**
* #ORM\ManyToOne(targetEntity="\sfCommands\UserBundle\Entity\User", inversedBy="applications", cascade={"persist"})
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
#Bundle :
/**
* #ORM\ManyToOne(targetEntity="\sfCommands\ContentBundle\Entity\Application", inversedBy="bundles", cascade={"persist"})
* #ORM\JoinColumn(name="application_id", referencedColumnName="id")
*/
protected $application;
/**
* #ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Entity", mappedBy="bundle", cascade={"remove"}, orphanRemoval=true)
* #ORM\OrderBy({"name" = "ASC"})
*/
protected $entitys;
#Entity :
/**
* #ORM\ManyToOne(targetEntity="\sfCommands\ContentBundle\Entity\Bundle", inversedBy="entitys", cascade={"persist"})
* #ORM\JoinColumn(name="bundle_id", referencedColumnName="id")
*/
protected $bundle;
So, thanks to this French forum, I fixed the problem.
I needed to add nullable=true & onDelete="SET NULL" in #ORM\JoinColumn
Here is the workable configuration, maybe it will help someone :
#User.
/**
* #ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Application", mappedBy="user", cascade={"remove"}, orphanRemoval=true)
* #ORM\OrderBy({"name" = "ASC"})
*/
protected $applications;
/**
* #ORM\OneToOne(targetEntity="\sfCommands\ContentBundle\Entity\Entity")
* #ORM\JoinColumn(name="entity1_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
*/
private $entity1;
/**
* #ORM\OneToOne(targetEntity="\sfCommands\ContentBundle\Entity\Entity")
* #ORM\JoinColumn(name="entity2_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
*/
private $entity2;
#Application.
/**
* #ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Bundle", mappedBy="application", cascade={"remove"}, orphanRemoval=true)
* #ORM\OrderBy({"name" = "ASC"})
*/
protected $bundles;
/**
* #ORM\ManyToOne(targetEntity="\sfCommands\UserBundle\Entity\User", inversedBy="applications", cascade={"persist"})
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
*/
protected $user;
#Bundle.
/**
* #ORM\ManyToOne(targetEntity="\sfCommands\ContentBundle\Entity\Application", inversedBy="bundles", cascade={"persist"})
* #ORM\JoinColumn(name="application_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
*/
protected $application;
/**
* #ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Entity", mappedBy="bundle", cascade={"remove"}, orphanRemoval=true)
* #ORM\OrderBy({"name" = "ASC"})
*/
protected $entitys;
#Entity.
/**
* #ORM\ManyToOne(targetEntity="\sfCommands\ContentBundle\Entity\Bundle", inversedBy="entitys", cascade={"persist"})
* #ORM\JoinColumn(name="bundle_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
*/
protected $bundle;
Use onDelete="CASCADE" if you are using annotation
/**
* #ORM\ManyToOne(targetEntity="Report", inversedBy="responses")
* #ORM\JoinColumn(name="reportId", referencedColumnName="id",onDelete="CASCADE")
*/
Use onDelete: CASCADE if you are using yml
joinColumn:
name: pid
referencedColumnName: id
onDelete: CASCADE
onDelete="CASCADE" also works fine. But don't forget to run app/console doctrine:schema:update --force before DB level changes will take an effect.
orphanRemoval some times doesn't work because it depends on (gets schedulled in) PersistencCollection. And we might be calling ArrayCollection#removeElement().
Following is a snippet of the PersistencCollection#remove()
if ($this->association !== null &&
$this->association['type'] & ClassMetadata::TO_MANY &&
$this->owner &&
$this->association['orphanRemoval']) {
$this->em->getUnitOfWork()->scheduleOrphanRemoval($removed);
}
and ArrayCollection does not do that.

Resources