Skip to content

Commit 4d19368

Browse files
authored
[Woo POS][Local Catalog] Enable storing data for multiple sites at once (#16120)
2 parents 7b5f2dc + 43c7bf6 commit 4d19368

13 files changed

+213
-98
lines changed

Modules/Sources/Storage/GRDB/Migrations/V001InitialSchema.swift

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ struct V001InitialSchema {
2929
// not derive them from a Swift class.
3030
// https://swiftpackageindex.com/groue/grdb.swift/v7.6.1/documentation/grdb/migrations#Good-Practices-for-Defining-Migrations
3131
try db.create(table: "product") { productTable in
32-
productTable.primaryKey("id", .integer).notNull()
32+
productTable.column("id", .integer).notNull()
33+
productTable.primaryKey(["siteID", "id"]) // SiteID column created by belongsTo relationship
3334
productTable.belongsTo("site", onDelete: .cascade).notNull()
3435

3536
productTable.column("name", .text).notNull()
@@ -56,7 +57,12 @@ struct V001InitialSchema {
5657
try db.create(table: "productAttribute") { productAttributeTable in
5758
// This table holds local product attributes only. Global attributes belong to a site.
5859
productAttributeTable.autoIncrementedPrimaryKey("id").notNull()
59-
productAttributeTable.belongsTo("product", onDelete: .cascade).notNull()
60+
productAttributeTable.column("siteID", .integer).notNull()
61+
productAttributeTable.column("productID", .integer).notNull()
62+
productAttributeTable.foreignKey(["siteID", "productID"],
63+
references: "product",
64+
columns: ["siteID", "id"],
65+
onDelete: .cascade)
6066

6167
productAttributeTable.column("name", .text).notNull()
6268
productAttributeTable.column("position", .integer).notNull()
@@ -68,8 +74,14 @@ struct V001InitialSchema {
6874

6975
private static func createProductImageTable(_ db: Database) throws {
7076
try db.create(table: "productImage") { productImageTable in
71-
productImageTable.primaryKey("id", .integer).notNull()
72-
productImageTable.belongsTo("product", onDelete: .cascade).notNull()
77+
productImageTable.column("siteID", .integer).notNull()
78+
productImageTable.column("id", .integer).notNull()
79+
productImageTable.primaryKey(["siteID", "id"])
80+
productImageTable.column("productID", .integer).notNull()
81+
productImageTable.foreignKey(["siteID", "productID"],
82+
references: "product",
83+
columns: ["siteID", "id"],
84+
onDelete: .cascade)
7385

7486
productImageTable.column("dateCreated", .datetime).notNull()
7587
productImageTable.column("dateModified", .datetime)
@@ -82,9 +94,14 @@ struct V001InitialSchema {
8294

8395
private static func createProductVariationTable(_ db: Database) throws {
8496
try db.create(table: "productVariation") { productVariationTable in
85-
productVariationTable.primaryKey("id", .integer).notNull()
97+
productVariationTable.column("id", .integer).notNull()
98+
productVariationTable.primaryKey(["siteID", "id"]) // SiteID column created by belongsTo relationship
99+
productVariationTable.column("productID", .integer).notNull()
86100
productVariationTable.belongsTo("site", onDelete: .cascade).notNull()
87-
productVariationTable.belongsTo("product", onDelete: .cascade).notNull()
101+
productVariationTable.foreignKey(["siteID", "productID"],
102+
references: "product",
103+
columns: ["siteID", "id"],
104+
onDelete: .cascade)
88105

89106
productVariationTable.column("sku", .text)
90107
productVariationTable.column("globalUniqueID", .text)
@@ -104,7 +121,12 @@ struct V001InitialSchema {
104121
try db.create(table: "productVariationAttribute") { productVariationAttributeTable in
105122
// This table holds local variation attributes only. Global attributes belong to a site.
106123
productVariationAttributeTable.autoIncrementedPrimaryKey("id").notNull()
107-
productVariationAttributeTable.belongsTo("productVariation", onDelete: .cascade).notNull()
124+
productVariationAttributeTable.column("siteID", .integer).notNull()
125+
productVariationAttributeTable.column("productVariationID", .integer).notNull()
126+
productVariationAttributeTable.foreignKey(["siteID", "productVariationID"],
127+
references: "productVariation",
128+
columns: ["siteID", "id"],
129+
onDelete: .cascade)
108130

109131
productVariationAttributeTable.column("name", .text).notNull()
110132
productVariationAttributeTable.column("option", .text).notNull()
@@ -113,8 +135,14 @@ struct V001InitialSchema {
113135

114136
private static func createProductVariationImageTable(_ db: Database) throws {
115137
try db.create(table: "productVariationImage") { productVariationImageTable in
116-
productVariationImageTable.primaryKey("id", .integer).notNull()
117-
productVariationImageTable.belongsTo("productVariation", onDelete: .cascade).notNull()
138+
productVariationImageTable.column("siteID", .integer).notNull()
139+
productVariationImageTable.column("id", .integer).notNull()
140+
productVariationImageTable.primaryKey(["siteID", "id"])
141+
productVariationImageTable.column("productVariationID", .integer).notNull()
142+
productVariationImageTable.foreignKey(["siteID", "productVariationID"],
143+
references: "productVariation",
144+
columns: ["siteID", "id"],
145+
onDelete: .cascade)
118146

119147
productVariationImageTable.column("dateCreated", .datetime).notNull()
120148
productVariationImageTable.column("dateModified", .datetime)

Modules/Sources/Storage/GRDB/Model/PersistedProduct.swift

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -53,25 +53,33 @@ public struct PersistedProduct: Codable {
5353
extension PersistedProduct: FetchableRecord, PersistableRecord {
5454
public static var databaseTableName: String { "product" }
5555

56+
public static var primaryKey: [String] { [CodingKeys.siteID.stringValue, CodingKeys.id.stringValue] }
57+
5658
public enum Columns {
57-
static let id = Column(CodingKeys.id)
58-
static let siteID = Column(CodingKeys.siteID)
59-
static let name = Column(CodingKeys.name)
60-
static let productTypeKey = Column(CodingKeys.productTypeKey)
61-
static let fullDescription = Column(CodingKeys.fullDescription)
62-
static let shortDescription = Column(CodingKeys.shortDescription)
63-
static let sku = Column(CodingKeys.sku)
64-
static let globalUniqueID = Column(CodingKeys.globalUniqueID)
65-
static let price = Column(CodingKeys.price)
66-
static let downloadable = Column(CodingKeys.downloadable)
67-
static let parentID = Column(CodingKeys.parentID)
68-
static let manageStock = Column(CodingKeys.manageStock)
69-
static let stockQuantity = Column(CodingKeys.stockQuantity)
70-
static let stockStatusKey = Column(CodingKeys.stockStatusKey)
59+
public static let id = Column(CodingKeys.id)
60+
public static let siteID = Column(CodingKeys.siteID)
61+
public static let name = Column(CodingKeys.name)
62+
public static let productTypeKey = Column(CodingKeys.productTypeKey)
63+
public static let fullDescription = Column(CodingKeys.fullDescription)
64+
public static let shortDescription = Column(CodingKeys.shortDescription)
65+
public static let sku = Column(CodingKeys.sku)
66+
public static let globalUniqueID = Column(CodingKeys.globalUniqueID)
67+
public static let price = Column(CodingKeys.price)
68+
public static let downloadable = Column(CodingKeys.downloadable)
69+
public static let parentID = Column(CodingKeys.parentID)
70+
public static let manageStock = Column(CodingKeys.manageStock)
71+
public static let stockQuantity = Column(CodingKeys.stockQuantity)
72+
public static let stockStatusKey = Column(CodingKeys.stockStatusKey)
7173
}
7274

73-
public static let images = hasMany(PersistedProductImage.self)
74-
public static let attributes = hasMany(PersistedProductAttribute.self)
75+
public static let images = hasMany(PersistedProductImage.self,
76+
using: ForeignKey([PersistedProductImage.CodingKeys.siteID.stringValue,
77+
PersistedProductImage.CodingKeys.productID.stringValue],
78+
to: primaryKey))
79+
public static let attributes = hasMany(PersistedProductAttribute.self,
80+
using: ForeignKey([PersistedProductAttribute.CodingKeys.siteID.stringValue,
81+
PersistedProductAttribute.CodingKeys.productID.stringValue],
82+
to: primaryKey))
7583
}
7684

7785
// periphery:ignore - TODO: remove ignore when populating database

Modules/Sources/Storage/GRDB/Model/PersistedProductAttribute.swift

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import GRDB
44
// periphery:ignore - TODO: remove ignore when populating database
55
public struct PersistedProductAttribute: Codable {
66
public private(set) var id: Int64?
7+
public let siteID: Int64
78
public let productID: Int64
89
public let name: String
910
public let position: Int64
@@ -12,13 +13,15 @@ public struct PersistedProductAttribute: Codable {
1213
public let options: [String]
1314

1415
public init(id: Int64? = nil,
16+
siteID: Int64,
1517
productID: Int64,
1618
name: String,
1719
position: Int64,
1820
visible: Bool,
1921
variation: Bool,
2022
options: [String]) {
2123
self.id = id
24+
self.siteID = siteID
2225
self.productID = productID
2326
self.name = name
2427
self.position = position
@@ -32,26 +35,32 @@ public struct PersistedProductAttribute: Codable {
3235
extension PersistedProductAttribute: FetchableRecord, MutablePersistableRecord {
3336
public static var databaseTableName: String { "productAttribute" }
3437

38+
public static var primaryKey: [String] { [CodingKeys.id.stringValue] }
39+
3540
public enum Columns {
36-
static let id = Column(CodingKeys.id)
41+
public static let id = Column(CodingKeys.id)
42+
public static let siteID = Column(CodingKeys.siteID)
3743
public static let productID = Column(CodingKeys.productID)
38-
static let name = Column(CodingKeys.name)
39-
static let position = Column(CodingKeys.position)
40-
static let visible = Column(CodingKeys.visible)
41-
static let variation = Column(CodingKeys.variation)
42-
static let options = Column(CodingKeys.options)
44+
public static let name = Column(CodingKeys.name)
45+
public static let position = Column(CodingKeys.position)
46+
public static let visible = Column(CodingKeys.visible)
47+
public static let variation = Column(CodingKeys.variation)
48+
public static let options = Column(CodingKeys.options)
4349
}
4450

4551
public mutating func didInsert(_ inserted: InsertionSuccess) {
4652
id = inserted.rowID
4753
}
54+
55+
public static let product = belongsTo(PersistedProduct.self)
4856
}
4957

5058

5159
// periphery:ignore - TODO: remove ignore when populating database
52-
private extension PersistedProductAttribute {
60+
extension PersistedProductAttribute {
5361
enum CodingKeys: String, CodingKey {
5462
case id
63+
case siteID
5564
case productID
5665
case name
5766
case position

Modules/Sources/Storage/GRDB/Model/PersistedProductImage.swift

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import GRDB
33

44
// periphery:ignore - TODO: remove ignore when populating database
55
public struct PersistedProductImage: Codable {
6+
public let siteID: Int64
67
public let id: Int64
78
public let productID: Int64
89
public let dateCreated: Date
@@ -11,13 +12,15 @@ public struct PersistedProductImage: Codable {
1112
public let name: String?
1213
public let alt: String?
1314

14-
public init(id: Int64,
15+
public init(siteID: Int64,
16+
id: Int64,
1517
productID: Int64,
1618
dateCreated: Date,
1719
dateModified: Date?,
1820
src: String,
1921
name: String?,
2022
alt: String?) {
23+
self.siteID = siteID
2124
self.id = id
2225
self.productID = productID
2326
self.dateCreated = dateCreated
@@ -32,21 +35,25 @@ public struct PersistedProductImage: Codable {
3235
extension PersistedProductImage: FetchableRecord, PersistableRecord {
3336
public static var databaseTableName: String { "productImage" }
3437

38+
public static var primaryKey: [String] { [CodingKeys.siteID.stringValue, CodingKeys.id.stringValue] }
39+
3540
public enum Columns {
36-
static let id = Column(CodingKeys.id)
41+
public static let siteID = Column(CodingKeys.siteID)
42+
public static let id = Column(CodingKeys.id)
3743
public static let productID = Column(CodingKeys.productID)
38-
static let dateCreated = Column(CodingKeys.dateCreated)
39-
static let dateModified = Column(CodingKeys.dateModified)
40-
static let src = Column(CodingKeys.src)
41-
static let name = Column(CodingKeys.name)
42-
static let alt = Column(CodingKeys.alt)
44+
public static let dateCreated = Column(CodingKeys.dateCreated)
45+
public static let dateModified = Column(CodingKeys.dateModified)
46+
public static let src = Column(CodingKeys.src)
47+
public static let name = Column(CodingKeys.name)
48+
public static let alt = Column(CodingKeys.alt)
4349
}
4450
}
4551

4652

4753
// periphery:ignore - TODO: remove ignore when populating database
48-
private extension PersistedProductImage {
54+
extension PersistedProductImage {
4955
enum CodingKeys: String, CodingKey {
56+
case siteID
5057
case id
5158
case productID
5259
case dateCreated

Modules/Sources/Storage/GRDB/Model/PersistedProductVariation.swift

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,22 +44,32 @@ public struct PersistedProductVariation: Codable {
4444
extension PersistedProductVariation: FetchableRecord, PersistableRecord {
4545
public static var databaseTableName: String { "productVariation" }
4646

47+
public static var primaryKey: [String] { [CodingKeys.siteID.stringValue, CodingKeys.id.stringValue] }
48+
4749
public enum Columns {
48-
static let id = Column(CodingKeys.id)
49-
static let siteID = Column(CodingKeys.siteID)
50-
static let productID = Column(CodingKeys.productID)
51-
static let sku = Column(CodingKeys.sku)
52-
static let globalUniqueID = Column(CodingKeys.globalUniqueID)
53-
static let price = Column(CodingKeys.price)
54-
static let downloadable = Column(CodingKeys.downloadable)
55-
static let fullDescription = Column(CodingKeys.fullDescription)
56-
static let manageStock = Column(CodingKeys.manageStock)
57-
static let stockQuantity = Column(CodingKeys.stockQuantity)
58-
static let stockStatusKey = Column(CodingKeys.stockStatusKey)
50+
public static let id = Column(CodingKeys.id)
51+
public static let siteID = Column(CodingKeys.siteID)
52+
public static let productID = Column(CodingKeys.productID)
53+
public static let sku = Column(CodingKeys.sku)
54+
public static let globalUniqueID = Column(CodingKeys.globalUniqueID)
55+
public static let price = Column(CodingKeys.price)
56+
public static let downloadable = Column(CodingKeys.downloadable)
57+
public static let fullDescription = Column(CodingKeys.fullDescription)
58+
public static let manageStock = Column(CodingKeys.manageStock)
59+
public static let stockQuantity = Column(CodingKeys.stockQuantity)
60+
public static let stockStatusKey = Column(CodingKeys.stockStatusKey)
5961
}
6062

61-
public static let attributes = hasMany(PersistedProductVariationAttribute.self).forKey("attributes")
62-
public static let image = hasOne(PersistedProductVariationImage.self).forKey("image")
63+
public static let attributes = hasMany(PersistedProductVariationAttribute.self,
64+
key: "attributes",
65+
using: ForeignKey([PersistedProductVariationAttribute.CodingKeys.siteID.stringValue,
66+
PersistedProductVariationAttribute.CodingKeys.productVariationID.stringValue],
67+
to: primaryKey))
68+
public static let image = hasOne(PersistedProductVariationImage.self,
69+
key: "image",
70+
using: ForeignKey([PersistedProductVariationImage.CodingKeys.siteID.stringValue,
71+
PersistedProductVariationImage.CodingKeys.productVariationID.stringValue],
72+
to: primaryKey))
6373
}
6474

6575

Modules/Sources/Storage/GRDB/Model/PersistedProductVariationAttribute.swift

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,18 @@ import GRDB
44
// periphery:ignore - TODO: remove ignore when populating database
55
public struct PersistedProductVariationAttribute: Codable {
66
public private(set) var id: Int64?
7+
public let siteID: Int64
78
public let productVariationID: Int64
89
public let name: String
910
public let option: String
1011

1112
public init(id: Int64? = nil,
13+
siteID: Int64,
1214
productVariationID: Int64,
1315
name: String,
1416
option: String) {
1517
self.id = id
18+
self.siteID = siteID
1619
self.productVariationID = productVariationID
1720
self.name = name
1821
self.option = option
@@ -24,23 +27,29 @@ public struct PersistedProductVariationAttribute: Codable {
2427
extension PersistedProductVariationAttribute: FetchableRecord, MutablePersistableRecord {
2528
public static var databaseTableName: String { "productVariationAttribute" }
2629

30+
public static var primaryKey: [String] { [CodingKeys.id.stringValue] }
31+
2732
public enum Columns {
28-
static let id = Column(CodingKeys.id)
33+
public static let id = Column(CodingKeys.id)
34+
public static let siteID = Column(CodingKeys.siteID)
2935
public static let productVariationID = Column(CodingKeys.productVariationID)
30-
static let name = Column(CodingKeys.name)
31-
static let option = Column(CodingKeys.option)
36+
public static let name = Column(CodingKeys.name)
37+
public static let option = Column(CodingKeys.option)
3238
}
3339

3440
public mutating func didInsert(_ inserted: InsertionSuccess) {
3541
id = inserted.rowID
3642
}
43+
44+
public static let variation = belongsTo(PersistedProductVariation.self)
3745
}
3846

3947

4048
// periphery:ignore - TODO: remove ignore when populating database
41-
private extension PersistedProductVariationAttribute {
49+
extension PersistedProductVariationAttribute {
4250
enum CodingKeys: String, CodingKey {
4351
case id
52+
case siteID
4453
case productVariationID
4554
case name
4655
case option

0 commit comments

Comments
 (0)