如何使用 GRDB 将项目添加到结构中但不在 Db table 中的数组中

How to add items into an array that are in the struct but not in the Db table using GRDB

在 Db 的 My_Quotes table(参见下面的 MyQuotes 结构)中,我存储了从设备上的用户联系人中选择的联系人 ID。这样,如果联系人信息发生更改,ID 将保持不变。因此,我从 Db 中的 My_Quotes table 中提取 ContactID,并使用它通过 func get_ContactName_Org.

构建 ContactFullName、ContactFirst、ContactLast 和 ContactOrg

我遇到的问题是 ContactFullName、ContactFirst、ContactLast 和 ContactOrg(请参阅下面的 QuoteList 结构)不在 My_Quotes table 中。错误是:致命错误:无法从缺少的列 ContactFullName

中读取字符串

我理解错误,但将这些项目添加到 My_Quotes table 会导致许多其他问题。 如何将这些项目插入到数组中?

        var theArray = [QuoteList]()

        do {
            try dbQueue_GRDB.read { db in
                for quotes in try QuoteList.fetchAll(db, sql: "SELECT * FROM My_Quotes ORDER BY QuoteID")
                {
                    let fullString = ModelData.get_ContactName_Org(contactID: quotes.ContactID).fullStaring
                    let first = ModelData.get_ContactName_Org(contactID: quotes.ContactID).firstName
                    let last = ModelData.get_ContactName_Org(contactID: quotes.ContactID).lastName
                    let org = ModelData.get_ContactName_Org(contactID: quotes.ContactID).clientOrg

                    theArray.append(QuoteList(QuoteID: quotes.QuoteID,
                                              Quote_Number: quotes.Quote_Number,
                                              Quote_Status: quotes.Quote_Status,
                                              ContactID: quotes.ContactID,
                                              ContactFullName: fullString, // This is not in the My_Quotes table
                                              ContactFirst: first, // This is not in the My_Quotes table
                                              ContactLast: last, // This is not in the My_Quotes table
                                              ContactOrg: org, // This is not in the My_Quotes table
                                              Inv_ID: quotes.Inv_ID,
                                              Locked_Bool: quotes.Locked_Bool))
                }
            }
        } catch {
            print("Couldn't populate the quote list! \(error)")
        }


    static func get_ContactName_Org(contactID: String) -> (firstName: String, lastName: String, clientOrg: String, fullStaring: String)
    {
        let store = CNContactStore()
        var theName = CNContact()
        var theStaring: String = ""
        var firstName: String = ""
        var lastName: String = ""
        var clientOrg: String = ""
        
        do {
            theName = try store.unifiedContact(withIdentifier: contactID, keysToFetch: [CNContactFormatter.descriptorForRequiredKeys(for: .fullName)])
            
        } catch {
            print("Fetching contact data failed: \(error)")
        }
        
        firstName = theName.givenName
        lastName = theName.familyName
        clientOrg = theName.organizationName
        
        theStaring = theName.organizationName == "" ? theName.givenName + " " + theName.familyName : theName.givenName + " " + theName.familyName + ", " + theName.organizationName
        
        return (firstName,lastName,clientOrg,theStaring)
    }
    

struct QuoteList: Codable, FetchableRecord
{
    let QuoteID: Int64
    var Quote_Number: String
    var Quote_Status: String
    var ContactID: String
    var ContactFullName: String // This is not in the My_Quotes table
    var ContactFirst: String // This is not in the My_Quotes table
    var ContactLast: String // This is not in the My_Quotes table
    var ContactOrg: String // This is not in the My_Quotes table
    var Inv_ID: Int64?
    var Locked_Bool: String
}


struct MyQuotes: Codable, FetchableRecord
{
    let QuoteID: Int64
    var Quote_Number: String
    var Quote_Date: String
    var Quote_Status: String
    var NumOf_People: String?
    var Event_Venue: String?
    var Routine_ID: Int64?
    var Routine_Price: Double?
    var Quote_Deposit: Double?
    var Deposit_Date: String?
    var Age_Range: String?
    var Lodging_Exp: Double?
    var Gas_Exp: Double?
    var Meals_Exp: Double?
    var Miscel_Exp: Double?
    var Airfare_Exp: Double?
    var Show_Start: String?
    var Show_End: String?
    var Show_Date: String?
    var Notes_Exp: String?
    var ContactID: String
    var Quote_Rejection: String?
    var Inv_ID: Int64?
    var Quote_Discount: Int?
    var Locked_Bool: String
}

首先,没有QuoteList采纳FetchableRecord。为什么?因为数据库行没有包含足够的信息来构建 QuoteListFetchableRecord 带有一个 init(row: Row) 初始值设定项,无法正确实现,正如致命错误告诉您的那样。让 QuoteList 采用 FetchableRecord 并向其提供不包含足够信息的数据库行是程序员的错误。

FetchableRecord是一个协议,自带方便的抓取方式,这就是你被它吸引的原因。但现在很明显 QuoteList 需要其他东西,是时候以不同的方式获取

一种可能的技术是获取原始数据库行:

// Instead of:
// for quotes in try QuoteList.fetchAll(db, sql: "SELECT * FROM My_Quotes ORDER BY QuoteID")
for rows in try Row.fetchAll(db, sql: "SELECT * FROM My_Quotes ORDER BY QuoteID") {
    let fullString = ModelData.get_ContactName_Org(contactID: quotes.ContactID).fullStaring
    let first = ModelData.get_ContactName_Org(contactID: quotes.ContactID).firstName
    let last = ModelData.get_ContactName_Org(contactID: quotes.ContactID).lastName
    let org = ModelData.get_ContactName_Org(contactID: quotes.ContactID).clientOrg
    
    let quoteList = QuoteList(
        QuoteID: row["QuoteID"],
        Quote_Number: row["Quote_Number"],
        Quote_Status: row["Quote_Status"],
        ContactID: row["ContactID"],
        ContactFullName: fullString,
        ContactFirst: first,
        ContactLast: last,
        ContactOrg: org,
        Inv_ID: row["Inv_ID"],
        Locked_Bool: row["Locked_Bool"])
    
    theArray.append(quoteList)
}

另一种技术是定义一个实际可获取的模型:

struct PartialQuoteList: Codable, FetchableRecord
{
    let QuoteID: Int64
    var Quote_Number: String
    var Quote_Status: String
    var ContactID: String
    var Inv_ID: Int64?
    var Locked_Bool: String
}
for partial in try PartialQuoteList.fetchAll(db, sql: "SELECT * FROM My_Quotes ORDER BY QuoteID") {
    let fullString = ModelData.get_ContactName_Org(contactID: quotes.ContactID).fullStaring
    let first = ModelData.get_ContactName_Org(contactID: quotes.ContactID).firstName
    let last = ModelData.get_ContactName_Org(contactID: quotes.ContactID).lastName
    let org = ModelData.get_ContactName_Org(contactID: quotes.ContactID).clientOrg
    
    let quoteList = QuoteList(
        QuoteID: partial.QuoteID,
        Quote_Number: partial.Quote_Number,
        Quote_Status: partial.Quote_Status,
        ContactID: partial.ContactID,
        ContactFullName: fullString,
        ContactFirst: first,
        ContactLast: last,
        ContactOrg: org,
        Inv_ID: partial.Inv_ID
        Locked_Bool: partial.Locked_Bool)
    
    theArray.append(quoteList)
}