NSPredicate avec NSArrray: effectuer une boucle

Bonjour,


 


Je vous expose rapidement mon problème, j'ai 3 entités (Muscle - Effectue (classe d'association) - Exercice). Je récupère dans une TableView tout mes muscles ensuite je souhaite récupérer uniquement les exercices correspondant au muscle sélectionné (dans une autre TableView). J'effectue donc une boucle sur mon NSArray dans lequel j'insère mon NSPredicate mais je n'arrive à  afficher qu'un seul résultat.



 NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@Exercice  inManagedObjectContext:managedObjectContext];
    [fetchRequest setEntity:entity];
    
   //Arrayeffectue (NSArray) contient les idExercices que je dois afficher
 
    for (NSManagedObject *info in arrayEffectue){
    
    NSLog(@Mon array Effectue contient %@", [info valueForKey:@idExercice]);
    predicate = [NSPredicate predicateWithFormat:@idExercice == %@", [info valueForKey:@idExercice]];
    
        // Ajout du predicate à  la requète
        [fetchRequest setPredicate:predicate];
       
    }
    
    // set sort properties
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@nomExercice ascending:YES];
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
   
    [fetchRequest setSortDescriptors:sortDescriptors];
    
    NSError *error = nil;
    idExerciceInExerciceDicts = [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error];
 
//traitement de l'erreur ci-dessous..... 

Pouvez-vous me dire svp pourquoi je n'obtiens qu'un seul résultat?


J'obtiens le dernier idExercice en l'occurence!


 


Merci


Mots clés:

Réponses

  • KubernanKubernan Membre
    juillet 2013 modifié #2

    Bonjour,


     


    Merci de bien vouloir te présenter dans la section adéquate.


    Ca fait un sacré moment que je n'ai pas modéliser une base de données mais je ne suis pas certain que Effectue soit vraiment approprié pour Core Data (qui n'est pas une base de données).


     


    Du coup s'il te reste deux entités "Muscle" et "Exercice" ta requête deviendra quelque chose comme :



    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@Exercice inManagedObjectContext:managedObjectContext];

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@muscle == %@", [muscleSelection.objectID]];

    // etc.

    muscleSelection correspondant à  l'objet qui a été sélectionné depuis la table view.


    (correction d'un erreur au lieu de muscleSelection, j'avais écrit muscle.selection)


  • Oui effectivement avec 2 entités ça ne pose aucun problème... c'est ma première version mais je suis obligé d'avoir l'entité "Effectue" sinon à  moyen/long terme ce n'est plus gérable et bonjour la redondance d'informations!!!!


    Ma question est donc la suivante: Comment faire pour passer mon NSArray à  mon predicate en ne connaissant biensûr pas les valeurs présentes dans mon NSArray?


     


    Merci de votre aide.




  • <...> sinon à  moyen/long terme ce n'est plus gérable et bonjour la redondance d'informations!!!!




     


    C'est à  dire ? (Il y a sûrement quelque chose d'évident qui m'échappe là ).

  • C'est à  dire que je dois pouvoir assigner un même exercice pour plusieurs muscles donc si je le fais avec seulement deux entités je vais devoir copier plusieurs fois les mêmes informations (description de l'exercice, exécution de l'exercice...) donc la redondance d'informations sera assez importante selon moi et de plus c'est fastidieux.


    Je ne vois donc qu'une entité intermédiaire (entité "Effectue") permettant de retenir l'idMuscle et l'idExercice de ces deux entités.


    C'est selon moi une bonne solution pour palier à  ce problème.


    Une solution?


     


    Merci

  • AliGatorAliGator Membre, Modérateur
    juillet 2013 modifié #6
    Tu confonds conception d'une BDD et Modèle de Données.

    CoreData n'est pas un système de BDD. C'est un framework de MDD.

    Avec CoreData tu peux indiquer des relations 1-* et *-* entre 2 entités, ce n'est pas a toi de créer la "table" intermédiaire qui fait la connections comme tu le ferais dans une BDD comme MySQL ou autre.

    Il suffit de créer une relation de type "To-Many Relationship" entre les entités Muscle et Exercice, et de même créer une relation inverse de type "To-Many Relationship" entre Exercice et Muscle, et tu auras tout ce qu'il te faut (voir captures)
  • Oui effectivement mais comment tu ferais alors dans ce cas pour palier à  mon problème?


    J'ai créé mes relations entre mes entités mais comment faire pour pouvoir attribuer un même exercice à  plusieurs muscles? Je ne vois pas comment malheureusement. Merci de m'éclairer.


     


    Thx


  • AliGatorAliGator Membre, Modérateur
    juillet 2013 modifié #8
    Tu as répondu trop tôt : j'ai édité mon message plus haut avec des captures et explications supplémentaires ;)

    Faut cocher "To-Many" pour que tes relations soient des 1-* et pas des 1-1 et donc puissent associer plusieurs muscles au même exercice.
  • Je viens de le voir effectivement et je l'avais déjà  fait avec mon propre modèle.  ::)


    J'ai bien effectué mes relations mais je ne vois pas comment faire pour assigner un même exercice à  différents muscles.


    Mon modèle effectue aussi une relation (one to many) vers les entités...  


    Thx 


  • AliGatorAliGator Membre, Modérateur
    juillet 2013 modifié #10
    Bah je ne vois pas trop le souci, qu'est ce qui te pose problème ? Qu'as-tu essayé au juste ?

    Une fois que tu as un "Exercice* ex1" et que tu as tes 3 "Muscle *m1, *m2, *m3" que tu veux associer à  ton exercice, si ta relationship 1-* de Exercice vers Muscle s'appelle "affectedMuscles" comme dans mon exemple, tu peux simplement appeler [ex1 addAffectedMusclesObject:m1] puis [ex1 addAffectedMusclesObject:m2] puis [ex1 addAffectedMusclesObject:m3] et tu auras créé ton lien entre ton exercice ex1 et tes 3 muscles m1, m2 et m3. Ou tu peux faire ça en un seul coup avec [ex1 addAffectedMuscles:[NSSet setWithObjects:m1,m2,m3,nil]] ce qui revient strictement au même. Tout est expliqué dans la doc CoreData, et tu as toutes les méthodes qu'il te faut dans les .h des classes générées par Xcode à  partir de tes entités CoreData...
  • Ok j'ai bien compris le rôle des accessors générés par CoreData mais comment puis-je récupérer le nom d'un exercice et l'attribuer à  un muscle (ou inversément)... Via des NSArray contenant mes entités?


    A chaque fois que je tente d'attribuer un exercice à  un muscle, je crée en fait un nouveau muscle et un nouvel exercice dans mon TableView. Comment procéder alors? 


     


    Merci

  • AliGatorAliGator Membre, Modérateur
    Si tu nous expliquais déjà  comment tu t'y prends ?


    http://whathaveyoutried.com
  • KubernanKubernan Membre
    juillet 2013 modifié #13


    Ok j'ai bien compris le rôle des accessors générés par CoreData mais comment puis-je récupérer le nom d'un exercice et l'attribuer à  un muscle (ou inversément)... Via des NSArray contenant mes entités?


    A chaque fois que je tente d'attribuer un exercice à  un muscle, je crée en fait un nouveau muscle et un nouvel exercice dans mon TableView. Comment procéder alors? 


     


    Merci




     


    Si tu parle d'un muscle qui a été préalablement sélectionné dans ta table view, il te suffit de récupérer cet objet muscle via l'index de selection de ta table view.


    Plutôt que d'utiliser un NSArray pour afficher les objets muscles dans ta table view, je te conseille vivement d'utiliser NSFetchedResultsController.


     


    Ainsi, si tu affiche la liste des muscles par ce biais, lorsque l'utilisateur sélectionnera un muscle tu peux récupérer cet objet en utilisant (par exemple) :



    Muscle *selectedMuscle = [self.fetchedResultsController objectAtIndexPath:selectedIndexPath];

    Et ensuite tu fais ce que tu veux avec selectedMuscle, par exemple, lui ajouter un (ou plusieurs) exercices, le supprimer etc...


  • IBimiIBimi Membre
    juillet 2013 modifié #14

    Effectivement je récupère l'objet muscle ensuite je passe cet objet à  ma classe ExerciceViewController. 


    Mais une fois dans ma classe ExerciceView... je ne sais pas comment attribuer un ou plusieurs exercices pour ce muscle.


    Auparavant, j'avais relié dans ma db un muscle à  un exercice et quand je cliquais sur un muscle, j'obtenais l'exercice(s) relié à  cet id.


    Simple quoi  ::) ...


     


    MuscleViewController.m



    - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
       
        Muscle *selectedMuscle = [_fetchedResultsController objectAtIndexPath:indexPath];
        cell.textLabel.text = selectedMuscle.nomMuscle;
        cell.detailTextLabel.text = selectedMuscle.nomLatin;
     
        NSString *imagePath = selectedMuscle.thumbMuscle && selectedMuscle.thumbMuscle.length ? selectedMuscle.thumbMuscle : @pectoraux.png;
        UIImage *musclesImage = [UIImage imageNamed:imagePath];
        musclesImage = musclesImage ? musclesImage : [UIImage imageNamed:@pectoraux.png];
        
        cell.imageView.image = musclesImage;
     
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
        
    }

    Thx


  • Si ta relation Muscle<<->>Exercice s'intitule exercices alors : 



    NSMutableSet *exercices = [selectedMuscle mutableSetValueForKey:@exercices];
    [exercices addObject:unExercice];
    // autant que tu en veux

    Voir également la réponse d'Ali sur ce sujet.


  • IBimiIBimi Membre
    juillet 2013 modifié #16

    Pardonnez moi d'être aussi lourd mais lorsque je fais ça je rajoute un muscle ou un exercice dans le tableView mais mes exercices sont déjà  créés dans la bdd et je veux juste en sélectionner un de la bdd et l'attribuer au muscle.


    Est-ce le bon procédé dans ce cas? 


    J'ai bien essayé via les accessors core data mais je n'arrive pas.   :(



          NSEntityDescription *entityMuscle = [NSEntityDescription entityForName:@Muscle inManagedObjectContext:self.managedObjectContext];
           Muscle *muscle1 = [NSEntityDescription insertNewObjectForEntityForName:[entityMuscle name]inManagedObjectContext:managedObjectContext];
           muscle1.nomMuscle= @Abdominaux;
            
            Exercice *exercice1 = [NSEntityDescription insertNewObjectForEntityForName:[entityExercice name] inManagedObjectContext:managedObjectContext];
            exercice1.nomExercice = @Dip;
           [muscle1 addR_idExerciceObject:exercice1];
     

  • AliGatorAliGator Membre, Modérateur
    Pourquoi "insertNewObject" alors que justement tu dis toi-même ne pas vouloir insérer un nouvel objet dans ta base mais en récupérer des existant ?!
  • Je ne comprends pas ce que tu veux faire. Tu dis que tes exercices existent déjà  mais tu montre un code où tu en créé un :



    Exercice *exercice1 = [NSEntityDescription insertNewObjectForEntityForName:[entityExercice name] inManagedObjectContext:managedObjectContext];

    Donc la... je vois pas.


  • Effectivement oui tu as raison mais je ne sais plus quelle méthode employer parce que je tourne en rond depuis ce matin!! 


    Donc il faut que je récupère mon objet sélectionné (ça c'est fait) et pour cet objet j'affiche une série d'exercices que j'affecte en utilisant l'accesseur core data : "- (void)addR_idExerciceObject:(Exercice *)value;".  J'ai bien essayé avec ValueKey mais ça ne fonctionne pas.


    Le problème c'est que je sais ce qu'il faut utiliser mais je ne sais pas comment!!!


    Une idée? 



  • Une idée? 




     


    (Re)lire la documentation Core Data ? à‰tudier les sample code ? Parce que, comme tu dis, on "tourne en rond" et je ne vois pas comment t'expliquer autrement qu'en répétant les précédents posts (Ali devrai faire mieux que moi j'en suis sûr  o:) )

  • AliGatorAliGator Membre, Modérateur
    Je ne comprends rien à  ce que tu veux faire (et je suis en train de me demander si toi-même tu comprends... ça vaut peut-être le coup que tu te poses 30mn pour réfléchir)

    D'un côté tu dis que tu veux ajouter des objets Exercice existants à  un objet Muscle existant, d'un autre côté tu montre un code qui fait clairement un insert pour créer de nouveaux objets Exercice dans ta base CoreData, et finalement quand tu expliques la chose tu sembles non pas créer des associations entre un Muscle et des Exercices, mais lister des relations déjà  existantes dans ta base, et donc lister tous les Exercices déjà  reliés dans ta base CoreData au Muscle sélectionné...

    Du coup j'ai l'impression que tout s'emmêle dans ta tête, entre créer de nouveaux objets en base, ou récupérer des objets existants mais créer des relations (qui n'existent pas encore en base), ou encore récupérer des Exercices qui sont déjà  dans ta base CoreData ET déjà  liés à  un Muscle...

    Il aurait peut-être mieux valu commencer à  apprendre CoreData avec un bouquin, pour te poser un peu et comprendre les actions de base, ou bien utiliser un framework comme MagicalRecord, qui simplifie grandement le code autour de CoreData et te permet d'y voir encore plus clair dans ce que tu fais ?
  • En vérité j'ai appris les bases de Core Data et j'ai également un bouquin mais je ne trouve pas les infos désirées.


    En fait je veux clairement "récupérer des objets existants mais créer des relations (qui n'existent pas encore en base)".


    Les seules explications que je voient concernant les relations permettent d'ajouter des objets... Voilà  pourquoi je me retourne encore une fois vers vous.


    Je vais tout de même regarder le framework MagicalRecord.


    Merci de votre aide tout de même.  ::) 


  • AliGatorAliGator Membre, Modérateur
    Ok donc pour moi ma réponse plus haut (message #10) répond à  ta question.

    Tu récupères le Muscle* m1 dans une variable (ça tu dois déjà  l'avoir c'est celui correspondant à  la cellule cliquée si j'ai à  peu près compris), tu récupères un Exercice* ex1 existant dans ta base CoreData (c'est cette étape qu'il te manque, pourtant il s'agit d'un simple fetch de l'entité Exercice que tu veux, une des opérations de base en CoreData), et une fois que tu as ces deux NSMO tu crées l'association entre m1 et ex1 avec les méthodes que j'ai mentionnées dans #10. Je ne vois pas trop ce qui te gêne dans cette démarche.
  • Ci joint un projet (Xcode 4.6.3, iOS 6.1) vite fait qui peut correspondre à  ce que tu veux faire :


     


    L'application Musclor ! affiche des muscles et des exercices (2 muscles, 4 exercices).


     


     


    Le controller CCFMasterViewController affiche la liste des muscles avec leur nom et le nombre d'exercices associés.


     


    Quand tu sélectionnes un muscle le controller CCFExerciceViewController est instancié.


    Il affiche tous les exercices disponibles et gère l'association d'un ou plusieurs exercices à  un muscle. Les exercices associés à  un muscle sont marqués d'un check mark.


     


    Le code n'a pas pour vocation a être utilisé tel que en production, c'est juste pour l'exemple.


     


  • Héhé ça tourne donc je remercie sincèrement "Kubernan" et "AliGator.


    Merci de votre patience envers mon ignorance, de votre jusqu'au-boutisme et de votre acharnement à  me faire comprendre.


    Enfin le sujet est résolu et c'est grâce à  vous donc MERCI.


    J'aimerais vous dire que je ne vous embêterais plus mais ce ne sera pas le cas parce que j'ai encore pas mal de boulot sur mon application mais je me renseignerai bien avant de poster. 


    Voilà  sujet clos et encore juste MERCI.  o:)


     


    PS: J'ai revu la doc Core Data, c'était nécessaire un petit refresh quand même....


Connectez-vous ou Inscrivez-vous pour répondre.