OOP model in class

Overzicht Reageren

Sponsored by: Vacatures door Monsterboard

Pagina: « vorige 1 2

Ozzie PHP

Ozzie PHP

14/02/2019 17:01:38
Quote Anchor link
Oké, das op zich helder, maar ik vraag me af hoe je dan het e-mailadres ophaalt.

Als ik het zo zie, dan lijkt het nog steeds alsof er geen aparte User class is, maar enkel een mapper.
 
PHP hulp

PHP hulp

07/05/2024 02:39:20
 
Ward van der Put
Moderator

Ward van der Put

15/02/2019 11:01:17
Quote Anchor link
Jawel, de UserMapper retourneert een User wanneer je een gebruiker opvraagt. Omgekeerd accepteert de UserMapper ook een User-object wanneer je het wilt opslaan.

Een data mapper "mapt" eigenschappen van een object naar kolommen in een database(tabel) en vice versa. Het object zelf weet niet waar of hoe het wordt opgeslagen: dat is de verantwoordelijkheid van de mapper.

Dat schaalt flexibel omdat je veel gemakkelijker databases kunt vervangen door andere databases of bijvoorbeeld door een API. Dat gaat allemaal voorbij aan het User-object en alle andere functionaliteit die User-objecten gebruikt. Als die logica echter allemaal "in" het User-object zelf zit, heb je een enorme berg werk wanneer je bijvoorbeeld MySQL-queries moet herschrijven naar API-calls.

Een voorbeeld van een UserMapper die een User-class afhandelt:

https://github.com/domnikl/DesignPatternsPHP/blob/master/Structural/DataMapper/UserMapper.php

Als je het nog eenvoudiger en directer wilt doen, kan dat overigens met het active record pattern. Daarbij komt het User(Model) overeen met één record in een databasetabel:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
<?php
$user
= new User();
$user->name  = 'Jan';
$user->email = '[email protected]';
$user->save();
?>
 
Ozzie PHP

Ozzie PHP

15/02/2019 11:24:56
Quote Anchor link
Ah oke ... ietsje duidelijker wat betreft die mapper. Maar wordt dan de daadwerkelijke query ingesteld?

SELECT * FROM users WHERE id = 1
 
Ward van der Put
Moderator

Ward van der Put

15/02/2019 11:45:04
Quote Anchor link
Dat kan en is het eenvoudigst, maar aangezien de UserMapper het enige object is dat weet hoe zowel het User-model enerzijds als de User-tabel anderzijds eruitzien, kan de data mapper ook gerichter te werk gaan dan met alleen een SELECT *. Dat is bijvoorbeeld al gauw het geval wanneer je meer dan één tabel gebruikt voor data die wel gerelateerd zijn aan de gebruiker maar daarmee geen directe één-op-één-relatie hebben.
 
Ozzie PHP

Ozzie PHP

15/02/2019 12:11:35
Quote Anchor link
>> Dat kan en is het eenvoudigst ...

Uhm, wát kan?

Mijn vraag was waar de queries worden ingesteld. Iemand zei eerder dat een model/mapper niet weet of hij met een database of bijv. Xml-bestand te maken heeft. Maar ergens moeten toch de queries worden ingesteld.
 
Ward van der Put
Moderator

Ward van der Put

15/02/2019 12:45:43
Quote Anchor link
De data mapper voert de queries uit. Of roept een API aan. Of maakt een XML-bestand. Het model zelf niet, omdat het niet weet waar en hoe het wordt opgeslagen. En eigenlijk weet het model niet eens dát het wordt opgeslagen.
 
Ozzie PHP

Ozzie PHP

15/02/2019 14:10:59
Quote Anchor link
Nu ben ik je even kwijt. Ik zie in jouw voorbeeldcode helemaal geen model?? :-s

Waarom zou je de queries niet in een model zetten, en als je vervolgens je data ergens anders vandaan wilt halen een ander model toepassen om de data op te halen?

Even heel simplistisch:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
<?php

if ($user_in_database) {
  $model = new UserModelDB($db);
}
else {
  $model = new UserModelXML($xml);
}


$user = new User($model);

?>
 
Ward van der Put
Moderator

Ward van der Put

15/02/2019 14:48:26
Quote Anchor link
User is het model. De data mapper haalt dat met een @return User ergens vandaan:

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
class UserMapper
{
    /**
     * @param int $id
     *
     * @return User
     */

    public function findById(int $id): User
    {
        // ...
    }
}

?>


De verwarring ontstaat mede door jouw eigen naamgeving: je hebt een "UserModel", dat duidelijk een model is, maar daarnaast een "User" waarvan niet in een oogopslag duidelijk is wat het dan is. Geen model kennelijk, maar ook niet expliciet iets anders.

Om onduidelijke redenen vind je dat je het UserModel in een User moet injecteren. Kun je eens wat code van die User laten zien, zodat we die redenen begrijpen?
 
Ozzie PHP

Ozzie PHP

15/02/2019 15:05:59
Quote Anchor link
Wellicht zit de verwarring inderdaad in de naamgeving.

Even heel simpel ... hieronder een stukje van de User class, nu even met slechts 1 method om de naam op te halen. Via die User class kun je dus dit doen "echo $user->getName();".

Volgens mij is het bovenstaande wel duidelijk. Het model haalt nu vervolgens de data op uit de database, maar stel dat ik data zou willen ophalen op een andere manier, dan maak ik een nieuw model en dat geef ik dan in plaats van het huidige model mee aan de constructor van de User class. De User class weet dus niet waar de gegevens vandaan komen. Die spreekt gewoon telkens het model aan, en het model haalt de data op.

Wil je op een gegeven moment je data ergens anders vandaan halen, dan kan overal in je website de User class gewoon gebruikt blijven worden. Echter, bij het initiëren van het object wijzig je het model.

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

class User {

  private $model;

  public function __construct($model) {
    $this->model = $model;
  }


  public function getName() {
    return $this->model->getName();
  }

}


?>


Is het nu duidelijker wat ik bedoel?
 
Ward van der Put
Moderator

Ward van der Put

15/02/2019 15:10:21
Quote Anchor link
Duidelijk.

Aan de herhaling van de methode getName() zie je al dat dit niet DRY is. Sterker nog, het is een letterlijke herhaling: User->getName() doet niets meer of minder dan UserModel->getName().

Met een User extends UserModel bereik je precies hetzelfde, maar belangrijker is de vraag of je niet gewoon één van die twee klassen moet weggooien.
 
Ozzie PHP

Ozzie PHP

15/02/2019 15:14:27
Quote Anchor link
Ja klopt, ik herhaal inderdaad mezelf, maar ik weet niet hoe ik het anders moet doen eigenlijk.

Als ik een class weggooi, gaat het mis als ik wil wisselen van de manier waarop ik m'n data ophaal.

Stel, overal in je applicatie gebruik ik bijv. $user->getName();

Maar ... gebruikers kunnen zijn opgeslagen in de interne database, of ze komen van (ik noem maar wat) Facebook. Dan moet in het ene geval de naam uit de database worden opgehaald, en in het andere geval via een API van Facebook. Ik het dus 2 aparte modellen nodig ... denk ik :-s

Als je een goede methode weet om dit op te lossen dan hoor ik dat heel graag.
 
Ward van der Put
Moderator

Ward van der Put

15/02/2019 15:20:25
Quote Anchor link
Dat kan met een data mapper. ;-)

Behandel de User die je nu hebt als het model, met getters en setters voor de eigenschappen, maar delegeer het opslaan en weer opvragen van het model naar een andere klasse, bijvoorbeeld de UserMapper.
 
Ozzie PHP

Ozzie PHP

15/02/2019 15:24:34
Quote Anchor link
Poeh ... ik volg 'm effe niet meer Ward ;-)

Kun je misschien een heeeeeeel simplistisch voorbeeld geven van hoe jij het dan bedoelt? Bijv. aan de hand van de method getUsername()? Dat zou heel fijn zijn. Als ik het principe doorheb, kan ik daar op voortborduren :-)
 
Ward van der Put
Moderator

Ward van der Put

15/02/2019 16:14:03
Quote Anchor link
Zonder handen, zonder foutafhandeling, enzovoort:

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
<?php
class UserMapper()
{

    protected $Database;

    public function __construct(\PDO $database)
    {

        $this->Database = $database;
    }


    public function fetchUserById($user_id)
    {

        $stmt = $this->Database->prepare('SELECT * FROM users WHERE user_id = :user_id');
        $stmt->bindValue(':user_id', $user_id, \PDO::PARAM_INT);
        $stmt->execute();
        $result = $stmt->fetchAll(\PDO::FETCH_ASSOC);
        $user_data = $result[0];
        $user = new User();
        $user->setUsername($user_data['username']);
        return $user;
    }
}


class User
{
    protected $Username;

    public function getUsername()
    {

        return $this->Username;
    }


    public function setUsername($username)
    {

        $this->Username = $username;
    }
}


// Gebruik:
$db = new PDO('mysql:dbname=test;host=localhost', 'root', '');
$user_mapper = new UserMapper($db);
$user = $user_mapper->fetchUserById($_SESSION['user_id']);
echo $user->getUsername();
?>
 
Ozzie PHP

Ozzie PHP

15/02/2019 16:40:33
Quote Anchor link
Ah, kijk eens aan. een stuk helderder. Thanks Ward!

Alleen eens over nadenken hoe je dat in de praktijk toepast, want je hebt in jouw opzet dus altijd 2 objecten, de User en de Mapper. Stel ik laad/fetch de User in "het begin" van de applicatie en wil deze verderop in de applicatie opslaan, dan moet ik dus ook die $user_mapper bij de hand hebben ... of een nieuw object initialiseren. Hmmm, iets om over na te denken ...
 
Ward van der Put
Moderator

Ward van der Put

15/02/2019 17:55:53
Quote Anchor link
Klopt, je zult soms iets met de state van een object moeten doen voor dit soort situaties:

1. App A vraagt user X op.
2. App B vraagt user X op.
3. App A wijzigt user X.

Wat gebeurt er nu met user X in app B?
 
Ozzie PHP

Ozzie PHP

15/02/2019 20:50:49
Quote Anchor link
Geen idee? Is het een raadsel? :-D
 

Pagina: « vorige 1 2



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.