0

I am trying to understand the concept in saving and retrieving records in a one-to-many relationship entities in CoreData. i have two tables Products (Master) and Sales (Details) hence (one-to-many) relationship with the relationship key being (sales).

Issue 1: when registering a sale to a product. i create an instance of the Sales entity, populate the values, then add it to the product.sales (relationship) and attempt a managedObjectContext save.

First Question: Do i also have to enter the instance of the Sales entity into the Sales Entity? or that will be updated automatically since there is a relationship?

Second Question: If i want to query all the sales that happened to date, do i query the Sales Entity? or do i query the relationship in the Products Entity?

Thanks for your help!

tab images

Maz
  • 245
  • 3
  • 11

4 Answers4

1

First Question: Do i also have to enter the instance of the Sales entity into the Sales Entity? or that will be updated automatically since there is a relationship?

Something you might not realize here is that you are inherently saving the object by adding it to the managedObjectContext. As soon as you do something like

let sale = Sale(context: managedObjectContext)

followed by

managedObjectContext.save()

the context issues a save request to your persistent store (your actual SQL database).

Therefore your question whether you need to store the Sale as well is answered, it will always be stored upon saving the context.

Second Question: If i want to query all the sales that happened to date, do i query the Sales Entity? or do i query the relationship in the Products Entity?

That depends...

First let me give you a little tip/best practise:

Always make sure to set up an inverse relationship

In the Core Data Editor for your Product entity's relationships you can do something like this:

enter image description here

Your sales relationships look something like this:

enter image description here

A relationship is nothing more but a dependency between two entities, therefore there is always an inverse relationship between two entities, make sure you hook them up as shown above.

Why am I telling you this ? Remember I mentioned it depends what entity you do your query on ? This is where it matters.

For example, if you want the Sale for a given Product, you would query the product itself (by querying its relationship called sale):

let product = [A product instance from your Core Data store]
let sale = product.sale // returns the sale the product is associated to

If you want all the products from a given sale, you would query the Sale entity leveraging the products relationship:

let sale = [A sale from your Core Data store]
let products = sale.products // the products contained in the sale

You mentioned that you want all the sales to a given date:

It would not make any sense querying the Product entity for that because each product only has a relationship to the sale it is contained in.

So, to answer your question, you should query the Sale entity to retrieve all the sales to a given date.

I hope that helps, let me know if something is unclear.

the_critic
  • 12,720
  • 19
  • 67
  • 115
  • thanks again @the_critic, i wish you have some youtube tutorials cause you really explain things spot on. im creating a personal app to train on coredata. it has 3 tabs (1: Product list / 2: Cart / 3: Sale History) in tab1 you can select how many items of each product you want to buy, in tab2 shows you a cart of the selected items, and tab3 is you order history. so i noticed when i add items to the cart, they appear in the order history even before i commit changes to database, and i figured that i was using the same managedObjContext when i query the sales table like you said in ur explanton. – Maz Mar 08 '16 at 13:29
  • i added an image for you to see also, and if you notice just by selecting 4 products and adding few quantities (total of 8 quantities) i got 8 records in the order history before i even save. i only want to show the orders that were made before. maybe the ones that are in the Products.sales but how do i get them from there and populate the tableview and sort by date. – Maz Mar 08 '16 at 13:51
  • I think your problem is that you have the products from the last time you opened the app. Whenever you save the context, the data persists in your database. If you open the app back up, your saved products are still there. You'll need to delete them wherever appropriate... – the_critic Mar 08 '16 at 14:19
  • its not that, i think what you explained earlier creating an instance of that Sales entity and modifying it is updating the context but not the DB thats why when i restart the app i dont see those records. but i figured that out i added a predicate setting the relation key (products) in Sales != nil. now my issue is how to save into NSSet object. any tips for that? – Maz Mar 08 '16 at 20:17
  • i've tried this (product.sales = NSSet(object: order);) order is an instance of the Sales. when i try to save the context i get this error -> Error Domain=NSCocoaErrorDomain Code=1570 \"The operation couldn\U2019t be completed. (Cocoa error 1570.)\" UserInfo={NSValidationErrorKey=date, NSLocalizedDescription=The operation couldn\U2019t be completed. (Cocoa error 1570.), NSValidationErrorObject= – Maz Mar 08 '16 at 20:34
  • (entity: Sales; id: 0x7fdfe8d12ed0 ; data: {\n date = nil;\n price = 0;\n products = nil;\n quantity = 0;\n})} – Maz Mar 08 '16 at 20:34
  • Check out this answer: http://stackoverflow.com/questions/6667585/core-data-save-error-nsvalidationerrorkey-cocoa-error-1570-saving-nsdate It seems you have fields that require a value which are `nil` ? – the_critic Mar 08 '16 at 20:49
  • i ran this to check: print("quantity: \(order.quantity?.integerValue) price: \(order.price?.doubleValue) date: \(NSDateFormatter.defaultDateToString (order.date!))"); product.sales = NSSet(object: order); and i got: quantity: Optional(1) price: Optional(1699.0) date: March 8, 2016 at 9:22 PM really confused. am i storing data in NSSet correctly? i dont know objC would you mind emailing you the project? – Maz Mar 08 '16 at 21:48
  • you can upload it somewhere and send me the link (Google drive/ dropbox etc ) – the_critic Mar 08 '16 at 21:49
  • http://expirebox.com/download/7e7cc64baf4be13c9bbb3cd788faa903.html thats the link – Maz Mar 08 '16 at 22:13
  • I need directions on how to reproduce your issue – the_critic Mar 08 '16 at 22:17
  • alright you got it. well the issue is in CartViewController.swift last function (placeOrder). to get to that, this is the process: the user starts with ProductsTableViewController, adding items using the stepper and that populates a dictionary called sales which will be passed to Cart to update the tableview. when the user clicks on place order the (placeOrder) fires. for now this is the major issue im facing. – Maz Mar 08 '16 at 22:27
  • also please if you can take a look around and tell me what other stuff you advise on enhancing i would really appreciate. this is literally my first app and im using it to learn. thanks alot for all your efforts - god bless you! – Maz Mar 08 '16 at 22:28
  • I'm placing the order without issues ... what is supposed to be happening ? – the_critic Mar 08 '16 at 22:31
  • are you not getting any errors like the error message i posted earlier? – Maz Mar 08 '16 at 22:35
  • can you see the order you placed in the history tab? – Maz Mar 08 '16 at 22:36
  • i deleted the app from the virtual device and reinstalled it, its working now. but pls can you still look at that function in cart file to see if thats the right way to save data in NSSet? i just read it online somewhere few hours ago. and the reason why you are seeing different values in the history is because i didnt group them, i think i need to use an NSFetchedResultsController and group them – Maz Mar 08 '16 at 22:40
  • if you are referring to the order history details, its just a dummy to see how the table with display. just ignore that however if its something else please do point out. – Maz Mar 08 '16 at 22:42
  • Yes, that should all be working fine. If this is really your first app then I have to say that you are really doing great! I'm impressed! – the_critic Mar 08 '16 at 22:46
  • wow, thanks alot man! thats an encouragement! please can you retain the project file for sometime until im done incase something else comes up? and also is it possible to group fetchrequest without using FetchedResultsController? – Maz Mar 08 '16 at 22:50
  • sure, I can do that! – the_critic Mar 08 '16 at 22:50
  • hello @the_critic, hope you're good. i need your help if possible, im stuck at one point regarding selectors if you can take a look at this thread please: [link](http://stackoverflow.com/questions/36025243/including-protocol-function-in-tapgesture-action-selector-ios-swift?noredirect=1) – Maz Mar 16 '16 at 02:13
1
    let manageObjectContext = appDelegateObj.managedObjectContext
    let entity =  NSEntityDescription.entityForName("", inManagedObjectContext:manageObjectContext)
let manageObj =  NSManagedObject(entity: entity!,insertIntoManagedObjectContext: manageObjectContext)
manageObj.setValue("Mark Developer", forKey: "AttributeName")
        do {
            try manageObjectContext.save()
        } catch let error as NSError {
            print("Could not save \(error), \(error.userInfo)")
        }
0

I will need to look into this a bit further, as I too am still getting my head around Core Data, but until I can find the time to do that I think what might work for you is this:

issue 1: to also enter the sale into the sales entity. I do not believe this will be done for you. Where relationships really become important is in deleting. You can specify what you want he delete rules to be, whether nullify, deny, cascade, or no action. Raywenderlich.com has an excellent tutorial series on Core Data that may help you.

issue 2: I think it depends on in which table (although in CoreData the term is ManagedObject) the data you are looking for is stored. It would make most sense to me to query the sales entity. You don't have to query the whole sales entity if you do not want to. You can specify search parameters.

user2807654
  • 122
  • 1
  • 1
  • 11
0

As of swift 3 you don't have to create the properties of a class of an entity that you created in Core Data because Core Data does that for you. You do have to initialize those properties though. Same thing is true for relationships. For example in the case of the product Class (the parent) the relationship to sales(the child) is already instantiated for you. So you can access and set the Entity Sale by getting the instance of the relationship in the Product Class. There might be other ways of doing that but I think using extensions is pretty cool.

extension Product {

//You would call this constructor like that: Product(sale) from any //part of your program as long as you "import CoreData" //If its 1 Sale then s 1-1 if it's more than 1 it's 1-many convenience init?(Sale or Sales) {

    let MOC = (UIApplication.shared.delegate as! AppDelegate).managedObjectContext

    self.init(context: MOC)

    //case 1 when sale is 1-many
    let sale = Sale(context: MOC)

    //case 2 when sale is many-many(this can be an array of objects that you sent)

    let sale1 = Sale(context: MOC)
    let sale2 = Sale(context: MOC)
    let sale3 = Sale(context: MOC)

    //setting sale  object
     sale.property = ""

//Now is time to set the relationship with the object that you just set

    //case when is 1 - many
    self.sale = sale

    //case when is many - many
    self.sale?.adding(sale1)
    self.sale?.adding(sale2)
    self.sale?.adding(sale3)

}

Now your Product will be related to your sales....

Fidel
  • 1,173
  • 11
  • 21