Werken met meerdere objecten van hetzelfde type
Ter illustratie:
Ik haal een lijst met gebruikers uit de database, en in plaats van per gebruiker een object aan te maken moet er toch een manier zijn om dit netter te doen.
Vooral bij grote hoeveelheden data (10000+) werkt het aanmaken van objecten wel erg vertragend.
Kan me geen design pattern bedenken dat dit probleem kan aanpakken.
Hoor graag jullie ideeën en meningen hierover.
Ik gebruik Doctrine 2 die dat allemaal regelt voor je dus hoef je daar ook zelf niets voor te bouwen :)
Daarmee kan ik wel een ruwe set met database gegevens krijgen (een object per rij).
Echter lijkt het me netter om deze data om te zetten naar een logisch object.
Gebruikers gegevens in een gebruiker object
adres gegevens in een adres object
etc.
Werkt Doctrine 2 anders dan hierboven beschreven?
Toevoeging op 03/08/2011 11:31:56:
Zit zelf te denken aan http://www.php.net/manual/en/language.oop5.iterations.php, maar kan geen passend voorbeeld vinden.
Geef eens anders een code voorbeeldje hoe je het zou willen gebruiken? :)
Flyweight pattern, maar in welke situatie heb je daadwerkelijk meer dan 100 objecten tegelijkertijd in je geheugen?
Je probleem doet me denken aan Jelmer rrrr op 03/08/2011 14:36:58:
Je probleem doet me denken aan Flyweight pattern, maar in welke situatie heb je daadwerkelijk meer dan 100 objecten tegelijkertijd in je geheugen?
Als ik alle gebruikers op het scherm wil tonen (n=5000).
De vraag is ook of er een andere manier is dan het aanmaken van 5000 objecten.
Kan de resultaten uit de database ook aan een lijst object koppelen, maar hoe blijf ik in de view dan gewoon OOP gebruiken.
foreach($objectList as $object){
echo $object->getVoornaam();
}
Ik ben me ervan bewust dat het maken van 5000 objecten overkill is, maar het vinden van een gestructureerde oplossing wil nog niet echt lukken.
Kees Schepers op 03/08/2011 14:23:21:
Geef eens anders een code voorbeeldje hoe je het zou willen gebruiken? :)
Code voorbeeld:
Code (php)
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
if (is_numeric($id) && $id > 0) {
$select = $this->getDao()->select();
$select->where('methode_id = ?', $id);
$select->order('datum DESC');
$rowset = $this->getDao()->fetchAll($select);
if (null != $rowset) {
$result = $this->createObjectArray($rowset);
}
}
$select = $this->getDao()->select();
$select->where('methode_id = ?', $id);
$select->order('datum DESC');
$rowset = $this->getDao()->fetchAll($select);
if (null != $rowset) {
$result = $this->createObjectArray($rowset);
}
}
createObjectArray function:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
protected function createObjectArray(Zend_Db_Table_Rowset_Abstract $rowset)
{
$result = array();
foreach ($rowset as $row) {
$model = new Notes();
$this->populate($row, $model);
if (isset($row['id'])) {
$result[$row['id']] = $model;
} else {
$result[] = $model;
}
}//endforeach;
return $result;
}
{
$result = array();
foreach ($rowset as $row) {
$model = new Notes();
$this->populate($row, $model);
if (isset($row['id'])) {
$result[$row['id']] = $model;
} else {
$result[] = $model;
}
}//endforeach;
return $result;
}
De populate functie
Code (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
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
private function populate($row, $model)
{
// zet de purifier uit om overhead te voorkomen
if (isset($row['id'])) {
$model->setId($row['id']);
}
if (isset($row['type'])) {
$model->setType($row['type']);
}
if (isset($row['tekst'])) {
$model->setLog($row['tekst']);
}
if (isset($row['methode_id'])) {
$model->setSurveyMethodId($row['methode_id']);
}
if (isset($row['klant_id'])) {
$model->setCustomerId($row['klant_id']);
}
if (isset($row['gebruiker_aangemaakt_tekst'])) {
$model->setCreatedByUser($row['gebruiker_aangemaakt_tekst']);
}
if (isset($row['gebruiker_gewijzigd_tekst'])) {
$model->setUpdatedByUser($row['gebruiker_gewijzigd_tekst']);
}
if (isset($row['gebruiker_aangemaakt'])) {
$model->setCreatedByUserId($row['gebruiker_aangemaakt']);
}
if (isset($row['gebruiker_gewijzigd'])) {
$model->setUpdatedByUserId($row['gebruiker_gewijzigd']);
}
if (isset($row['datum_aangemaakt'])) {
$model->setDateCreated($row['datum_aangemaakt']);
}
if (isset($row['datum_gewijzigd'])) {
$model->setDateUpdated($row['datum_gewijzigd']);
}
$model->clearMapper();
return $model;
}
{
// zet de purifier uit om overhead te voorkomen
if (isset($row['id'])) {
$model->setId($row['id']);
}
if (isset($row['type'])) {
$model->setType($row['type']);
}
if (isset($row['tekst'])) {
$model->setLog($row['tekst']);
}
if (isset($row['methode_id'])) {
$model->setSurveyMethodId($row['methode_id']);
}
if (isset($row['klant_id'])) {
$model->setCustomerId($row['klant_id']);
}
if (isset($row['gebruiker_aangemaakt_tekst'])) {
$model->setCreatedByUser($row['gebruiker_aangemaakt_tekst']);
}
if (isset($row['gebruiker_gewijzigd_tekst'])) {
$model->setUpdatedByUser($row['gebruiker_gewijzigd_tekst']);
}
if (isset($row['gebruiker_aangemaakt'])) {
$model->setCreatedByUserId($row['gebruiker_aangemaakt']);
}
if (isset($row['gebruiker_gewijzigd'])) {
$model->setUpdatedByUserId($row['gebruiker_gewijzigd']);
}
if (isset($row['datum_aangemaakt'])) {
$model->setDateCreated($row['datum_aangemaakt']);
}
if (isset($row['datum_gewijzigd'])) {
$model->setDateUpdated($row['datum_gewijzigd']);
}
$model->clearMapper();
return $model;
}
Gewijzigd op 03/08/2011 14:55:27 door Mark Eilander
Je kunt natuurlijk ook de model notes de arrayacces iterface laten implementeren. Dan maak je een partialloop en ipv $object->getVoornaam() doe je bijv. $object['voornaam'].
Code (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
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
<?php
class UserStore
{
public function findAll()
{
$select = $this->getDao()->select();
$select->order('datum DESC');
$rowset = $this->getDao()->fetchAll($select);
if ($rowset === null)
return null;
return new UserIterator($rowset);
}
}
class UserIterator implements Iterator
{
public function __construct(Zend_Db_Table_Rowset_Abstract $rowset)
{
$this->rows = $rowset;
}
// hier moet je dan ook alle andere methods van de Iterator interface implementeren,
// maar je kan bijna altijd direct $this->rowset->$method() aanroepen.
public function current()
{
$row = $this->rows->current();
return $this->buildUser($row);
}
protected function buildUser(array $data)
{
$user = new User();
$user->setID($data['row_id']);
// ...
return $user;
}
}
$users = new UserStore(...);
foreach ($users->findAll() as $user) {
// tada, User object wordt aangemaakt in UserIterator::current -> UserIterator::build
echo $user->getVoornaam();
// $user wordt overschreven met nieuwe user, oude User object wordt vrijgegeven
}
?>
class UserStore
{
public function findAll()
{
$select = $this->getDao()->select();
$select->order('datum DESC');
$rowset = $this->getDao()->fetchAll($select);
if ($rowset === null)
return null;
return new UserIterator($rowset);
}
}
class UserIterator implements Iterator
{
public function __construct(Zend_Db_Table_Rowset_Abstract $rowset)
{
$this->rows = $rowset;
}
// hier moet je dan ook alle andere methods van de Iterator interface implementeren,
// maar je kan bijna altijd direct $this->rowset->$method() aanroepen.
public function current()
{
$row = $this->rows->current();
return $this->buildUser($row);
}
protected function buildUser(array $data)
{
$user = new User();
$user->setID($data['row_id']);
// ...
return $user;
}
}
$users = new UserStore(...);
foreach ($users->findAll() as $user) {
// tada, User object wordt aangemaakt in UserIterator::current -> UserIterator::build
echo $user->getVoornaam();
// $user wordt overschreven met nieuwe user, oude User object wordt vrijgegeven
}
?>
?>
Kan het ook oplossen door de magic method __get te gebruiken.
Gewijzigd op 03/08/2011 15:51:51 door Mark Eilander
Code (php)
Maar hoe meer "magic" je in je programma stopt, hoe idioter het zich gaat gedragen. Bijvoorbeeld, deze code zou niet werken zoals je in eerste instantie zou verwachten:
Moet je niet even terug naar de basis van je probleem? Waarom ga je in godsnaam 5000 gebruikers op je scherm tonen? Webdesign heeft te maken met usability (gebruiksvriendelijkheid) en 5000 users op 1 pagina tonen is NOOIT gebruiksvriendelijk.
Waarom toon je niet bijvoorbeeld standaard alle gebruikers met beginletter A en dan aan de bovenkant van de pagina toon je de letters van het alfabet. Druk je op de letter B dan krijg je alle gebruikers met beginletter B enzovoorts.
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
Aad
Adriaan
Amber
Antoine
Anton
Antonie
Astrid
enz.
Ozzie PHP op 03/08/2011 17:41:18:
Even afgezien van het technische gebrabbel wat ik lastig te volgen vind...
Moet je niet even terug naar de basis van je probleem? Waarom ga je in godsnaam 5000 gebruikers op je scherm tonen? Webdesign heeft te maken met usability (gebruiksvriendelijkheid) en 5000 users op 1 pagina tonen is NOOIT gebruiksvriendelijk.
Waarom toon je niet bijvoorbeeld standaard alle gebruikers met beginletter A en dan aan de bovenkant van de pagina toon je de letters van het alfabet. Druk je op de letter B dan krijg je alle gebruikers met beginletter B enzovoorts.
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
Aad
Adriaan
Amber
Antoine
Anton
Antonie
Astrid
enz.
Moet je niet even terug naar de basis van je probleem? Waarom ga je in godsnaam 5000 gebruikers op je scherm tonen? Webdesign heeft te maken met usability (gebruiksvriendelijkheid) en 5000 users op 1 pagina tonen is NOOIT gebruiksvriendelijk.
Waarom toon je niet bijvoorbeeld standaard alle gebruikers met beginletter A en dan aan de bovenkant van de pagina toon je de letters van het alfabet. Druk je op de letter B dan krijg je alle gebruikers met beginletter B enzovoorts.
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
Aad
Adriaan
Amber
Antoine
Anton
Antonie
Astrid
enz.
Ik ben het helemaal met je eens, helaas zijn er hogere machten (werkgever) die heeft bepaald dat alles op een scherm getoond moet worden. Ondanks dat 70% niet zichtbaar is.
Toevoeging op 04/08/2011 13:27:28:
Jelmer rrrr op 03/08/2011 16:28:17:
Inderdaad, je zou UserIterator kunnen gebruiken als iterator-object en als user-object tegelijkertijd, iets a la:
Maar hoe meer "magic" je in je programma stopt, hoe idioter het zich gaat gedragen. Bijvoorbeeld, deze code zou niet werken zoals je in eerste instantie zou verwachten:
Code (php)
Maar hoe meer "magic" je in je programma stopt, hoe idioter het zich gaat gedragen. Bijvoorbeeld, deze code zou niet werken zoals je in eerste instantie zou verwachten:
Heb bovenstaande geimplementeerd met de magic method get en dit werkt perfect!
Heb nog geen lazy loading toegepast, maar nu al duurt het laden van 10000 rijen minder dan een seconden. Hetgeen het meeste tijd kost is het genereren van de pagina zelf.
Iedereen bedankt voor de hulp, ik ga morgen het ontwerp aanpassen met jullie opmerkingen erin verwerkt.
Waarom zou je in dit geval niet gewoon oop achterwege laten en met de gewone fetch functies en arrays werken?