Werken met meerdere objecten van hetzelfde type

Overzicht Reageren

Sponsored by: Vacatures door Monsterboard

Mark Eilander

Mark Eilander

03/08/2011 10:57:19
Quote Anchor link
Heeft iemand tips voor een nette en goed manier voor het gebruik van meerder 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.
 
PHP hulp

PHP hulp

19/04/2024 22:12:29
 
Kees Schepers

kees Schepers

03/08/2011 11:05:53
Quote Anchor link
Wat gebruik je nu om data op te halen uit de database? Dat is misschien wel zo makkelijk om te weten om te bepalen wat je het beste kunt doen :)

Ik gebruik Doctrine 2 die dat allemaal regelt voor je dus hoef je daar ook zelf niets voor te bouwen :)
 
Mark Eilander

Mark Eilander

03/08/2011 11:11:06
Quote Anchor link
Maak zoveel mogelijk gebruik van het Zend Framework.
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.
 
Kees Schepers

kees Schepers

03/08/2011 14:23:21
Quote Anchor link
Geef eens anders een code voorbeeldje hoe je het zou willen gebruiken? :)
 
Jelmer -

Jelmer -

03/08/2011 14:36:58
Quote Anchor link
Je probleem doet me denken aan Flyweight pattern, maar in welke situatie heb je daadwerkelijk meer dan 100 objecten tegelijkertijd in je geheugen?
 
Mark Eilander

Mark Eilander

03/08/2011 14:43:27
Quote Anchor link
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)
PHP script in nieuw venster Selecteer het PHP script
1
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);
    }
}

createObjectArray function:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
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;
    }

De populate functie
Code (php)
PHP script in nieuw venster Selecteer het PHP script
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
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;
    }
Gewijzigd op 03/08/2011 14:55:27 door Mark Eilander
 
Kees Schepers

kees Schepers

03/08/2011 14:59:15
Quote Anchor link
Oke zover ik nu begrijp heb je dan een array met 5000 items en elk item is een object. Maar wat is hier de bedoeling van dan?

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'].
 
Jelmer -

Jelmer -

03/08/2011 15:14:10
Quote Anchor link
Of je maakt het fetchen lazy: je bouwt pas een instantie van het object op wanneer het nodig is. Met iterators, waar je zelf al naar verwees, is dat goed te doen.
Code (php)
PHP script in nieuw venster Selecteer het PHP script
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
<?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
}
?>

?>
 
Mark Eilander

Mark Eilander

03/08/2011 15:51:28
Quote Anchor link
Het aanmaken van een user object is geen noodzaak.
Kan het ook oplossen door de magic method __get te gebruiken.
Gewijzigd op 03/08/2011 15:51:51 door Mark Eilander
 
Jelmer -

Jelmer -

03/08/2011 16:28:17
Quote Anchor link
Inderdaad, je zou UserIterator kunnen gebruiken als iterator-object en als user-object tegelijkertijd, iets a la:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
UserIterator implements Iterator
{
    private $current;

    public function current()
    {

        $this->current = $this->rows->current();
        return $this;
    }


    public function __get($key)
    {

        return $this->current[$key];
    }
}

?>


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)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

foreach ($users->findAll() as $user)
{

    if (!isset($prev_user) || $user->voornaam[0] != $prev_user->voornaam[0])
        echo '<h2>' . $user->voornaam[0] . '</h2>';

    echo '<a href="user.php?id=' . $user->id . '">' . $user->voornaam . '</a>';

    $prev_user = $user;
}


?>
 
Ozzie PHP

Ozzie PHP

03/08/2011 17:41:18
Quote Anchor link
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.
 
Mark Eilander

Mark Eilander

04/08/2011 08:41:41
Quote Anchor link
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.


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:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
UserIterator implements Iterator
{
    private $current;

    public function current()
    {

        $this->current = $this->rows->current();
        return $this;
    }


    public function __get($key)
    {

        return $this->current[$key];
    }
}

?>


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)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

foreach ($users->findAll() as $user)
{

    if (!isset($prev_user) || $user->voornaam[0] != $prev_user->voornaam[0])
        echo '<h2>' . $user->voornaam[0] . '</h2>';

    echo '<a href="user.php?id=' . $user->id . '">' . $user->voornaam . '</a>';

    $prev_user = $user;
}


?>


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.
 
Pim -

Pim -

04/08/2011 15:49:01
Quote Anchor link
Waarom zou je in dit geval niet gewoon oop achterwege laten en met de gewone fetch functies en arrays werken?
 



Overzicht Reageren

 
 

Om de gebruiksvriendelijkheid van onze website en diensten te optimaliseren maken wij gebruik van cookies. Deze cookies gebruiken wij voor functionaliteiten, analytische gegevens en marketing doeleinden. U vindt meer informatie in onze privacy statement.