Symfony Doctrine Delete only one site in one to one relationship - symfony

I probably can't think clearly anymore right now.
I have an entity Image related (oneToOne) to the entity airline.
Images can be replaced or deleted but airlines should still be kept after the images has been deleted.
How do I set this up in my Entity class?
Right now I have:
Airline entity
/**
* #ORM\OneToOne(targetEntity="AppBundle\Entity\Image", inversedBy="airline")
* #ORM\JoinColumn(name="image_id", referencedColumnName="id", onDelete="SET NULL")
*/
private $image;
Image entity
/**
* #ORM\OneToOne(targetEntity="AppBundle\Entity\Airline", inversedBy="image", orphanRemoval=true)
* #ORM\JoinColumn(name="airline_id", referencedColumnName="id", onDelete="SET NULL")
*/
private $airline;
But I get an ForeignKeyConstraintViolation Exception because it tries to delete the airline entity, too. I'm sure I have to set the onDelete differently or the orphanRemoval is misplaced_?
I already played around with it but couldn't figure it out.
Thank you!

Related

Doctrine throw error: "A new entity was found through the relationship" after removing entity

Let's say I have two entities, Project and User with relation.
Project.php
/**
* #var User
*
* #ORM\ManyToOne(targetEntity="User")
* #ORM\JoinColumn(onDelete="SET NULL")
*/
private $creator;
When I remove the User entity, the doctrine leaves the User object(without ID) in the Project entity. In a normal situation, this is fine but I am using DomainEvents. In this scenario, after removing the User entity, DomainEvent triggers saving some data in the DB and secondary saving data(after removing) throw this error. This happens because of now in the Project entity we have the detached(from the EM) User object without ID.
I thought about a listener, that will remove empty objects in the entity after removing, but I am not sure that is a good variant
What is the best variant for solving this error?
The onDelete option doesn't apply a cascade removing.
If you want to do so I think you should have to add the cascade={"remove"} option to the ManyToOne.
Try as following :
/**
* #var User
*
* #ORM\ManyToOne(targetEntity="User", cascade={"remove"})
* #ORM\JoinColumn(onDelete="SET NULL")
*/
private $creator;
Removing entity in doctrine

Many to Many, One to Many or Many to One

I am trying to get my head around Doctrine 2 ORM relationships, I thought I had the hang of it but after reading a few symfony cookbook entries, I suspect I am actually a little confused.
I currently have a system where a template can contain multiple modules (including more than one of each type) and multiple templates can use the same module.
I thought that this would warrant a ManyToMany relationship and indeed looking at my table, it seems to work quite well.
However I realised as I was writing the database query that I needed the modules to load in a certain order, which means my join table needs to have a third 'order_by' column. I've read that a true join table is only ever two columns.
Hence the confusion. What should I set this up as in my entities?
Like #Kris said - You'll go for One to Many towards middle entity. If you go for Many to Many instead then you won't have class file for middle table which is an issue in the most cases.
M-N assumption: ONE Student studies in MANY Courses and ONE COURSE can have MANY Students.
Both of the examples below give you this ERD in database but you want to go for ONE to MANY version.
MANY to MANY:
This will create StudentCourse entity in database but as you see no actual class file for you to deal with.
class Student
{
protected $id;
protected $name;
/**
* #ORM\ManyToMany(targetEntity="Course")
* #ORM\JoinTable(
* name="StudentCourse",
* joinColumns={#ORM\JoinColumn(name="studentId", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="courseId", referencedColumnName="id")}
* )
*/
private $course;
}
class Course
{
protected $id;
protected $name;
}
ONE TO MANY:
This will create StudentCourse entity in database and as you see there is an actual class file for you to deal with when coding e.g. persist() etc.
class Student
{
protected $id;
protected $name;
/**
* #ORM\OneToMany(targetEntity="StudentCourse", mappedBy="studentMap",
* cascade={"persist", "remove"})
*/
protected $studentInverse;
}
class StudentCourse
{
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Course", inversedBy="courseInverse")
* #ORM\JoinColumn(name="course", referencedColumnName="id",
* nullable=false, onDelete="CASCADE")
*/
protected $courseMap;
/**
* #ORM\ManyToOne(targetEntity="Student", inversedBy="studentInverse")
* #ORM\JoinColumn(name="student", referencedColumnName="id",
* nullable=false, onDelete="CASCADE")
*/
protected $studentMap;
}
class Course
{
protected $id;
protected $name;
/**
* #ORM\OneToMany(targetEntity="StudentCourse", mappedBy="courseMap",
* cascade={"persist", "remove"})
*/
protected $courseInverse;
}
EDIT:
onDelete="CASCADE" and cascade={"persist", "remove"} bits are not compulsory. They handle data redundancy. Is it bad to use redundant relationships?
You would need a OneToMany/ManyToOne
If you didn't need to save the order, it would be a manytomany, but since you do, that middle table now must be its own entity unfortunately.
So you would need the following Entities
Template
with a OneToMany to
TemplateModules (probably a better name for this one)
With a ManyToOne to
Modules

How do I map this relationship?

I'm having trouble mapping this relationship in Doctrine. I have a UseCase, which has many UseCaseSteps. A UseCaseStep has many sub-steps, which is a OneToMany on UseCaseStep. Here's the pertinent code I have atm:
/**
* UseCase
*
* #ORM\Table(name="use_cases")
* #ORM\Entity(repositoryClass="DesignCase\Bundle\Bundle\Entity\UseCaseRepository")
*/
class UseCase
{
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="Actor", inversedBy="use_cases", cascade={"persist", "remove"})
* #ORM\JoinTable(name="actors_use_cases")
*/
private $actors;
/**
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="UseCaseStep", mappedBy="useCase", cascade={"persist", "remove"})
* #ORM\OrderBy({"order" = "ASC"})
*/
private $steps;
}
/**
* UseCaseStep
*
* #ORM\Table(name="use_case_steps")
* #ORM\Entity(repositoryClass="DesignCase\Bundle\Bundle\Entity\UseCaseStepRepository")
*/
class UseCaseStep
{
/**
* #var integer
*
* #ORM\ManyToOne(targetEntity="UseCase")
*/
private $useCase;
/**
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="UseCaseStep", mappedBy="parent", cascade={"persist", "remove"})
* #ORM\OrderBy({"order" = "ASC"})
*/
private $subSteps;
/**
* #var UseCase
*
* #ORM\ManyToOne(targetEntity="UseCase")
*/
private $useCaseReference;
/**
* #var UseCaseStep
*
* #ORM\ManyToOne(targetEntity="UseCaseStep")
* #ORM\JoinColumn(nullable=true)
*/
private $parent;
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="BusinessRule", cascade={"persist", "remove"})
*/
private $businessRules;
}
That code isn't complete, but I think it has all the relevant information. What I want to do is create a new entity TestCase, which has many TestCaseSteps. A TestCase IS a UseCase with a little more information... same for TestCaseStep and UseCaseStep. IE, a TestCaseStep is a UseCaseStep with data input and expected output fields added to it. A user can create many TestCases from one UseCase.
I tried making UseCase and UseCaseStep #MappedSuperclass, but that doesn't have the desired effect. I get the obvious, "It is illegal to put an inverse side one-to-many or many-to-many association on mapped superclass" error. Plus, from the docs, I don't think that's the right approach anyway.
I'm having trouble wrapping my brain around it. Any thoughts? I hope I explained that well enough...
You have an issue there that is much more fundamental than Doctrine. You want to show inheritance in the database. For this approach there is no real correct solution. You could go with making an Entity TestCase that extends UseCase and overwrites the respective properties (need to make them protected) with another relation to TestCaseStep.
You do something similar with UseCaseStep and TestCaseStep. That way you have inheritance in the entities. Now you would have to make sure that you use another table and you end up with completely seperate entities database-wise. They just share the same properties in the entities but are seperate in database.
That approach would be in my opinion the easiest one to follow. Everything else seems to be very complicated as you cannot properly use one table with a different amount of properties for each entity. Each database table has a fixed set of properties that need to be reflected in an entity.
Another approach would surely be to just use the properties in the sub-entity that is used by this entity and create another relationship (many-to-one) between the TestCase and the UseCase or TestCaseStep and UseCaseStep respectively. But the latter approach isn't very easy and can end up being very complicated if you don't have a lot of knowledge about Doctrine, Symfony and databases in general.

Doctrine2 orphanRemoval ManyToMany only if there are no other records connected

My purpose is to remove the record in the entity artist only if there are no other records of the entity connected soundtrack.
I tried with orphanRemoval in this way:
Soundtrack.php
/**
* #Assert\NotBlank(message = "soundtrack.artists.blank")
* #ORM\ManyToMany(targetEntity="Artist", inversedBy="soundtrack", cascade={"persist", "remove"}, orphanRemoval=true)
* #ORM\JoinTable(name="soundtrack_artist")
* #ORM\OrderBy({"name" = "ASC"})
**/
private $artists;
Artist.php
/**
* #ORM\ManyToMany(targetEntity="Soundtrack", mappedBy="artists")
*/
private $soundtrack;
but when I delete an entity record soundtrack, also clears the record of the entity artist even if it is linked to other records soundtrack (I think this is what you should expect from orphanRemoval).
Is there a way to remove that record an "orphan" only when no other records connected?
I also tried just like this:
**Soundtrack.php**
/**
* #Assert\NotBlank(message = "soundtrack.artists.blank")
* #ORM\ManyToMany(targetEntity="Artist", inversedBy="soundtrack", cascade={"persist"}, orphanRemoval=true)
* #ORM\JoinTable(name="soundtrack_artist")
* #ORM\OrderBy({"name" = "ASC"})
**/
private $artists;
but does not delete the records entity artist ..
orphanRemoval option explicitly thinks, that owning side object is the only instance that references its children. To make it works you should detach child from parent (unset reference) to make child deleted. With Many-2-many associations you should detach entities on both side (owned and inversed)
See Docs
When using the orphanRemoval=true option Doctrine makes the assumption
that the entities are privately owned and will NOT be reused by other
entities. If you neglect this assumption your entities will get
deleted by Doctrine even if you assigned the orphaned entity to
another one.

Symfony: How do I annotate entity properties that are objects to get Doctrine to store a foreign key?

I'm still getting to grips with Symfony and Doctine and I appreciate this might sound overly simple.
I have at present two basic entities: WebSite (having id and canonicalUrl properties) and Job which has, as one property, a WebSite.
A Job has one WebSite; a WebSite can be referenced by many Jobs. Both are under the same namespace.
Relevant here is the Job entity:
/**
*
* #ORM\Entity
*/
class Job
{
/**
*
* #var integer
*
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
*
* #var WebSite
*/
protected $website;
}
In database terms, a persisted Job should be storing the id of the relevant WebSite.
Without any changes to the above, calling php app/console doctrine:migrations:diff generates a new migration for a table named Job with a single id field.
How do I annotate Job::website such that Doctrine knows to create an integer field and to get the value as the id of the Website object?
You must explicitly define the relationship. The shortest would be
/**
* #ORM\Entity
*/
class Job
{
/**
* #var WebSite
*
* #ORM\ManyToOne(targetEntity="Website")
*/
protected $website;
}
However, should you find yourself wanting to tweak the relationship to better suit your needs, have a look at the annotation reference (ManyToOne and JoinColumn for this particular case). There's also quite a comprehensive article about association mapping, which you might find interesting.

Resources