IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage PHP Discussion :

MicroiFramework Slim / ORM Doctrine - Classes absentes dans le dossier vendor


Sujet :

Langage PHP

  1. #1
    Membre à l'essai
    Homme Profil pro
    retraité
    Inscrit en
    Mars 2022
    Messages
    46
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : retraité

    Informations forums :
    Inscription : Mars 2022
    Messages : 46
    Points : 18
    Points
    18
    Par défaut MicroiFramework Slim / ORM Doctrine - Classes absentes dans le dossier vendor
    Bonjour,

    J'ai installé Slim et testé un CRUD sans soucis. Je suis novice dans les framework.

    Je veux installer l'ORM Doctrine d"après le tuturiel suivant:

    https://www.slimframework.com/docs/v...-doctrine.html

    Dans le fichier bootstrap.php (partie Define the EntityManager service) , j'ai des soulignements "Undefined type". Les importations de classes
    semblent bonnes (non souligné). Mais par exemple, je ne retrouve pas dans vendor le dossier Symfony/Component. Il y a d'autres dossiers et classes absentes.

    Voici le soulignements:

    Nom : Capture d’écran du 2024-03-25 10-27-51.png
Affichages : 97
Taille : 123,7 Ko

    Voici le code accessible.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    <?php
     
    namespace App\config;
    // bootstrap.php
     
    use Doctrine\Common\Cache\Psr6\DoctrineProvider;
    use Doctrine\ORM\EntityManager;
    use Doctrine\ORM\Tools\Setup;
    use Symfony\Component\Cache\Adapter\ArrayAdapter;
    use Symfony\Component\Cache\Adapter\FilesystemAdapter;
    use UMA\DIC\Container;
     
    $container = new Container(require __DIR__ . '/settings.php');
     
    $container->set(EntityManager::class, static function (Container $c): EntityManager {
        /** @var array $settings */
        $settings = $c->get('settings');
     
        // Use the ArrayAdapter or the FilesystemAdapter depending on the value of the 'dev_mode' setting
        // You can substitute the FilesystemAdapter for any other cache you prefer from the symfony/cache library
        $cache = $settings['doctrine']['dev_mode'] ?
            DoctrineProvider::wrap(new ArrayAdapter()) :
            DoctrineProvider::wrap(new FilesystemAdapter(directory: $settings['doctrine']['cache_dir']));
     
        $config = Setup::createAttributeMetadataConfiguration(
            $settings['doctrine']['metadata_dirs'],
            $settings['doctrine']['dev_mode'],
            null,
            $cache
        );
     
        return EntityManager::create($settings['doctrine']['connection'], $config);
    });
     
    return $container;
     
     
    ($routes = require __DIR__ . '/routes.php')($app);
    et le composer.json:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
        "autoload": {
            "psr-4": {
                "App": "src/"
            }
        },
        "require": {
            "slim/slim": "4.*",
            "slim/psr7": "^1.6",
            "doctrine/orm": "^3.1",
            "symfony/cache": "^6.4",
            "php-di/php-di": "^7.0"
        }
    }
    J'ai essayé de supprimer le dossier vendor et composer.lock.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    rm -rf vendor/ composer.lock
    pusi:
    sans succès.

    Il y a les m^mes problèmes sous PHPStorm.

    Merci pour votre aide.

  2. #2
    Membre à l'essai
    Homme Profil pro
    retraité
    Inscrit en
    Mars 2022
    Messages
    46
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : retraité

    Informations forums :
    Inscription : Mars 2022
    Messages : 46
    Points : 18
    Points
    18
    Par défaut
    Je viens de constater aujourd'hui que les documentations v3 et v4 sur l'utilisation de doctrine sont similaires, sauf erreur de ma part.
    - https://www.slimframework.com/docs/v...-doctrine.html
    - https://www.slimframework.com/docs/v...-doctrine.html
    La question que je me pose:
    Est-ce que la documentation de doctrine(V3) permet de faire fonctionner SLIM4 avec Doctrine?
    Si ce n'est pas le cas, cela expliquerait les absences des classes
    Qu'en pensez-vous?

    PS; j'ai posé la question sur le forum de Slim. On m'a juste demandé mon composer.json. C'est tout. Je vais en demander plus.

  3. #3
    Membre confirmé
    Homme Profil pro
    Autre
    Inscrit en
    Juillet 2021
    Messages
    321
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Autre

    Informations forums :
    Inscription : Juillet 2021
    Messages : 321
    Points : 642
    Points
    642
    Par défaut
    Bonjour,

    La solution est indiquée dans la doc Slim que tu as partagée :
    Slim 4 nécessite une implémentation de conteneur PSR-11. Cet exemple utilise uma/dic, un conteneur PSR-11 simple et concis.
    Pour tester l'exemple de la doc, il faut donc ajouter la dépendance uma/dic à ton projet : composer require uma/dic

    Pour la classe DoctrineProvider, celle-ci a été supprimée depuis la version 3 de doctrine/orm, or tu as installé la version 3.1 d'après ton composer.json.
    La classe Setup est dépréciée (remplacée par ORMSetup) ainsi que la méthode EntityManager::create (remplacée par DriverManager::getConnection), un exemple en 3.1 sur la documentation de doctrine-orm :
    https://www.doctrine-project.org/pro...-entitymanager

    Si tu préfères suivre les exemples avec la version 2, retire la version que tu as installé (composer remove doctrine/orm) et utilises une contrainte de version pour ajouter par exemple la release 2.19 : composer require doctrine/orm:^2.19

    Dans la doc Slim que tu as partagée, tu peux trouver en bas de page un lien vers un exemple de projet Slim avec Doctrine : https://github.com/1ma/Slim-Doctrine-Demo
    L'EntityManager est défini dans ce fichier : https://github.com/1ma/Slim-Doctrine...I/Doctrine.php
    On peut voir que la classe DoctrineProvider n'est plus utilisée avec ORMSetup.
    Voir le diff : https://github.com/1ma/Slim-Doctrine...e2876df1934c1a

    Ton exemple pourrait donc ressembler à ceci (non testé) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    <?php
     
    // bootstrap.php
     
    use Doctrine\ORM\EntityManager;
    use Doctrine\ORM\ORMSetup;
    use Symfony\Component\Cache\Adapter\ArrayAdapter;
    use Symfony\Component\Cache\Adapter\FilesystemAdapter;
    use UMA\DIC\Container;
     
    require_once __DIR__ . '/vendor/autoload.php';
     
    $container = new Container(require __DIR__ . '/settings.php');
     
    $container->set(EntityManager::class, static function (Container $c): EntityManager {
        /** @var array $settings */
        $settings = $c->get('settings');
     
        $config = ORMSetup::createAttributeMetadataConfiguration(
            $settings['doctrine']['metadata_dirs'],
            $settings['doctrine']['dev_mode'],
            null,
            $settings['doctrine']['dev_mode'] ?
                new ArrayAdapter() :
                new FilesystemAdapter(directory: $settings['doctrine']['cache_dir'])
        );
     
        return EntityManager::create($settings['doctrine']['connection'], $config);
    });
     
    return $container;

  4. #4
    Membre à l'essai
    Homme Profil pro
    retraité
    Inscrit en
    Mars 2022
    Messages
    46
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : retraité

    Informations forums :
    Inscription : Mars 2022
    Messages : 46
    Points : 18
    Points
    18
    Par défaut
    Merci beaucoup Pytet pour tes explications, ta patience et la clarté de tes propos.
    Comme tu l'as dit EntityManager::create n'est plus valable.
    Comme la fonction doit retourner une EntityManager et que la documentation de Doctrine donne en fin de code
    $entityManager = new EntityManager($connection, $config); (pour la création d'une EntityManager - htps://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/configuration.html ),
    j'ai changé:
    return EntityManager::create($settings['doctrine']['connection'], $config);,
    par
    return new EntityManager($settings['doctrine']['connection'], $config);,
    Est-ce correct?
    J'ai deux questions. Voici mon code de bootstrap.php:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
     ?php
     
    // bootstrap.php
     
    use Doctrine\ORM\ORMSetup;
    use Symfony\Component\Cache\Adapter\ArrayAdapter;
    use Symfony\Component\Cache\Adapter\FilesystemAdapter;
    use UMA\DIC\Container;
    use Doctrine\ORM\EntityManager;
    use App\Service\UserService;
     
    $container = new Container(require __DIR__ . '/settings.php');
     
    $container->set(EntityManager::class, static function (Container $c): EntityManager {
        /** @var array $settings */
        $settings = $c->get('settings');
     
        $config = ORMSetup::createAttributeMetadataConfiguration(
            $settings['doctrine']['metadata_dirs'],
            $settings['doctrine']['dev_mode'],
            null,
            $settings['doctrine']['dev_mode'] ?
                new ArrayAdapter() :
                new FilesystemAdapter(directory: $settings['doctrine']['cache_dir'])
        );
     
        return new EntityManager($settings['doctrine']['connection'], $config);
    });
     
    // enregistrer un service dans un conteneur d'injection de dépendances.
    $container->set(UserService::class, static function (Container $c) {
        return new UserService($c->get(EntityManager::class));
    });
     
    return $container;
    En ce qui concerne le bout de code suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    // définir un service dans un conteneur d'injection de dépendances.
    $container->set(UserService::class, static function (Container $c) {
    return new UserService($c->get(EntityManager::class));
    });
    - Est-ce la bonne place pour ce bout de code?
    - je pense qu'il est déprécié. Je ne trouve pas d'informations pou corriger ce bout de code

    Merci d'avance pour votre aide.

  5. #5
    Membre confirmé
    Homme Profil pro
    Autre
    Inscrit en
    Juillet 2021
    Messages
    321
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Autre

    Informations forums :
    Inscription : Juillet 2021
    Messages : 321
    Points : 642
    Points
    642
    Par défaut
    Dans ton éditeur VSCode ou PHPStorm, tu peux faire un Ctrl + clic gauche sur la méthode EntityManager::create pour voir son code.
    (n'hésite pas à regarder le code des classes que tu utilises, pas besoin de chercher à comprendre tout le code mais il peut être intéressant de regarder)

    Tu peux voir que le code de la méthode EntityManager::create est très simple :
    - elle déclenche un message concernant la dépréciation (osef)
    - elle appelle la méthode EntityManager::createConnection (cette méthode étant elle aussi dépréciée, remplacée par DriverManager::getConnection())
    - puis elle retourne l'instance via un return new EntityManager($connection, $config);

    Ta modification semble donc presque correct, la seule différence est que la méthode EntityManager::createConnection (ou DriverManager::getConnection()) n'est pas appelée suite à ta modification, à tester :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
        $config = ORMSetup::createAttributeMetadataConfiguration(
            $settings['doctrine']['metadata_dirs'],
            $settings['doctrine']['dev_mode'],
            null,
            $settings['doctrine']['dev_mode'] ?
                new ArrayAdapter() :
                new FilesystemAdapter(directory: $settings['doctrine']['cache_dir'])
        );
     
        $connection = DriverManager::getConnection($settings['doctrine']['connection'], $config);
     
        return new EntityManager($connection, $config);
    La doc pour la version 2.19 : https://www.doctrine-project.org/pro...-entitymanager


    Ton dernier bout de code concerne le conteneur d'injection de dépendance (en anglais Dependency Injection Container ou DIC).
    Ton code est correct mais il s'agit d'un exemple simple de définition d'un service dans ton conteneur.
    Dans un vrai projet, une partie des services seront automatiquement configurés dans un conteneur via l'autowiring, seules quelques configurations seront nécessaires (les paramètres de connexion à la bdd par exemple).

    Slim est un framework très ouvert qui n'impose pas l'utilisation de l'injection de dépendance, celle-ci est recommandée car c'est une bonne pratique qui apporte plusieurs avantages pour la qualité de code (principe de responsabilité unique, inversion de contrôle, testabilité, etc).
    Un conteneur d'injection de dépendance est un outil permettant de faciliter la mise en place de l'injection de dépendance dans ton projet.

    Slim te laisse libre d'utiliser le container de ton choix comme tu le souhaites.
    La doc Slim concernant doctrine utilise le container uma/dic mais un autre container évoqué dans la doc Slim concernant l'injection de dépendance est PHP-DI
    PHP-DI semble être souvent utilisé avec Slim et sa documentation te donnera plus d'éléments de réponse sur l'utilisation d'un DIC : https://php-di.org/doc/

    L'utilisation d'un DIC implique que tu connais déjà l'injection de dépendance.
    C'est un sujet vaste et pas toujours facile à appréhender au début. Je n'ai pas de bonne recommandation mais trouver un cours à ce sujet serait une bonne idée.
    Si tu n'es pas allergique au format vidéo, la présentation AFUP de Lior CHAMLA est aussi une bonne introduction : youtube.com/watch?v=W6y9mXDgTiE

  6. #6
    Membre à l'essai
    Homme Profil pro
    retraité
    Inscrit en
    Mars 2022
    Messages
    46
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : retraité

    Informations forums :
    Inscription : Mars 2022
    Messages : 46
    Points : 18
    Points
    18
    Par défaut
    -Je ne connaissais pas le Crrl + clic. Bien pratique. Merci. J'explorais le dossier Vendor pour vérifier la présence de classes. J'avoue ne pas avoir eu la curiosité de regarder le code de la méthode create de la classe EntityManager. Mais j'ai beau regarder, je ne vois pas ce que tu me dis à l'intérieur d'EntityManager.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    457
    458
    459
    460
    461
    462
    463
    464
    465
    466
    467
    468
    469
    470
    471
    472
    473
    474
    475
    476
    477
    478
    479
    480
    481
    482
    483
    484
    485
    486
    487
    488
    489
    490
    491
    492
    493
    494
    495
    496
    497
    498
    499
    500
    501
    502
    503
    504
    505
    506
    507
    508
    509
    510
    511
    512
    513
    514
    515
    516
    517
    518
    519
    520
    521
    522
    523
    524
    525
    526
    527
    528
    529
    530
    531
    532
    533
    534
    535
    536
    537
    538
    539
    540
    541
    542
    543
    544
    545
    546
    547
    548
    549
    550
    551
    552
    553
    554
    555
    556
    557
    558
    559
    560
    561
    562
    563
    564
    565
    566
    567
    568
    569
    570
    571
    572
    573
    574
    575
    576
    577
    578
    579
    580
    581
    582
    583
    584
    585
    586
    587
    588
    589
    590
    591
    592
    593
    594
    595
    596
    597
    598
    599
    600
    601
    602
    603
    604
    605
    606
    607
    608
    609
    610
    611
    612
    613
    614
    615
    616
    617
    618
    619
    620
    621
    622
    623
    624
    625
    626
    <?php
     
    declare(strict_types=1);
     
    namespace Doctrine\ORM;
     
    use BackedEnum;
    use DateTimeInterface;
    use Doctrine\Common\EventManager;
    use Doctrine\DBAL\Connection;
    use Doctrine\DBAL\LockMode;
    use Doctrine\ORM\Exception\EntityManagerClosed;
    use Doctrine\ORM\Exception\InvalidHydrationMode;
    use Doctrine\ORM\Exception\MissingIdentifierField;
    use Doctrine\ORM\Exception\MissingMappingDriverImplementation;
    use Doctrine\ORM\Exception\ORMException;
    use Doctrine\ORM\Exception\UnrecognizedIdentifierFields;
    use Doctrine\ORM\Internal\Hydration\AbstractHydrator;
    use Doctrine\ORM\Mapping\ClassMetadata;
    use Doctrine\ORM\Mapping\ClassMetadataFactory;
    use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
    use Doctrine\ORM\Proxy\ProxyFactory;
    use Doctrine\ORM\Query\Expr;
    use Doctrine\ORM\Query\FilterCollection;
    use Doctrine\ORM\Query\ResultSetMapping;
    use Doctrine\ORM\Repository\RepositoryFactory;
    use Throwable;
     
    use function array_keys;
    use function is_array;
    use function is_object;
    use function ltrim;
    use function method_exists;
     
    /**
     * The EntityManager is the central access point to ORM functionality.
     *
     * It is a facade to all different ORM subsystems such as UnitOfWork,
     * Query Language and Repository API. The quickest way to obtain a fully
     * configured EntityManager is:
     *
     *     use Doctrine\ORM\Tools\ORMSetup;
     *     use Doctrine\ORM\EntityManager;
     *
     *     $paths = ['/path/to/entity/mapping/files'];
     *
     *     $config = ORMSetup::createAttributeMetadataConfiguration($paths);
     *     $connection = DriverManager::getConnection(['driver' => 'pdo_sqlite', 'memory' => true], $config);
     *     $entityManager = new EntityManager($connection, $config);
     *
     * For more information see
     * {@link http://docs.doctrine-project.org/projects/doctrine-orm/en/stable/reference/configuration.html}
     *
     * You should never attempt to inherit from the EntityManager: Inheritance
     * is not a valid extension point for the EntityManager. Instead you
     * should take a look at the {@see \Doctrine\ORM\Decorator\EntityManagerDecorator}
     * and wrap your entity manager in a decorator.
     *
     * @final
     */
    class EntityManager implements EntityManagerInterface
    {
        /**
         * The metadata factory, used to retrieve the ORM metadata of entity classes.
         */
        private readonly ClassMetadataFactory $metadataFactory;
     
        /**
         * The UnitOfWork used to coordinate object-level transactions.
         */
        private readonly UnitOfWork $unitOfWork;
     
        /**
         * The event manager that is the central point of the event system.
         */
        private readonly EventManager $eventManager;
     
        /**
         * The proxy factory used to create dynamic proxies.
         */
        private readonly ProxyFactory $proxyFactory;
     
        /**
         * The repository factory used to create dynamic repositories.
         */
        private readonly RepositoryFactory $repositoryFactory;
     
        /**
         * The expression builder instance used to generate query expressions.
         */
        private Expr|null $expressionBuilder = null;
     
        /**
         * Whether the EntityManager is closed or not.
         */
        private bool $closed = false;
     
        /**
         * Collection of query filters.
         */
        private FilterCollection|null $filterCollection = null;
     
        /**
         * The second level cache regions API.
         */
        private Cache|null $cache = null;
     
        /**
         * Creates a new EntityManager that operates on the given database connection
         * and uses the given Configuration and EventManager implementations.
         *
         * @param Connection $conn The database connection used by the EntityManager.
         */
        public function __construct(
            private readonly Connection $conn,
            private readonly Configuration $config,
            EventManager|null $eventManager = null,
        ) {
            if (! $config->getMetadataDriverImpl()) {
                throw MissingMappingDriverImplementation::create();
            }
     
            $this->eventManager = $eventManager
                ?? (method_exists($conn, 'getEventManager')
                    ? $conn->getEventManager()
                    : new EventManager()
                );
     
            $metadataFactoryClassName = $config->getClassMetadataFactoryName();
     
            $this->metadataFactory = new $metadataFactoryClassName();
            $this->metadataFactory->setEntityManager($this);
     
            $this->configureMetadataCache();
     
            $this->repositoryFactory = $config->getRepositoryFactory();
            $this->unitOfWork        = new UnitOfWork($this);
            $this->proxyFactory      = new ProxyFactory(
                $this,
                $config->getProxyDir(),
                $config->getProxyNamespace(),
                $config->getAutoGenerateProxyClasses(),
            );
     
            if ($config->isSecondLevelCacheEnabled()) {
                $cacheConfig  = $config->getSecondLevelCacheConfiguration();
                $cacheFactory = $cacheConfig->getCacheFactory();
                $this->cache  = $cacheFactory->createCache($this);
            }
        }
     
        public function getConnection(): Connection
        {
            return $this->conn;
        }
     
        public function getMetadataFactory(): ClassMetadataFactory
        {
            return $this->metadataFactory;
        }
     
        public function getExpressionBuilder(): Expr
        {
            return $this->expressionBuilder ??= new Expr();
        }
     
        public function beginTransaction(): void
        {
            $this->conn->beginTransaction();
        }
     
        public function getCache(): Cache|null
        {
            return $this->cache;
        }
     
        public function wrapInTransaction(callable $func): mixed
        {
            $this->conn->beginTransaction();
     
            try {
                $return = $func($this);
     
                $this->flush();
                $this->conn->commit();
     
                return $return;
            } catch (Throwable $e) {
                $this->close();
                $this->conn->rollBack();
     
                throw $e;
            }
        }
     
        public function commit(): void
        {
            $this->conn->commit();
        }
     
        public function rollback(): void
        {
            $this->conn->rollBack();
        }
     
        /**
         * Returns the ORM metadata descriptor for a class.
         *
         * Internal note: Performance-sensitive method.
         *
         * {@inheritDoc}
         */
        public function getClassMetadata(string $className): Mapping\ClassMetadata
        {
            return $this->metadataFactory->getMetadataFor($className);
        }
     
        public function createQuery(string $dql = ''): Query
        {
            $query = new Query($this);
     
            if (! empty($dql)) {
                $query->setDQL($dql);
            }
     
            return $query;
        }
     
        public function createNativeQuery(string $sql, ResultSetMapping $rsm): NativeQuery
        {
            $query = new NativeQuery($this);
     
            $query->setSQL($sql);
            $query->setResultSetMapping($rsm);
     
            return $query;
        }
     
        public function createQueryBuilder(): QueryBuilder
        {
            return new QueryBuilder($this);
        }
     
        /**
         * Flushes all changes to objects that have been queued up to now to the database.
         * This effectively synchronizes the in-memory state of managed objects with the
         * database.
         *
         * If an entity is explicitly passed to this method only this entity and
         * the cascade-persist semantics + scheduled inserts/removals are synchronized.
         *
         * @throws OptimisticLockException If a version check on an entity that
         * makes use of optimistic locking fails.
         * @throws ORMException
         */
        public function flush(): void
        {
            $this->errorIfClosed();
            $this->unitOfWork->commit();
        }
     
        /**
         * {@inheritDoc}
         */
        public function find($className, mixed $id, LockMode|int|null $lockMode = null, int|null $lockVersion = null): object|null
        {
            $class = $this->metadataFactory->getMetadataFor(ltrim($className, '\\'));
     
            if ($lockMode !== null) {
                $this->checkLockRequirements($lockMode, $class);
            }
     
            if (! is_array($id)) {
                if ($class->isIdentifierComposite) {
                    throw ORMInvalidArgumentException::invalidCompositeIdentifier();
                }
     
                $id = [$class->identifier[0] => $id];
            }
     
            foreach ($id as $i => $value) {
                if (is_object($value)) {
                    $className = DefaultProxyClassNameResolver::getClass($value);
                    if ($this->metadataFactory->hasMetadataFor($className)) {
                        $id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
     
                        if ($id[$i] === null) {
                            throw ORMInvalidArgumentException::invalidIdentifierBindingEntity($className);
                        }
                    }
                }
            }
     
            $sortedId = [];
     
            foreach ($class->identifier as $identifier) {
                if (! isset($id[$identifier])) {
                    throw MissingIdentifierField::fromFieldAndClass($identifier, $class->name);
                }
     
                if ($id[$identifier] instanceof BackedEnum) {
                    $sortedId[$identifier] = $id[$identifier]->value;
                } else {
                    $sortedId[$identifier] = $id[$identifier];
                }
     
                unset($id[$identifier]);
            }
     
            if ($id) {
                throw UnrecognizedIdentifierFields::fromClassAndFieldNames($class->name, array_keys($id));
            }
     
            $unitOfWork = $this->getUnitOfWork();
     
            $entity = $unitOfWork->tryGetById($sortedId, $class->rootEntityName);
     
            // Check identity map first
            if ($entity !== false) {
                if (! ($entity instanceof $class->name)) {
                    return null;
                }
     
                switch (true) {
                    case $lockMode === LockMode::OPTIMISTIC:
                        $this->lock($entity, $lockMode, $lockVersion);
                        break;
     
                    case $lockMode === LockMode::NONE:
                    case $lockMode === LockMode::PESSIMISTIC_READ:
                    case $lockMode === LockMode::PESSIMISTIC_WRITE:
                        $persister = $unitOfWork->getEntityPersister($class->name);
                        $persister->refresh($sortedId, $entity, $lockMode);
                        break;
                }
     
                return $entity; // Hit!
            }
     
            $persister = $unitOfWork->getEntityPersister($class->name);
     
            switch (true) {
                case $lockMode === LockMode::OPTIMISTIC:
                    $entity = $persister->load($sortedId);
     
                    if ($entity !== null) {
                        $unitOfWork->lock($entity, $lockMode, $lockVersion);
                    }
     
                    return $entity;
     
                case $lockMode === LockMode::PESSIMISTIC_READ:
                case $lockMode === LockMode::PESSIMISTIC_WRITE:
                    return $persister->load($sortedId, null, null, [], $lockMode);
     
                default:
                    return $persister->loadById($sortedId);
            }
        }
     
        public function getReference(string $entityName, mixed $id): object|null
        {
            $class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
     
            if (! is_array($id)) {
                $id = [$class->identifier[0] => $id];
            }
     
            $sortedId = [];
     
            foreach ($class->identifier as $identifier) {
                if (! isset($id[$identifier])) {
                    throw MissingIdentifierField::fromFieldAndClass($identifier, $class->name);
                }
     
                $sortedId[$identifier] = $id[$identifier];
                unset($id[$identifier]);
            }
     
            if ($id) {
                throw UnrecognizedIdentifierFields::fromClassAndFieldNames($class->name, array_keys($id));
            }
     
            $entity = $this->unitOfWork->tryGetById($sortedId, $class->rootEntityName);
     
            // Check identity map first, if its already in there just return it.
            if ($entity !== false) {
                return $entity instanceof $class->name ? $entity : null;
            }
     
            if ($class->subClasses) {
                return $this->find($entityName, $sortedId);
            }
     
            $entity = $this->proxyFactory->getProxy($class->name, $sortedId);
     
            $this->unitOfWork->registerManaged($entity, $sortedId, []);
     
            return $entity;
        }
     
        /**
         * Clears the EntityManager. All entities that are currently managed
         * by this EntityManager become detached.
         */
        public function clear(): void
        {
            $this->unitOfWork->clear();
        }
     
        public function close(): void
        {
            $this->clear();
     
            $this->closed = true;
        }
     
        /**
         * Tells the EntityManager to make an instance managed and persistent.
         *
         * The entity will be entered into the database at or before transaction
         * commit or as a result of the flush operation.
         *
         * NOTE: The persist operation always considers entities that are not yet known to
         * this EntityManager as NEW. Do not pass detached entities to the persist operation.
         *
         * @throws ORMInvalidArgumentException
         * @throws ORMException
         */
        public function persist(object $object): void
        {
            $this->errorIfClosed();
     
            $this->unitOfWork->persist($object);
        }
     
        /**
         * Removes an entity instance.
         *
         * A removed entity will be removed from the database at or before transaction commit
         * or as a result of the flush operation.
         *
         * @throws ORMInvalidArgumentException
         * @throws ORMException
         */
        public function remove(object $object): void
        {
            $this->errorIfClosed();
     
            $this->unitOfWork->remove($object);
        }
     
        public function refresh(object $object, LockMode|int|null $lockMode = null): void
        {
            $this->errorIfClosed();
     
            $this->unitOfWork->refresh($object, $lockMode);
        }
     
        /**
         * Detaches an entity from the EntityManager, causing a managed entity to
         * become detached.  Unflushed changes made to the entity if any
         * (including removal of the entity), will not be synchronized to the database.
         * Entities which previously referenced the detached entity will continue to
         * reference it.
         *
         * @throws ORMInvalidArgumentException
         */
        public function detach(object $object): void
        {
            $this->unitOfWork->detach($object);
        }
     
        public function lock(object $entity, LockMode|int $lockMode, DateTimeInterface|int|null $lockVersion = null): void
        {
            $this->unitOfWork->lock($entity, $lockMode, $lockVersion);
        }
     
        /**
         * Gets the repository for an entity class.
         *
         * @psalm-param class-string<T> $className
         *
         * @psalm-return EntityRepository<T>
         *
         * @template T of object
         */
        public function getRepository(string $className): EntityRepository
        {
            return $this->repositoryFactory->getRepository($this, $className);
        }
     
        /**
         * Determines whether an entity instance is managed in this EntityManager.
         *
         * @return bool TRUE if this EntityManager currently manages the given entity, FALSE otherwise.
         */
        public function contains(object $object): bool
        {
            return $this->unitOfWork->isScheduledForInsert($object)
                || $this->unitOfWork->isInIdentityMap($object)
                && ! $this->unitOfWork->isScheduledForDelete($object);
        }
     
        public function getEventManager(): EventManager
        {
            return $this->eventManager;
        }
     
        public function getConfiguration(): Configuration
        {
            return $this->config;
        }
     
        /**
         * Throws an exception if the EntityManager is closed or currently not active.
         *
         * @throws EntityManagerClosed If the EntityManager is closed.
         */
        private function errorIfClosed(): void
        {
            if ($this->closed) {
                throw EntityManagerClosed::create();
            }
        }
     
        public function isOpen(): bool
        {
            return ! $this->closed;
        }
     
        public function getUnitOfWork(): UnitOfWork
        {
            return $this->unitOfWork;
        }
     
        public function newHydrator(string|int $hydrationMode): AbstractHydrator
        {
            return match ($hydrationMode) {
                Query::HYDRATE_OBJECT => new Internal\Hydration\ObjectHydrator($this),
                Query::HYDRATE_ARRAY => new Internal\Hydration\ArrayHydrator($this),
                Query::HYDRATE_SCALAR => new Internal\Hydration\ScalarHydrator($this),
                Query::HYDRATE_SINGLE_SCALAR => new Internal\Hydration\SingleScalarHydrator($this),
                Query::HYDRATE_SIMPLEOBJECT => new Internal\Hydration\SimpleObjectHydrator($this),
                Query::HYDRATE_SCALAR_COLUMN => new Internal\Hydration\ScalarColumnHydrator($this),
                default => $this->createCustomHydrator((string) $hydrationMode),
            };
        }
     
        public function getProxyFactory(): ProxyFactory
        {
            return $this->proxyFactory;
        }
     
        public function initializeObject(object $obj): void
        {
            $this->unitOfWork->initializeObject($obj);
        }
     
        /**
         * {@inheritDoc}
         */
        public function isUninitializedObject($obj): bool
        {
            return $this->unitOfWork->isUninitializedObject($obj);
        }
     
        public function getFilters(): FilterCollection
        {
            return $this->filterCollection ??= new FilterCollection($this);
        }
     
        public function isFiltersStateClean(): bool
        {
            return $this->filterCollection === null || $this->filterCollection->isClean();
        }
     
        public function hasFilters(): bool
        {
            return $this->filterCollection !== null;
        }
     
        /**
         * @psalm-param LockMode::* $lockMode
         *
         * @throws OptimisticLockException
         * @throws TransactionRequiredException
         */
        private function checkLockRequirements(LockMode|int $lockMode, ClassMetadata $class): void
        {
            switch ($lockMode) {
                case LockMode::OPTIMISTIC:
                    if (! $class->isVersioned) {
                        throw OptimisticLockException::notVersioned($class->name);
                    }
     
                    break;
                case LockMode::PESSIMISTIC_READ:
                case LockMode::PESSIMISTIC_WRITE:
                    if (! $this->getConnection()->isTransactionActive()) {
                        throw TransactionRequiredException::transactionRequired();
                    }
            }
        }
     
        private function configureMetadataCache(): void
        {
            $metadataCache = $this->config->getMetadataCache();
            if (! $metadataCache) {
                return;
            }
     
            $this->metadataFactory->setCache($metadataCache);
        }
     
        private function createCustomHydrator(string $hydrationMode): AbstractHydrator
        {
            $class = $this->config->getCustomHydrationMode($hydrationMode);
     
            if ($class !== null) {
                return new $class($this);
            }
     
            throw InvalidHydrationMode::fromMode($hydrationMode);
        }
    }
    -DriveManager:getConnection est tellement éloigné de EntityManager:create que je me suis dit que c'était une blague...
    -Je suis en contact sur le forum de Slim (à l'écoute et disponible). Une personne s'est proposée de me guider dans mon apprentissage et mon installation.
    - Odan (Daniel Opitz) m'a conseillé deux fois de prendre comme container PHP-DI plutôt que uma-dic. Ce dernier ne gère pas l'autowiring. Avec tes explication, je comprends mieux.
    En ce qui concerne tes liens, je t'en remercie. J'en ai besoin. Je commence à comprendre le béa-ba. Mais cela ne suffit pas.
    Merci encore pour ta disponibilité et ton indulgence.

  7. #7
    Membre à l'essai
    Homme Profil pro
    retraité
    Inscrit en
    Mars 2022
    Messages
    46
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : retraité

    Informations forums :
    Inscription : Mars 2022
    Messages : 46
    Points : 18
    Points
    18
    Par défaut
    Bonjour,

    Je pense avoir compris un certain nombre de choses. J'espère ne pas trop dire de bétises. Je rappelle que je suis novice en framework. Donc j'espère que vous serez indulgent
    avec mes erreurs.

    Ancien programme
    - Le programme précédent n'avait pas la bonne structure et se référait à une ancienne documentation Docrine.
    - le conteneur uma/dic ne gère pas l'autowire et doit être aidé par la console pour trouver les injections à effectuer. Voici une documentation avec du code obsèlte mais qui décrit bien l'utilité de la console:
    https://www.slimframework.com/docs/v...-doctrine.html)
    Ce qui n'est pas le cas de d'un containeur PH-DI. Les injections se font automatiquement, lorsque cela est demandé dans le programme (autowiring)

    Nouveau programme
    Voici les deux documents de références qui m'ont servi à faire mon programme.
    * un excellent tutoriel qui décrit la construction d'un programme PHP-DI et qui m'a appris beaucoup de choses.
    [https://odan.github.io/2019/11/05/sl...l#di-container (contrairement à la documentation slim qui n'est pas à jour. Je l'ai testé sans problème)
    L'auteur attache aussi beaucoup d'l'importance à bien structurer son programme. Il l'explique au fur et à mesure. Ce qui est génial pour un novice comme moi. Avec quelques connaissances sur l'injection des dépendances, ce tutoriel est vraiment une référence
    * documentation doctrine:https://www.doctrine-project.org/pro...iguration.html

    Voici les programmes qui ont permit d'installer le servcie EntityManager dans le programme pour pouvoir à partir de données JSON générées par POSTMAN et de les de les enregistrer dans une base de données "users" (OPFDoction.
    Voici la structure json à trairer
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    {
      "nom": "Dupont",
      "age": 30,
      "estEmploye": true
    }
    la structure des fichiers/dossiers:
    ├── config/             Configuration files
    ├── public/             Web server files (DocumentRoot)
    │   └── index.php       The front controller
    ├── src/                PHP source code (The App namespace)
    ├── vendor/             Reserved for composer
    ├── .gitignore          Git ignore rules
    └── composer.json       Project dependencies

    Voici le programme [Slim - PHP-DI - Doctrine ORM)

    Je n'ai pas encore gérer la sécurité et les erreurs

    - public/index.php (celui du tutoriel (point d'entrée du programme -de ce tutoriel
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    <?php
    (require __DIR__ . '/../config/bootstrap.php')->run();
    - config/bootstrap celui du tutoriel:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <?php
     
    use DI\ContainerBuilder;
    use Slim\App;
     
    require_once __DIR__ . '/../vendor/autoload.php';
     
    // Construire l'instance de container de type PHP-DI
    $container = (new ContainerBuilder())
        ->addDefinitions(__DIR__ . '/container.php')
        ->build();
     
    //  renvoyer l'instance du container
    return $container->get(App::class);

    config/settings.php (code perso)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <?php
    // settings.php
    return [
        'doctrine' => [
            'pathToModels' => '__DIR__ . /../src/Models',
            'isDevMode' => true,
            'connectionMysql' => [
                'driver' => 'pdo_mysql',
                'dbname' => 'university',
                'user' => 'toto',
                'password' => 'mdp',
            ],
        ],
    ];
    Je n'ai pas eu encore le temps de mettre les données sensibles dans .env

    - container.php (code du tutoriel modifié)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    <?php
     
    use Slim\App;
    use Slim\Factory\AppFactory;
    use Doctrine\DBAL\DriverManager; 
    use Doctrine\ORM\EntityManager; 
    use Doctrine\ORM\ORMSetup;
    use Psr\Container\ContainerInterface; 
     
    return[
        'settings' => function () {
            $settings = require __DIR__ . '/settings.php';
            return $settings;
        },
        // Construction de l'instance EntutyManager
        EntityManager::class => function (ContainerInterface $container) : EntityManager {
     
            $settings = (array)$container->get('settings')['doctrine'];
     
     
            $paths = [$settings['pathToModels']]; //chemin de l'entité
            $isDevMode = $settings['isDevMode']; // est-ce en développement?
     
            // paramètres de connexion
            $dbParams = $settings['connectionMysql']; //connexion de la base de données
     
            // construction er retour de l'instance AppFactory
            $config = ORMSetup::createAttributeMetadataConfiguration($paths, $isDevMode);
            $connection = DriverManager::getConnection($dbParams, $config);
            return new EntityManager($connection, $config);   
        },
     
        // construction de l'application $app
        App::class => function (ContainerInterface $container) {
            $app = AppFactory::createFromContainer($container);
     
            // Register routes
            (require __DIR__ . '/routes.php')($app);
     
            // Register middleware
            (require __DIR__ . '/middleware.php')($app);
     
            return $app;
        }
    ];
    - config/routes.php (code perso -
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <?php
     
    use Slim\App;
    use App\Controllers\StudentController;
    use Psr\Http\Message\ServerRequestInterface as Request;
    use Psr\Http\Message\ResponseInterface as Response;
     
    return function (App $app) {
        //route de test
        $app->get('/', function (Request $request, Response $response) {
            $response->getBody()->write('Hello, World!');
            return $response;
        });
        //Appel de la méthode createUser de la classe StudentContreller à l'aide de l'URL "localhost:8082/create/student"
        $app->post('/create/student', StudentController::class . ':createStudent');
    };
    (code perso)

    - src/Contollers/StudentController.php (code perso)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    <?php
     
    namespace App\Controllers;
     
    use App\Models\Student;
    use Doctrine\ORM\EntityManager;
    use Psr\Http\Message\ResponseInterface as Response;
    use Psr\Http\Message\ServerRequestInterface as Request;
     
    class StudentController
    {
        private  $entityManager;
     
        public function __construct(EntityManager $entityManager){
            $this->entityManager = $entityManager;
        }
     
        public function createStudent(Request $request, Response $response): Response
        {
            //récupération des données
            $data = $request->getParsedBody();
            error_log(print_r($data, true), 3, __DIR__ . '/StudentController.log');
     
            // création des données de l'étudiant
            $student = new Student();
            $student->setName($data['name']);
            $student->setFirstname($data['firstname']);
            $student->setAge($data['age']);
     
            //transfert des données de l'étudiant dans la base de données
            $this->entityManager->persist($student);
            $this->entityManager->flush();
     
            $response->getBody()->write('Étudiant créé avec succès avec l\'ID :' . $student->getId());
            return $response->withHeader('Content-Type', 'application/json');
        } 
    }
    et src/Models/Student.php (code perso):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
     
    <?php
     
    namespace App\Controllers;
     
    use App\Models\Student;
    use Doctrine\ORM\EntityManager;
    use Psr\Http\Message\ResponseInterface as Response;
    use Psr\Http\Message\ServerRequestInterface as Request;
     
    <?php
     
    namespace App\Models;
     
    //Student.php
     
    use Doctrine\ORM\Mapping as ORM;
     
    #[ORM\Entity]
    #[ORM\Table(name: "students")]
    class Student{
     
        #[ORM\Id]
        #[ORM\Column(type: "integer")]
        #[ORM\GeneratedValue]
        private $id;
     
        #[ORM\Column(type: "string", length: 50, nullable: false)]
        private $name;
     
        #[ORM\Column(type: "string", length: 50, nullable: false)]
        private $firstname;
     
        #[ORM\Column(type: "integer", nullable: false)]
        private $age;
     
        // Getter de l'Id
        public function getId()
        {
            return $this->id;
        }
     
        // Getter et setterde "name" 
        public function getName()
        {
            return $this->name;
        }
     
        public function setName($name): void
        {
            $this->name = $name;
        }
     
        // Getter et setter pour firstname
        public function getFirstname()
        {
            return $this->firstname;
        }
     
        public function setFirstname($firstname): void
        {
            $this->firstname = $firstname;
        }
     
        // Getter et setter pour "age"
        public function getAge()
        {
            return $this->age;
        }
     
        public function setAge($age): void
        {
            $this->age = $age;
        } 
    }
    Vos remarques sont les bienvenues.
    Bonne fin de journée.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 4
    Dernier message: 16/04/2009, 01h43
  2. Réponses: 6
    Dernier message: 23/09/2005, 12h54
  3. [Language] Explications classe définie dans une classe
    Par etiennegaloup dans le forum Langage
    Réponses: 6
    Dernier message: 13/09/2005, 22h15
  4. [JAR]Class-Path dans le fichier Manifest
    Par Kleb dans le forum Général Java
    Réponses: 5
    Dernier message: 08/01/2005, 08h51
  5. [GEF]class Figure dans container SWING ?
    Par Albarad dans le forum Eclipse Java
    Réponses: 2
    Dernier message: 01/06/2004, 12h12

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo