why create a new row when gorm update associations - go

I followed the document code same struct and migrate
here's my model
type User struct {
gorm.Model
AddressID uint ``
Address UserAddress `gorm:"references:ID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL"`
}
type UserAddress struct {
gorm.Model
Address string `gorm:"type:varchar(50);notNull"`
}
document works fine when migrate and create data
db.AutoMigrate(&UserAddress{})
db.AutoMigrate(&User{})
but when I try update data use associations
user := &User{
ID: 1,
AddressID: 1,
Address: UserAddress{
Address: "new address",
},
}
db.Session(&gorm.Session{FullSaveAssociations: true}).Updates(&user)
document says:
If you want to update associations’s data, you should use the FullSaveAssociations mode:
then it still create a new data of address and update user.address_id
INSERT INTO `user_address` (`address`,`created_at`,`updated_at`,`deleted_at`) VALUES ('changed','2021-08-13 14:07:39.149','2021-08-13 14:07:39.149',NULL) ON DUPLICATE KEY UPDATE `address`=VALUES(`address`),`created_at`=VALUES(`created_at`),`updated_at`=VALUES(`updated_at`),`deleted_at`=VALUES(`deleted_at`)
where am I missing here or it just doesn't work at all?

You didn't give a unique id for Address. So it create new address record with autoincrement ID. And update the ID value associated with it.
user := &User{
ID: 1,
AddressID: 1,
Address: UserAddress{
ID: 1,
Address: "new address",
},
}

Related

How to use a struct field into another struct without referring to it as a key

I want to insert a struct field into another struct without having to use the struct name.
I know that I can do this:
type Person struct {
Name string
}
type User struct {
Person
Email, Password string
}
But It results in this struct:
user := User{Person: Person{Name: ""}, Email: "", Password: ""}
How can I do something like this:
type Person struct {
Name string
}
type User struct {
Name Person.Name // Here
Email, Password string
}
To use it like this
user := User{Name: "", Email: "", Password: ""}
Is it possible?
Simply put, with the current language implementation you can't.
When initialising a literal you need to be explicit (or, put another way: literal! [sic]). Since a User contains a Person, a literal User must contain a literal Person, as you illustrate:
u := User{
Person: Person{
Name: "Bob",
},
Email: "bob#bobspot.com",
Password: "you're kidding right?",
}
However, once you have a variable of type User, you can then leverage the anonymous field to set (or get) the Name of the anonymous Person with the User:
u := User{}
u.Name = "Bob"
u.Email = "bob#bobspot.com",
u.Password = "you're kidding right?",
Why Does Go Make Me Do All This Work?
Let us imagine that it was possible to initialise the inner Person in the way you are looking for:
u := User{ Name: "Bob" }
Now let us further imagine that we later modify the User struct and add its own Name field:
type User struct {
Person
Name string
Email string
Password string
}
And now you can obviously initialise the new Name field:
u := User{ Name: "Bob" }
Notice that this is identical to the previous code that initialised User.Person.Name but now it is initialising User.Name. Not good.
More Gotchas
There are further traps lurking with code like this.
First, the addition of a Name field in User already similarly "breaks" unqualified references to Name on User variables:
u.Name = "Bob" // used to set User.Person.Name, now sets User.Name
Also, with only an anonymous Person field, the User.Person.Name field is marshalled to JSON by default as a "Name" field:
{
"Name": "",
"Email": "",
"Password": ""
}
If a Name field is added, then this is the field that is marshalled as "Name" and the User.Person.Name field is not marshalled at all.
You might think you can add a json tag for the User.Person.Name, e.g.
type User struct {
Person `json:"PersonName"`
Name string
Email string
Password string
}
But now the Person is marshalled as an object with a Name field:
{
"PersonName": {
"Name": ""
},
"Name": "",
"Email": "",
"Password": ""
}
This also happens if you try to change the marshalled field name of the anonymous Person even if User does not have a Name field.
In short: using anonymous structs within structs as a way of "adding fields" is potentially problematic and fragile and should probably be avoided.

Gorm create and return value many2many

I want to create a data and then return the value, but the value is related to another table.
User response struct
type UserRespone struct {
ID int `json:"id,omitempty"`
Email string `json:"email"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Address string `json:"address"`
Roles []roles.Roles `json:"roles" gorm:"many2many:users_roles;foreignKey:ID;joinForeignKey:UserID;references:ID;joinReferences:RolesID"`
}
Roles struct
type Roles struct {
ID int `json:"id" gorm:"<-:false;primaryKey"`
Name string
}
And this is a function to create a data, and I want to return the value [ ]roles.Roles
func (r *UserRepositoryImpl) AuthSignUp(ctx context.Context, req user.AuthSignUp) (user.UserRespone, error) {
createdUser := req.ToUser()
hashPassword, _ := bcrypt.GenerateFromPassword([]byte(createdUser.Password), bcrypt.DefaultCost)
createdUser.Password = string(hashPassword[:])
result := r.Db.
WithContext(ctx).
Create(&createdUser)
for _, rolesID := range req.RolesID {
userRole := new(user.UsersRoles)
userRole.UserID = createdUser.ID
userRole.RolesID = rolesID
result = r.Db.WithContext(ctx).Create(&userRole)
}
return createdUser.ToResponse(), result.Error
}
I want to return the value like this:
user.UserResponse{
ID: 4,
Email: "putri4#gmail.com",
FirstName: "putri",
LastName: "cantik",
Address: "bonang",
Roles: []roles.Roles{
{
ID: 1,
Name: "Admin",
},
{
ID: 2,
Name: "Consumer",
},
},
}
But when I create a data set, I only get values like :
user.UserRespones{
ID: 4,
Email: "putri4#gmail.com",
FirstName: "putri",
LastName: "cantik",
Address: "bonang",
Roles: []roles.Roles(nil),
}
As far as I can tell, you already have the user data and the role ids. I figure you just want to get the role names as well:
err := r.db.Find(&createdUser.Roles, req.RolesID)
// err handling
That said, your types and names are a bit unclear. UserRespone (note the typo) should probably named like DBUser - it isn't important whether it is used as response or something else, it represents the database entry for an user, right?
Additionally, I can only make assumptions about the type and fields of createdUser and UsersRoles, so I might have missed something.
I figure createdUser is of type UserRespone - but then I would expect that Roles is already complete (and you don't have to query anything at all). If not, then you should introduce a new type for that, e.g. RequestUser (as opposed to DBUser), which only contains Role ids but not Role names. Squeezing the data from the request, the db entry and the response data into the same type is confusing (and a tad too tightly coupled for my taste).

Using ampersand for struct and asterisk for Go

Two questions on below code that I saw during youtube tutorial for Go.
Trying to read this code and I see that slice of pointer struct being assign to productList... Ok, that's good but why ampersand for Product? (I understand that is address of). I understand struct does get tagged with ampersand when you want to pass to function (since it is pass by value by default) but even that I understand if receiver is pointer, you do not need to use ampersand.. So why use the ampersand here?
type Product struct {
ID int
name string
}
var productList = []*Product{
&Product{
ID: 1,
Name: "Latte",
},
&Product{
ID: 2,
Name: "Ice Coffee",
},
}
Another words, what's the difference between above and below?
var productList = []*Product{
Product{
ID: 1,
Name: "Latte",
},
Product{
ID: 2,
Name: "Ice Coffee",
},
}
var productList = []*Product
means that productList is a list of pointers. Each pointer points to a Product.
If you do
var productList = []*Product{
Product{
ID: 1,
Name: "Latte",
}
}
you are initializing productList with a list of things of type Product instead of a list of things of type pointer to Product (which is illegal and will raise a Compile Time Error saying "Cannot use Product literal (type Product) as type *Product in slice literal).

Laravel Eloquent to get relational table data by one column as a key and another as value

I have two tables users and user_profiles with the following schema
Users Table
id
username
email
password
role
is_root
created_at
updated_at
User Profiles Table
id
user_id (FK -> users -> id)
key
value
User Modal
public function profileData()
{
return $this->hasMany(UserProfile::class);
}
UserProfile Modal
public function user()
{
return $this->belongsTo(User::class);
}
So if I use $user->profileData it is giving me this result, which is correct.
=> Illuminate\Database\Eloquent\Collection {#3244
all: [
App\UserProfile {#3251
id: 3,
user_id: 10,
key: "country",
value: "India",
},
App\UserProfile {#3242
id: 1,
user_id: 10,
key: "first_name",
value: "John",
},
App\UserProfile {#3252
id: 2,
user_id: 10,
key: "last_name",
value: "Doe",
},
],
}
Question
However, I wan to fetch the value field's value/data from the user_profiles table by providing the key fields value by
user id. For instance, if I pass the country it should return
India or first_name then John in the above example. How to
achieve that?
U need to use where(), select() and value() statement for needed value
UserProfile::where('user_id', $id)->where('key', $key)->select(['key','value'])->value('value')

GraphQL mutation - confusion designing gql tag for Apollo Client

I need help figuring out the GraphQL tag for use with Apollo Client. The Docs don't go far beyond the most basic use case for mutations.
My goal is to have the only required input be an email. If the other variables are present, I would like those to be accepted and create a proposal with all that information.
I have the mutation (in both only email and full variables scenarios) working successfully on the GraphQl Playground (if it helps, you can find it here and test it out, look at the schema, etc.,): https://prisma2-graphql-yoga-shield.now.sh/playground)
mutation {
createOneProposal(
data: {
email: "fake#gmail.com"
name: "Sean"
types: {
create: {
model: PURCHASE
name: "e-commerce"
cost: 600
services: {
create: [
{ service: "Responsive web design" }
{ service: "Another service!" }
{ service: "And yet another service!" }
]
}
}
}
}
) {
created_at
proposal_id
types {
cost
model
name
type_id
services {
service
service_id
}
}
}
}
Producing as a result:
{
"data": {
"createOneProposal": {
"created_at": "2020-02-27T21:28:53.256Z",
"proposal_id": 35,
"types": [
{
"cost": 600,
"model": "PURCHASE",
"name": "e-commerce",
"type_id": 6,
"services": [
{
"service": "Responsive web design",
"service_id": 10
},
{
"service": "Another service!",
"service_id": 11
},
{
"service": "And yet another service!",
"service_id": 12
}
]
}
]
}
}
}
My initial design for the gql tag:
export const NEW_PROPOSAL = gql`
mutation createOneProposal(
$email: String!
$name: String
$cost: Int
$model: Model
$service: Service
) {
createOneProposal(
email: $email
name: $name
cost: $cost
model: $model
service: $service
) {
created_at
proposal_id
types {
services {
service_id
}
}
}
}
`;
But, I get a lot of errors with this.
{"errors":[
{"Variable "$service" cannot be non-input type `"Service`".","locations":[{"line":1,"column":97}]},
{"Unknown argument "email" on field "createOneProposal`" of type "Mutation`".","locations":[{"line":2,"column":21}]},
{"Unknown argument "name" on field "createOneProposal`" of type "Mutation`".","locations":[{"line":2,"column":36}]},
{"Unknown argument"cost" on field "createOneProposal\" of type "Mutation`".","locations":[{"line":2,"column":49}]},
{"Unknown argument "model" on field "createOneProposal`" of type "Mutation`".","locations":[{"line":2,"column":62}]},
{"Unknown argument "service" on field "createOneProposal`" of type "Mutation`".","locations":[{"line":2,"column":77}]},
{"Field "createOneProposal" argument "data" of type "ProposalCreateInput!`" is required, but it was not provided.","locations":[{"line":2,"column":3}]}]}
So, how can I go about this... I figured out the query version (much easier...), but I just can't figure this out!
My schema, if it helps:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("MYSQL_URL_PRISMA2")
}
model Post {
content String #default("")
created_at DateTime #default(now())
post_id Int #default(autoincrement()) #id
published Boolean #default(false)
published_at DateTime?
title String #default("")
author User
}
model Profile {
bio String?
profile_id Int #default(autoincrement()) #id
user_id User
}
model Proposal {
email String #unique
name String?
proposal_id Int #default(autoincrement()) #id
created_at DateTime #default(now())
types Type[]
}
model Type {
cost Int?
name String?
model Model? #default(SUBSCRIPTION)
services Service[]
type_id Int #default(autoincrement()) #id
proposal_id Proposal
}
model Service {
service_id Int #default(autoincrement()) #id
service String?
type_id Type
}
model User {
email String #default("") #unique
name String #default("")
password String #default("")
role Role #default(USER)
user_id Int #default(autoincrement()) #id
posts Post[]
profiles Profile[]
}
enum Role {
USER ADMIN
}
enum Model {
SUBSCRIPTION PURCHASE CUSTOM
}
GraphQL types are categorized as either input types or output types. Input types are used for inputs like variable definitions or argument definitions. Output types are used for typing fields, which are what compose the actual response. Certain types, like scalars and enums, can be used as either an input or an output. However, with objects, there are output object types (sometimes referred to just object types or objects) and input object types.
Service is an output type, so it can't be used where an input type is expected (in this case, a variable definition). Examine the schema generated by Prisma to determine the appropriate type to use.
Thanks to some very needed direction from #xadm, I figured out the structure of the tag! For anyone who comes across this in the future:
mutation createOneProposal($input: ProposalCreateInput!){
createOneProposal(data:$input){
created_at
name
email
proposal_id
type{
cost
description
model
name
type_id
services{
service
cost
service_id
}
}
}
}

Resources