Пересечь значения отношений «многие ко многим»? - PullRequest
1 голос
/ 28 ноября 2011

Я нуб, когда дело доходит до обхода Core Data many-to-many relationships (я читал многочисленные темы и документацию по этому вопросу, поэтому, если это не конец и все, пожалуйста, никаких ссылок на документацию или темы).

Я делаю приложение для инвентаризации, и в настоящее время у меня есть модель Core Data, которая включает в себя "Thing", "Category", "Location" и "ThingLocation" (ThingLocation - это объект, который содержит ссылки Thing и Location, но включает количество вещей в этом конкретном местоположении. Также many-to-many relationship) объекты, которыми я хотел бы заполнить свой пользовательский интерфейс. Я хорошо разбираюсь в GUI, так что это не вопрос пользовательского интерфейса, а то, как я собираю информацию, используя (возможно) NSPredicates.

Пример: Если я покажу TableView, состоящий из Category подробностей сущности, то как бы я заполнил его Things в этой Category сущности.

Пример: Если бы я хотел отобразить UILabel, показывающий общее количество Thing в нем. (т.е. сложите все суммы по каждому Location).

РЕДАКТИРОВАТЬ: Я хочу иметь возможность использовать NSFetchedResultsController!

1 Ответ

2 голосов
/ 28 ноября 2011

Я не совсем уверен, что ваш вопрос задает.Так, например, вы хотите перебрать все категории и все вещи в этой категории, вы сначала сделаете запрос для сущностей категории без предиката (это вернет все объекты категории) и перебираете все те с быстрым перечислением:

//iOS 5 way of doing it
NSFetchRequest* request = [NSFetchRequest fetchRequestWithEntity:@"Category"];
NSArray* arrayOfObjects = [context executeFetchRequest: request withError: nil];
for (Category* cat in arrayOfObjects)
{
    //iterate over all the things in that category
    for (Thing* thing in cat.things){
    {
        //do something?
    }
}

Для вашего первого примера заполнения табличного представления вещами в категории:

Если у вас есть категория, вы получите вещи очень легко, как это:

NSSet* things= category.things;
//you can get it into an array by sorting it somehow or just get it like that.
NSMutableArray* things = [[category.things allObjects] mutableCopy];

Вы можете повторить это обычным способом или использовать их в качестве источника данных для просмотра таблицы.Если у вас нет категории, вам нужно что-то отличить, и в этом случае вы бы настроили предикат следующим образом:

NSFetchRequest* request = [NSFetchRequest fetchRequestWithEntity:@"Thing"];
NSPredicate* pred = [NSPredicate predicateWithFormat:@"(relationToCat.DestAtt = %@)",howToDestinguish];

Это вернет все Вещи, которые связаны с категорией, которая имеетатрибут.

Для вашего второго примера, вы должны установить NSPredicate, чтобы получить все ThingLocations для этого конкретного Thing.Затем переберите их и сложите значения в местах.Если вы хотите сделать это для каждой категории во всем, то вам просто потребуется вложить цикл for, начинающийся с категорий.затем для каждой вещи получите все ThingLocations и для каждого из них сложите значения.

Я надеюсь, что это отвечает на ваши вопросы.Отношения ко-многим являются просто множествами и могут рассматриваться как таковые.Я считаю, что мышление снизу вверх помогает мне сформировать предикаты.думая, что мне нужны все вещи в этой категории, поэтому я бы настроил сущность вещей и соединил ее с категорией в предикате.

Редактировать: пример NSFetchedResultsController В вашем .hПосле объявления вашего суперкласса добавьте NSFetchedResultsControllerDelegate к реализованным делегатам.

Создайте ivar: @property (nonatomic, retain) NSFetchedResultsController * fetchedResultsController;

На стороне реализации, которую я виделдва разных подхода, первый - написание пользовательского метода доступа для свойства, которое его там инициализирует, другой - просто сделать это в viewDidLoad, в любом случае настройка выглядит следующим образом:

NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Thing" inManagedObjectContext:context];
NSPredicate* pred=[NSPredicate predicateWithFormat:@"(relToCat.something=%@)",something];
[fetchRequest setPredicate:pred];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20]; //tells it how many objects you want returned at a time.

//this is for displaying the things in some sort of order. If you have a name attribute you'd do something like this
NSSortDescriptor *descriptor = [[[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES] autorelease];
NSArray *sortDescriptors = [[[NSArray alloc] initWithObjects: descriptor, nil] autorelease];
[fetchRequest setSortDescriptors:sortDescriptors];

//this is the set up. If you put a sectionNameKeyPath it will split the results into sections with distinct values for that attribute
NSFetchedResultsController *frc = nil;
frc = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context] sectionNameKeyPath:@"attribute that splits it into sections" cacheName:nil];
[frc setDelegate:self];
[self setFetchedResultsController:frc];
[frc release];
frc = nil;

//Tells it to start.
[fetchedResultsController performFetch:nil];

Затемдля методов делегата табличного представления это очень просто:

-(NSString*)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{

    return [[[fetchedResultsController sections] objectAtIndex:section] name];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView*)tableView {
        return [[fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
        return [[[fetchedResultsController sections] objectAtIndex: section] numberOfObjects];
}
/* If you want the bar on the right with the names of the sections...
-(NSArray*) sectionIndexTitlesForTableView:(UITableView *)tableView{
    return [fetchedResultsController sectionIndexTitles];
}*/
-(UITableViewCell* )tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
        static NSString* ident=@"cellident";
    UITableViewCell *cell = [self.itemTable dequeueReusableCellWithIdentifier:ident]; 
    if (!cell) {
        cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier:ident] autorelease];
    }
    [self configureCell:cell atIndexPath:indexPath];
    return cell;
}
- (void) configureCell:(UITableViewCell*)cell atIndexPath:(NSIndexPath*)indexPath {
    NSManagedObject* item=[fetchedResultsController objectAtIndexPath:indexPath];
    //set up your cell somehow
    return cell;
}

Вам также необходимо добавить методы делегата для контроллера полученных результатов.Все они очень просты и выглядят примерно так:

- (void)controllerWillChangeContent:(NSFetchedResultsController*)controller {
    [tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController*) controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
    NSIndexSet *set = [NSIndexSet indexSetWithIndex:sectionIndex];
    switch(type) {
        case NSFetchedResultsChangeInsert: [tableView insertSections:set  withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeDelete:
            [tableView deleteSections:set withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}
- (void)controller:(NSFetchedResultsController*)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath*)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath*)newIndexPath
{
    UITableView *tv = tableView;
    switch(type) {
        case NSFetchedResultsChangeInsert:
            [tv insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];

            break;
        case NSFetchedResultsChangeDelete:
            [tv deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];

            break;
        case NSFetchedResultsChangeUpdate:
            [self configureCell:[tv cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;
        case NSFetchedResultsChangeMove:
            [tv deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [tv insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }

}
- (void)controllerDidChangeContent:(NSFetchedResultsController*)controller {
    [tableView endUpdates];
}

Это обновит таблицу, если в какой-то другой части программы будет добавлена ​​вещь, она автоматически появится в таблице, если предикат совпадет.

...