Laravel 5.8 hasOneThrough relationship problems - eloquent

Can someone PLEASE correct this! I have been banging my head on it on and off for two days! I have never managed to successfully make a hasOneThrough relationship work, but I decided to try again. Right now the error I am getting is "Call to undefined method App\Models\OppModels\opp_main::hasOneThrough()". I believe my issue is with the local keys vs foriegn keys, but if I truly knew the issues, then i could fix it. I do realize I could hard code the org_id into my opp_main table, but I really want to make this work due to future considerations. My table design is:
---------
opp_main
---------
id
---------
---------
opp_org
---------
id
opp_id
org_id
---------
---------
org_main
---------
id
---------
My relationship looks this (the comments were taken from the documentation to try to organize it in my mind):
public function org()
{
return $this->hasOneThrough(
'App\Models\OrgModels\org_main', // Final model
'App\Models\OppModels\opp_org', // Intermediate model
'opp_id', // Foreign key on intermediate model
'id', // Foreign key on the final model
'id', // local key
'org_id' // local key of the intermediate model
);
}
And this is how I am calling it (I have tried both $item->org; and $item->org(); ):
public function getAllOpportunities()
{
$opps = opp_main::orderBy('status', 'asc')->get();
$opps->map(function ($item) {
$item['org'] = $item->org;
return $item;
});
return response()->json([ 'success' => true, 'data' => $opps ]);
}
I appreciate any help!

Using the documentation this is how it is supposed to be arranged. Note difference from your solution
Org_main has an FK to opp_org which is needed in hasOneThrough() to
link the required model to the intermediate model
org_main
id
opp_org_id
opp_org
id
opp_id
org_id
opp_main
id
return $this->hasOneThrough(
'org_main', // final model
'opp_org', // intermediate or linking model
'opp_id', //fk on opp_org linking to opp_main
'opp_org_id', // fk on org_main linking ot opp_org table,
'id', //local key on opp_main
'id', //local key on opp_org / intermediate model
);

Related

Why can't EF handle two properties with same foreign key, but separate references/instances?

Apparently, EF6 doesn't like objects that have multiple foreign key properties that use the same key value, but do not share the same reference. For example:
var user1 = new AppUser { Id = 1 };
var user2 = new AppUser { Id = 1 };
var address = new Address
{
CreatedBy = user1, //different reference
ModifiedBy = user2 //different reference
};
When I attempt to insert this record, EF throws this exception:
Saving or accepting changes failed because more than one entity of type
'AppUser' have the same primary key value. [blah blah blah]
I've discovered that doing this resolves the issue:
var user1 = new AppUser { Id = 1 };
var user2 = user1; //same reference
I could write some helper code to normalize the references, but I'd rather EF just know they're the same object based on the ID alone.
As for why EF does this, one explanation could be that its trying to avoid doing multipe CRUD operations on the same object since separate instances of the same entity could contain different data. I'd like to be able to tell EF not to worry about that.
Update
So it's as I suspected per my last paragraph above. In absense of a means to tell EF not to do CRUD on either instance, I will just do this for now:
if (address.ModifiedBy.Id == address.CreatedBy.Id)
{
address.ModifiedBy = address.CreatedBy;
}
Works well enough so long as I am not trying to do CRUD on either.
Update2
I've previously resorted to doing this to prevent EF from validating otherwise-required null properties when all I need is the child entity's ID. However, it doesn't keep EF from going into a tizzy over separate instances with the same ID. If it's not going to do CRUD on either AppUser object, why does it care if the instances are different?
foreach (var o in new object[] { address.ModifiedBy, address.CreatedBy })
{
db.Entry(o).State = EntityState.Unchanged;
}
If you get AppUser from context, then you will not need to do anything, because Entity Framework will track entities:
var user1 = context.AppUsers.Find(1);
var user2 = context.AppUsers.Find(1);
var address = new Address
{
CreatedBy = user1, //different reference
ModifiedBy = user2 //different reference
};
Now, they both will point to same objects and will not cause to conflict.
You can add two extra properties to have the Id for the main objects which is the AppUser, then you can use only one AppUser object and reference it for both the created and modified by properties.
CreatedById = user1.Id,
ModifiedById = user1.Id
Otherwise, your code will end up by saving two instances of AppUser with the same primary key.
Another approach is to set both the foreign key properties to only one AppUserobject
The explanation is that EF's change tracker is an identity map. I.e. a record in the database is mapped to one, and only one, CLR object.
This can be demonstrated easily by trying to attach two objects with the same key:
context.AppUsers.Attach(new AppUser { Id = 1 });
context.AppUsers.Attach(new AppUser { Id = 1 });
The second line will throw an exception:
Attaching an entity of type 'AppUser' failed because another entity of the same type already has the same primary key value.
This also happens if you assign
CreatedBy = user1, //different reference
ModifiedBy = user2 //different reference
Somewhere in the process, user1 and user2 must be attached to the context, giving rise to the exception you get.
Apparently, you have a function that receives two Id values that can be different or identical. Admittedly, it would be very convenient if you could simply create two AppUser instances from these Ids, not having to worry about identical keys. Unfortunately, your solution ...
if (address.ModifiedBy.Id == address.CreatedBy.Id)
... is necessary. Solid enough, though.

How can I return all information (via IQueryable) regarding table that is connected to the foreign key in another table?

public IQueryable<Student> GetStudents()
{
var balance = (from StandardX in db.Standards
join StudentX in db.Students
on StandardX.StandardId equals StudentX.StandardRefId
select new { StudentX.StudentID, StudentX.StudentName, StandardX.StandardName });
return balance; // I can't handle that. how can I return? Please help.
}
Question:
I have two tables named respectively standard and student. I want to retrieve all student information via foreign key relationship. I searched everywhere regarding this topic but didn't find any suitable solution. Please help me with solving this issue.
If you have the relationship configured, try the include statement (assuming standard is the parent and 1:1, otherwise flip and/or add collection):
var balance = (from StandardX std in db.Standards.Include("Student")
select new { std.Student.StudentID, std.Student.StudentName, std.StandardName });
https://msdn.microsoft.com/en-us/library/bb896272.aspx

Associations with combination foreign key in Sails.js/Waterline

I need to have a model called 'Package' which can have one or more children of several different types. For example, a package can contain guides, as well as forms and other content types (some of which will be added later). Each of these content items, from different tables, can be in multiple packages. So I have the following schema:
Package table
=============
id
name
....
PackageContent table
=============
id
packageId
contentType
contentId
Guide table
=============
id
name
...
Form table
=============
id
name
How can I define the 'content' association for my packages in my Package.js model file in sails.js? I have not been able to find any information on combination foreign keys in sails.js or Waterline. I would hope to find something along the lines of:
// models/Package.js
module.exports = {
attributes = {
name: 'text',
....
content: {
through: 'PackageContent',
collection: contentType,
via: 'contentId'
}
}
};
I have a similar problem recently. My solution is to create different foreign keys in the intermediary model and set attributes as 'required:false'. In your example, the PackageContent table could look like this:
//PackageContent.js
module.exports={
attributes={
package:{
model:'package',
},
guide:{
model:'guide'
require:false
},
form:{
model:'form'
require:false
}
//other attributes...
}
}
In order to avoid duplicated package+content combination, you may need to write a beforeValidate method to check the duplication. I am not sure if this is a 'good design', but it solves the probelm

EJB inheritance strategy

BIG UPDATE:
Ok, I see my problem is much more complicated than I thought. I have tables like this:
Patient:
id
bloodtype
level (FK to level table)
doctor (FK to doctor table)
person (FK to person table)
Doctor:
id
person (FK)
speciality
level (FK to level)
Paramedic:
id
person (FK)
Person:
id
name
surname
Account:
id
login
pass
Level:
id
account (FK)
name (one of: 'patient' , 'paramedic' , 'doctor')
In entity class I'm using now #Inheritance(strategy= InheritanceType.JOINED)
#DiscriminatorColumn(discriminatorType=DiscriminatorType.STRING,name="name") in class Level. To check if someone is for ex. patient I have function:
public boolean ifPatient(String login) {
account = accountfacade.getByLogin(login);
for (Level l : account.getLevelCollection()) {
if (l instanceof Patient) {
return true;
}
}
return false;
}
Now I have situation: I'm logged in as a doctor. I want to add a patient. I have something like this:
public Doctor findDoctor(String login) {
account = accountfacade.getByLogin(login);
for (Doctor d : doctorfacade.findAll()) {
if (d.getLevel().getAccount().getLogin().equals(login)) {
doctor = d;
return doctor;
}
}
}
#Override
public void addPatient(Account acc, Patient pat, Person per, String login) {
Doctor d = findDoctor(login);
accountfacade.create(acc);
personfacade.create(per);
Patient p = new Patient();
p.setAccount(acc);
p.setBlood(pat.getBlood());
// etc
p.setPerson(per);
d.getPatientCollection().add(p);
acc.getLevelCollection().add(p);
}
But it doesn't work. Always totally weird errors like duplicate value of primary key in table Account (but I use TableGenerator...) or NULL value in field Account but for INSERT INTO Doctor (how?! I'm creating new Patient, NOT Doctor...). I'm totally lost now, so I think most important for me now is to know if actually I can use InheritanceType.JOINED in this case.
UPDATE:
You are using the field nazwa as discriminator
#DiscriminatorColumn(discriminatorType=DiscriminatorType.STRING,name="nazwa")
The framework stores there the name of the class that it has to use to deserialize the object (it is the name of the PoziomDostepu class).
As far as I can see you are using a different table for each class, so the Strategy.JOINED would make little sense.
More update:
Where I said class it meant entity. You can check the effect by changing the entity name (say to "CacaCuloPedoPis") of PoziomDostepu and seeing which is the new value being inserted.
Looks like all that is missing is a #DiscriminatorValue annotation on the PoziomDostepu class to use one of the values in your constraint, otherwise it defaults to the class name. The insert of PoziomDostepu is coming from the this.poziom - if you don't want PoziomDostepu instances inserted you might make the class abstract and make sure this instance is a subclass instead. You shouldn't need to change the inheritance type, as doing so will require changing the database tables to each contain the same fields.
Ok, I've done what I wanted. It's not elegant, but efficient. Tables and inheritance strategy are the same. In endpoint I create account and person entities, then I create Patient entity but using EntityManager persist() method. I also need to set id of level manually ((Integer)poziomfacade.getEntityManager().createQuery("SELECT max(p.idpoziom) FROM PoziomDostepu p").getSingleResult())+1 because Generator in Level entity class doesn't work. Nevertheless the problem is solved, thanks for every constructive comment or answer.

Entity Framework: perform a query on a linking table / many to many relationship

I am trying to perform a query using linq to entities to that an entity/table doesn't contain the same values before I update it.
The structure of the database is as follows:
Users User_IPAddresses IPAddresses
----- ---------------- -----------
UserID >------ UserID ------< IPAddressID
User IPAddressID Address
So, the structure of the entity object is as follows
UserSet IPAddressSet
------- >-----< ------------
User IPAddress
All the ID fields are Primary Keys, so the link table (User_IPAddresses) must contain unique rows.
The problem I am having is that I can't get my head around how to check the entities so that I don't violate the unique row constraint on the User_IPAddresses table before I update it.
Any EF gurus out there that can help me?
//returns true if pair exists
public bool CheckIfUserIPPairExists(int ipID, int userID)
{
bool exists
= db.UserSet.Any(user=>user.UserID==userID
&& user.IPAddress.Any(ip=>ip.IPAddressID == ipID));
return exists;
}