[PHP-OO] Hoe omgaan met associaties en database toegang?

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • MrData
  • Registratie: Februari 2000
  • Laatst online: 25-07-2022
Ik zit met OO-ontwerp / efficientie kwestie waarvan ik hoop dat de experts hier mij in de goede richting kunnen sturen :)

In PHP(5) ben ik bezig met een relatief simpel systeem, een 7 a 8 classes in totaal. Een aantal classes zijn geassocieerd, bijvoorbeeld Group en User. Group bevat een x aantal users.

Nu wil binnen de applicatie een instance van Group aan kunnen maken en daarbij de Users (objecten) kunnen bijhouden en opvragen in een array. Dus zoiets in een simpele weergave:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Group() {

   public $naam;
   public $users;
 
   public function __construct() {
      //
   }

}

class User() {
   $public naam;
}


Vraag is nu: hoe ga ik nu om met het vullen van de $users array met user objecten?
Roep ik de database aan in de constructor van Group en vul de array daar, zodat als er een nieuw Group object wordt aangemaakt de bijbehorende users meteen beschikbaar zijn? Dit lijkt me nog te overzien als ik alleen kijk naar groups en users, maar als ik het zelfde principe aan hou bij de User class en daar ook weer in de constructor de database aanroep en alle geassocieerde items ophaal (die op hun beurt ook weer alle geassocieerde items ophalen etc.) krijg je een kettingreactie die misschien niet gewild is. Soms wil ik alleen wat weten over een bepaalde group, maar soms wil ik een group object hebben met daarin alle bijbehorende users. Dan lijkt met me niet nodig een stortvloed aan queries te veroorzaken die data ophalen die misschien niet eens gebruikt wordt.

Of maak ik een functie binnen de Group class die op commando de $users array vult en teruggeeft:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
class Group() {

   public $naam;
   public $users;
 
   public function __construct() {
      //
   }
   
   public function getUsers() {
      // haal alle users van deze group op uit de database en vul $this->users
   }
}


Het nadeel hiervan lijkt mij dat als er daadwerkelijk gewerkt moet worden met meerdere associaties je meerdere loops moet gaan construeren om de data gevuld te krijgen. Stel dat van een user ook een geschiedenis (ik noem maar wat) bijgehouden wordt. Om dan alles op te halen:


PHP:
1
2
3
4
5
$testgroep = new Group();
 
foreach ($testgroep->getUsers as $testuser) {
   $testuser->getHistory();
}


En dan voor elke associatie die erbij komt weer een loop daarbinnen. Iemand hier suggesties over of misschien een design pattern waar ik wat mee kan?

"We set sail on this new sea because there is new knowledge to be gained and new rights to be won." - John F. Kennedy - USS Valiant NCC-74210 dedication plaque


Acties:
  • 0 Henk 'm!

  • SchizoDuckie
  • Registratie: April 2001
  • Laatst online: 18-02 23:12

SchizoDuckie

Kwaak

Zoek even in het rond op PHP O/R mapper, dat is eigenlijk wat je nu aan't doen bent en er is een hoop over geschreven. Ik heb zelf ook zoiets in elkaar geklust, zie http://www.schizofreend.nl/Pork.dbObject/

Wat ik dus eigenlijk doe is in de constructor alleen de relaties definieren, en in bijvoorbeeld een display() functie alle nodige meuk bij elkaar vinden ( in mijn geval met $group->Find('User') ) en alleen dat spul outputten :)
Er leuk effectje is het inderdaad op het moment dat je al je relaties automagisch laat vullen en die hun relaties ook laat vullen etc etc etc :P Ik heb nog nooit zo snel apache zo hard op zn bek zien gaan :Y)

edit:
oh wacht ik zie nu wat je verder nog wil


Wat ik ook heb als optie in mijn O/R mapper is de mogelijkheid om een query te doen, die een aantal rows te laten fetchen en die automagisch om te zetten naar een o/r mapped object. Dan kan je in 1x bijv. alle gebruikers uit $group binnen trekken en met een factory ofzo deze chachen en teruggeven aan het object wat ze nodig heeft.

[ Voor 23% gewijzigd door SchizoDuckie op 28-04-2007 18:48 ]

Stop uploading passwords to Github!


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 00:04
De vraag die je je moet stellen is: heb ik altijd 'direct' toegang nodig tot de history van een user; of heb ik altijd direct toegang nodig tot de users van een group ?
Mij lijkt het niet. Daarom zou je met 'lazy loading' kunnen werken.

Een andere manier van werken, is dat je classes zoals 'user' , 'group', whatever op zich eigenlijk nooit direct connectie maken met de DB, maar dat dit via een andere class (repository) gaat. Bv, GroupRepository, die dan een method 'GetGroup' heeft, en die je dan een group object returned.
Dan kan je ook een User repository hebben, die een GetUser method heeft, maar ook een method 'GetUsersWithinGroup ( group )' of 'GetUsersWithinGroup(int groupId)' heeft. Die method returned dan een collectie van users.

Wat je dan aan de andere kant misschien wel meer / vlugger nodig zullt hebben, is een collectie binnen je user-object die de groups bevat waartoe een user behoort. Die kan je dan weer wel als een 'associatie' binnen je user class hebben (en die kan je dan ook alsnog mbhv lazy loading ophalen [pas als je ze nodig hebt dus]).

[ Voor 18% gewijzigd door whoami op 28-04-2007 18:58 ]

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • MrData
  • Registratie: Februari 2000
  • Laatst online: 25-07-2022
Dank voor de reacties!

Ik was in het ontwerp al zo ver dat ik voor Group en User een aantal statische factory-methods (hoop dat ik de term goed gebruik :)) had gedefinieerd die niets anders doen dan object-instanties teruggeven aan de hand van een id. Misschien wat minder netjes dan hier aparte classes voor te gebruiken.

Mijn zorg was meer wat te doen met gerelateerde objecten, maar ik zie wel in dat het automatisch vullen van de relaties database servers onderuit kan gaan trekken dus ik denk dat ik inderdaad ga voor de 'lazy loading' aanpak.

Ik was ook de hoogte van dergelijke SchizoDukie (wel bedankt voor de link!), maar ik wil voor dit project toch meer handmatig doen, voornamelijk voor mijn eigen educatie :)

"We set sail on this new sea because there is new knowledge to be gained and new rights to be won." - John F. Kennedy - USS Valiant NCC-74210 dedication plaque


Acties:
  • 0 Henk 'm!

  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
whoami schreef op zaterdag 28 april 2007 @ 18:57:
Een andere manier van werken, is dat je classes zoals 'user' , 'group', whatever op zich eigenlijk nooit direct connectie maken met de DB, maar dat dit via een andere class (repository) gaat. Bv, GroupRepository, die dan een method 'GetGroup' heeft, en die je dan een group object returned.
Dan kan je ook een User repository hebben, die een GetUser method heeft, maar ook een method 'GetUsersWithinGroup ( group )' of 'GetUsersWithinGroup(int groupId)' heeft. Die method returned dan een collectie van users.
Wat ik persoonlijk erg prettig vind werken is een variant hierop, waarbij je de methods op de 'repository' class een extra argument meegeeft dat omschrijft welke associaties erbij geladen moeten worden. Het voordeel is dan dat je niet allemaal verschillende varianten van dezelfde methode krijgt (GetUsers, GetUsersWithGroups, GetUsersWithCategories, GetUsersWithGroupsAndCategories, etc.), die alleen verschillen in de gegevens die er nog bij worden geladen.

In plaats daarvan krijg je dan de volgende syntax:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[FlagsAttribute]
public enum SchemaRelations{
   None = 0x00,
   UsrGroups= 0x01
   UsrCategories = 0x02,
   UsrAll = UsrGroups | UsrCategories,
   GrpUsers = 0x04,
   //etc.
}

public UserCollection GetUsersWithinGroup(Group grp, SchemaRelations loadGraph){ 
   /**/ 
}

//Load users for 'myGroup', where each User has its Categories loaded:
UserCollection users = repository.GetUsersWithinGroup(myGroup, SchemaRelations.UsrCategories);