Getting Started
Introduction
Goravel makes it easy for developers to interact with databases using facades.Orm()
. Currently, it provides official support for the following four databases:
- MySQL 5.7+
- PostgreSQL 9.6+
- SQLite 3.8.8+
- SQL Server 2017+
Before you start, configure the database in .env
and confirm the default
configuration in config/database.go
.
Configuration
To configure databases, navigate to config/database.go
. This is where you can customize all database connections and choose a default
connection. The configuration in this file relies on the project's environment variables and showcases various database configurations that Goravel supports.
Read & Write Connections
Sometimes you may wish to use one database connection for SELECT
statements, and another for INSERT
, UPDATE
, and DELETE
statements. Goravel makes this a breeze.
To see how read/write connections should be configured, let's look at this example:
import "github.com/goravel/framework/contracts/database"
// config/database.go
"connections": map[string]any{
"mysql": map[string]any{
"driver": "mysql",
"read": []database.Config{
{Host: "192.168.1.1", Port: 3306, Database: "forge", Username: "root", Password: "123123"},
},
"write": []database.Config{
{Host: "192.168.1.2", Port: 3306, Database: "forge", Username: "root", Password: "123123"},
},
"host": config.Env("DB_HOST", "127.0.0.1"),
"port": config.Env("DB_PORT", 3306),
"database": config.Env("DB_DATABASE", "forge"),
"username": config.Env("DB_USERNAME", ""),
"password": config.Env("DB_PASSWORD", ""),
"charset": "utf8mb4",
"loc": "Local",
},
}
We have updated the configuration array with two new keys - read
and write
. The read
connection will use 192.168.1.1
as the host, while the write
connection will use 192.168.1.2
. Both connections will share the same database prefix, character set, and other options specified in the main mysql array. In case of multiple values in the host
configuration array, a database host will be selected randomly for each request.
Connection Pool
You can configure a connection pool in the configuration file, reasonable configuration of connection pool parameters can greatly improve concurrency performance:
Key | Action |
---|---|
pool.max_idle_conns | Max idle connections |
pool.max_open_conns | Max open connections |
pool.conn_max_idletime | Connections max idle time |
pool.conn_max_lifetime | Connections max lifetime |
Model Definition
To create a custom model, refer to the model file app/models/user.go
that is included in the framework. The struct
in app/models/user.go
contains two embedded frameworks: orm.Model
and orm.SoftDeletes
. These frameworks define id
, created_at
, updated_at
, and deleted_at
properties respectively. With orm.SoftDeletes
, you can enable soft deletion for the model.
Model Convention
- The model is named with a big hump;
- Use the plural form of the model "snake naming" as the table name;
For example, the model name is UserOrder
, and the table name is user_orders
.
Create Model
go run . artisan make:model User
go run . artisan make:model user/User
Specify Table Name
package models
import (
"github.com/goravel/framework/database/orm"
)
type User struct {
orm.Model
Name string
Avatar string
orm.SoftDeletes
}
func (r *User) TableName() string {
return "goravel_user"
}
Database Connections
By default, all models utilize the default database connection configured for your application. If you wish to specify a distinct connection to be used when interacting with a particular model, you need to define a Connection
method on the model.
package models
import (
"github.com/goravel/framework/database/orm"
)
type User struct {
orm.Model
Name string
Avatar string
orm.SoftDeletes
}
func (r *User) Connection() string {
return "postgresql"
}
facades.Orm available functions
Name | Action |
---|---|
Connection | Specify Database Connection |
DB | Generic Database Interface sql.DB |
Query | Get Database Instance |
Transaction | Transaction |
WithContext | Inject Context |
facades.Orm().Query & facades.Orm().Transaction available functions
Query Builder
Inject Context
facades.Orm().WithContext(ctx)
Specify Database Connection
If multiple database connections are defined in config/database.go
, you can use them through the Connection
function of facades.Orm()
. The connection name passed to Connection
should be one of the connections configured in config/database.go
:
facades.Orm().Connection("mysql")
Generic Database Interface sql.DB
Generic database interface sql.DB, then use the functionality it provides:
db, err := facades.Orm().DB()
db, err := facades.Orm().Connection("mysql").DB()
// Ping
db.Ping()
// Close
db.Close()
// Returns database statistics
db.Stats()
// SetMaxIdleConns sets the maximum number of connections in the idle connection pool
db.SetMaxIdleConns(10)
// SetMaxOpenConns sets the maximum number of open connections to the database
db.SetMaxOpenConns(100)
// SetConnMaxLifetime sets the maximum amount of time a connection may be reused
db.SetConnMaxLifetime(time.Hour)
Get Database Instance
Before each specific database operation, it's necessary to obtain an instance of the database.
facades.Orm().Query()
facades.Orm().Connection("mysql").Query()
facades.Orm().WithContext(ctx).Query()
Select
Query one line
var user models.User
facades.Orm().Query().First(&user)
// SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1;
Sometimes you may wish to perform some other action if no results are found. The FirstOr
method will return a single model instance or, if no results are found, execute the given closure. You can set values to model in closure:
facades.Orm().Query().Where("name", "first_user").FirstOr(&user, func() error {
user.Name = "goravel"
return nil
})
Query one or multiple lines by ID
var user models.User
facades.Orm().Query().Find(&user, 1)
// SELECT * FROM `users` WHERE `users`.`id` = 1;
var users []models.User
facades.Orm().Query().Find(&users, []int{1,2,3})
// SELECT * FROM `users` WHERE `users`.`id` IN (1,2,3);
Not found return error
var user models.User
err := facades.Orm().Query().FindOrFail(&user, 1)
When the primary key of the user table is string
type, you need to specify the primary key when calling Find
method
var user models.User
facades.Orm().Query().Find(&user, "uuid=?" ,"a")
// SELECT * FROM `users` WHERE `users`.`uuid` = "a";
Query multiple lines
var users []models.User
facades.Orm().Query().Where("id in ?", []int{1,2,3}).Get(&users)
// SELECT * FROM `users` WHERE id in (1,2,3);
Retrieving Or Creating Models
The FirstOrCreate
method searches for a database record using the specified column/value pairs. If the model cannot be found in the database, it creates a new record with the attributes from merging the first argument with the optional second argument.
Similarly, the FirstOrNew
method also tries to locate a record in the database based on the attributes given. However, if it is not found, a new instance of the model is returned. It's important to note that this new model has not been saved to the database yet and you need to manually call the Save
method to do so.
var user models.User
facades.Orm().Query().Where("gender", 1).FirstOrCreate(&user, models.User{Name: "tom"})
// SELECT * FROM `users` WHERE `gender` = 1 AND `users`.`name` = 'tom' ORDER BY `users`.`id` LIMIT 1;
// INSERT INTO `users` (`created_at`,`updated_at`,`name`) VALUES ('2023-09-18 12:51:32.556','2023-09-18 12:51:32.556','tom');
facades.Orm().Query().Where("gender", 1).FirstOrCreate(&user, models.User{Name: "tom"}, models.User{Avatar: "avatar"})
// SELECT * FROM `users` WHERE `gender` = 1 AND `users`.`name` = 'tom' ORDER BY `users`.`id` LIMIT 1;
// INSERT INTO `users` (`created_at`,`updated_at`,`name`,`avatar`) VALUES ('2023-09-18 12:52:59.913','2023-09-18 12:52:59.913','tom','avatar');
var user models.User
facades.Orm().Query().Where("gender", 1).FirstOrNew(&user, models.User{Name: "tom"})
// SELECT * FROM `users` WHERE `gender` = 1 AND `users`.`name` = 'tom' ORDER BY `users`.`id` LIMIT 1;
facades.Orm().Query().Where("gender", 1).FirstOrNew(&user, models.User{Name: "tom"}, models.User{Avatar: "avatar"})
// SELECT * FROM `users` WHERE `gender` = 1 AND `users`.`name` = 'tom' ORDER BY `users`.`id` LIMIT 1;
Not Found Error
When the requested item is not found, the First
method does not generate an error. To generate an error, use the FirstOrFail
method:
var user models.User
err := facades.Orm().Query().FirstOrFail(&user)
// err == orm.ErrRecordNotFound
Where
facades.Orm().Query().Where("name", "tom")
facades.Orm().Query().Where("name = 'tom'")
facades.Orm().Query().Where("name = ?", "tom")
facades.Orm().Query().WhereBetween("age", 1, 10)
facades.Orm().Query().WhereNotBetween("age", 1, 10)
facades.Orm().Query().WhereNotIn("name", []any{"a"})
facades.Orm().Query().WhereNull("name")
facades.Orm().Query().WhereIn("name", []any{"a"})
facades.Orm().Query().OrWhere("name = ?", "tom")
facades.Orm().Query().OrWhereNotIn("name", []any{"a"})
facades.Orm().Query().OrWhereNull("name")
facades.Orm().Query().OrWhereIn("name", []any{"a"})
Limit
var users []models.User
facades.Orm().Query().Where("name = ?", "tom").Limit(3).Get(&users)
// SELECT * FROM `users` WHERE name = 'tom' LIMIT 3;
Offset
var users []models.User
facades.Orm().Query().Where("name = ?", "tom").Offset(5).Limit(3).Get(&users)
// SELECT * FROM `users` WHERE name = 'tom' LIMIT 3 OFFSET 5;
Order
var users []models.User
facades.Orm().Query().Where("name = ?", "tom").Order("sort asc").Order("id desc").Get(&users)
// SELECT * FROM `users` WHERE name = 'tom' ORDER BY sort asc,id desc;
facades.Orm().Query().Where("name = ?", "tom").OrderBy("sort").Get(&users)
// SELECT * FROM `users` WHERE name = 'tom' ORDER BY sort asc;
facades.Orm().Query().Where("name = ?", "tom").OrderBy("sort", "desc").Get(&users)
// SELECT * FROM `users` WHERE name = 'tom' ORDER BY sort desc;
facades.Orm().Query().Where("name = ?", "tom").OrderByDesc("sort").Get(&users)
// SELECT * FROM `users` WHERE name = 'tom' ORDER BY sort desc;
facades.Orm().Query().Where("name = ?", "tom").InRandomOrder().Get(&users)
// SELECT * FROM `users` WHERE name = 'tom' ORDER BY RAND();
Paginate
var users []models.User
var total int64
facades.Orm().Query().Paginate(1, 10, &users, &total)
// SELECT count(*) FROM `users`;
// SELECT * FROM `users` LIMIT 10;
Query Single Column
var ages []int64
facades.Orm().Query().Model(&models.User{}).Pluck("age", &ages)
// SELECT `age` FROM `users`;
Specify Table Query
If you want to query some aggregate data, you need to specify a specific table.
Specify a model
var count int64
facades.Orm().Query().Model(&models.User{}).Count(&count)
// SELECT count(*) FROM `users` WHERE deleted_at IS NULL;
Specify a table
var count int
facades.Orm().Query().Table("users").Count(&count)
// SELECT count(*) FROM `users`; // get all records, whether deleted or not
Get SQL
Get SQL with placeholder:
facades.Orm().Query().ToSql().Get(models.User{})
// SELECT * FROM "users" WHERE "id" = $1 AND "users"."deleted_at" IS NULL
Get SQL with value:
facades.Orm().Query().ToRawSql().Get(models.User{})
// SELECT * FROM "users" WHERE "id" = 1 AND "users"."deleted_at" IS NULL
The methods can be called after ToSql
and ToRawSql
: Count
, Create
, Delete
, Find
, First
, Get
, Pluck
, Save
, Sum
, Update
.
Count
var count int64
facades.Orm().Query().Table("users").Where("name = ?", "tom").Count(&count)
// SELECT count(*) FROM `users` WHERE name = 'tom';
Specify Fields
Select
allows you to specify which fields to retrieve from the database, by default the ORM retrieves all fields.
facades.Orm().Query().Select("name", "age").Get(&users)
// SELECT `name`,`age` FROM `users`;
facades.Orm().Query().Select([]string{"name", "age"}).Get(&users)
// SELECT `name`,`age` FROM `users`;
Group By & Having
type Result struct {
Name string
Total int
}
var result Result
facades.Orm().Query().Model(&models.User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "tom").Get(&result)
// SELECT name, sum(age) as total FROM `users` GROUP BY `name` HAVING name = "tom";
Join
type Result struct {
Name string
Email string
}
var result Result
facades.Orm().Query().Model(&models.User{}).Select("users.name, emails.email").Join("left join emails on emails.user_id = users.id").Scan(&result)
// SELECT users.name, emails.email FROM `users` LEFT JOIN emails ON emails.user_id = users.id;
Create
user := models.User{Name: "tom", Age: 18}
result := facades.Orm().Query().Create(&user)
// INSERT INTO users (name, age, created_at, updated_at) VALUES ("tom", 18, "2022-09-27 22:00:00", "2022-09-27 22:00:00");
Multiple create
users := []models.User{{Name: "tom", Age: 18}, {Name: "tim", Age: 19}}
result := facades.Orm().Query().Create(&users)
created_at
andupdated_at
will be filled automatically.
Cursor
Can be used to significantly reduce your application's memory consumption when iterating through tens of thousands of Eloquent model records. Note, the Cursor
method can be used with With
at the same time, please use Lazy Eager Loading to load relationship in the for
logic.
cursor, err := facades.Orm().Query().Model(models.User{}).Cursor()
if err != nil {
return err
}
for row := range cursor {
var user models.User
if err := row.Scan(&user); err != nil {
return err
}
fmt.Println(user)
}
Save Model
Update an existing model
var user models.User
facades.Orm().Query().First(&user)
user.Name = "tom"
user.Age = 100
facades.Orm().Query().Save(&user)
// UPDATE `users` SET `created_at`='2023-09-14 16:03:29.454',`updated_at`='2023-09-18 21:05:59.896',`name`='tom',`age`=100,`avatar`='' WHERE `id` = 1;
Update columns
facades.Orm().Query().Model(&models.User{}).Where("name", "tom").Update("name", "hello")
// UPDATE `users` SET `name`='hello',`updated_at`='2023-09-18 21:06:30.373' WHERE `name` = 'tom';
facades.Orm().Query().Model(&models.User{}).Where("name", "tom").Update(models.User{Name: "hello", Age: 18})
// UPDATE `users` SET `updated_at`='2023-09-18 21:07:06.489',`name`='hello',`age`=18 WHERE `name` = 'tom';
When updating with
struct
, Orm will only update non-zero fields. You might want to usemap
to update attributes or useSelect
to specify fields to update. Note thatstruct
can only beModel
, if you want to update with nonModel
, you need to use.Table("users")
, however, theupdated_at
field cannot be updated automatically at this time.
Update or create
Query by name
, if not exist, create by name
, avatar
, if exists, update avatar
based on name
:
facades.Orm().Query().UpdateOrCreate(&user, models.User{Name: "name"}, models.User{Avatar: "avatar"})
// SELECT * FROM `users` WHERE `users`.`name` = 'name' AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT 1;
// INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`,`name`,`avatar`) VALUES ('2023-03-11 10:11:08.869','2023-03-11 10:11:08.869',NULL,'name','avatar');
// UPDATE `users` SET `name`='name',avatar`='avatar',`updated_at`='2023-03-11 10:11:08.881' WHERE users`.`deleted_at` IS NULL AND `id` = 1;
Delete
Delete by model, the number of rows affected by the statement is returned by the method:
var user models.User
facades.Orm().Query().Find(&user, 1)
res, err := facades.Orm().Query().Delete(&user)
// DELETE FROM `users` WHERE `users`.`id` = 1;
num := res.RowsAffected
Delete by ID
facades.Orm().Query().Delete(&models.User{}, 10)
// DELETE FROM `users` WHERE `users`.`id` = 10;
facades.Orm().Query().Delete(&models.User{}, []int{1, 2, 3})
// DELETE FROM `users` WHERE `users`.`id` IN (1,2,3);
Multiple delete
facades.Orm().Query().Where("name = ?", "tom").Delete(&models.User{})
// DELETE FROM `users` WHERE name = 'tom';
Want to force delete a soft-delete data.
facades.Orm().Query().Where("name = ?", "tom").ForceDelete(&models.User{})
You can delete records with model associations via Select
:
// Delete Account of user when deleting user
facades.Orm().Query().Select("Account").Delete(&user)
// Delete Orders and CreditCards of user when deleting user
facades.Orm().Query().Select("Orders", "CreditCards").Delete(&user)
// Delete all child associations of user when deleting user
facades.Orm().Query().Select(orm.Associations).Delete(&user)
// Delete all Account of users when deleting users
facades.Orm().Query().Select("Account").Delete(&users)
Note: The associations will be deleted only if the primary key of the record is not empty, and Orm uses these primary keys as conditions to delete associated records:
// Delete user that name='goravel', but don't delete account of user
facades.Orm().Query().Select("Account").Where("name = ?", "goravel").Delete(&models.User{})
// Delete user that name='goravel' and id = 1, and delete account of user
facades.Orm().Query().Select("Account").Where("name = ?", "goravel").Delete(&models.User{ID: 1})
// Delete user that id = 1 and delete account of that user
facades.Orm().Query().Select("Account").Delete(&models.User{ID: 1})
If execute batch delete without any conditions, ORM doesn't do that and returns an error. So you have to add some conditions, or use native SQL.
Query Soft Delete Data
var user models.User
facades.Orm().Query().WithTrashed().First(&user)
Filter Repetition
var users []models.User
facades.Orm().Query().Distinct("name").Find(&users)
Get Driver
driver := facades.Orm().Query().Driver()
// Judge driver
if driver == orm.DriverMysql {}
Execute Native SQL
type Result struct {
ID int
Name string
Age int
}
var result Result
facades.Orm().Query().Raw("SELECT id, name, age FROM users WHERE name = ?", "tom").Scan(&result)
Execute Native Update SQL
The number of rows affected by the statement is returned by the method:
res, err := facades.Orm().Query().Exec("DROP TABLE users")
// DROP TABLE `users`;
num := res.RowsAffected
Exists
var exists bool
facades.Orm().Query().Where("name", "tom").Exists(&exists)
Transaction
You can execute a transaction by Transaction
function.
import (
"github.com/goravel/framework/contracts/database/orm"
"github.com/goravel/framework/facades"
"goravel/app/models"
)
...
return facades.Orm().Transaction(func(tx orm.Transaction) error {
var user models.User
return tx.Find(&user, user.ID)
})
You can also manually control the flow of the transaction yourself:
tx, err := facades.Orm().Query().Begin()
user := models.User{Name: "Goravel"}
if err := tx.Create(&user); err != nil {
err := tx.Rollback()
} else {
err := tx.Commit()
}
Scopes
Allows you to specify commonly used queries that can be referenced when method are called.
func Paginator(page string, limit string) func(methods orm.Query) orm.Query {
return func(query orm.Query) orm.Query {
page, _ := strconv.Atoi(page)
limit, _ := strconv.Atoi(limit)
offset := (page - 1) * limit
return query.Offset(offset).Limit(limit)
}
}
// scopes.Paginator is a custom function: func(ormcontract.Query) ormcontract.Query
facades.Orm().Query().Scopes(scopes.Paginator(page, limit)).Find(&entries)
Raw Expressions
You can use the db.Raw
method to update fields:
import "github.com/goravel/framework/database/db"
facades.Orm().Query().Model(&user).Update("age", db.Raw("age - ?", 1))
// UPDATE `users` SET `age`=age - 1,`updated_at`='2023-09-14 14:03:20.899' WHERE `users`.`deleted_at` IS NULL AND `id` = 1;
Pessimistic Locking
The query builder also includes a few functions to help you achieve "pessimistic locking" when executing your select
statements.
To execute a statement with a "shared lock", you may call the SharedLock
method. A shared lock prevents the selected rows from being modified until your transaction is committed:
var users []models.User
facades.Orm().Query().Where("votes", ">", 100).SharedLock().Get(&users)
Alternatively, you may use the LockForUpdate
method. A "for update" lock prevents the selected records from being modified or from being selected with another shared lock:
var users []models.User
facades.Orm().Query().Where("votes", ">", 100).LockForUpdate().Get(&users)
Sum
var sum int
if err := facades.Orm().Query().Model(models.User{}).Sum("id", &sum); err != nil {
return err
}
fmt.Println(sum)
Events
Orm models dispatch several events, allowing you to hook into the following moments in a model's lifecycle: Retrieved
, Creating
, Created
, Updating
, Updated
, Saving
, Saved
, Deleting
, Deleted
, ForceDeleting
, ForceDeleted
.
The Retrieved
event will dispatch when an existing model is retrieved from the database. When a new model is saved for the first time, the Creating
and Created
events will dispatch. The Updating
/ Updated
events will dispatch when an existing model is modified and the Save
method is called. The Saving
/ Saved
events will dispatch when a model is created or updated - even if the model's attributes have not been changed. Event names ending with -ing
are dispatched before any changes to the model are persisted, while events ending with -ed
are dispatched after the changes to the model are persisted.
To start listening to model events, define a DispatchesEvents
method on your model. This property maps various points of the model's lifecycle to your own event classes.
import (
contractsorm "github.com/goravel/framework/contracts/database/orm"
"github.com/goravel/framework/database/orm"
)
type User struct {
orm.Model
Name string
}
func (u *User) DispatchesEvents() map[contractsorm.EventType]func(contractsorm.Event) error {
return map[contractsorm.EventType]func(contractsorm.Event) error{
contractsorm.EventCreating: func(event contractsorm.Event) error {
return nil
},
contractsorm.EventCreated: func(event contractsorm.Event) error {
return nil
},
contractsorm.EventSaving: func(event contractsorm.Event) error {
return nil
},
contractsorm.EventSaved: func(event contractsorm.Event) error {
return nil
},
contractsorm.EventUpdating: func(event contractsorm.Event) error {
return nil
},
contractsorm.EventUpdated: func(event contractsorm.Event) error {
return nil
},
contractsorm.EventDeleting: func(event contractsorm.Event) error {
return nil
},
contractsorm.EventDeleted: func(event contractsorm.Event) error {
return nil
},
contractsorm.EventForceDeleting: func(event contractsorm.Event) error {
return nil
},
contractsorm.EventForceDeleted: func(event contractsorm.Event) error {
return nil
},
contractsorm.EventRetrieved: func(event contractsorm.Event) error {
return nil
},
}
}
Note: Just register the events you need. Model events are not dispatched when doing batch operations through Orm.
Observers
Defining Observers
If you are listening to many events on a given model, you may use observers to group all of your listeners into a single class. Observer classes have method names that reflect the Eloquent events you wish to listen for. Each of these methods receives the affected model as their only argument. The make:observer
Artisan command is the easiest way to create a new observer class:
go run . artisan make:observer UserObserver
go run . artisan make:observer user/UserObserver
This command will place the new observer in your app/observers
directory. If this directory does not exist, Artisan will create it for you. Your fresh observer will look like the following:
package observers
import (
"fmt"
"github.com/goravel/framework/contracts/database/orm"
)
type UserObserver struct{}
func (u *UserObserver) Retrieved(event orm.Event) error {
return nil
}
func (u *UserObserver) Creating(event orm.Event) error {
return nil
}
func (u *UserObserver) Created(event orm.Event) error {
return nil
}
func (u *UserObserver) Updating(event orm.Event) error {
return nil
}
func (u *UserObserver) Updated(event orm.Event) error {
return nil
}
func (u *UserObserver) Saving(event orm.Event) error {
return nil
}
func (u *UserObserver) Saved(event orm.Event) error {
return nil
}
func (u *UserObserver) Deleting(event orm.Event) error {
return nil
}
func (u *UserObserver) Deleted(event orm.Event) error {
return nil
}
func (u *UserObserver) ForceDeleting(event orm.Event) error {
return nil
}
func (u *UserObserver) ForceDeleted(event orm.Event) error {
return nil
}
To register an observer, you need to call the Observe
method on the model you wish to observe. You may register observers in the Boot
method of your application's app/providers/event_service_provider.go::Boot
service provider:
package providers
import (
"github.com/goravel/framework/facades"
"goravel/app/models"
"goravel/app/observers"
)
type EventServiceProvider struct {
}
func (receiver *EventServiceProvider) Register(app foundation.Application) {
facades.Event().Register(receiver.listen())
}
func (receiver *EventServiceProvider) Boot(app foundation.Application) {
facades.Orm().Observe(models.User{}, &observers.UserObserver{})
}
func (receiver *EventServiceProvider) listen() map[event.Event][]event.Listener {
return map[event.Event][]event.Listener{}
}
Note: If you set
DispatchesEvents
andObserver
at the same time, onlyDispatchesEvents
will be applied.
Parameter in Observer
The event
parameter will be passed to all observers:
Method | Action |
---|---|
Context | Get context that passed by facades.Orm().WithContext() |
GetAttribute | Get the modified value, if not modified, get the original value, if there is no original value, return nil |
GetOriginal | Get the original value, if there is no original value, return nil |
IsDirty | Determine whether the field is modified |
IsClean | IsDirty reverse |
Query | Get a new Query, which can be used with transaction |
SetAttribute | Set a new value for a field |
Muting Events
You may occasionally need to temporarily "mute" all events fired by a model. You may achieve this using the WithoutEvents
method:
var user models.User
facades.Orm().Query().WithoutEvents().Find(&user, 1)
Saving A Single Model Without Events
Sometimes you may wish to "save" a given model without dispatching any events. You may accomplish this with the SaveQuietly
method:
var user models.User
err := facades.Orm().Query().FindOrFail(&user, 1)
user.Name = "Goravel"
err := facades.Orm().Query().SaveQuietly(&user)