I'm trying to implement something similar to the iTunes browser, to browser a simple database of Books. I have the following entities - Author, Genre and Book. I would like to display an author list and a genre list, which act to filter the main Book list.
I have tried doing this in 2 different ways - modeled as:
Author ( has many ) Genres ( has many ) Books
...with multiple instances of the same Genre so each author has their own for a given genre name, I have a nice drill-down hierarchy to display in my table views (albeit a bit illogical to duplicate Genres). However, when I select multiple Authors, I end up displaying dupes of the same Genre, because they are, in fact, distinct objects.
So, I tried doing it, more sensibly, with these relationships:
Author ( has many ) Books
Book ( has one ) Genre
I can get the Genre array by taking the distinct union of Genre's in the current selected Author(s) book array, but now i'm left with the problem of filtering the book list displayed based on the selected Genre(s). Because the Genre's are shared, I can't just use CurrentGenre.books, or I lose the selected Author filtering. I have noticed the 'filter predicate' field in interface builder, available on the object controllers, but am stuck working out how to actually use it to apply the selected Genre as a filter to an episode list. The apple documentation says:
"You can type a predicate directly
into the predicate editor text field
in the inspector panel of Interface
Builder or you can set it
programmatically using
setFetchPredicate:.
which gives me the impression i'm on the right track, but that's about the end of it. I'm trying to lock down the model in a nice Cocoa-esque fashion now, so as to minimize 'glue code' bits and changes later down the track. It seems like a fairly simple problem I should be able to sort out graphically in IB, but so far it's eluded me!
Thanks in advance.
I am a bit confused by your introducing "episodes" in the middle of the discussion, but I will assume you just mean "books" still.
You are definitely on the right track. You want a data model like this:
Author <-->> Book
Genre <-->> Book
Or maybe even:
Author <<-->> Book (if you support reference books, etc)
Genre <<-->> Book (if you want multi-genre support)
Once a user has selected author(s) and genre(s) you will want a Book array controller to use a filter predicate that only shows books with those author(s) or genre(s).
UPDATE
This should work:
Bind the Book array controller's filter predicate to a new predicate property "bookFilterPredicate" in your app delegate.
Add outlets for the Author and Genre array controllers.
Observe changes to the selectedObjects properties of both array controllers.
When either changes, update the filter predicate property like this: self.bookFilterPredicate = [NSPredicate predicateWithFormat:#"author IN %# && genre IN %#",authorArrayController.selectedObjects,genreArrayController.selectedObjects];
Related
I am sure I could google this if I knew what to search for, if that makes sense.
I'm trying to get the hang of entity framwork, and considering how i would implement some real-world scenarios in it.
Imagine a simple data model with a Person entity, and a Color entity, and an association called ColorsLiked.
I want to use Color a bit like an enum; there will only be 3 defined (red, green, blue).
I want a drop-down to allow the used to add Colors to their list of colors they like - which is all easy enough. But, what query can I use to select only the colors the person doesn't already like? i.e so that as they pick a color, it is no longer available for selection in the list.
In SQL, it's a simple query with a left outer join. But I don't understand how to do something like this in EF.
any guidance would be appreciated
slip
Assuming that you want all the colors the person has not yet liked this is the LINQ expression to do that.
var unlikedColors = allColors.Except(person.ColorsLiked);
I have an example application I'm working on to help me learn about Core Data. In this application I created a model consisting of the entities "Friend" and "City". The application list my friends and which city they are from in an NSTableView. In this table view I would like to have the City column be an NSComboBoxCell with a list of the cities. I got this far... now for the problem:
When I select the city from the combo box, the application takes the value of the selected city name and applies it to the name of the city the friend is currently from. Instead, I would like the application to actually change the city the user is from and not the name of the city... That's a bit confusing of a question, so here's an example: starting with a list of friends like
Andy Asheville
Francois Montreal
Jeff Asheville
If I use the NSComboBoxCell to change the city for Andy from Asheville to Montreal, the application actually changes the name of the City Asheville to Montreal, so the result looks like:
Andy Montreal
Francois Montreal
Jeff Montreal
There are still two distinct cities in the application, but they now both have the name Montreal.
This all makes sense to me given the way I set up my bindings. I bound the value of the city table column by setting Model Key Path to "city.name" and the Controller Key to arrangedObjects, which contains the list of friends. So of course, when the value of a cell changes it modifies city.name. So then my question becomes what's the proper way to do this so that the city changes instead of the city name?
The purpose of NSComboBox is to "allow you to either enter text directly (as you would with an NSTextField) or (...) select from a displayed list of items". (taken from Apple Cocoa Ref).
So The combo box is a glorified NSTextField. The behavior you expect is more in line with the NSPopupButton.
As you noted, the NSComboBox bindings change the name value of the current City, but do not alter the object relationship, hence they change the name of the current city represented by the relationship (which then gets "propagated" to other Friends pointing to the same City entity).
If you look at the available bindings for NSPopupButton you'll see the difference. You probably want to use the NSPopupButton(Cell) to assign Cities to Friends, with some sort of "on the side", NSTableView-based editor to manage your city names — which are really two distinct tasks.
I am reading this asp.net article on building your first asp.net mvc 2 website and I came across a Linq query that uses the Include method in the query. I have used some linq, but I have never used the Include method and would like a better explanation. Does it translate to an join in linq? What is the benefit? Here is the query from the article:
var genreModel = storeDB.Genres
.Include("Albums")
.Single(g => g.Name == genre);
The article states that:
Entity Framework feature that allows us to specify other related entities we want loaded as well, called Query Result Shaping. We want to load the Albums for the matching Genre, so we'll query from Genres.Include("Albums") to indicate that we want related albums as well. This is more efficient, since it will retrieve both our Genre and Album data in a single database request.
I sort of understand what the author is saying above, but feel I would need a better example or explanation, especially since I have never used the Include method before.
If you inspect the generated sql, you'll notice that the Albums table is joined in. It should look something like:
SELECT [t0].*, [t1].*
FROM Genres [t0]
LEFT JOIN Albums [t1] ON [t0].GenreId = [t1].GenreId
WHERE [t0].Name == #p0
When the results get back to the client side, the ObjectContext will turn the row-column shape into instances of Genres and Albums. These instances will be related hierarchically - the single Genre with all of its Albums.
Suppose this genre has 5 albums. The query will return 5 rows. The object context will create one instance of Genre (each of the 5 rows has the same Genre primary key value).
I really hope someone can help on this because I'm learning cocoa and have hit a road block.
I am trying to model a simple poker tournament. For now, my entities are simply a Tournament (with a number) and a Player (with a Name). A Tournament has an array of Players.
I can bind two independent table views to display the tournaments and the players just fine. But I want the players table view to just show the players that belong to the selected tournament from the first table view.
Each has it's own array controller. I have tried a variety of different bindings for the second (players) table but to no avail. Has anyone accomplished this? If so maybe you could spell it out for me, as I there are few examples online.
Update
I can now ALMOST get where I need to, mostly through rial and error and hours of googling. I have bound the player AC's content to the tournament AC, with controller key 'selected objects' and Model Key Path 'players', which is the name of the array in my Tournament entity.
I have the bound the column in the players table view to this second Player AC, controller key arranged objects. But what to put in the Model Key Path? I know it is working because if I stick #count in there I get the correct number of players for the selected tournament. But 'name' and 'player.name' are no good. Is there any kind of 'item.name' or 'players.item.name' I can try?
Sooo close, thanks for the help so far:
I think this tutorial will help you. They also create a master/detail view.
In short: Bind the contentArray of your player's array controller to the tournament's array controller, set ControllerKey to selection and the remaining properties accordingly to your model.
I found the answer here:
Implementing parent->child drill down in Cocoa with Core Data bindings that span multiple entities.
The child controller needs to know about the managedObjectContext through its own binding.
The child controller must not be in Entity Mode, but rather operate as a NSMutableDictionary class.
And, finally, the child controller does not prepare its data. It retrieves it from the parent, through the Content Set binding. Use the controller key selection, and the model key path that connects to the children.
I'm surprised this is not a more commonly used practice, and hope the next person reading this doesn't spend so long finding the answer!
Let's take two Core Data entities, set up as follows:
Entity A: Car
Attributes:
year
model
Relationships:
manufacturer (<<-> Manufacturer)
Entity B: Manufacturer
Attributes:
name
country
Relationships:
cars (<->> Car)
Now, what I want to do is bind the display to an NSTableView where we have the model of the car in one column, followed by the manufacturer, followed by the year. Binding the model and year are no problem, but if I bind the relationship to a column in the table, I get the text of a relationship fault error in each cell in that column instead of anything I'm looking for. How can I play with the binding to allow me to display the proper manufacturer name associated with the car?
Extending the question a bit further, how could I set up another table view to display, say, other Car entries with the same manufacturer relationship?
A bit more information about how you have it set up currently would be helpful. You should be able to bind to your Array Controller in exactly the same fashion as your other attributes, with the same binding and controller key. Just use the full key path manufacturer.name for the model key path.
For a to-many relationship, you use two array controllers. Set up the 'master' array controller to prepare its own content from your Core Data Manufacturer class (in Entity mode). Then, you create a secondary, 'detail' array controller. Leave the detail array controller in Class mode (with the default NSMutableDictionary class), and bind its content set to your master array controller, with the controller key set to selection and the model key path to cars.
Many many tutorials exist out there that do exactly this. I highly recommend running through one or two; I found this MacResearch.org tutorial particularly helpful. The entire series is great.