Doctrine ORM : Joined table inheritance - symfony

Related to this previous topic : Database : Account table with Joined Tables Inheritance
I've created a joined tables inheritance in my database (MySQL) :
client_id attributes are both PK and FK.
Now I need to represent it with Doctrine ORM but I can't find a solution of how to make that.
I created, with Symfony commands, the Client entity and for the ClientCompany entity I don't know which relationship to use nor how to use the Client ID as primary key of the CompanyClient entity.
Does anyone know how to do it?

First of all, be really carefull with inheritance, this is a really good feature of doctrine but need to be used with a lot of cautious because of it's counterpart.
For your case, i would advise to not try to put "person" and "company" under the same abstract "client" class for conception reason i already explain in this answer since a company and a person are totally different things: Symfony 6 inheritance Mapping : How to submit a form depends on a clicked radio button?
But i will still answer on how to properly do a join table inheritance :
AbstractClient.php
#[Entity]
#[InheritanceType('JOIN_TABLE')]
#[DiscriminatorColumn(name: 'discr', type: 'string')]
#[DiscriminatorMap(['person' => Person::class, 'company' => Company::class])]
abstract class Client
{
// you do not need clientType since it is hold by the "discr" column
// And if you want to know what type your client is you can do it using
// if($client instanceof Person::class) { do something}
}
Person.php
#[Entity]
class Person extends Client
{
// ...
}
Company.php
#[Entity]
class Company extends Client
{
// ...
}
Take a look at #[InheritanceType('JOIN_TABLE')]
It will create one table for each entity and they will share ids.
If you create a company with id 2, there will be a client with id 2. So a Person with id 2 will never be possible.
But if you use 'SINGLE_TABLE' it will create only one table with all the field of all the entity which will be empty depending on which child you inserted inside.
But again i strongly advise you to not use join table inheritance for your usecase

Related

How can I inherit using TPT using EntityFramework from different Views?

Supposing I have two tables Meetings and Attendees.
My database looks like this.
// Table Meeting
Id
Description
// other properties ...
// Attendee
Id
Name
MeetingId
// other properties ...
I have two views that are mapped to these tables. One is ViewMeetings with just the meeting data the other ViewMeetingAttendees joined with Attendees.
I want to use table per type(TPT), mapping each table to a view.
public abstract class MeetingBase
{
// Some properties here
}
public class ViewMeeting : MeetingBase
{
}
public class ViewMeetingAttendee : MeetingBase
{
public String AttendeeName { get;set; }
}
// Configuration
moduleBuilder.Entity<ViewMeeting>().ToTable("ViewMeetings");
moduleBuilder.Entity<ViewMeetingAttendee>().ToTable("ViewMeetingAttendees");
// NOTE fixed the ViewMeeting error as stated in HansVG answer below.
Every time I try to run this code I get an error Invalid Column Name "Discriminator"
I understand that the entity framework is trying to resolve the types as a table per hierarchy(TPH). However, I still want to map the properties using inheritance without the inferred TPH. This is reasonable since all the columns are the same except for one. I have ten other columns and two views. Also I have a single meeting entity needing most of the same columns for CRUD operations.
Is there a way to keep the inheritance but lose the discriminator error? [NotMapped] is not an option since I am still pulling the data from the database. Also, I don't prefer to join the tables locally using LINQ since there are joined entities that don't need to be mapped otherwise.
You defined 'ViewMeeting' twice and didn't configure 'ViewMeetingAttendee'.
Your configuration should be:
moduleBuilder.Entity<ViewMeeting>().ToTable("ViewMeetings");
moduleBuilder.Entity<ViewMeetingAttendee>().ToTable("ViewMeetingAttendees");

Symfony2 - extending existing non-abstract entity?

Let's say I have a Setting entity with some fields like IntValue, dateValue, stringValue and some linked entities, like countries (ManyToMany to entity Country), languages (ManyToMany to Language) etc.
Settings are created by users and assigned to specific objects (not important here, but I wanted to clarify).
Now I suddenly need to have UserDefaultSetting, which will be the same, but with additional user field (ManyToOne to User entity).
I tried to extend existing Setting entity class with one more field added. The problem is, as I looked at the schema update SQL, it created new table for the new entity, but without all the tables needed to ORM connections (mostly ManyToMany). Just one table with "scalar" fields.
So previously I've had setting table with int_value, date_value etc. but also setting_country and setting_language tables, linking ManyToMany relations. After creating child entity, Doctrine created only user_default_setting table with int_value, date_value etc. and additionally user_id column, but I can't see any relation/link tables.
I know I should've been do it with abstract base entity class, but at the time I started, I didn't know that and now part of the project is on production (don't look at me like that, I blame the client) and I don't want to change that "base" class now. Can I inherit everything from non-abstract entity class in a way it will work?
UPDATE: everything explained. See Cerad's comment. Thanks!

symfony create a table with attribute from multiple entities

I have different entities like:
Element, Status, Action, Contributor ...
And I need to create an entity, let say Synonym, which can be related to one of these previous entities
My entity Synonym, can be a synonym of the entity Element or Status or Action ...
I try to use a MappedSuperclass but I'm really lost.
I think this kind of relation is common and must be easy to create with doctrine but I can't find the good way.

Embedded form persistence : get or create

I work with Symfony2 and doctrine. I currently have an entity called Person. This entity is related to some other entities as a One-To-Many relation (as a Many-To-One unidirectional relation). I want each Person entity to be unique (what I have done in my database by using the UniqueConstraint annotation).
To be clear, I will assume that I have two entities called Home and Car which both have a Many-to-One relation to the target entity Person.
Then I am using forms to create or edit my entities Car and Home. In these forms, I display a embedded form to create a Person entity or select one existing. I explain : the first field of my embedded form is the name of the person. As the user type the name of the person, a list of the existing persons is displayed (using JQuery Autocomplete UI) and if the user select one of them the other fields are autocompleted.
The issue is when the user submit the form with an existing person, I caught an Integrity Error (and I know why, because of my Unique Constraint).
One of the first workaround is to add the id field as an hidden input in the embedded form.
But then the user can edit the other fields and corrupt the current entity.
So no.
Another one could be to prevent the persist in the controller if the Person already exist, but as I am using this in many other entities. I will have to duplicate my code, and I do not want to, as the unique constraint is related to the Person entity and not to the Car or Home entity.
So no again.
The workaround that I am working about is to use a PrePersist Listener waiting for a Person entity, but I do not know how to cancel persist (and maybe it is not possible).
I have the following code :
public function prePersist(LifecycleEventArgs $args) {
$entity = $args->getEntity();
if($entity instanceof Personne) {
$em = $args->getEntityManager();
$persons = $em->getRepository('MyBundle:Person')->findBy(array(
// unique fields
));
if(count($persons) > 0) {
// ... ???
}
}
I have tried a $em->detach but it is useless as I am already persisting the entity.
What I want it is just a kind of a "Get Or Create". I explain, there are only two cases :
the Person entity (not persisted) has all the same fields that one existing in the database (excepted the id field), so the Person entity is the one in the database. I have to "replace" it by the one in the database ;
the Person entity (not persisted) is unique in the database, so we create a new one (persist).
Create your own getOrCreate() method and call it inside your listener.
See this post Symfony2 doctrine FindOneOrCreate
Another possibility would be the data transformers. http://symfony.com/doc/current/cookbook/form/data_transformers.html

Many to many relationship with junction table in Entity Framework?

I'm trying to create a many-to-many relationship in Entity Framework (code first), according to the following post: Database design for limited number of choices in MVC and Entity Framework?
However, I can't get it to work properly, and I'm sure I'm doing something very simple the wrong way. Here's the diagram I have no from my attempts:
The point of the junction table is that I need to have an extra property, Level, in the relationship, so I can't just go with a direct relationship between Consultant and Program. I added the ConsultantProgramLink entity manually in the designer, and then added associations to Program and Consultant respectively, selecting to add a FK for each, and then made them both primary keys. But when I do it like this it doesn't work as I expected:
If I had done a direct association between Consultant and Program, I would have been able to refer to, say, Consultant.Programs in my code. But that doesn't work now with the junction table. Is there any way to remedy this, or do I always have to go through the junction property (Consultant.ConsultantProgramLink.Programs)? In any case, even if I do try to go through the junction property it doesn't help. I can do Consultant.ConsultantProgramLink in my code, but another dot doesn't give me the navigation property Programs (which for some reason also became simply Program, why? Can I just rename them if I eventually get access to them at all?).
So what am I doing wrong? Why can't I access the properties through dot notation in my code?
Once you model a junction table as an entity you indeed lose direct many-to-many relation between Consultant and Program. That is how it works. You will either have direct many-to-many relation or additional properties in the junction table. Not both. If you want both you can try creating custom Programs property on Consultant and use linq query to get related programs:
public IEnumerable<Program> Programs
{
get
{
return this.ConsultantProgramLinks.Select(l => l.Program);
}
}
The example is also the explanation of your last problem. You can't have Program property on ConsultantProgramLink because it is a collection of related entities, not single entity (it should be called ConsultantProgramLinks). The property in ConsultantProgramLink entity is called simply Programbecause it represents single entity not collection.
Edit:
If you need each Program to be automatically associated with each Consultant you must enforce it when you are going to create new Program. Having junction table exposed as separate entity will probably allow you achieving it easily:
var program = new Program();
...
context.Programs.AddObject(program);
var ids = from c in context.Consultants
select c.Id;
foreach (var id in ids)
{
var link = new ConsultantProgramLink
{
ConsultantId = id,
Program = program
};
context.ConsultantProgramLinks.AddObject(link);
}
context.SaveChanges();
If you add new Consultant you will have to create links to all programs in the same way.
The disadvantage is that if you have for example 1000 consultants this construct will create 1001 database inserts where each insert will be executed in separate roundtrip to the database. To avoid it the only option is either use stored procedur or trigger on Program table.

Resources