Core Data: creation d'une nouvelle instance dans une table avec une relationship to-Many

WincentyWincenty Membre
septembre 2013 modifié dans API UIKit #1

Bonjour à  Tous,

 

Voilà  je débute en développement iOS, et depuis quelques semaines je développe une application iphone pour une boite espagnol. Dans ma base de donnée local de l'appli, j'ai plusieurs tables ayant des relation to_Many. Et c'est là  où j'ai du mal à  comprendre le fonctionnement de Core Data...

 

J'ai enregistré dans la table “TPAC_TipoPaciente” mes types de patients, et je veux enregistrer des patients dans "PAC_Pacientes" et c'est là  que ça bloque car je ne comprends pas comment relier un patient à  un type de patients spécifique.

Pour l'instant mon code pour insérer une nouvelle instance patient et celui-ci :

 



NSManagedObjectContext * context = [self managedObjectContext];
PAC_Pacientes *newPaciente= [NSEntityDescription insertNewObjectForEntityForName:@PAC_Pacientes inManagedObjectContext:context];

[newPaciente setValue:@Pedro forKey:@nombre];
[newPaciente setValue:@Muà±oz forKey:@apellido1];
[newPaciente setValue:@Carlos forKey:@apellido2];

TPAC_TipoPaciente *tipo= [NSEntityDescription insertNewObjectForEntityForName:@TPAC_TipoPaciente inManagedObjectContext:context];

[tipo addPacienteObject:newPaciente];

NSError *error;
if (![context save:&error])
{
NSLog(@Erreur lors de l'enregistrement des données: %@ - %@", error, [error userInfo]);
}

Mais ce code ne me relie pas mon patient à  mon type "pacientAvecUneMutuel" (par ex.)...

Ce que j'essai de faire, c'est que "tipo_de_pacient" de “PAC_Pacientes” pointe vers l'instance "pacientAvecUneMutuel" de la table "TPAC_TipoPaciente".

 

Est ce que qu'un peut m'éclairer? :)


Réponses

  • xylowebxyloweb Membre
    septembre 2013 modifié #2

    il faudrait que l'on puisse voir la déclaration de ton model CoreData


     


    perso, pour signifier que je suis dans une relation 1->n je mettrais un 's' à  paciente


  • KubernanKubernan Membre
    septembre 2013 modifié #3

    Ce que tu fais dans ton code, c'est la création d'un patient ET la création d'un type de patient (tu fais deux insertNewObjectForEntityForName :).


    Or j'imagine que les types de patient sont déjà  renseignés et que tu veux simplement lié un nouveau patient à  un type déjà  existant. Non ?


     


    Il faut donc que tu créé ton patient et que tu fetch le type de patient adéquat. Ensuite tu peux alors lier les deux.


     


    Par exemple :



    // Creation d'un nouveau patient

    NSManagedObjectContext * context = [self managedObjectContext];
    PAC_Pacientes *newPaciente= [NSEntityDescription insertNewObjectForEntityForName:@PAC_Pacientes inManagedObjectContext:context];

    [newPaciente setValue:@Pedro forKey:@nombre];
    [newPaciente setValue:@Muà±oz forKey:@apellido1];
    [newPaciente setValue:@Carlos forKey:@apellido2];

    // Recherche du type de patient adéquat
    NSFetchRequest *reqTypePatient = [NSFetchRequest fetchRequestWithEntityName:@TPAC_TipoPaciente];
    NSPredicate *predTypePatient = [NSPredicate predicateWithFormat:@codigo == %@", codigoPourMonTypeDePatient]; // Je cherche un type de patient particulier
    [reqTypePatient setPredicate:predTypePatient];
    NSArray *typesDePatients = [context executeFetchRequest:reqTypePatient error:&error];
    if (!typesDePatients) {
    // Erreur
    }

    TPAC_TipoPaciente *typePatient = typesDePatients[0];
    newPatiente.tipo_de_paciente = typePatient; // Association de mon nouveau patient au type de patient

    NSError *error;
    if (![context save:&error])
    {
    NSLog(@Erreur lors de l'enregistrement des données: %@ - %@", error, [error userInfo]);
    }

    Même remarque que xyloweb pour le pluriel des relations to-many (sauf que j'en trave que dalle en espagnol et que je ne sais pas comme on y exprime le pluriel...).


  • AliGatorAliGator Membre, Modérateur
    septembre 2013 modifié #4
    Merci d'aller te présenter dans le salon adéquat "Présentation des Membres" que l'on en sache un peu plus sur toi, en particulier ton expérience et ton niveau, pour te répondre de manière plus adéquate par la suite.

    Sinon, perso comme j'utilise l'excellent framework MagicalRecord pour me simplifier CoreData (voir ma présentation CocoaHeads #13 Rennes, qui sort en vidéo demain), j'aurais écris le code de Kub comme ceci :
    PAC_Pacientes *newPaciente = [PAC_Pacientes createEntity];
    newPaciente.nombre = @Pedro;
    newPaciente.apellido1 = @Muà±oz;
    newPaciente.apellido2 = @Carlos;

    newPatiente.tipo_de_paciente = [TPAC_TipoPaciente findFirstByAttribute:@codigo withValue:@(codigoPourMonTypeDePatient)]; // Je cherche un type de patient particulier et l'assigne dans la foulée

    NSError *error;
    if (![context save:&error])
    {
    NSLog(@Erreur lors de l'enregistrement des données: %@ - %@", error, [error userInfo]);
    }
    Ca fait exactement la même chose que le code de Kubernan, mais perso je trouve que MR rend le code plus clair et plus lisible, et en plus évite les risques de fautes de frappes puisque tu ne manipules plus des NSString mais des classes et méthodes, donc l'autocomplétion t'aide et une erreur de frappe est repérée au compile-time. Après tout est affaire de goût.
  • KubernanKubernan Membre
    septembre 2013 modifié #5

    y m'énerve !  :'(


     


    .


    .


    .


    .


    .


    .


    À chaque fois t'arrive à  caser ton MR... je suis hilare  :D


  • AliGatorAliGator Membre, Modérateur
    Bah en même temps il est tellement magique ce framework :D
  • Merci Kubernan!!


    Oui effectivement, dans mon code je créais à  chaque fois une nouvelle instance de TPAC_TipoPaciente... avec un fetch c'est mieux!!


     


    Et je vais rajouter un s à  paciente dans ma table, ça sera + claire!! (comme en français! ;) )


    Quant à  MagicalRecord, effectivement c'est plus rapide...! (bon, je vais déjà  faire le tour de CoreData avant de me lancer dans un framework!)


     


    Encore une petite question..., comment je peux faire pour connaitre la valeur du "type de patient" à  partir d'une instance "patient"?


    Par exemple, mon code est celui ci:



    NSManagedObjectContext * context = [self managedObjectContext];
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@PAC_Pacientes inManagedObjectContext:context];
    [fetchRequest setEntity:entity];
    NSError *error;
    NSArray * listePacientes = [NSArray array];
    listePacientes = [context executeFetchRequest:fetchRequest error:&error];

    if (liste == nil)
    {
    NSLog(@Erreur lors de la requête : %@ - %@", error, [error userInfo]);
    }
    PAC_Pacientes * unPaciente = [lecture objectAtIndex:0];
    NSLog(@%@", unPaciente.tipo_de_paciente); // -> ça, ça me renvoie la relationship avec la table TPAC_TipoPaciente

    Comment fait on pour acceder à  la valeur de l'attribut "descripcion" (de la table TipoPaciente) de "unPaciente"?


    Voilou! muchas gracias!!! :D


     


    Ps: ok AliGator, je vais me presenter sur le "Présentation des Membres"!


  • AliGatorAliGator Membre, Modérateur

    Quant à  MagicalRecord, effectivement c'est plus rapide...! (bon, je vais déjà  faire le tour de CoreData avant de me lancer dans un framework!)

    Je ne sais pas si c'est une bonne chose justement... dans le sens où MagicalRecord est justement fait pour faciliter la vie.
    Perso quand j'ai voulu me mettre à  CoreData, quand j'ai vu la complexité du framework CoreData j'ai fait demi-tour tout de suite. Trop de classes nouvelles de partout, des NSPersistentStore des NSFetchRequest des NSEntityDescription des NSManagedObjectContext pfiouuu j'ai rapidement abandonné.

    Puis j'ai découvert MagicalRecord. Plus besoin de connaà®tre tout ça.
    - Je veux récupérer tous les PAC_Pacientes de ma base ? [PAC_Patientes findAll]. C'est tout. Une seule ligne. Même pas besoin d'un MOC, d'une EntityDescription, d'une FetchRequest ou de ce genre de trucs auxquels je ne comprenais rien à  l'époque.
    - Je veux tous les patients triés par nom ? NSArray* patientes = [PAC_Patientes findAllSortedBy:@nombre]. Pareil, une seule ligne, pas besoin de plein de classes obscures partout
    - Je veux récupérer la description du type d'un patient? unPaciente.tipo_de_patiente.descripcion. Basta.

    Avec MagicalRecord, tout devient plus simple et magique, bien moins besoin de se prendre la tête ou de commander de l'aspirine quand on veut faire du CoreData. J'ai appris CoreData 10x plus vite. Je sais pas si j'aurais abordé CoreData un jour sans ça tellement ça m'a rebuté avant toutes ces nouvelles classes et lignes à  écrire pour un simple fetch.
    Bien sûr maintenant que j'ai pratiqué MR et fait pas mal d'applis qui utilisent CoreData, du coup je suis depuis allé plus loin, j'ai découvert les MOC et ce qu'on pouvait faire avec, et plein d'autres trucs, je les utilise maintenant (toujours avec MagicalRecord), mais à  mes débuts dans CoreData où j'avais déjà  assez de choses à  apprendre, j'étais bien content de ne pas avoir à  m'en soucier grâce au fait que MR permet de ne pas avoir besoin de savoir ce que c'est si on ne veut pas s'en servir. Ne pas avoir besoin d'apprendre et manipuler 10 nouvelles classes juste pour faire un simple fetch, c'est quand même très appréciable surtout quand on débute sur CoreData.




  • Comment fait on pour acceder à  la valeur de l'attribut "descripcion" (de la table TipoPaciente) de "unPaciente"?


    Voilou! muchas gracias!!! :D




     


    Tout simplement : unPaciente.tipo_de_paciente.descripcion

  • Logique!!


    Mais dans mon code avec unPaciente.tipo_de_paciente.descripcion, Xcode me marque cette erreur: "Property 'descripcion' not found on object of type 'NSManagedObject *'" :*


     


    Je suis arrivé à  acceder à  mon type de patient avec une manip un poil + long:



    NSDictionary * unPaciente = [NSDictionary dictionary];
    unPaciente = [listePacientes objectAtIndex:i];
    NSDictionary * elTipo = [NSDictionary dictionary];
    elTipo = [unPaciente valueForKey:@tipo_de_paciente];

    NSLog(@" -> %@", [elTipo valueForKey:@descripcion]);

    Pour MR, as tu un tuto à  me recomender Aligator?


    Merci et a+ :)


  • AliGatorAliGator Membre, Modérateur

    Logique!!
    Mais dans mon code avec unPaciente.tipo_de_paciente.descripcion, Xcode me marque cette erreur: "Property 'descripcion' not found on object of type 'NSManagedObject *'" :*

    Bah c'est sûr que si ta propriété tipo_de_paciente est de type générique NSManagedObject et pas de type TPAC_TipoPaciente* c'est normal qu'il te mette cette erreur... L'erreur qu'il te ressort me semble assez explicite et devrait tout de suite te mettre sur la voie que tu n'as pas le type d'objet que tu attends. Suffit de corriger ton typage évidemment (et ça c'est pas spécialement propre à  CoreData de toute façon ce genre d'erreur)

    Je suis arrivé à  acceder à  mon type de patient avec une manip un poil + long:


    NSDictionary * unPaciente = [NSDictionary dictionary];
    unPaciente = [listePacientes objectAtIndex:i];
    NSDictionary * elTipo = [NSDictionary dictionary];
    elTipo = [unPaciente valueForKey:@tipo_de_paciente];

    NSLog(@" -> %@", [elTipo valueForKey:@descripcion]);

    OMG quelle horreur !!

    Tu as conscience du non-sens de ton code-là  ?
    1) Tu crees un tout nouveau NSDictionary et l'affecte à  une variable unPaciente
    2) Sauf que juste après, tu réaffectes cette variable unPaciente à  autre chose (au resultat de [listePacientes objectAtIndex:i]). Donc la ligne 1 ne sert strictement à  rien
    3) En plus tu as déclaré unPaciente comme étant un NSDictionary* alors que listePacientes est un tableau de PAC_Pacientes et pas de NSDictionary, donc tu affectes un objet d'un certain type à  une variable d'un autre type
    4) Pareil quand tu déclares elTipo, tu crées un tout nouveau NSDictionary tout ça pour rien puisque la ligne d'après tu réaffectes cette variable elTipo à  tout autre chose.
    5) Et là  encore, cet autre chose est un truc qui n'a rien à  voir avec un NSDictionary, puisque c'est un NSManagedObject (et même plus précisément tu t'attends à  ce que ce soit un TPAC_TipoPaciente pour être précis) et pas du tout un NSDictionary, donc là  encore tu affectes des poireaux à  des tomates.

    Tout ce code que tu fais tu as le gros coup de bol que ça te pète pas à  la tronche juste parce que tu utilises les méthodes de KVC ("valueForKey:", qui est une méthode un peu universelle sur NSObject pour récupérer des propriétés via leur nom). Si tu ne sais pas ce qu'est le KVC, comme c'est un design pattern de base en Cocoa, je t'invite à  te renseigner sur le sujet.
    Je ne vois pas ce que tes NSDictionary (tant la déclaration de tes variables avec ce type que la création de dicos pour rien) viennent foutre là .

    Alors que, soit l'utilisation directe du KVC (sans aucun besoin de mettre des NSDictionary qui n'ont rien à  voir avec la choucroute) aurait suffi, même si je ne la conseille pas car il n'y a alors aucun type checking, une erreur de frappe dans ta chaà®ne et c'est au Runtime que ça crash donc tu t'en rendras compte trop tard et seulement si tu penses à  faire passer ton appli par ce bout de code, tu n'as pas d'autocompletion, ça marchera plus si tu renommes tes propriétés... bref loin d'être idéal.

    Et encore 100x mieux que le KVC, l'appel à  la propriété directement aurait suffit, à  condition de caster ton unPaciente du type générique NSManagedObject en type spécifique PAC_Patiente (ça m'étonne un peu que ce soit pas déjà  le cas dans ton tableau, mais bon).
  • autant pour moi!!


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