Laravel with() method, pass another column than the primary - sql

I am using Eloquent to pull users out of my database, and i want to "join" the postal code from my users table with the postal code from my cities table, to retrieve the city name.
Heres a simplified version of my tables:
users =>
id
username
password
postal_code
cities =>
postal_code
city_name
In my User model i have a method called city() that declares the relationship with my cities table:
public function city() {
return $this->hasOne('cities', 'postal_code');
}
The problem now is when i try to do User::with('city')->find($user_id), the value being passed to the method is the primary key, the id, instead of the postal_code.
So my query ends up being (this is for the user with id 1):
select * from `cities` where `cities`.`postal_code` in ('1')
Is there someway to specify that i want to pass the postal_code instead?

There are a few things going on here. This is actually a belongs to relationship because the user holds the value that relates it to the city. The relationship would be better defined like this.
public function city()
{
return $this->belongsTo('City', 'postal_code');
}
The user only belongs to one city but the city has many users. In the City model you would have.
public function users()
{
return $this->hasMany('User', 'postal_code');
}
Now you will still have problems here because Laravel expects the relationships to use the primary key of the model. In order to get this working you will need to make the postal code the primary key for the City model.
class City extends Eloquent {
protected $primaryKey = 'postal_code';
}
This will effect every other place that you use a primary key with the City model but I think that should be OK given your current structure.
$city = City::find($postal_code);
I hate to throw a wrench into your plan here but in many places a city will have many postal codes. :)

Since the Relation class uses parent->getKey() (parent is User on your case) this will result in '1'.
I don't think changing the key for User to postal_code is good option ;)
So my guess is to give the users table a 'city_id' column and the cities an 'id' column, so things can work as designed.
An other would be not to return a relation, but something like.. return City::wherePostalCode($this->postal_code)->first();

Related

Sort records with an associated field

I want to order records with associated records in GORM V2.
I have two structs:
type Client struct {
gorm.Model
UUID uuid.UUID `gorm:"type:uuid"`
ClientProfile ClientProfile
}
type ClientProfile struct {
gorm.Model
ClientID uint
FirstName string
LastName string
}
Now, I can load all the records with:
db.
Preload(clause.Associations).
Find(&clients).
How can I now order these records with a field from the ClientProfile struct?
I tried without success:
db.
Preload(clause.Associations).
Order("client_profiles.last_name DESC").
Find(&clients)
This throws the following error:
ERROR: missing FROM-clause entry for table "client_profiles" (SQLSTATE 42P01)
Find with Preload queries the clients table first, and then queries the client_profiles table with the IDs from the first, thus it can't sort the first query by data from the second one.
Since this is a HasOne relationship you can use Joins Preloading which actually does use a single query for both entities. Then you will be able to sort by the related entity's field:
err := db.
Model(&Client{}).
Joins("ClientProfile").
Order("client_profiles.last_name DESC").
Find(&clients).
Error
// handle error

Where to use table types and where structures?

Why we use Table Type in SAP/ABAP? We can declare as type of table as shown below.
DATA it_doc TYPE TABLE OF table_name.
And if I want to store for specific attribute of table, I can use structure type.
TYPES: BEGIN OF st_student,
sid TYPE i,
sname TYPE c LENGTH 8,
END OF st_student.
DATA student TYPE st_student.
Is there any performance difference between table type and structure?
If I understand you correctly, you are talking about table types in the data dictionary. Since you can use the statement TYPE TABLE OF <structure> it might seem unintuitive to create a table type on top of that. However you need this table type if you want to pass a whole table as an argument to a Function Module or Class Method.
For example, you cannot write the following:
methods PASS_TABLE
returning
value(rt_table) TYPE TABLE OF <structure> .
In this case you have to use a dictionary table type:
methods PASS_TABLE
returning
value(rt_table) TYPE dict_table_type .
No, tables and structures are actually very different, so your concerns about performance are a bit unnecessary. As I stated in my comment, a table is a list of elements.
Example
You want to store information about a school class. Your application should be able to store data like name, birthdate, gender etc. of one student. To group those fields together, one would use a structure:
TYPES:
BEGIN OF student,
name TYPE string,
birth TYPE d,
gender TYPE char1,
END OF student.
Now you can declare a variable of type student and assign data like that:
DATA stud1 TYPE student.
stud1-name = 'Joe'.
...
You now want to put students together in a classroom. You'll need an internal table for this.
TYPES classroom TYPE STANDARD TABLE OF student WITH EMPTY KEY.
" ignore STANDARD and WITH EMPTY KEY for now
DATA e3fi TYPE classroom.
" fill table here
DATA joe TYPE student.
" init joe
APPEND joe TO e3fi.
DATA susan TYPE student.
" init susan
APPEND susan TO e3fi
After that your classroom e3fi contains two students susan and joe. Each of these students has individual name, birthdate and so on.

Conditionally map two columns to one field with Fluent NHibernate

I have an NHibernate entity called Owner that has a SSN column and a TaxID column in the database, and I would like to conditionally map one of those two values to a more generic property on the Owner entity based on the value of a different property, StructureType. So, if the StructureType is "I", I want to map the SSN value to the generic property, and if its "C" I want to map the TaxID value to the generic property. Is this possible using Fluent NHibernate (or even regular NHibernate)? The Owner entity is a read-only entity, nothing will be written back to the database.
I was able to solve this using a Formula in Fluent NHibernate:
Map(x => x.Identification)
.Formula("CASE WHEN StructureType = 'I' THEN SSN ELSE TaxID END");
(In my original post I said it was between 'I' and 'C' but is in fact just between 'I' and every other type)
Why don't you add a readonly property?
public string Identification
{
get
{
string identification = string.Empty;
if (StructureType.Equals("I"))
identification = SSN;
else if (StructureType.Equals("C"))
identification = TaxID;
return identification;
}
}

What's the best way to handle "type" tables with LINQ to SQL?

I have some tables that represent various types. They usually just consist of an ID (int), and a name. Ideally I would have this in an enum. Is there a way to map a table like this onto an enum?
EDIT: How would I handle it if there were extra fields other than an ID and Name?
If it's just an id and a name, I generally do this:
public enum FootScent : int
{
Unknown = 0,
Mild = 1,
Sweaty =2,
SteppedInSomething = 3
}
and then on the LINQ entity property:
[Column("foot_scent_id", DbType = "Int NOT NULL")]
public FootScent Scent { get; set; }
For lookup tables with columns other than "id" and "name" that are needed, I usually just make a normal LINQ entity for them, though depending on your implementation it would probably be worth caching these to minimize trips to the DB.

NHibernate Mapping from multiple tables

Consider the following table structure...
Appointment
-----------
ID integer
Description nvarchar
StatusID smallint
Status
------
ID smallint
DisplayText nvarchar
Now, for good or for evil, we want this situation to map to a class that looks like this
class Appointment
{
public int ID {g;s;}
public string Description { g;s; }
public string Status { g; private s; }
}
I.e. we want to pull the normalised status display text straight into the entity.
The obvious answer is to create a Status entity and make the appointment class have a reference to that and map it in the normal way.
Don't create an entity class. Use an enum and EnumStringType as shown here. This is exactly what you want I think.