I'm using Gorm and Golang to fetch data from my database. Is it possible to make Gorm fetch also the objects children (foreign keys)?
Database tables
users
+----+---------+------------+
| id | name | country_id |
+----+---------+------------+
| 1 | Adam | 1 |
| 2 | Bertil | 1 |
| 3 | Charlie | 2 |
+----+---------+------------+
countries
+----+--------+
| id | name |
+----+--------+
| 1 | Sweden |
| 2 | Norway |
+----+--------+
Models
type User struct {
Id int64 `json:"-"`
Name string `json:"name"`
CountryId int64 `json:"-"`
Country Country `json:"country"`
}
type Country struct {
Id int64 `json:"-"`
Name string `json:"name"`
}
Code to fetch all users
var users []User
DB.Find(&users) // Question: How should this be modified to automatically fetch the Country?
Actual result
[
{
"name": "Adam",
"country" : {
"name": "",
}
},
...
]
Desired result
[
{
"name": "Adam",
"country" : {
"name": "Sweden",
}
},
...
]
Thanks a lot for you input!
/Klarre
Yes it is possible, it's called Preloading.
users := make([]User,0)
DB.Preload("Country").Find(&users)
Related
I came here as I stuck with typescript and ApolloClient !
For the use of ApolloClient v3, possibleTypes as required and so, I have upgraded codegen as the latest version and since, my app typescript is not matching anymore !!
Here one example on one of my queries generated by codegen before upgrade.
export type GetMemberListQuery = (
{ __typename?: 'Query' }
& { userList: Maybe<(
{ __typename?: 'UserList' }
& Pick<UserList, 'totalCount' | 'maxPage'>
& { users: Array<Maybe<(
{ __typename?: 'User' }
& Pick<User, 'firstName' | 'lastName' | 'description'>
& { teams: Maybe<Array<Maybe<(
{ __typename?: 'Team' }
& __TeamFragment
)>>> }
)>> }
)> }
);
and there is the same query after upgrade :
export type GetMemberListQuery = {
userList?: {
__typename?: 'UserList',
totalCount?: number | null,
maxPage?: number | null,
users: Array<{
__typename?: 'User',
firstName?: string | null,
lastName?: string | null,
description?: string | null,
id: string, status?: string | null,
email?: string | null,
displayName?: string | null,
employeeNumber?: string | null,
avatarUri?: string | null,
teams?: Array<{
__typename?: 'Team',
id: string,
status?: string | null,
name?: string | null,
createdAt?: any | null,
updatedAt?: any | null
} | null> | null,
} | null>
} | null
};
Using useQuery hooks like this below :
const { data, loading } = useQuery<GetMemberListQuery, GetMemberListQueryVariables)(GetMemberList, {...})
and then passing users={data.userList.users} to a sub component with this following prop :
users: Users[]
An typescript error occured telling me my users props doesn't match with User types.
I am really lost... I need to upgrade my apollo/client and so, codegen... but I need that the generated file's query stay, as much as possible, the same than now.
Thanks a lot for reading
Cheers
I want to create an API that display and fetch data from table(users) and another table(fruits) together base on the unique id (fruit_id) common in both table.
table->users
+----------+------------+----------+--------------+
| Id | Fruit_id | Name | Gender |
+----------+------------+----------+--------------+
| 1 | B456 | Mary | Female |
| 2 | O174 | James | Male |
table->fruits
+----------+------------+----------+--------------+
| Id | Fruit_id | Name | Quantity |
+----------+------------+----------+--------------+
| 1 | B456 | Banana | 22 |
| 2 | O174 | Orange | 34 |
I created an API in laravel where fetch all data in the users table from my controller
class UserApiController extends Controller
{
public function index()
{
return Users::all();
}
}
API display result
[
{
id: 1,
fruit_id: B456,
name: Mary,
gender: Female,
},
{
id: 2,
fruit_id: O174,
name: James,
gender: Male,
}
]
I want the API to display like the below based on the unique id (fruit_id) selection that is common between both tables how will I write the code in my controller to display the desired result.
Desired result
[
{
id: 1,
fruit_id: B456,
name: Mary,
gender: Female,
fruits: [
id: 1,
fruit_id: B456,
name: Banana,
Quantity: 22,
]
},
{
id: 2,
fruit_id: O174,
name: James,
gender: Male,
fruits: [
id: 2,
fruit_id: B456,
name: Orange,
Quantity: 34,
]
}
]
You should create a relationship inside Model\User. Then, you can use with() or load().
Example:
Users::all()->load('fruits');
https://laravel.com/docs/8.x/eloquent-relationships#one-to-many
you need to make one-to-many relationship between tables
table->users
Id - Name - Gender
table->fruits
Id - user_id - Name - Quantity
class User extends Model{
public function fruits(){
return $this->hasMany(Fruit::class,'user_id');
}
}
class Fruit extends Model{
public function user(){
return $this->belongsTo(User::class,'user_id');
}
}
User::with('fruits')->get();
So I have a table of users where each user is associated with a main tool and tools in their backpacks. Each tool also have a "quality". But I have no idea of how to fetch this value so I appreciate any help on how to do this.
Databases
users
+----+------+--------------+
| id | name | main_tool_id |
+----+------+--------------+
| 1 | adam | 1 |
+----+------+--------------+
tools
+----+-------------+
| id | name |
+----+-------------+
| 1 | hammer |
| 2 | screwdriver |
+----+-------------+
user_tools
+----+---------+---------+---------+
| id | user_id | tool_id | quality |
+----+---------+---------+---------+
| 1 | 1 | 1 | LOW |
| 2 | 1 | 2 | HIGH |
+----+---------+---------+---------+
Models
type User struct {
Id int64 `json:"-"`
Name string `json:"name"`
MainToolId int64 `json:"-"`
MainTool Tool `json:"main_tool"`
ToolsInBackpack []Tool `json:"tools_in_backpack" gorm:"many2many:user_tools"`
}
type Tool struct {
Id int64 `json:"-"`
Name string `json:"name"`
Quality string `json:"quality"`
}
Code
var users []User
DB.Preload("MainTool").Preload("ToolsInBackpack").Find(&users) // <-- Modify this (I guess)
Real result
{
"name": "adam",
"main_tool": {
"name": "hammer",
"quality": ""
},
"tools_in_backpack": [
{
"name": "hammer",
"quality": ""
},
{
"name": "screwdriver",
"quality": ""
},
]
}
Desired result
{
"name": "adam",
"main_tool": {
"name": "hammer",
"quality": "LOW"
},
"tools_in_backpack": [
{
"name": "hammer",
"quality": "LOW"
},
{
"name": "screwdriver",
"quality": "HIGH"
},
]
}
Thanks a lot for your help!
If you do not require MainTool to return an object.
maybe you uses it
type User struct {
ID uint `json:"-"`
Name string
MainTool int64
ToolsInBackpack string
Tools []*Tool
}
type Tool struct {
ID uint`json:"-"`
Name string
UserID uint
}
User{MainTool: 1, ToolsInBackpack: "[1,2]", Tools: []*Tool{&t1, &t2}}
if you don't wana to use string at ToolsInBackpack string you can't use datatypes.JSON at gorm.io/datatypes
for example
ToolsInBackpack datatypes.JSON
This isn't supported in Gorm so there won't be an easy way to just Preload and it's done.
Here are a few techniques you could look at:
Use a Custom Join Table Model that contains the fields that you need
type UserTool struct {
UserID int64 `gorm:"primaryKey"`
ToolID int64 `gorm:"primaryKey"`
Quality string
}
err := db.SetupJoinTable(&User{}, "ToolsInBackpack", &UserTool{})
// handle error
(I left out the UserTools.ID field, Gorm doesn't need it, and you only need it if a user can have the same tool twice, in which case put it back on as part of the primaryKey. I don't know whether Gorm will be happy with it but try it out).
Then you can use this model to query the field as any other model:
userTools := []UserTool
err := db.Where("user_id = ?", user.ID).Find(&userTools).Error
// handle error
// now userTools[i].Quality is filled, you could use it to update the corresponding users[j].ToolsInBackpack[k].Quality
It's a pain because you need to match IDs as a post-processing step.
Use a Has-Many/Belongs-To hybrid relation to model the Many-To-Many Join Table:
Here the User Has Many UserTools and a Tool belongs to one or more UserTools. This effectively models the meaning of Many-to-Many (in ERD, a relationship like [User]>--<[Tool] can be decomposed into [User]--<[UserTool]>--[Tool]).
type User struct {
ID int64
Name string
MainToolId int64
MainTool Tool
ToolsInBackpack []UserTool
}
type UserTool struct {
UserID int64 `gorm:"primaryKey"`
ToolID int64 `gorm:"primaryKey"`
Tool Tool
Quality string
}
type Tool struct {
ID int64
Name string
}
Now you can Preload this association like this:
err := db.Model(&User{}).Preload("ToolsInBackpack.Tool").Find(&users).Error
// handle error
// now users[i].ToolsInBackpack[j].Quality and
// users[i].ToolsInBackpack[j].Tool.Name will be set correctly.
Only problem now is that you've got a weird shape in the model that you're then trying to marshal into JSON (most likely to be used in an API). My advice here is to split the DB model from the JSON API model, and have a mapping layer in your code. The DB model and the API Messages invariably diverge in important ways and trying to reuse one model for both soon leads to pain.
I am trying to create a policy for updating a user to ensure that either the person updating or the user being updated belongs to a place.
I have the current database structure for the users
| id | first_name | last_name | email | password | created_at | updated_at |
and this is the places
| id | name | description | created_at | updated_at |
I then have a pivot table which is
| user_id | place_id |
Everything links up correctly and I can pass and associate data into each models.
What I am trying to achieve is that when I send in a request according to the JSON API spec which looks like the following:
{
"type": "users",
"attributes": {
...
},
"relationships": {
"places": {
"data": [
{
"type": "places",
"id": "1"
},
{
"type": "places",
"id": "2"
}
]
}
}
}
Inside of the updated policy, I want to check that either the authenticated user or the user being updated belongs to that place.
I have a method on the User model which is the following:
public function belongsToPlaces($placeIds)
{
return $this->places()->whereIn('id', $placeIds)->count() === count($placeIds);
}
This seems to work but it really doesn't feel very secure as realistically I should check each of the IDs. I'm just wondering if there is a built-in method to do this?
I would do a diff on the placeIds you already have with the one on the user. It will return all place ids there is not in the request, if it's not present on the users places. So check if the diff is empty, then everything is fine, otherwise the id's are invalid.
public function belongsToPlaces($placeIds)
{
return collect($placeIds)->diff($this->places()->pluck('id'))->isEmpty();
}
I have two types in my schema:
type Resident = { type Visitor = {
id id
name name
} }
In my database:
Residents and Visitors Tables:
+--------+-------+ +--------+---------+
| res_id | name | | vis_id | name |
+--------+-------+ +--------+---------+
| 1 | Alice | | 1 | Charlie |
| 2 | Bob | +--------+---------+
+--------+-------+
And then a table that shows which visitor belongs to which resident:
+--------+--------+--------------+
| res_id | vis_id | relationship |
+--------+--------+--------------+
| 1 | 1 | fam/fri |
| 2 | 1 | contractor |
+--------+--------+--------------+
Each visitor could either be a "fam/fri" or a "contractor" to a resident. So Charlie is Alice's visitor as her family or friend. However, Charlie is also a visitor to Bob, but instead as a contractor.
Question: How do I structure my schema so that when I query Alice, Charlie returns as a fam/fri, and when I query Bob, Charlie is returned as a contractor? I imagine this:
{
Resident(id: 1) { "Resident" {
name "Alice"
Visitor { "Visitor" {
id ===> "1"
name "Charlie"
relationship "fam/fri"
} }
} }
}
and also:
{
Resident(id: 2) { "Resident" {
name "Bob"
Visitor { "Visitor" {
id ===> "1"
name "Charlie"
relationship "contractor"
} }
} }
}
Something like:
type Query {
resident(id: Int): Resident
}
type Resident {
id: Int!
name: String!
visitors: [Visitor!]!
}
type Vistor {
id: Int!
name: String!
relationship: VisitorRelationship!
}
enum VisitorRelationship {
CONTRACTOR
FAMILY_OR_FRIEND
}
Note that by convention field names should be camelCase and type names should be in PascalCase. If the data returned from your data source (whether that's a database, API, or whatever) is not in the same shape as what you want to return from your GraphQL service, then you should transform the data before returning it inside your resolver, for example:
const relationshipMap = {
'fam/fri': 'FAMILY_OR_FRIEND',
'contractor': 'CONTRACTOR',
}
const resolvers = {
Query: {
resident: (root, args) => {
const resident = await Resident.findById(args.id)
// assuming resident has a property named joinTable that's
// an array and each relationship has a property named visitor
return {
...resident,
visitors: resident.joinTable.map(relationship => {
return {
...joinTable.visitor,
relationship: relationshipMap[joinTable.relationship],
}
})
}
},
},
}
You can also map enums to custom values this way.