Core data - Building a NSPredicate from a ManyToMany relationship

大憨熊 提交于 2020-01-03 04:50:14

问题


This is a follow up question to my previous question on:

Core data: Managing employee contracts in a many-to-many relationship?

There is a diagram on that question, and as a quick reminder there is the following:

company --< contracts >-- employees

I have been able to manually save 1 entity inside each of the entities, and verified them all in NSLog.

I've created a CompanyListPage which lists all companies. The idea is that when you click on a company you will be presented with a list of all employees who have a contract with said company.

As context see below:

Company: 
name: A

Contract:
length: 33 wks
salary: 20000

Employee:
name: Bob Jones

In my didSelectRowAtIndex page within my CompanyListPage I have the following.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {


    Company *c = [fetchedResultsController objectAtIndexPath:indexPath];    
    NSLog(@"You clicked %@", c.name);
    NSString *companyName = c.name;

    EmployeesListPage *employeesListPage = [[EmployeesListPage alloc] initWithNibName:@"EmployeesListPage" bundle:[NSBundle mainBundle]];

    [self.navigationController pushViewController:employeesListPage animated:YES];

    employeesListPage.title = companyName;  
    employeesListPage.managedObjectContext = self.context;
    employeesListPage.managedObject = c;

    [superstarsList release];

}

The problem however is, I am not sure what my NSPredicate should look like when I eventually go to the employeesListPage.

At the moment, its this:

- (NSFetchedResultsController *)fetchedResultsController 
{

    if (fetchedResultsController != nil) {
        return fetchedResultsController;
    }

    // Create and configure a fetch request
    NSFetchRequest *fetchRequest    = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity     = [NSEntityDescription entityForName:@"Employees" inManagedObjectContext:managedObjectContext];
    [fetchRequest setEntity:entity];

    // Create the sort descriptors array
    NSSortDescriptor *authorDescriptor  = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:authorDescriptor, nil];
    [fetchRequest setSortDescriptors:sortDescriptors];

    // Create and initialize the fetch results controller
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:@"Root"];
    self.fetchedResultsController = aFetchedResultsController;
    fetchedResultsController.delegate = self;

    // Memory management.
    [aFetchedResultsController release];
    [fetchRequest release];
    [authorDescriptor release];
    [sortDescriptors release];

    return fetchedResultsController;
}    

Obviously this is wrong, because its not:

a) Looking in the contracts entity b) Using the company entity in any way, shape or form

I know I need to use a NSPredicate, but I just know how to make it say "Find me all the employees with a contract length > 0 and working with company A" then order it by the name of the person descending, or even order it by the least contract length first.

Any pointers or help on this would be great. Thank you.


EDIT: First attempt (removed because I got it to work following an answer provided below)

EDIT: Unable to get contract information back?

I've been able to get all the employees that work for Company A back in my table view controller.

However I'm wanting to display in my cell information about the employee and their contract length/salary.

I've tried the following:

Employee *emp = [fetchedResultsController objectAtIndexPath:indexPath];


NSString *firstname = emp.firstname;
NSString *surname = emp.surname;

NSString *fullname = [firstname stringByAppendingString:@" "];
fullname = [fullname stringByAppendingString:surname];

// Logging tests
NSLog(@"Name: %@", fullname); // This is fine
NSLog(@"Contracts: %@", emp.empContracts);  // This tells me of a problem with "Relationship fault for <NSRelationshipDescription: 0x602fdd0>"

I believe I need to get the NSPredicate to grab all the contract data, and not just the contract data; however I may be mistaken.

Again, help on this would be greatly appreciated.


回答1:


I think if you use the ANY keyword, you'll restore the left side of the equation to a single quantity, which will agree with the right side of the equation:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@“ANY contracts.employer ==  %@“, employer];



回答2:


I think I have an answer now.

I did the following in my table view and got a valid relationship and all the output; however I am not sure if I am doing it right because it involves a for-loop.

I used the following StackOverflow related question/answer for my solution.

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath 
{
    Employee *emp = [fetchedResultsController objectAtIndexPath:indexPath];

    NSString *firstname = emp.firstname;
    NSString *surname   = emp.surname;

    NSString *fullname  = [firstname stringByAppendingString:@" "];
    fullname            = [fullname stringByAppendingString:surname];

    NSLog(@"Employee: %@", fullname);

    // Output the contract deals.
    for (Contracts *deals in emp.contracts) 
    {

        NSNumber *cLength   = deals.length;
        NSNumber *cSalary   = deals.salary;

        NSLog(@"Length: %@", cLength);
        NSLog(@"Salary: %@", cSalary);
    } // next
}

The next step for me is to include this in a custom view with custom labels to hold all this info.

If the for-loop is not the most efficient way to do this, I would welcome any suggestions/improvements, or even what is considered best practice.

Thank you.




回答3:


Looks like it’s coming along well. I think all you need from there is to set the cell’s text: cell.textLabel = fullname.

But I’m not clear why you’re delving back into the contracts relationship — until I look at your sample text at the top. There, it looks like what you want is a list of contracts for the employer, and for each contract you want to show its to-one employee relationship and other attrs. In which case you don’t need a new fetch at all; you already have the employer and, through its to-many contracts relationship, you also have the contracts entities and, through them, the term, salary, employee, whatever.

Upon selection change in the employers table, you would do something like this:

NSUInteger row = [indexPath row];
Employer *employer = [self.arrayOfEmployersUsedToPopulateTable objectAtIndex:row];
self.selectedEmployer = employer;
self.arrayOfSelectedEmployersContracts = [employer.contracts allObjects]; // which probably faults them, but I think that’s OK here
// Then make a call to reload the other table, the one presenting the contracts info.

In the contracts table, you’d refer back to the selected employer, and present info for each contract:

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
NSUInteger row = [indexPath row];
Contract *contract = [self.arrayOfSelectedEmployersContracts objectAtIndex:row];
NSUInteger column = [indexPath column];
switch (column) {
    (0) :
    cell.textLabel = contract.length; // maybe you have to use an NSNumber method to convert to string, but probably not
    break;
    (1):
    cell.textLabel = contract.employee.firstname;
    break;
    // and so forth
}}

P.S. For the UITableCell stuff, I consulted Beginning iPhone Development, by Mark and LaMarche, “Using the New Table View Cell.” You might like this book too.



来源:https://stackoverflow.com/questions/5410595/core-data-building-a-nspredicate-from-a-manytomany-relationship

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!