[OOP] Een CMS, de user classes

Overzicht Reageren

Sponsored by: Vacatures door Monsterboard

Milo S

Milo S

15/04/2013 18:40:29
Quote Anchor link
Beste,

Afgelopen week weer moed kunnen verzamelen om met mijn OOP project verder te gaan. Een CMS.

Ik ben begonnen met het schrijven van een user classe, interface en mapper. Maar voor dat ik alle functies in mijn mapper ga maken ben ik erg benieuwd naar jullie mening. Ben ik zo op de goede weg of hebben jullie een voorkeur op iets anders.

Ik gebruik nu nog MySQLi, omdat de weg naar OOP mij al lastig genoeg gaat, later overschakelen op PDO moet geen probleem zijn.

Ik heb de overbodige functies even verwijderd, ben nu echt alleen nieuwsgierig of dit goed gaat. Bedoel het werkt wel, maar is dit de manier of zit ik fout?

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
<?php
class UserMapper implements MapperInterface  {
    
    protected $id;
    protected $fields;
    
        
    function
read()
    {
        
        /*
        * Query opbouwen, uitvoeren
        */

        $stmt = new mysqli( HOST, USER, PASS, DATB );
        
        $query = "SELECT id, username, password, name, email, phone FROM user";
        
        if( !$result = $stmt->query( $query ) )
        {

            return $stmt->connect_error();            
        }

        else
        {
            while( $record = $result->fetch_array() )
            {

                $User[] = new User( $record['id'], $record['username'], $record['password'], $record['name'], $record['email'], $record['phone'] );
            }
        }

        
    return $User;
    }
}

?>
 
PHP hulp

PHP hulp

28/04/2024 21:14:16
 
Erwin H

Erwin H

15/04/2013 18:50:03
Quote Anchor link
Ik zou 3 dingen anders doen:
1) geen protected properties gebruiken, altijd private. Protected properties kunnen betekenen dat je op later moment geen mogelijkheid meer hebt om zaken aan te passen, zonder ook allerlei afgeleide klassen te moeten aanpassen.

2) je mapper maakt nu zelf connectie met de database en voert een query uit. Dat betekent dat als je zometeen 5 mappers hebt bijvoorbeeld, dat je 5 klassen hebt die direct met de database werken. Wil je dan omschakelen naar PDO dan wordt het wel een probleem, je moet namelijk 5 klassen aanpassen. Er zou er maar een moeten zijn en deze mapper zou dis klas moeten gebruiken om de query uit te voeren. Deze mapper moet verder geen weet hebben van hoe dat allemaal gebeurt.

3) deze mapper geeft nu een array van User objecten terug. Dat betekent dat deze mapper bepaalt hoe de user gegevens gebruikt worden. Wil je zo een nieuwe applicatie bouwen waarin je wel de mapper kunt gebruiken, maar niet het User object, dan moet je dus ook de mapper aanpassen, dat wil je niet. De mapper zou alleen de rauwe data moeten teruggeven, een ander object mag dan uitvogelen in wat voor object die data gestopt wordt.
 
Milo S

Milo S

15/04/2013 18:53:49
Quote Anchor link
Als ik het goed begrijp zal ik in mijn constructor van de MySQLi_Database klasse een Database connectie moeten aanmaken? Of kan ik beter een connect functie toevoegen aan die klasse en dan in een config bestand die functie aanroepen?

Gewoon een simpele array met de data zou dus voldoende zijn? Niet direct in een object stoppen?
Gewijzigd op 15/04/2013 18:56:10 door Milo S
 
Erwin H

Erwin H

15/04/2013 19:04:42
Quote Anchor link
Volgens mij is het het mooiste als je in deze mapper in de constructor een database object meegeeft. Dat database object gebruikt dan nu nog MySQLi (later PDO) om queries uit te voeren. Zodra deze mapper een opdracht krijgt om gegevens op te halen bouwt het de query en vraagt aan het opgeslagen database object om die query uit te voeren en de gegevens terug te geven.

Hoe het database object het allemaal regelt is voor deze mapper niet van belang. Je kan die connectie dus al leggen in de constructor van die database klas, of je maakt de connectie de eerste keer dat de connectie nodig is.

Wil je later dan van MySQLi naar PDO overstappen dan hoef je alleen een nieuwe database klas te bouwen en in je applicatie de ene klas door de andere te vervangen en je bent klaar.

Verder zou ik inderdaad alleen de array met alle gegevens teruggeven. Wil je dan later niet User klas gebruiken om user objecten van te maken, maar UserMetHond klas, dan kan je deze mapper nog gewoon gebruiken.
 
Wouter J

Wouter J

15/04/2013 19:09:13
Quote Anchor link
Goed we gooien er een basis principe van OO in: Elke klasse/method mag maar 1 verantwoordelijkheid hebben, zodra het er meer zijn moet het opgesplitst worden in meerdere klassen/methods

Dan mag jij concluderen wat beter is.

Nog wat andere tips:
- Gebruik voor foutafhandeling exceptions en geef de fout niet terug
- gebruik betere methodnamen. Read, wat betekend dat? Betekend dat dat ik alle users terug krijg of alleen 1 user?
- In tegenstelling tot Erwin vind ik het niet erg dat de mapper al een user maakt. Het is immers een UserMapper. DataMappers hebben 1 doel: Het contact tussen database en object verzorgen. Het object weet dus niet hoe hij gemaakt wordt en de database weet niet hoe zijn gegevens gebruikt worden. Het is juist de mapper zijn taak om deze 2 te verbinden en dus moet hij zowel de database kant als de object kant weten.
Wat ik wel zou doen is een method maken (populate) deze moet dan de raw data omzetten in een object. Hierdoor kun je hem telkens hergebruiken.
- waarom zou een UserMapper een $id property hebben?
 
- Raoul -

- Raoul -

15/04/2013 19:13:10
Quote Anchor link
En ook heel belangrijk: visibility op je methods!
 
Milo S

Milo S

15/04/2013 19:26:27
Quote Anchor link
De klasse mag maar 1 verantwoordelijkheid hebben. Dus alleen de connectie! Ik zorg voor een connectie in de Database klasse. Of zal ik hier slechts een configuratie bestand voor maken, zonder klasses en goede?
Gewoon PDO gebruiken voor de rest. Ik lees nu de tutorial van blanche door en dat moet toch wel redelijk gaan lukken denk ik.

Om alle methodes zo logisch mogelijk een naam te geven kom ik op het volgende uit; createUser, getUser, getUserById, deleteUser en updateUser. Hierbij komt dan wel mijn MapperInterface te vervallen. Ik kan moeilijk een interface methodes als updateUser meegeven als de interface ook gebruikt gaat worden voor pagina's. De conclusie is dus, ik haal de klasnaam User eruit en hou het volgende over; create, getAll, getById, delete en update. Lijkt mij beter?

Voor Wouter zijn punt over de UserMapper valt wat te zeggen, al klinkt dat van Erwin wat vrijer in gebruik?

De id is een soort van "left-over" aan de hand van al mijn probeersels. Ik heb het niet eens opgemerkt...

-------------------------------

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<?php
class UserMapper
{
    public function getAll( $db )
    {

        # Query
        $sql =
        "
        SELECT
            id,
            username,
            password,
            name,
            email,
            phone
        FROM
            user
        "
;
        
        # Query Interpreteren en Controleren door Database
        $stmt = $db->prepare( $sql );
        
        # Query Uitvoeren
        $stmt->execute();
        
        # Result Opvangen en Retouneren d.m.v populate function
        while( $res = $stmt->fetch( PDO::FETCH_ASSOC ) )
        {
            
            return $this->populate( $res );
        }
    }

    
    public function getById( $db, $id )
    {

        # Query
        $sql =
        "
        SELECT
            id,
            username,
            password,
            name,
            email,
            phone
        FROM
            user
        WHERE
            id = '"
.$id."'
        "
;
        
        # Query Interpreteren en Controleren door Database
        $stmt = $db->prepare( $sql );
        
        # Query Uitvoeren
        $stmt->execute( array( (int) $id ) );
        
        # Result Opvangen
        $res = $stmt->fetch( PDO::FETCH_ASSOC );
        
        # Result Retouneren d.m.v populate function
        return $this->populate( $res );
    }

    
    public function populate( array $data )
    {

        $User[] = new User(  $data['id'], $data['username'], $data['name'], $data['email'], $data['phone'] );
        
        return $User;
    }
}

?>


Het gebruik is al volgt:

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
<?php                  
    # UserMapper aanroepen
    $UserMapper = new UserMapper();
    
    # Alle gebruikers ophalen
    $User = $UserMapper->getById( $db, '1' );
    

    $i = 0;
    foreach( $User as $item )
    {

        echo $item->getId().'<br>'.$item->getName().'<br>'.$item->getUsername().'<br>'.$item->getEmail().'<br>'.$item->getPhone();
    }

?>


Ik vind het dan nu alleen lastig een connectie op te zetten en het daadwerkelijk te gebruiken. Hier ga ik morgen weer even mee stoeien!
Bovenstaande code werkt als het gaat om alleen ophalen met het ID, alles ophalen wil hij maar niet doen. Ik ben nog bezig, maar het gaat niet zonder tegenslagen. Jullie advies is altijd welkom.
Gewijzigd op 16/04/2013 09:17:52 door Milo S
 
Wouter J

Wouter J

16/04/2013 22:52:28
Quote Anchor link
Geef de $db niet bij elke method mee. Injecteer hem via de constructor in de klasse waardoor je hem dan altijd kunt gebruiken.
 
Milo S

Milo S

16/04/2013 23:02:23
Quote Anchor link
Ik vraag mij sowieso af of ik niet een db handler moet gebruiken? Zodat ik makkelijk kan veranderen van database..
 
- Raoul -

- Raoul -

16/04/2013 23:49:06
Quote Anchor link
Probeer je ook even te houden aan de PSR standaarden.
Gewijzigd op 16/04/2013 23:49:32 door - Raoul -
 
Milo S

Milo S

17/04/2013 08:10:10
Quote Anchor link
Wouter, als ik mijn database variabelen in mijn constructor opneem, wil niets meer werken. Daarom had ik het op deze manier gedaan. Zou jij mij wellicht kunnen wijzen op iets dat er nog ontbreekt?

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
<?php
public $db;
    
public function __constructor($db)
{

        $this->db = $db;
}


public function getAll()
{

    # Query
    $sql = "SELECT id, username, password, name, email, phone FROM user";
    
    # Query Interpreteren en Controleren door Database
    $stmt = $this->db->prepare($sql);
    
    # Query Uitvoeren
    $stmt->execute();
    
    # Result Opvangen en Retouneren d.m.v populate function
    while ($res = $stmt->fetch(PDO::FETCH_ASSOC))
    {

        $User_lib[] = $this->populate($res);
    }

    
return $User_lib;
}

?>


Wat er nu theoretisch gezien moet gebeuren is het volgende;
Database connectie wordt aangemaakt en de UserMapper wordt aangeroepen. Usermapper zijn constructor pakt de $db variabele op uit de connectie en maakt hem bruikbaar voor in de class. Maar kennelijk ontbreekt er iets. Op het net zag ik dingen als global maken, maar dat werd toch ook weer behoorlijk afgezeken?

----------------------

Ik heb ook was topic hier nog gelezen en aan de hand van de kennis die ik nu heb verzameld een UML gemaakt, liever gezegd aangepast.
Het UML

Met bovenstaande plan kan ik aan de gang gaan met coderen. Ik gebruik PDO als communicatie naar de database. Hierdoor zou ik bewijs van spreken alleen in de connectie hoeven zeggen dat ik een andere database zou willen hebben in plaats van MySql.

Ik kan alles doen wat betrekking heeft tot mijn nieuws, pagina's, gebruikersrollen en gebruikers zelf. Dingen bij voegen is geen probleem, slechts de benodigde classes aanmaken en koppelen aan al bestaande classes. Hiervoor hoef ik zo min mogelijk in mijn huidige classes te knoeien.

Ik had over een database storage gelezen in het topic van Jasper. Nu dacht ik dat te gaan gebruiken op de volgende manier: http://www.youtube.com/watch?v=uF-L7ympvfw
Alleen dan met PDO. Dit scheelt heel wat keer die Query tikken in al mijn mappers? Daarbij kan ik ook cookies, sessies etc. Door middel van zo'n storage gaan behandelen.

Zien jullie ergens nog haken en ogen aan zitten?
Gewijzigd op 17/04/2013 09:05:27 door Milo S
 
Nic W

Nic W

18/04/2013 12:35:12
Quote Anchor link
Hoi Milo,

Moet het niet __construct($db) zijn in plaats van __constructor($db)? Mogelijk is dat de reden dat je code niet werkt.
 
Ozzie PHP

Ozzie PHP

18/04/2013 12:37:46
Quote Anchor link
Zo te zien heb je gelijk Nic.

(Als je error reporting aanzet krijg je dit soort fouten ook te zien.)
 
Ward van der Put
Moderator

Ward van der Put

18/04/2013 13:14:12
Quote Anchor link
Doe je in getAll() niet veel te veel? Je maakt een kopie van de databasetabel user inclusief persoonsgegevens en wachtwoorden en stopt die in een public array...
 
Milo S

Milo S

18/04/2013 13:20:31
Quote Anchor link
Constructor moest inderdaad construct zijn hier kwam ik gister achter. Achja...

Uiteraard moet wachtwoord daar niet meer bij, maar derest kan toch gewoon publiek zijn?
 
YzRz -

yzRz -

18/04/2013 19:30:45
Quote Anchor link
Je kunt ook een abstract class maken die je mapperinterface implementeert. Daarin zou je de constructor kunnen maken waarmee je de database meegeeft. Dan hoef je die constructor niet in elke mapper aan te maken.

Als je niet meer dan 1 database connectie binnen je applicatie gaat gebruiken (wat in 99% van de gevallen zo is), kun je er ook over denken om de connectie via een static variable mee te geven. Je levert wat flexibiliteit in op het gebied van testen maar je code wordt veel overzichtelijker
 

18/04/2013 19:44:46
Quote Anchor link
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
<?php

/**
 * Waarom gebruik je populate? PDO kan dit toch voor jou doen? Je moet
 * er dan alleen voor zorgen dat de velden in de database ook bestaan voor de gebruiker.
 *
 * Eventueel zou je ook gebruik kunnen maken van __set(string $name, mixed $value), maar normaal
 * gezien hoeft dat niet tenzei je je velden zou opslaan in een variabele $values of iets dergelijks.
 *
 * Ik heb even de connectie weggelaten.
 */

$db = '{pdoDatabaseConnection}';

$sth = $db->prepare('SELECT {velden} FROM users');
$sth->execute();

return $sth->fetchAll(PDO::FETCH_CLASS, 'User');

?>


Toevoeging
Zelf gebruik ik een database manager met alle connecties voor de applicatie omdat er meestal meerdere zijn. Deze kan ik dan gewoon van de database manager krijgen en daarmee aan de slag gaan.

Eventueel zou je een data mapper kunnen maken die een constructor heeft om de database in $db te laden.

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
<?php

/**
 * DataMapper class. Dit zorgt ervoor dat je de database
 * kunt aanroepen.
 */

class DataMapper {

    private $db;

    /**
     * De constructor. Hier wordt de database geladen.
     *
     * @param string $db
     */

    public function __construct($db = 'default') {
    
        /**
         * Roep de database manager aan om de database in handen te
         * krijgen.
         */

        $this->db = DatabaseManager::get($db);

    }

}


/**
 * Dan heb je nog de user mapper die de database aanvult.
 */

class UserMapper extends DataMapper {

    /**
     * Hier kun je nu gewoon $this->db gebruiken.
     */


}

?>

Anders kun je zoals Wouter als zei voor elke mapper de database via de constructor injecteren.
Gewijzigd op 18/04/2013 20:04:21 door
 
Milo S

Milo S

19/04/2013 08:30:40
Quote Anchor link
Ik heb het nu met een DatabaseStorage opgelost, hier wordt de uiteindelijke query uitgevoerd en geretourneerd. Zo kun je de UserMapper ook nog gebruiken om eventueel een sessie aan te maken met de SessionStorage of een CookieStorage. Net wat jij makkelijk vindt.

Nu vind ik jou voorbeeld wel heel interessant... Ik ben dus nog lang niet klaar met het uitpluizen van mijn uiteindelijke UML. Zo veel dingen waar je rekening mee moet houden en waar je de mogelijkheid nog niet eens van weet. Ik ben er dan ook van overtuigt dat mijn eerste OO applicatie niet 100% is wat het moet zijn. Ik doe hard me best!
Bij mij zou het dan gaan om een connectie tussen de DatabaseStorage en de DatabaseManager.

Wat betreft je eerste voorbeeld, dat bedoel ik nou dingen waarvan je de mogelijkheid nog niet eens wist. Ik denk alleen wel dat je dan beperkter wordt in je doen en laten binnen de applicatie. Zo slaat hij altijd alles op in een User object, of zie ik dit fout? Ik ga er in iedergeval naar kijken, of ik het echt ga gebruiken durf ik nog niet te zeggen.

Momenteel ben ik bezig met een ErrorHandler om zou alle fouten netjes op te vangen.

Jullie horen van mij! In ieder geval bedankt voor alle hulp tot dus ver, jullie hebben enorm geholpen!!
Gewijzigd op 19/04/2013 08:30:55 door Milo S
 



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.