Core Data NSPredicate IN entre NSSet et NSArray

2»

Réponses



  • mon problème vient plus de mon manque de connaissance en Core Data je epnse car je n'arrive même pas à  récupérer les allotements_projet_ticket ...



    [localisationPredicate addObject:[NSPredicate predicateWithFormat:
    @ANY allotement_projet_ticket_list_rel._uuid == %@", @b8db5014-35cd-49ee-85fb-e69613917aa0]];


    Cette contrainte ne me retourne aucun projet alors que ces données sont bien en base ...




     


    Fais voir un peu l'ensemble de ton code pour ce que tu viens de faire.

  • Et si tu fais un fetch sur chaque type d'entité sans mettre de predicate, est-ce que tu récupères bien le nombre d'items attendus ?




  • Et si tu fais un fetch sur chaque type d'entité sans mettre de predicate, est-ce que tu récupères bien le nombre d'items attendus ?




     


    Avec ce code : 



    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    [fetchRequest setReturnsObjectsAsFaults:NO]; // -> sans effet
    // Edit the entity name as appropriate.
    NSEntityDescription *entity = [NSEntityDescription entityForName:@ETICKET_ALLOTEMENT_PROJET_TICKET inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];

    // Set the batch size to a suitable number.
    [fetchRequest setFetchBatchSize:20];

    // Set predicate
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@SELF._uuid = 'b8db5014-35cd-49ee-85fb-e69613917aa0'];
    [fetchRequest setPredicate:predicate];
    NSError *er = nil;
    NSArray *allot = [self.managedObjectContext executeFetchRequest:fetchRequest error:&er];
    [allot enumerateObjectsUsingBlock:^(ETICKET_ALLOTEMENT_PROJET_TICKET *obj, NSUInteger idx, BOOL *stop){
    obj._uuid;
    }];
    NSLog(@allot: %@", allot);

    je récupère bien les bonnes valueurs

  • En admettant que ton code soit bon, la seule explication c'est que tes relations n'ont pas été établies comme tu l'imagines lors de l'import de ta base existante.


     


    Pour le savoir tu fais un fetch tout simple sans predicate sur ton entité PROJECT et pour chaque PROJECT, tu parcours la relation allotement_bidule à  l'aide d'un "for". Là  tu vas bien voir si tes objets sont liés. 


    (Tu sais que si tu fais un valueForKey:@allotement_bidule sur tes objets de type PROJECT  tu vas récupérer un NSSet.)




  • En admettant que ton code soit bon, la seule explication c'est que tes relations n'ont pas été établies comme tu l'imagines lors de l'import de ta base existante.


     


    Pour le savoir tu fais un fetch tout simple sans predicate sur ton entité PROJECT et pour chaque PROJECT, tu parcours la relation allotement_bidule à  l'aide d'un "for". Là  tu vas bien voir si tes objets sont liés. 


    (Tu sais que si tu fais un valueForKey:@allotement_bidule sur tes objets de type PROJECT  tu vas récupérer un NSSet.)




     


    Lorsque je créé une requête pour récupérer pour afficher les ALLOTEMENT_PROJET_TICKET, les données sont bien récupérées.


     


    Par contre lorsque je récupère des projets et que j'affiche leur relation a_projetc.allotement_projet_ticket_list_rel,


    le NSSet est vide. La relation ne se fait pas entre le PROJET et le ALLOTEMENT_PROJET_TICKET.


     


    En cherchant un peu, j'ai découvert que ces sous-classes de NSManagedObject ont été générées par Xcode alors que j'ai ajouté la relation ultérieurement dans le modèle Core Data et sa déclaration dans le code à  la main.


     


    Cela peut-il poser un problème ?



  •  


    Cela peut-il poser un problème ?




     


    Si l'ajout est correct, alors non, c'est pas magique.


    Apres, si tu t'es trompé de type (NSSet ou NSOrderedSet) ou d'orthographe dans le nom de la relation, alors peut-être.


     


    Si tu n'as pas ajouté de code, tu peux re-générer les classes pour voir (fais une copie ou un commit de ton projet si tu n'es pas sur de toi avant).


     


    Bon c'est pas magique mais c'est quand même un peu mystérieux et parfois buggé (comme dans le cas des NSOrderedSet), donc ce que je fais maintenant par rapport à  ces problèmes de classes générées, c'est que je ne modifie jamais le code généré pour les dérivés de NSManagedObject. Si j'ai besoin de rajouter du code sur ces objets, j'utilise des categories.

  • AliGatorAliGator Membre, Modérateur


    Bon c'est pas magique mais c'est quand même un peu mystérieux et parfois buggé (comme dans le cas des NSOrderedSet), donc ce que je fais maintenant par rapport à  ces problèmes de classes générées, c'est que je ne modifie jamais le code généré pour les dérivés de NSManagedObject. Si j'ai besoin de rajouter du code sur ces objets, j'utilise des categories.




     


     


    Oui c'est une bonne méthode.


     


    Une autre alternative existe, c'est d'utiliser l'outil tierce mogenerator, un outil qui permet de générer les fichiers de classe des entitées de ton modèle CoreData, comme le fait Xcode quand tu sélectionnes tes entitées et lui demande via le menu adéquat, sauf qu'au lieu de générer un fichier "Toto.h" et "Toto.m" pour l'entité Toto, il génère/écrase "_Toto.h" et "_Toto.m" qui déclarent une classe "_Toto", et si "Toto.h" n'existe pas, il le crée avec "@interface Toto : _Toto".


    L'objectif est que tu ne modifies que Toto.h / Toto.m et ajoute tes trucs à  toi dans ces fichiers, et ne touche pas à  "_Toto.h/m" qui contient le code généré. Si tu modifies ton modèle CoreData et réexécute mogenerator, ça va regénérer "_Toto.h/m" mais ne va pas toucher à  "Toto.h/m" qui contient ton code, donc ne va rien écraser de ce que tu auras ajouté.

  • Je confirme que mogenerator est super.


    Un peu compliqué à  mettre en place, mais après c'est cool et facile.


  • mushumushu Membre
    septembre 2013 modifié #40

    Bon,


    j'ai re-généré les sous-classes NSManagedObject mais sans succès.


    Je dois faire un truc mal mais je ne sais pas quoi.


     


     


    Quelqu'un peut m'expliquer d'ailleurs ce qu'est une relation Core Data, son utilité, ... ?


     


    J'ai cherché sur internet et la doc Apple mais sans succès. Le concept reste flou pour moi


  • AliGatorAliGator Membre, Modérateur
    Une relation entre des entités CoreData est exactement le même concept d'une relation entre 2 tables dans une BDD relationnelle, ou au pattern de composition ou d'aggrégation classique en POO.

    Je présenterai rapidement cela lors de ma prez aux CocoaHeads Rennes demain si ça te tente...


  • Une relation entre des entités CoreData est exactement le même concept d'une relation entre 2 tables dans une BDD relationnelle, ou au pattern de composition ou d'aggrégation classique en POO.


    Je présenterai rapidement cela lors de ma prez aux CocoaHeads Rennes demain si ça te tente...




     


    ça aurait été avec plaisir mais demain je bosses sur Paris.


    Existe-t-il un podcast, playlist Youtube, ... des sessions ? je n'ai vu que les slides


     


     


    Les relations Core Data gérées par des NSSet sont censées se remplir automatiquement ou il faut ajouter les éléments à  la main ?

  • AliGatorAliGator Membre, Modérateur
    Il y aura un screencast video après coup, on le fait à  chaque fois (tu peux regarder les vidéos des sessions précédentes, on les poste sur cocoaheads.fr et vimeo)

    Pour ta question, les relations CoreData se remplissent automatiquement... après bien sûr faut affecter un bout de la relation, mais si tu le fais la relation inverse est automatiquement remplie.

    Par exemple imaginons que tu as une entité Company et une entité Person, avec une relation Person->Company nommée "employer" et la relation to-many inverse correspondante Company->Person nommée "employees".
    Si tu crées 2 Company, puis 5 objets Person, il ne va pas tout seul savoir à  quelle Company ils appartiennent tant que tu n'as aucunement relié les 2. Pour relier chaque Person avec la Company qui l'emploie, tu as le choix :
      • soit tu affectes la propriété "employer" de chaque objet Person à  l'objet Company correspondant à  son employeur
      • soit tu ajoutes l'objet Person a la Company, en utilisant les méthodes de la classe Company que t'aura généré CoreData, comme [company addEmployees:[NSSet setWithArray:@[person1, person2, person4]]];
      • Dans tous les cas si tu fais une de ces 2 actions (lier la Company à  la Person ou ajouter la Person à  la company, au choix) pas besoin de faire l'autre. Dès que tu as affecté une Company à  la propriété "employer" d'un objet Person, cet objet Person est ajouté au NSSet "employees" de cette Company.
        Donc dans ce sens oui les NSSet se remplissent automatiquement (du moins tout ça c'est à  condition que tu aies correctement indiqué dans ton modèle que "Company.employees" était la relation inverse de "Person.employer" bien sûr).
  • KubernanKubernan Membre
    septembre 2013 modifié #44

    Salut,


     


    Les relations sont ce qui constituent ton réseau d'objets dans Core Data.


     


    Les relations peuvent exprimer la partie métier de ton application : Par exemple, dans une application type de gestion de comptes bancaires, tu vas avoir plusieurs relations qui vont te permettre de lier un compte bancaire avec ses opérations bancaires.


     


    Du style :


  • Ce qu'il manquait effectivement à  mon modèle de donnée, c'était d'alimenter le NSSet "à  la main" lors du paramétrage de mes objets de données.


     


    Je donne un peu de code pour éviter les maux de tête aux débutant Core Data comme moi ;-)


     


    Petite explication :


    J'ai une relation entre un projet et des tickets : 3 sous-classes de NSManagedObject (PROJET, TICKET, ALLOTEMENT_PROJET_TICKET)


    Lorsque les propriétés projet_uuid et ticket_uuid sont paramétrées, j'ajoute le ticket dans le NSSet (cf. - (void)addTicketToProject; )



    @implementation ALLOTEMENT_PROJET_TICKET
    [...]
    - (void)setProjet_uuid:(NSString *)projet_uuid {

    if (![projet_uuid isEqualToString:self.projet_uuid]) {
    [self willChangeValueForKey:@projet_uuid];
    [self setPrimitiveValue:projet_uuid forKey:@projet_uuid];
    [self didChangeValueForKey:@projet_uuid];
    }
    [self addTicketToProject];
    }
    - (void)setTicket_uuid:(NSString *)newTicket_uuid
    {
    if (![newTicket_uuid isEqualToString:self.ticket_uuid]) {
    [self willChangeValueForKey:@ticket_uuid];
    [self setPrimitiveValue:newTicket_uuid forKey:@ticket_uuid];
    [self didChangeValueForKey:@ticket_uuid];

    }
    [self addTicketToProject];
    }
    - (void)addTicketToProject {
    if (self.projet_uuid) {
    PROJET *projet = [self.managedObjectContext getObjectWithStringValue:self.projet_uuid forKey:@_uuid inEntityName:@PROJET];

    if(projet)
    [self addProjet_relObject:projet];
    }
    }
    [...]
    @end

    Merci beaucoup à  tous de m'avoir aidé à  résoudre ce problème,


    et à  bientôt  :p   :p


  • AliGatorAliGator Membre, Modérateur
    Pourquoi partir dans ce sens et pas plutôt fonctionner dans le sens naturel des choses, dans l'ordre inverse ?

    En particulier, quel est l'intérêt d'avoir une propriété "project_uuid" dans ton entité Ticket, alors que tu as justement une relation entre Ticket et Project et que Project a déjà  un attribut "_uuid" ?
    Du coup à  partir d'un ticket si tu veux récupérer le project_uuid tu peux déjà  récupérer le projet puis sont UUID, donc faire "self.project._uuid" !

    Donc pour moi l'idée n'est pas d'affecter la propriété "project_uuid" à  ton ticket et que ça t'ajoute le ticket au projet (avec le code additionnel que tu as eu à  écrire), mais plutôt l'inverse, récupérer le projet avec cet UUID et l'affecter à  la relationship "project" de ton entité "ticket".

    Donc en gros au lieu d'avoir à  écrire tout ton code que tu as mis au dessus, et de faire
    ticket.project_uuid = @12345;
    Pour moi tu peux enlever tout ton code additionnel de ton post au dessus et à  la place écrire :
    ticket.project = [Project findFirstByAttribute:@_uuid withValue:@12345];
    (NB: Ce code suppose que tu utilises MagicalRecord qui te permet d'utiliser des méthodes "findFirstByAttribute:withValue:" pour gagner en simplicité, perso je peux plus m'en passer)

    Voir ma session des CocoaHeads Rennes #13 sur le sujet où justement j'ai dans ma présentation un exemple illustrant cela (pour associer une personne à  une session à  partir de son nom et prénom, la créer en base si elle n'existe pas, etc, le tout en 3 lignes grace à  MagicalRecord)
  • mushumushu Membre
    septembre 2013 modifié #47

     


     


    Pourquoi partir dans ce sens et pas plutôt fonctionner dans le sens naturel des choses, dans l'ordre inverse ?

     


    c'est l'entité ALLOTEMENT_PROJET_TICKET qui a une propriété project_uuid et non l'entité TICKET.


     



     


     


    Voir ma session des CocoaHeads Rennes #13

     


    Je ne trouve pas le lien vers le screencast. Tu pourrais me le partager stp ?


  • AliGatorAliGator Membre, Modérateur

    c'est l'entité ALLOTEMENT_PROJET_TICKET qui a une propriété project_uuid et non l'entité TICKET.

    J'ai l'impression en voyant le nom de ta table que tu as trop raisonné en mode "Base de Données" avec une "Table" que tu as dû créer juste pour faire ta relation Many-To-Many.
    Alors qu'en CoreData on peut directement faire des relations *-* entre les Entities y'a pas besoin d'une "Table" intermédiaire. C'est un Modèle de Données Objets, pas un Modèle de Base de Données.

    Je ne trouve pas le lien vers le screencast. Tu pourrais me le partager stp ?

    En fait il n'est pas encore dispo en ligne, il faut attendre (ou alors fallait venir à  la session ^^)
  • Le modèle est en effet fortement inspiré d'une base de donnée distante.


    J'ai repris le projet en l'état et et je ne me sens pas d'opérer un refactoring aussi sérieux ...


     


    Néanmoins lorsque j'aurai du temps devant moi ( :lol:) ou au prochain projet, je modéliserai la couche modèle de manière plus adaptée à  Core Data  ^_^


  • AliGatorAliGator Membre, Modérateur
    Ok. Mais en tout cas ça explique pourquoi tu doit te compliquer la vie à  dupliquer le travail que normalement CoreData fait pour toi, puisque normalement les "RelationShips" sont justement faites pour créer des relations (One-To-One ou One-To-Many ou Many-To-Many) gérées automatiquement, contrairement aux BDD où il te faut une table intermédiaire pour gérer les relations Many-To-Many.

    Là  avec la "Table" (Entity) intermédiaire et inutile que tu as dans ton CoreData, bah du coup tu es obligé d'ajouter du glue code comme tu as fait pour coller avec la redondance de données que tu as dans ta base (relationship ET table intermédiaire avec champs comme project_uuid)... donc des entités en plus dans ton CoreData et du code en plus à  écrire et de la complexification... tout ça pour rien ^^
Connectez-vous ou Inscrivez-vous pour répondre.