1. Table structure
CREATE TABLE `orders` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `order_no` varchar(32) NOT NULL DEFAULT '', `orderable_id` int unsigned NOT NULL DEFAULT '0', `orderable_type` char(30) NOT NULL DEFAULT '', `status` tinyint unsigned NOT NULL DEFAULT '0', `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, `deleted_at` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; CREATE TABLE `phone` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `phone_name` varchar(50) NOT NULL DEFAULT '', `phone_model` varchar(30) NOT NULL DEFAULT '', `status` tinyint unsigned NOT NULL DEFAULT '0', `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, `deleted_at` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1003 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; CREATE TABLE `cars` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `car_name` varchar(50) NOT NULL DEFAULT '', `car_model` varchar(30) NOT NULL DEFAULT '', `status` tinyint unsigned NOT NULL DEFAULT '0', `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, `deleted_at` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1003 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
2. Interface definition
// The LoaderAble interface defines data loading behaviortype LoaderAble interface { LoadAble(IDs []int) (map[int]any, error) } // The LoadAbleItem interface defines a common method for add-onstype LoadAbleItem interface { GetAbleType() string // Get type key value GetAbleID() int // Get ID SetLoadedAbleData(data any) // Set the loaded data}
3. Model definition and implementation of interfaces
type Order struct { Id int `json:"id"` OrderNo string `json:"order_no"` OrderableId int `json:"orderable_id"` OrderableType string `json:"orderable_type"` Orderable any `json:"orderable" gorm:"-"` Status uint8 `json:"status"` CreatedAt * `json:"created_at"` UpdatedAt * `json:"updated_at"` DeletedAt `json:"deleted_at"` } func (tb *Order) TableName() string { return "orders" } func (tb *Order) GetAbleType() string { return } func (tb *Order) GetAbleID() int { return } func (tb *Order) SetLoadedAbleData(data any) { = data } //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- type Car struct { Id int `json:"id"` CarName string `json:"car_name"` CarModel string `json:"car_model"` Status uint8 `json:"status"` CreatedAt * `json:"created_at"` UpdatedAt * `json:"updated_at"` DeletedAt `json:"deleted_at"` } func (tb *Car) TableName() string { return "cars" } // CarLoaderAble implements the Loader interfacetype CarLoaderAble struct{} // LoadAble implements loading polymorphic association logic// IDs polymorphic association type ID (main parameter)func (loader *CarLoaderAble) LoadAble(IDs []int) (resultMap map[int]any, err error) { IDsLen := len(IDs) if IDsLen == 0 { return } car := make([]*Car, 0, IDsLen) err = ().Where("id IN (?) AND status = ?", IDs, 1).Find(&car).Error if err != nil { return } resultMap = make(map[int]any, IDsLen) for _, item := range car { resultMap[] = item } return } //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- type Phone struct { Id int `json:"id"` PhoneName string `json:"phone_name"` PhoneModel string `json:"phone_model"` Status uint8 `json:"status" gorm:"column:status"` StatusNew uint8 `json:"status_new" gorm:"-"` CreatedAt * `json:"created_at"` UpdatedAt * `json:"updated_at"` DeletedAt `json:"deleted_at"` } func (tb *Phone) TableName() string { return "phone" } func (tb *Phone) AfterFind(tx *) (err error) { = return } // PhoneLoaderAble implements the Loader interfacetype PhoneLoaderAble struct{} // LoadAble implements loading polymorphic association logic// IDs polymorphic association type ID (main parameter)func (loader *PhoneLoaderAble) LoadAble(IDs []int) (resultMap map[int]any, err error) { IDsLen := len(IDs) if IDsLen == 0 { return } phone := make([]*Phone, 0, IDsLen) err = ().Where("id IN (?) AND status = ?", IDs, 1).Find(&phone).Error if err != nil { return } resultMap = make(map[int]any, IDsLen) for _, item := range phone { resultMap[] = item } return }
4. Create a loader preloader
// LoaderAbleFactory is used to manage different types of loaderstype LoaderAbleFactory struct { loaders map[string]LoaderAble } func NewLoaderAbleFactory() *LoaderAbleFactory { return &LoaderAbleFactory{ loaders: make(map[string]LoaderAble), } } func (f *LoaderAbleFactory) RegisterLoader(typeName string, loader LoaderAble) { [typeName] = loader }
5. Register loader preloader service
var ( loaderAbleFactory *LoaderAbleFactory ) // init Select global loading when the project starts initializationfunc init() { loaderAbleFactory = NewLoaderAbleFactory() ("phone", &PhoneLoaderAble{}) ("car", &CarLoaderAble{}) ("Polymorphic model relationship registration was successful...") }
6. Implement LoadAble's general loading function
// LoadAble is a common load function that can handle any slice that implements the LoadableItem interfacefunc LoadAble[T LoadAbleItem](items []T) error { if len(items) == 0 { return nil } // Group IDs by type typeIDsMap := make(map[string][]int) for _, item := range items { typeKey := () typeIDsMap[typeKey] = append(typeIDsMap[typeKey], ()) } // Use the corresponding loader to load data typeDataMap := make(map[string]map[int]any) for typeName, ids := range typeIDsMap { loader, ok := [typeName] if !ok { continue } resultMap, err := (ids) if err != nil { return err } typeDataMap[typeName] = resultMap } // Fill in data for _, item := range items { if dataMap, ok := typeDataMap[()]; ok { if data, exists := dataMap[()]; exists { (data) } } } return nil }
7. Debugging
- Prepare data
--orderssurface INSERT INTO `orders` (`id`, `order_no`, `orderable_id`, `orderable_type`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES (1, '202411010001', 1002, 'car', 1, '2024-11-01 12:03:03', '2024-11-01 12:03:06', NULL); INSERT INTO `orders` (`id`, `order_no`, `orderable_id`, `orderable_type`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES (2, '202411010002', 1001, 'phone', 1, '2024-11-01 12:03:03', '2024-11-01 12:03:06', NULL); INSERT INTO `orders` (`id`, `order_no`, `orderable_id`, `orderable_type`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES (3, '202411010003', 1000, 'car', 1, '2024-11-01 12:03:03', '2024-11-01 12:03:06', NULL); INSERT INTO `orders` (`id`, `order_no`, `orderable_id`, `orderable_type`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES (4, '202411010004', 1001, 'car', 1, '2024-11-01 12:03:03', '2024-11-01 12:03:06', NULL); INSERT INTO `orders` (`id`, `order_no`, `orderable_id`, `orderable_type`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES (5, '202411010005', 1002, 'phone', 1, '2024-11-01 12:03:03', '2024-11-01 12:03:06', NULL); --phonesurface INSERT INTO `phone` (`id`, `phone_name`, `phone_model`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES (1000, 'XiaoMi', '2S', 2, '2024-11-01 11:59:37', '2024-11-01 11:59:40', NULL); INSERT INTO `phone` (`id`, `phone_name`, `phone_model`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES (1001, 'HUAWEI', 'mate60', 1, '2024-11-01 11:59:54', '2024-11-01 11:59:57', NULL); INSERT INTO `phone` (`id`, `phone_name`, `phone_model`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES (1002, 'Apple', 'iPhone 12 Pro Max', 1, '2024-11-01 12:00:26', '2024-11-01 12:00:28', NULL); --carssurface INSERT INTO `cars` (`id`, `car_name`, `car_model`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES (1000, 'Audi', 'A6L', 1, '2024-11-01 11:57:53', '2024-11-01 11:57:55', NULL); INSERT INTO `cars` (`id`, `car_name`, `car_model`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES (1001, 'BMW', '5 series', 1, '2024-11-01 11:58:12', '2024-11-01 11:58:15', NULL); INSERT INTO `cars` (`id`, `car_name`, `car_model`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES (1002, 'Benz', 'E300', 1, '2024-11-01 11:58:53', '2024-11-01 11:58:56', NULL);
- Writing code
// Gin framework + Gorm as an example("/orders", ) // GetOrderList Get the order list interfacefunc GetOrderList(c *) { orders := make([]*, 0) err := ().Find(&orders).Error if err != nil { (200, {"error": ()}) return } err = (orders) if err != nil { (200, {"error": ()}) return } (200, {"data": orders}) }
- Make a request
curl '127.0.0.1:16888/api/orders'
{ "data": [ { "id": 1, "order_no": "202411010001", "orderable_id": 1002, "orderable_type": "car", "orderable": { "id": 1002, "car_name": "Benz", "car_model": "E300", "status": 1, "created_at": "2024-11-01T11:58:53+08:00", "updated_at": "2024-11-01T11:58:56+08:00", "deleted_at": null }, "status": 1, "created_at": "2024-11-01T12:03:03+08:00", "updated_at": "2024-11-01T12:03:06+08:00", "deleted_at": null }, { "id": 2, "order_no": "202411010002", "orderable_id": 1001, "orderable_type": "phone", "orderable": { "id": 1001, "phone_name": "HUAWEI", "phone_model": "mate60", "status": 1, "status_new": 1, "created_at": "2024-11-01T11:59:54+08:00", "updated_at": "2024-11-01T11:59:57+08:00", "deleted_at": null }, "status": 1, "created_at": "2024-11-01T12:03:03+08:00", "updated_at": "2024-11-01T12:03:06+08:00", "deleted_at": null }, { "id": 3, "order_no": "202411010003", "orderable_id": 1000, "orderable_type": "car", "orderable": { "id": 1000, "car_name": "Audi", "car_model": "A6L", "status": 1, "created_at": "2024-11-01T11:57:53+08:00", "updated_at": "2024-11-01T11:57:55+08:00", "deleted_at": null }, "status": 1, "created_at": "2024-11-01T12:03:03+08:00", "updated_at": "2024-11-01T12:03:06+08:00", "deleted_at": null }, { "id": 4, "order_no": "202411010004", "orderable_id": 1001, "orderable_type": "car", "orderable": { "id": 1001, "car_name": "BMW", "car_model": "5 Series", "status": 1, "created_at": "2024-11-01T11:58:12+08:00", "updated_at": "2024-11-01T11:58:15+08:00", "deleted_at": null }, "status": 1, "created_at": "2024-11-01T12:03:03+08:00", "updated_at": "2024-11-01T12:03:06+08:00", "deleted_at": null }, { "id": 5, "order_no": "202411010005", "orderable_id": 1002, "orderable_type": "phone", "orderable": { "id": 1002, "phone_name": "Apple", "phone_model": "iPhone 12 Pro Max", "status": 1, "status_new": 1, "created_at": "2024-11-01T12:00:26+08:00", "updated_at": "2024-11-01T12:00:28+08:00", "deleted_at": null }, "status": 1, "created_at": "2024-11-01T12:03:03+08:00", "updated_at": "2024-11-01T12:03:06+08:00", "deleted_at": null } ] }
The above is the detailed content of Golang Gorm implementing custom polymorphic model association query. For more information about Golang Gorm association query, please pay attention to my other related articles!