Door
Ozzie PHP
op 11-02-2019 17:08
gewijzigd op 11-02-2019 17:12
3.996 views
Stel ik heb een User class en ik wil die laten samenwerken met een UserModel, wat is dan de beste manier om dat UserModel als property in de User class te krijgen?
Zelf dacht ik aan zoiets als dit:
<?php
$id = $_SESSION['userid'];
$model = new UserModel($db_conn);
$user = new User($id, $model);
?>
Is het bovenstaande een gebruikelijke manier? Ik zie namelijk ook wel eens dit:
<?php
$id = $_SESSION['userid'];
$model = new UserModel($db_conn);
$user = new User($id);
$user->loadModel($model);
?>
Weet iemand dit toevallig?
En nog een laatste vraag ... "mag" je het ID ook meegeven aan het UserModel in plaats van aan de User class?
<?php
$model = new UserModel($id, $db_conn);
?>
Of is dat niet gebruikelijk?
Ik vraag dat omdat als ik het ID mee zou geven aan het UserModel, ik in de constructor van het model meteen alle gegevens zou kunnen ophalen uit de database en die als property zou kunnen opslaan. Als ik het ID meegeef aan de User class zelf (zoals nu het geval is) dan moet de User class het ID eerst doorgeven aan het UserModel en moet er vanuit de User class een functie in het UserModel worden aangeroepen die de data ophaalt.
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:
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:
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.
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.
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.
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:
<?php
if ($user_in_database) {
$model = new UserModelDB($db);
} else {
$model = new UserModelXML($xml);
}
User is het model. De data mapper haalt dat met een @return User ergens vandaan:
<?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?
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.
<?php
class User {
private $model;
public function __construct($model) {
$this->model = $model;
}
public function getName() {
return $this->model->getName();
}
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.