[PHP] Doctrine 2 het generen van entities

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Hallo iedereen,

Wij hebben besloten om ORM toe te gaan passen binnen onze applicatie en hier gebruik van Doctrine 2 voor te maken. Laatse dagen ben ik flink in de studie gegaan over Doctrine 2 en vind het sowieso een erg gaaf project met veel mogelijkheden. Echter is het best een bittere pil om het onder de knie te krijgen door vele lagen die erin zitten.

Entities zijn volgens mij je models en de 'PHP' vertaling in objecten van je database. Echter zie ik bij Doctrine 2 ook Proxies, Mappings, etc. Nu probeer ik grip te krijgen op wat die termen allemaal precies betekenen en wat je ermee kunt. Misschien kan iemand mij een beetje opweg helpen met deze termen door dit in jip-en-janneke taal uit te leggen of me te verwijzen naar een goed artikel erover?

Ondertussen ben ik ermee aan het spelen en wil ik entities genereren van onze bestaande database. Dit kan vrij eenvoudig menen zijn met de commandline tool van Doctrine 2. Hier heb ik een script voor gevonden en aangepast:

PHP:
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
<?php
set_include_path(get_include_path() . ':' . '/var/www/xxxxx/library/');

use Doctrine\ORM\Tools\EntityGenerator;

ini_set("display_errors", "On");

// this is not necessary if you use Doctrine2 with PEAR
//$libPath = __DIR__ . '/../lib/doctrine2';

// autoloaders
require_once 'Doctrine/Common/ClassLoader.php';

//$classLoader = new \Doctrine\Common\ClassLoader('Doctrine', $libPath);    // custom path
$classLoader = new \Doctrine\Common\ClassLoader('Doctrine');    // with PEAR
$classLoader->register();

$classLoader = new \Doctrine\Common\ClassLoader('Entities', __DIR__);
$classLoader->register();
$classLoader = new \Doctrine\Common\ClassLoader('Proxies', __DIR__);
$classLoader->register();

// config
$config = new \Doctrine\ORM\Configuration();
$config->setMetadataDriverImpl($config->newDefaultAnnotationDriver(__DIR__ . '/Entities'));
$config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ArrayCache);
$config->setProxyDir(__DIR__ . '/Proxies');
$config->setProxyNamespace('Proxies');

$connectionParams = array(
  'dbname' => 'xxxxx',
  'user' => 'xxxx',
  'password' => 'xxxx',
  'host' => 'localhost',
  'driver' => 'pdo_mysql',
);

$em = \Doctrine\ORM\EntityManager::create($connectionParams, $config);

// custom datatypes (not mapped for reverse engineering)
$em->getConnection()->getDatabasePlatform()->registerDoctrineTypeMapping('set', 'string');
$em->getConnection()->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');
$em->getConnection()->getDatabasePlatform()->registerDoctrineTypeMapping('bit', 'integer');
$em->getConnection()->getDatabasePlatform()->registerDoctrineTypeMapping('mediumblob', 'string');
$em->getConnection()->getDatabasePlatform()->registerDoctrineTypeMapping('tinyblob', 'string');

// fetch metadata
$driver = new \Doctrine\ORM\Mapping\Driver\DatabaseDriver(
  $em->getConnection()->getSchemaManager()
);

$em->getConfiguration()->setMetadataDriverImpl($driver);
$cmf = new \Doctrine\ORM\Tools\DisconnectedClassMetadataFactory();
$cmf->setEntityManager($em);    // we must set the EntityManager

$classes = $driver->getAllClassNames();
$metadata = array();
foreach ($classes as $class) {
  //any unsupported table/schema could be handled here to exclude some classes
  if (true) {
    $metadata[] = $cmf->getMetadataFor($class);
  }
}

$generator = new EntityGenerator();
$generator->setUpdateEntityIfExists(true);  // only update if class already exists
//$generator->setRegenerateEntityIfExists(true);    // this will overwrite the existing classes
$generator->setGenerateStubMethods(true);
$generator->setGenerateAnnotations(true);
$generator->generate($metadata, __DIR__ . '/Entities');

print 'Done!';


En bij het uitvoeren van dit script lijkt hij het wel te gaan doen totdat ik een MappingException krijg met de melding: Property "customerid" in "Order" was already declared, but it must be declared only once.

Ik heb een tabel customer:

customerid
name
place
postalcode
etc

Tabel order:
orderid
customerid => foreign key naar customer
insertdate

Nu DENK ik dat Doctrine het niet leuk vind dat ik 2 tabellen een veld 'customerid' hebben. Als dit zo is zou dat best rampzalig zijn want zo werk ik in 80 tabellen :p

Wat is nu de beste optie? Moet ik al mijn entities zelf schrijven? Of kun je Doctrine op een of andere manier zo configureren dat het wel werkt?

Ik ben benieuwd!

Acties:
  • 0 Henk 'm!

  • armageddon_2k1
  • Registratie: September 2001
  • Laatst online: 27-07 10:18
Mag ik vragen waarom je Doctrine2 hebt gekozen als je niet op de hoogte bent van de werking? :)

Leesvoer:
http://symfony.com/doc/current/book/doctrine.html (Dit is in Symfony2, dus de console-commands zijn niet hetzelfde)
http://www.doctrine-proje...-started-xml-edition.html (moet je toch ook al gevonden hebben?)
http://www.doctrine-project.org/docs/orm/2.0/en/ (sowieso).

Daarnaast heb je nu een script gepakt en 'aangepast'. Het mooie aan Doctrine2 is dat je dat helemaal niet allemaal nodig hebt. Je kan Annotations (een implementatie in Doctrine Common package) gebruiken om je PHP objecten te mappen naar Mysql tabellen.

Ik zou zeggen: Lees die docs die ik je gegeven heb. Probeer erachter te komen waarom je Doctrine2 wil gebruiken. Gebruik de Annotation mogelijkheden met caching etc. Het mooie aan Doctrine is dat het erg goed gedocumenteerd is.

EDIT: Waarom ga je van database-tabellen naar Entities? Je moet de andere kant op werken over het algemeen.

[ Voor 5% gewijzigd door armageddon_2k1 op 14-07-2011 15:08 ]

Engineering is like Tetris. Succes disappears and errors accumulate.


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
He,

Met de documentatie die je post ben ik al druk mee bezig door te nemen :) Het is behoorlijk wat dus zal wel eventjes duren voordat ik het helemaal begrijp.

De reden dat ik voor Doctrine 2 kies is op puur gevoel en het feit dat we wel ORM willen gebruik gezien we nu vies door de hele applicatie heen bezig zijn met queries. Het is uiteraard wel een hele omslag maar de leercurve is ook groot.

Wat betreft het laatste; het gaat hier om ORM in een bestaande applicatie te implementeren waarbij de database dus al bestaat. Als ik het dus volgens jou voorstel doe dan wordt mijn hele database anders en krijgen we een hele grote migratie.

Als het niet anders kan of dat het beste is dan is dat uiteraard wel een optie maar gezien Doctrine reverse-enginering ondersteund is dat ook een optie..

Acties:
  • 0 Henk 'm!

  • ReenL
  • Registratie: Augustus 2010
  • Laatst online: 14-09-2022
Entities zijn volgens mij je models en de 'PHP' vertaling in objecten van je database. Echter zie ik bij Doctrine 2 ook Proxies, Mappings, etc. Nu probeer ik grip te krijgen op wat die termen allemaal precies betekenen en wat je ermee kunt. Misschien kan iemand mij een beetje opweg helpen met deze termen door dit in jip-en-janneke taal uit te leggen of me te verwijzen naar een goed artikel erover?
Entity Je object in php. Denk aan Car, User, Group etc;
Proxy Als je een enitity uit de database haalt krijg je eigenlijk een proxy terug. Deze extend van de Entity. De proxy is ervoor om de veranderingen in je entity bij te houden, voor de rest stuurt hij alles door naar de Entity die jij gemaakt hebt;
Mapping Een mapping is een omschrijving hoe een object vertaald moet worden naar een tabel en andersom.

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Bedankt!

Ondertussen verder aan het lezen. Een collega van me zegt dat in de proxies ook bepaalt is hoe je relaties liggen tussen entiteiten, is dat correct?

Wat moet je dus 'maken' om een volledige implemtatie te hebben? Entities (die moeten in PHP?) en mappings in PHP, XML of YML dus?

Acties:
  • 0 Henk 'm!

Verwijderd

Verwijderd schreef op donderdag 14 juli 2011 @ 15:53:
Bedankt!

Ondertussen verder aan het lezen. Een collega van me zegt dat in de proxies ook bepaalt is hoe je relaties liggen tussen entiteiten, is dat correct?

Wat moet je dus 'maken' om een volledige implemtatie te hebben? Entities (die moeten in PHP?) en mappings in PHP, XML of YML dus?
Volgens mij (maar dit kan ook alleen van toepassing zijn op 1.2) worden je mappings alleen gebruikt om je entities te genereren, vervolgens staan ze hier in.

Mijn workflow is meestal YML maken, entities genereren en evt tabellen genereren. Hier gebruikt hij de entities voor dus je kunt de YML stap ook vervangen door je entities van annotations voorzien of zelf schrijven.

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Dus als ik het goed begrijp kun je dit doen:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
<?php
/**
 * @Entity @Table(name="users")
 */
class User
{
    /**
     * @Id @GeneratedValue @Column(type="integer")
     * @var string
     */
    public $id;
}
?>


En dan daarin ook je eigen functies verwerken voor je model. Maar dan moet je dus bij alle entity attributen die docblock code zetten of je doet het op de XML/YML manier:

XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
                    http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">

     <entity name="User" name="users">
         <id name="id" type="integer">
             <generator strategy="AUTO" />
         </id>

         <field name="name" type="string" />

         <one-to-many target-entity="Bug" field="reportedBugs" mapped-by="reporter" />
         <one-to-many target-entity="Bug" field="assignedBugs" mapped-by="engineer" />
     </entity>
</doctrine-mapping>


En dan kun je zelf een PHP class maken:
PHP:
1
2
3
4
5
<?php
class User {
  public function myOwnFunction() {}
}
?>


Zit ik op de goede weg of ik begrijp ik er nog helemaal niets van? :p

Wat betreft je workflow; je genereert je database vanuit je yml files, maar doe je dit eenmalig? Of roep je een update commando aan zodra er een veld wijzigt of tabel bijkomt?

Acties:
  • 0 Henk 'm!

  • Cartman!
  • Registratie: April 2000
  • Niet online
Je kan ook zonder YML/XML werken, je maakt gewoon zelf je entities en repository's aan en wijst tijdens het opzetten van de entity manager naar de juiste mappen. Je schema maak je dan adhv. docblok-notatie (@Colum, @Id, @ManyToMany etc).

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Begin het langzaam te begrijpen, bedankt!

Ga zometeen deze video bekijken: http://www.zendcasts.com/...trine-2-entities/2011/02/

Ben ook wel benieuwd op wat voor manier men hier zijn entities e.d. implementeerd met docblock annotions of yml of xml..

Acties:
  • 0 Henk 'm!

  • armageddon_2k1
  • Registratie: September 2001
  • Laatst online: 27-07 10:18
Ik gebruik louter Annotations. Het is net zo snel (want hij 'compiled' het als het ware) en je houd alles in 1 class voor 1 Entity.

Engineering is like Tetris. Succes disappears and errors accumulate.


Acties:
  • 0 Henk 'm!

  • Cartman!
  • Registratie: April 2000
  • Niet online
Hier t zelfde, als je er een veld bij bedenkt moet je anders weer die xml/yml files aanpassen en de boel herbouwen, gedoe :)

Acties:
  • 0 Henk 'm!

  • alienfruit
  • Registratie: Maart 2003
  • Laatst online: 10-09 18:14

alienfruit

the alien you never expected

Ja, annotations is leuk totdat je een plugin infrastructuur wilt bouwen in je applicatie. Op dat moment kan je niet zo 1-2-3 je entities aanpassen in de plugin. Bijv. een nieuw veld toevoegen die nodig is voor je plugin. Ik heb nog niet in een methode gevonden hoe het moet. Komen er vast wel uit :+

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Ik ben nu aan de slag gegaan met entities in PHP dan kan ik daar ook extra functionaliteit voor me model erin verwerken. En proxies genereren is ook al gelukt :) orm:generate-proxies was genoeg :p Alleen druk bezig met het onder de knie krijgen van het annotations voor bijvoorbeeld relaties:

http://www.doctrine-proje.../association-mapping.html

Voorbeeldje wat ik tot nu toe heb gemaakt:
PHP:
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
<?php
namespace BestBuy\Entity;

/**
 * 
 * @Table(name="webshop")
 * @Entity
 * @author Kees Schepers <kees@***>
 */
class Webshop {
    /**
     * 
     * @var integer
     * @Column(type="smallint",nullable=false)
     * @Id
     * @GeneratedValue(strategy="IDENTITY")
     */
    private $webshopid;
    /**
     *
     * @var string
     * @Column(type="string",nullable=false)     
     */
    private $title;
    /**
     *
     * @var string
     * @Column(type="string",nullable=false)     
     */ 
    private $domain;
    /**
     *
     * @var string
     * @Column(type="datetime",nullable=false)   
     */ 
    private $insertdate;
    /**
     *
     * @var string
     * @Column(type="string",nullable=false)     
     */ 
    private $manageremail;
    /**
     *
     * @var string
     * @Column(type="text",nullable=false)   
     */ 
    private $description;
        
    /**
     * @OneToOne(targetEntity="Webshoptemplate")
     * @JoinColumn(name="webshoptemplateid", referencedColumnName="webshoptemplateid")
     */
    private $template;  
    /**
     *
     * @var string
     * @Column(type="string",nullable=false)     
     */ 
    private $shoptype;
    /**
     *
     * @var string
     * @Column(type="string",nullable=false)     
     */ 
    private $analyticsid;
    /**
     *
     * @var string
     * @Column(type="string",nullable=false)     
     */ 
    private $pagetitle;
    /**
     *
     * @var string
     * @Column(type="string",nullable=false)     
     */ 
    private $subject;   
    /**
     * @OneToOne(targetEntity="Webshopgroup")
     * @JoinColumn(name="webshopgroupid", referencedColumnName="webshopgroupid")
     */
    private $group;
    /**
     *
     * @var string
     * @Column(type="string",nullable=false)     
     */ 
    private $keywords;
}


Op de goede weg? :p

[ Voor 69% gewijzigd door RobIII op 15-07-2011 00:22 . Reden: domein weggehaald ]


Acties:
  • 0 Henk 'm!

  • alienfruit
  • Registratie: Maart 2003
  • Laatst online: 10-09 18:14

alienfruit

the alien you never expected

Ik zou een kleine wijziging maken:

PHP:
1
2
3
4
/** 
     * @OneToOne(targetEntity="AnalysticsProvider") 
     * @JoinColumn(name="analysticsproviderid", referencedColumnName="analysticsproviderid") 
     */


Op deze manier kan je de instellingen voor de Analystics provider opslaan in de database. Bijv. de OAuth key, website id etc. Op die manier kan je PHP API calls doen om de correcte tracking js code op te vragen.
Bij mij op het werk heb je klanten die specifieke statistieken oplossingen willen gebruiken de één Google en de ander PiWik en de volgende weer Grape (bagger, btw).

Acties:
  • 0 Henk 'm!

  • Reboot
  • Registratie: Januari 2009
  • Laatst online: 29-07 22:18
werkt doctrine niet op deze manier? (gebruik zelf 1.2 dus sorry als het verkeerd is voor 2.0)

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
Doctrine_Manager::getInstance()->bindComponent('CMS_model_News', 'doctrine');
abstract class CMS_model_BaseNews extends Doctrine_Record
{
    public function setTableDefinition()
    {
        $this->setTableName('news');
        $this->hasColumn('ID', 'integer', 4, array(
             'type' => 'integer',
             'length' => 4,
             'fixed' => false,
             'unsigned' => false,
             'primary' => true,
             'autoincrement' => true,
             ));
    }
    public function setUp()
    {
        parent::setUp();
        
    }
}

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Wat ik voor zover begrepen heb is dat bij Doctrine 2 niet meer gebruikt gemaakt voor van een 'base entity' net zoals in ZF e.d. waarbij dus een basemodel extend.

In doctrine 2 hoef je dus niet meer een class te extenden bij het maken van een entity omdat Doctrine 2 gebruik maakt van reflection in PHP en zo alle metadata uitleest.

Er is trouwens commandline wel een tool om alle entities van 1.2 naar 2.0 om te zetten!

Edit: je defineert je tabel eigenschappen dus niet met functies zoals hasColumn etc maar met annotations: http://www.doctrine-proje...erence/basic-mapping.html voor voorbeelden.

[ Voor 19% gewijzigd door Verwijderd op 15-07-2011 13:36 . Reden: linkje ]


Acties:
  • 0 Henk 'm!

  • Reboot
  • Registratie: Januari 2009
  • Laatst online: 29-07 22:18
Ja al mijn classes zoals je in mijn vorige post ziet zijn aangemaakt via CLI. Echter ga ik niet telkens een lokale db aanmaken dus pas en maak ik ze tegenwoordig manueel.

Acties:
  • 0 Henk 'm!

  • ReenL
  • Registratie: Augustus 2010
  • Laatst online: 14-09-2022
@Reboot:
Doctrine 1: Base classe houd changes bij, entities extenden van base;
Doctrine 2: Proxy classe houd changes bij, Proxy classes extenden van entities.

Met doctrine 2 ben je dus ook vrij om je eigen contructor etc te schrijven.

PS: Het is al gezegd, maar bij doctrine 2 php docs gebruiken, veel makkelijker om te leren en uit te leggen :)
Pagina: 1