In PHP, how to return nested entities after creating a new object is a common problem. When we need to create a new object in a class and use it as an attribute of another entity, we need to pay attention to some techniques and methods. First, we can create a new object in the constructor of the class and assign it to the property. We can then use the $this keyword to reference the current object and access its properties. In addition, we can also use static methods or factory patterns to create new objects and return them to the caller. Regardless of the method, the key to returning nested entities is to correctly handle the relationships between objects and ensure that they interact and pass data correctly between entities. Through these methods, we can easily create and return nested entity objects in PHP.
Model account
contains nested structures - currency
and user
When I create a new instance of account
in the database and then return it in the response, the nested entity is empty:
type account struct { basemodel name string `gorm:"size:64;not null" json:"name"` balance decimal.decimal `gorm:"type:decimal(16, 2);default:0;not null;" json:"balance"` userid int `gorm:"not null" json:"-"` user user `gorm:"foreignkey:userid" json:"user"` currencyid int `gorm:"not null" json:"-"` currency currency `gorm:"foreignkey:currencyid" json:"currency"` } type createaccountbody struct { name string `json:"name" binding:"required"` balance decimal.decimal `json:"balance"` currencyid int `json:"currency_id" binding:"required"` } func createaccount(ctx *gin.context) { body := createaccountbody{} if err := ctx.bind(&body); err != nil { log.println("error while binding body:", err) ctx.json( http.statusbadrequest, gin.h{"error": "wrong request parameters"}, ) return } account := account { name: body.name, balance: body.balance, currencyid: body.currencyid, userid: 1, } if result := db.db.create(&account); result.error != nil { log.println("unable to create an account:", result.error) } ctx.json(http.statuscreated, gin.h{"data": account}) }
To avoid this problem, I use a separate query to refresh the account variables:
db.DB.Create(&account) db.DB.Preload("User").Preload("Currency").Find(&account, account.ID) ctx.JSON(http.StatusCreated, gin.H{"data": account})
Is this the most efficient and correct way to achieve the desired results?
I will share with you how I usually handle this situation. First, let me share the code.
main.go
Filepackage main import ( "context" "gogindemo/handlers" "github.com/gin-gonic/gin" "gorm.io/driver/postgres" "gorm.io/gorm" ) var ( db *gorm.db ctx *gin.context ) func init() { dsn := "host=localhost user=postgres password=postgres dbname=postgres port=5432 sslmode=disable" var err error db, err = gorm.open(postgres.open(dsn), &gorm.config{}) if err != nil { panic(err) } db.automigrate(&handlers.currency{}) db.automigrate(&handlers.user{}) db.automigrate(&handlers.account{}) } func adddb() gin.handlerfunc { return func(ctx *gin.context) { ctx.request = ctx.request.withcontext(context.withvalue(ctx.request.context(), "db", db)) ctx.next() } } func main() { db.create(&handlers.user{id: 1, name: "john doe"}) db.create(&handlers.user{id: 2, name: "mary hut"}) db.create(&handlers.currency{id: 1, name: "eur"}) db.create(&handlers.currency{id: 2, name: "usd"}) r := gin.default() r.post("/account", adddb(), handlers.createaccount) r.run() }
Here, I just added the code for bootstrapping the database object and added some dummy data to it.
handlers/handlers.go
Filepackage handlers import ( "net/http" "github.com/gin-gonic/gin" "github.com/shopspring/decimal" "gorm.io/gorm" ) type User struct { Id int Name string } type Currency struct { Id int Name string } type Account struct { Id int Name string `gorm:"size:64;not null" json:"name"` Balance decimal.Decimal `gorm:"type:decimal(16, 2);default:0;not null;" json:"balance"` UserID int `gorm:"not null" json:"-"` User User `gorm:"foreignKey:UserID" json:"user"` CurrencyID int `gorm:"not null" json:"-"` Currency Currency `gorm:"foreignKey:CurrencyID" json:"currency"` } type CreateAccountBody struct { Name string `json:"name" binding:"required"` Balance decimal.Decimal `json:"balance"` CurrencyID int `json:"currency_id" binding:"required"` } func CreateAccount(c *gin.Context) { db, ok := c.Request.Context().Value("DB").(*gorm.DB) if !ok { c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"}) return } var accountReq CreateAccountBody if err := c.BindJSON(&accountReq); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "wrong request body payload"}) return } // create Account & update the "account" variable account := Account{Name: accountReq.Name, Balance: accountReq.Balance, CurrencyID: accountReq.CurrencyID, UserID: 1} db.Create(&account).Preload("Currency").Preload("User").Find(&account, account.Id) c.IndentedJSON(http.StatusCreated, account) }
In this file I actually communicate with the database via the db
passed in the context
. Now, back to your question.
If the relationship between currency
//account and user
/account
is 1:1
type, then you should rely on the preload
clause. This will load the related entities in a separate query rather than adding them to the inner join
clause.
Please tell me if this solves your problem, thank you!
The above is the detailed content of How to return nested entity after creating new object?. For more information, please follow other related articles on the PHP Chinese website!