Abp.io One-To-One Relationships - .net-core

I'm working on an application where I need to create two tables, however, one of the tables is a look-up table that has the following properties. However, Abp Suite only allows the joining of string columns. As you can see from the rough sample below, I need the IDs from the swine table and the enum. I've looked and asked this question several times and I've not been able to get an answer. So the goal I'm trying to accomplish here is to keep track of my pig relationships. I hope this makes sense.
PigRelations Table
RelationshipId (PK)
SwineId (FK - to swine table)
OffspringId (FK - to swine table)
RelationshipId (Enum Id such as 0,1,2,3,4...)
Main Swine Table
SwineId (PK)
Other properties
I've tried to manually create this, however, I'm not sure what all needs to be updated etc. I'm using Abp Suite to create most of my entities. Below is the code for the DbContext class. However, I'm stuck at this stage.
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Pig>()
.HasMany(p => p.Parents)
.WithOne(p => p.Offspring)
.HasForeignKey(p => p.PigId);
builder.Entity<Pig>()
.HasMany(p => p.Offspring)
.WithOne(p => p.Pig)
.HasForeignKey(p => p.OffspringId);
builder.Entity<PigRelation>()
.HasOne(p => p.Pig)
.WithMany(p => p.Offspring);
builder.Entity<PigRelation>()
.HasOne(p => p.Offspring)
.WithMany(p => p.Parents);
builder.ConfigureSwine();
}

Related

EntityType and many to many with extra field relation presented as dropdown (select)

I created a form like that
$builder->add('employees', EntityType::class, [
'class' => ActivityEmployee::class,
'choice_label' => function (ActivityEmployee $employee) {
return sprintf('%s %s', $employee->getEmployee()->getName(), $employee->getEmployee()->getLastName());
},
'multiple' => true,
])
As a result it presents already existing data fine. It shows me all employees with relation to edited activity.
However as choices there should be all employess to choose (employee entity) and as selected data only employess in activityEmployee relation like right now.
I tried to add a query_builder option to provide lists of all employess, but I can only use EntityRepository which means ActivityEmployeesRepository not EmployeesRepository per se.
A can't figure out how to implement it. Basically such relation can be done by CollectionType of custom activityEmployeeType but I'd like to use multi-select for selecting employees.
I can use another approach to not mapping my employees field to entity like that
$currentEmployees = [];
foreach ($activity->getEmployees() as $activityEmployee) {
$currentEmployees[] = $activityEmployee->getEmployee();
}
$builder->add('employees', EntityType::class, [
'class' => Employee::class,
'choice_label' => function (Employee $employee) {
return sprintf('%s %s', $employee->getName(), $employee->getLastName());
},
'mapped' => false,
'multiple' => true,
'data' => $currentEmployees,
]);
It works fine, but I need to deal with updating relation by myself. Which is ok, however I wonder how to achieve such thing in first approach.
Implementation details matter. As far as I can understand you have the following entities:
Activity (entity)
- employees (OneToMany -> ActivityEmployee)
ActivityEmployee (entity)
- activity (ManyToOne -> Activity)
- employee (ManyToOne -> Employee)
Employee (entity)
- activities (OneToMany -> ActivityEmployee) - this one might be missing, actually.
Now you apparently don't hide any implementation details. Meaning, your Activity::getEmployees() returns []ActivityEmployee.
I would have done it like this:
class Activity {
/** #ORM\OneToMany(targetEntity=ActivityEmployee::class) */
private $activityEmployees;
/** #return Employee[] */
public function getEmployees() :Collection {
return $this->activityEmployees->map(function(ActivityEmployee $ae) {
return $ae->getEmployee();
});
}
public function addEmployee(Employee $employee) {
// check, if the employee is already registered, add only then!
if(!$this->getEmployees()->contains($employee)) {
$this->activityEmployees->add(new ActivityEmployee($this, $employee));
}
}
public function removeEmployee(Employee $employee) {
foreach($this->activityEmployees as $activityEmployee) {
if($activityEmployee->getEmployee() === $employee) {
$this->activityEmployees->removeElement($activityEmployee);
}
}
}
}
This way, you hide away how Activity handles the employees and to the outside world (and specifically the PropertyAccessor, that the form component uses) it appears as if Activity has a property employees which are actually Employee[].
If you implement it like this, your first form should actually just work (obviously exchanging ActivityEmployee for Employee) - under the assumption that I didn't make some major mistake. Of course I would also add methods like getActivityEmployees when I would actually specificially need the relation objects.
This whole thing certainly is less beautiful if your many-to-many can contain duplicates.
IF your ActivityEmployee actually has NO other properties besides activity and employee, you could obviously replace the whole thing with a #ORM\ManyToMany and just work with Employee[] instead of the ActivityEmployee[]. However, I assume you have some additional columns like created or something.

ASP.NET Migration expects AspNetUsers even though its renamed in OnModelCreate

I'm trying to rename my Identity 2.0 tables to have my app name before them. So I've overridden OnModelCreating:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<IdentityUser>().ToTable("appname_Users");
modelBuilder.Entity<IdentityRole>().ToTable("appname_Roles");
modelBuilder.Entity<IdentityUserClaim>().ToTable("appname_UserClaims");
modelBuilder.Entity<IdentityUserLogin>().ToTable("appname_UserLogins");
modelBuilder.Entity<IdentityUserRole>().ToTable("appname_UserRoles");
}
I deleted the Migrations folder, made sure these tables did not exist, and ran:
enable-migrations -Force
update-database
When I try to login to the site, it still says it can't find dbo.AspNetUsers.
When I check the migration script, I do see the following:
CreateTable(
"dbo.AspNetUsers",
c => new
{
Id = c.String(nullable: false, maxLength: 128),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.appname_Users", t => t.Id)
.Index(t => t.Id);
Where is it even getting the idea that it needs AspNetUsers? I've scoured documentation and can't find out what's going on. Any help would be appreciated.
Delete all your identity tables from database manually. Then in onModelCreating insert
modelBuilder.Entity<ApplicationUser>().ToTable("appname_Users");
modelBuilder.Entity<IdentityUserRole>().ToTable("appname_UserRoles");
modelBuilder.Entity<IdentityUserLogin>().ToTable("appname_UserLogins");
modelBuilder.Entity<IdentityUserClaim>().ToTable("appname_UserClaims");
modelBuilder.Entity<IdentityRole>().ToTable("appname_Roles");
if you create custom identity classes then use
modelBuilder.Entity<ApplicationUser>().ToTable("appname_Users");
modelBuilder.Entity<ApplicationRole>().HasKey<string>(r => r.Id).ToTable("appname_Roles");
modelBuilder.Entity<ApplicationUser>().HasMany<ApplicationUserRole>((ApplicationUser u) => u.UserRoles);
modelBuilder.Entity<ApplicationUserRole>().HasKey(r => new { UserId = r.UserId, RoleId = r.RoleId }).ToTable("appname_UserRoles");
This way works for me.........

Symfony2 - Doctrine2 Association Class

I am having trouble converting a ManyToMany relationship to utilize an Association Class. I have 2 classes Asset and User Currently I have a ManyToMany on Asset#users and User#Assets
I needed to add some extra data so I now have 3 classes Assets Users and Associations. Asset#users and User#assets are now a OneToMany with Associations and Associations#Asset and Association#User are a ManyToOne
In my previous implementation I had a FormType for Assets with the following:
->add('users', 'entity', array(
'class' => 'AppUserBundle:User',
'query_builder' => function($er) use($username){
return $er->createQueryBuilder('u')
->where('u.username != ?1')
->setParameter(1,$username);
},
'expanded' => "true",
"multiple" => "true"
))
This would display a list of users (except for the current user), and any user already associated would be checked.
With my new Schema I get the error Found entity of type App\UserBundle\Entity\User on association App\AssetBundle\Entity\Asset#users, but expecting App\AssetBundle\Entity\UserAssociation
This makes sense as it is now looking for the UserAssociation and not the User
I need the form to display All Users (minus the logged in user) and show which users are already Associated with the Asset. I am lost at how to do this.

Symfony2: Two owning sides on many-to-many?

Let's say I have an Person entity and a ResearchArea entity. There is a ManyToMany Doctrine relationship between them with simple join table, a Person can have multiple ResearchAreas and a ResearchArea can have multiple Persons.
In my database, there are thousands of Persons, but only around 10 ResearchAreas.
On the Person edit form, I want to present a checkbox list for each ResearchArea. This is easy in a form builder:
->add('researchAreas', 'entity', array(
'label' => false,
'class' => 'AcmeDemoBundle:ResearchArea',
'property' => 'title',
'required' => false,
'multiple' => true,
'expanded' => true,
))
When submitting the form and binding the request data, this works well and the ManyToMany is handled nicely.
However, when editing a ResearchArea entity, I want to also provide a way to manage the Persons associated with that area. However, I can't use the same entity form type as I did above because there are so many Person entities.
Instead what I want is a collection form type, where the user can add/remove Person entities. I can do this by rendering text fields for each "row" and accept an ID of the person to add.
To support an approach like that, I need to change the relationship from a ManyToMany to a OneToMany -> ManyToOne and make the join table its own entity. But in doing that, then I can no longer use the nice checkboxes on the Person form which would only work with a direct ManyToMany.
Am I just making this all too complicated? Is there a solution to this?
For a similar use case I simply used the entity field type, rendered as a multiselect (multiple => true, expanded => false) and improved with a jquery plugins. This worked quite well, but I didn't have thousands of entities.
->add('persons', 'entity', array(
'label' => false,
'class' => 'AcmeDemoBundle:Person',
'required' => false,
'multiple' => true,
'expanded' => false,
))
I used a jquery plugin (like Chosen) to improve the multiselect and make it more user friendly. With chosen you can simply use:
$(".chosen-select").chosen();
With a bit of custom css you might be able to style it to fit your needs.
I too, would advocate the entity approach, but I used select2 (http://ivaynberg.github.io/select2/) instead.
In Many-to-Many Ajax Forms (Symfony2 Forms) I have some fairly detailed implementation examples.
Select2 can support ajax loading and searching, so the 3000 researchers is not a big deal.

symfony doctrine with two entity managers manually bind select tag

I googled a lot trying to find an answer for my problem, but no luck.
I have two tables in two different db db1.pages and db2.layouts I have defined the two entity managers and I can successfully manage the two as unrelated entities.
My problem is on table db1.pages, I have a layout_id column which contains a reference to the db2.layouts.id column, I didn't created an association between the two because, from my understanding, is not supported by doctrine. I successfully managed to display a selection list of possible layouts on the PageType using the following code:
$builder
->add('name', null, array('label' => 'Name'))
->add('label', null, array('label' => 'Title'))
->add('home', null, array('label' => 'Homepage'))
->add('layoutId', 'entity', array(
'class' => 'USGMCMSBundle:Layouts',
'property' => 'label',
'label' => 'Layout',
))
but it doesn't select the correct layout matched by the value stored into layoutId. Is there a way to select the current selected item and keep it in sync when the user choose a different one?
Maybe using an EventSubscriber? but on which stage? Or is there a better solution?
Hope I made myself clear (sorry for my english)

Resources