[OOP] Login systeem
ik ben terug aan het programmeren geslagen en wou nog eens proberen een OOP member systeem maken. Ik heb daarvoor het dataMapper patern gebruikt dat WouterJ me eens had geleerd.
Kunnen jullie eens kijken of mijn structuur goed zit?
User_User
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
51
52
53
54
55
56
57
58
59
60
61
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
<?php
/* user class */
class User_User
{
private $name;
private $password;
private $email;
private $id;
public function __construct($name, $password, $email)
{
$this->name = (string) $name;
$this->password = (string) $password;
$this->email = (string) $email;
}
public function setId($value)
{
$this->id = $value;
}
public function setName($value)
{
$this->name = $value;
}
public function setPassword($value)
{
$this->password = $value;
}
public function setEmail($value)
{
$this->email = $value;
}
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function getPassword()
{
return $this->password;
}
public function getEmail()
{
return $this->email;
}
}
?>
/* user class */
class User_User
{
private $name;
private $password;
private $email;
private $id;
public function __construct($name, $password, $email)
{
$this->name = (string) $name;
$this->password = (string) $password;
$this->email = (string) $email;
}
public function setId($value)
{
$this->id = $value;
}
public function setName($value)
{
$this->name = $value;
}
public function setPassword($value)
{
$this->password = $value;
}
public function setEmail($value)
{
$this->email = $value;
}
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function getPassword()
{
return $this->password;
}
public function getEmail()
{
return $this->email;
}
}
?>
User_UserMapper
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
51
52
53
54
55
56
57
58
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
<?php
class User_UserMapper
{
protected $db; // db abstractielaag
public function __construct(PDO $db)
{
$this->db = $db;
}
/**
* Uit de database komt een array, populate maakt van de array een User object
*/
public function populate(array $data)
{
$user = new User_User($data['name'], $data['password'], $data['email']);
$user->setId($data['id']);
return $user;
}
public function getUserById($id)
{
$qry = $this->db->prepare("SELECT name, password, email FROM users WHERE id = ?");
$qry->execute(array((int) $id));
$result = $qry->fetch(PDO::FETCH_ASSOC);
$result['id'] = (int) $id;
return $this->populate($result);
}
public function getUserByPassAndEmail($email, $password)
{
$qry = $this->db->prepare("SELECT id, name FROM users WHERE email = ? AND password = ?");
$qry->execute(array($email, $password));
$result = $qry->fetch(PDO::FETCH_ASSOC);
$result['email'] = $email;
$result['password'] = $password;
return $this->populate($result);
}
public function saveUser(User $user)
{
}
public function deleteUserById($id)
{
}
}
?>
class User_UserMapper
{
protected $db; // db abstractielaag
public function __construct(PDO $db)
{
$this->db = $db;
}
/**
* Uit de database komt een array, populate maakt van de array een User object
*/
public function populate(array $data)
{
$user = new User_User($data['name'], $data['password'], $data['email']);
$user->setId($data['id']);
return $user;
}
public function getUserById($id)
{
$qry = $this->db->prepare("SELECT name, password, email FROM users WHERE id = ?");
$qry->execute(array((int) $id));
$result = $qry->fetch(PDO::FETCH_ASSOC);
$result['id'] = (int) $id;
return $this->populate($result);
}
public function getUserByPassAndEmail($email, $password)
{
$qry = $this->db->prepare("SELECT id, name FROM users WHERE email = ? AND password = ?");
$qry->execute(array($email, $password));
$result = $qry->fetch(PDO::FETCH_ASSOC);
$result['email'] = $email;
$result['password'] = $password;
return $this->populate($result);
}
public function saveUser(User $user)
{
}
public function deleteUserById($id)
{
}
}
?>
Session_Session
Code (php)
Public/index.php
Code (php)
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
<?php
include('../lib/autoLoader.php');
$db = new PDO('mysql:host=localhost;dbname=test', 'root', '');
$userMapper = new User_UserMapper($db);
$jasper = $userMapper->getUserByPassAndEmail('jasp.***@gmail.com', 'test123');
echo $jasper->getName(); // geeft jasper -> OK
new Session_Session($jasper->getId());
echo $_SESSION['user_id']; // geeft 1 -> OK
?>
include('../lib/autoLoader.php');
$db = new PDO('mysql:host=localhost;dbname=test', 'root', '');
$userMapper = new User_UserMapper($db);
$jasper = $userMapper->getUserByPassAndEmail('jasp.***@gmail.com', 'test123');
echo $jasper->getName(); // geeft jasper -> OK
new Session_Session($jasper->getId());
echo $_SESSION['user_id']; // geeft 1 -> OK
?>
Bedankt!
Gewijzigd op 03/11/2012 21:02:25 door Jasper DS
Hetzelfde voor Session_Session. Als je zo wilt werken gebruik dan beter namespaces.
http://net.tutsplus.com/tutorials/php/namespacing-in-php-2/
Verder ziet de code er goed uit, een beetje zoals ik hem ook al een paar had gemaakt. Je bent zeker enorme stappen opgeschoten sinds je laatste OO script!
Ik heb 4 tips voor je:
1. Programmeer naar een interface1
Het is altijd goed om naar een interface te programmeren, naar iets wat je klassen groepeert, wat je zekerheid biedt welke methods je kunt gebruiken en iets waar je op kunt vertrouwen. Maak bijv. een DataMapperInterface die methods als Mapper::populate(), Mapper::create(), Mapper::read(), Mapper::update(), Mapper::delete() verplicht stelt. En zo ook voor alle andere soorten.
2. Zie klassen meer als components dan als deel van een applicatie
Je ziet de klasse nog net iets te veel als deel van een applicatie. Je moet klassen zien als componenten die je overal kunt gebruiken. Vervolgens plaats je al deze componenten bij elkaar en maak je een klasse of een stukje flat PHP om al deze componenten om te zetten in een applicatie.
Dat zie je overal gebeuren in de OO wereld: Zend Framework bestaat uit 48 modules die los te gebruiken zijn en 1 module + wat flat PHP die deze vervolgens allemaal in elkaar zet en er een framework van maakt. Symfony werkt net zo.
Bij jou is dit nog niet zo. De sessie opsla klasse is nu alleen voor het opslaan van een user_id sessie, wat een beetje beperkt is. Ik zou sowieso de key variabel maken en misschien zou ik wel willen gaan voor een soort SessionMapper met de CRUD functies.
3. Maak gebruik van je setters/getters
Je setters moeten de enige methoden zijn die aan de properties mogen komen. Zij moeten er voor zorgen dat je de juiste waarde opslaat in de properties. Gebruik de setters en getters ook in een klasse, bijv. in de User constructor:
4. Gebruik PHPdocs
Je code wil je zo duidelijk mogelijk maken, gebruik PHPdocs om aan te geven wat er allemaal in de functie moet gebeuren, bijv:
Nog een wat kleiner tipje: Ik zou wat precies zijn in je variabele. $value zegt niet heel veel, gebruik welke value dat is. Bijv niet setName($value) maar setName($name).
1) Met een interface wordt vaak een echt Interface bedoelt, het kan echter ook voorkomen dat er een abstracte klasse voor de 'superklasse' functie zorgt.
Raoul, het is niet raar eerder een oude versie van namespaces. Ik raad Jasper dan ook te gaan kijken naar het nieuwe namespace support in PHP, lees bijv. eens deze tutorial: Verder ziet de code er goed uit, een beetje zoals ik hem ook al een paar had gemaakt. Je bent zeker enorme stappen opgeschoten sinds je laatste OO script!
Ik heb 4 tips voor je:
1. Programmeer naar een interface1
Het is altijd goed om naar een interface te programmeren, naar iets wat je klassen groepeert, wat je zekerheid biedt welke methods je kunt gebruiken en iets waar je op kunt vertrouwen. Maak bijv. een DataMapperInterface die methods als Mapper::populate(), Mapper::create(), Mapper::read(), Mapper::update(), Mapper::delete() verplicht stelt. En zo ook voor alle andere soorten.
2. Zie klassen meer als components dan als deel van een applicatie
Je ziet de klasse nog net iets te veel als deel van een applicatie. Je moet klassen zien als componenten die je overal kunt gebruiken. Vervolgens plaats je al deze componenten bij elkaar en maak je een klasse of een stukje flat PHP om al deze componenten om te zetten in een applicatie.
Dat zie je overal gebeuren in de OO wereld: Zend Framework bestaat uit 48 modules die los te gebruiken zijn en 1 module + wat flat PHP die deze vervolgens allemaal in elkaar zet en er een framework van maakt. Symfony werkt net zo.
Bij jou is dit nog niet zo. De sessie opsla klasse is nu alleen voor het opslaan van een user_id sessie, wat een beetje beperkt is. Ik zou sowieso de key variabel maken en misschien zou ik wel willen gaan voor een soort SessionMapper met de CRUD functies.
3. Maak gebruik van je setters/getters
Je setters moeten de enige methoden zijn die aan de properties mogen komen. Zij moeten er voor zorgen dat je de juiste waarde opslaat in de properties. Gebruik de setters en getters ook in een klasse, bijv. in de User constructor:
Code (php)
4. Gebruik PHPdocs
Je code wil je zo duidelijk mogelijk maken, gebruik PHPdocs om aan te geven wat er allemaal in de functie moet gebeuren, bijv:
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
/**
* A class that can handle sessions.
*
* @author Wouter J <http://wouterj.nl>
*/
class SessionStorage
{
/**
* Edits a session.
*
* @param string $id The session name
* @param mixed $value The new value of the session
*
* @throws \InvalidArgumentException When the session did not exists
*
* @return string The new session
*/
public function update($id, $value)
{
// ...
}
}
?>
/**
* A class that can handle sessions.
*
* @author Wouter J <http://wouterj.nl>
*/
class SessionStorage
{
/**
* Edits a session.
*
* @param string $id The session name
* @param mixed $value The new value of the session
*
* @throws \InvalidArgumentException When the session did not exists
*
* @return string The new session
*/
public function update($id, $value)
{
// ...
}
}
?>
Nog een wat kleiner tipje: Ik zou wat precies zijn in je variabele. $value zegt niet heel veel, gebruik welke value dat is. Bijv niet setName($value) maar setName($name).
1) Met een interface wordt vaak een echt Interface bedoelt, het kan echter ook voorkomen dat er een abstracte klasse voor de 'superklasse' functie zorgt.
Gewijzigd op 03/11/2012 23:16:29 door Wouter J
Ik ga aan de slag. Die namespaces is inderdaad nog wel een belangrijk punt bij mij want User_User staat inderdaad niet.
bedankt!
Edit:
Je bedoelt dus met de CRUD-functions dat ik mijn sessie ook zou moeten kunnen wijzigen, lezen, maken, ... ? En eventueel ook koppel met de database (door middel van de mapper dan)?
Je bedoelt dus met de CRUD-functions dat ik mijn sessie ook zou moeten kunnen wijzigen, lezen, maken, ... ? En eventueel ook koppel met de database (door middel van de mapper dan)?
Gewijzigd op 03/11/2012 23:19:12 door Jasper DS
ik denk dat ik de namespaces nu wel goed gebruik. Ik heb ook geprobeerd een mapperInterface toe te voegen maar ik denk dat deze nog niet echt klopt...
Code (php)
De usermapper implementeert deze dan.
Ik heb ook geprobeerd PHPdocumentor te installeren maar dat is niet gelukt en ik snap ook nog niet 100% hoe het wel moet. De .php installer werkt niet op mijn pc.
namespace <app name/prefix>/<part>;
Ik prefix bijv. alles met Wj. Je krijgt dan bijv.:
namespace Wj\AdminBundle;
Vervolgens kan dit nog wat verder oplopen tot bijv. Wj\AdminBundle\Controller.
In jouw geval kan je bijv. dit doen:
namespace Jds\User;
De MapperInterface moet je ook weer in een namespace verder doen. Ik zou je mappen structuur zo maken:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
src/ (of lib)
Jds/
User/
User.php
UserMapper.php
Mapper/
MapperInterface.php
Storage/
StorageInterface.php
SessionStorage.php
DataBaseStorage.php
...
Jds/
User/
User.php
UserMapper.php
Mapper/
MapperInterface.php
Storage/
StorageInterface.php
SessionStorage.php
DataBaseStorage.php
...
Je interface ziet er gewoon goed uit, waarom zou hij niet werken?
Je hoeft niet eens PHPdocumentor the installeren, ik gebruik dat programma bijna nooit. Ik bedoel dat je de manier van documenteren, wat de standaard is in PHP, moet gaan gebruiken. Dus met annotations (@author, @return, @param, @version, @since, @throws, ...).
Quote:
Je bedoelt dus met de CRUD-functions dat ik mijn sessie ook zou moeten kunnen wijzigen, lezen, maken, ... ? En eventueel ook koppel met de database (door middel van de mapper dan)?
Ik zou allemaal storage klassen maken die je kunt gebruiken. Bijv: https://gist.github.com/4016844 (kijk naar de bestandsnaam om te zien in welke map ze staan).
Wat is gelukt:
- Namespaces (PSR-0 methode) gebruiken
- Commentaar toevoegen op de PHPdoc methode
- MapperInterface maken
Wat mij niet lukt:
- De juiste objecten vinden (niet voor de hand liggende objecten)
Waar ik graag meer info over wil:
- Waarom moet classX een interface hebben en ClassY niet
- Waarom moet ClassX een parent hebben en ClassY niet
Stel nu dat ik een systeem maak met een auto een bestuurder en een copiloot dan begrijp ik dat er een klasse Auto is met als parent voertuig en een klasse bestuurder en copiloot die een child zijn van de klasse persoon.
Wat ik moeilijker vind is om dit te doen met bijvoorbeeld een Sessie of een database.
Graag zou ik dus samen met jullie bekijken welke Classen / interfaces / ... er allemaal nodig zijn in een login-systeem zonder speciale rechten of users. Ik wil dit eerst en vooral graag doen zonder code zodat ik de logica snap want daar ben ik soms nog niet helemaal met mee.
Een voorbeeldje van WouterJ dat ik bijvoorbeeld niet snap. Waarom moet er voor een Sessie nog een Storage class gemaakt worden? Ik begrijp dat een sessie inderdaad iets "bij houd" en dat doet de Storage class ook maar dan is de Mapper toch ook een storage want een database doet dat ook?
Dit is wat ik op deze moment zou doen:
Alvast bedankt voor de vele hulp!
Gewijzigd op 06/11/2012 20:26:29 door Jasper DS
SessionStorage bewaart de sessie.
Je zou er eventueel een interface aan kunnen hangen voor de CRUD methodes.
Je kan echter nog een stapje verder en dat zou ik ook doen, het hoeft niet ik zou het doen.
Je hebt nu namelijk nog 2 zwakke plekken:
1) elke klasse heeft in principe een parent, nu heeft Session dat niet;
2) als je nu MySQLi ipv PDO wilt gebruiken heb je een probleem, aangezien je dan alle mappers moet aanpassen.
Beide zijn in 1 klapt te vangen:
Het zijn allebei manieren om iets op te slaan, de sessie in een sessie en bij een database in een database. Waarom groeperen we ze niet onder een StorageInterface?
Die bevat dan de basis methoden om er zeker van te zijn dat je die kan gebruiken. Ik stel voor de CRUD methods te pakken.
Deze Storage klassen zijn dan een database abstractie layer, zoals we dat zo mooi noemen. Ze zijn de laag die de database voorstelt. Die kunnen we dan gebruiken in de Mapper en op andere plaatsen waar we iets willen opslaan. De Mapper is een klasse voor communicatie tussen het object en een database. Wat die database is maakt die mapper niks uit, als hij maar ergens wordt opgeslagen. Daar hebben we dus een speciaal object voor gemaakt. Je UML wordt dus:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
+------------------+ +------------+
| <<interface>> | | UserMapper |
| StorageInterface | +------------+
+------------------+ | ... |
| create(obj) | HEEFT_EEN | |
| read(obj) | ---------------------> | |
| update(obj) | +------------+
| delete(obj) |
+------------------+
^ ^
IS_EEN |" | IS_EEN
+----------------+ +-----------------+
| SessionStorage | | DatabaseStorage |
+----------------+ +-----------------+
| create(obj) | | create(obj) |
| read(obj) | | read(obj) |
| update(obj) | | update(obj) |
| delete(obj) | | delete(obj) |
+----------------+ | connect(...) |
+-----------------+
| <<interface>> | | UserMapper |
| StorageInterface | +------------+
+------------------+ | ... |
| create(obj) | HEEFT_EEN | |
| read(obj) | ---------------------> | |
| update(obj) | +------------+
| delete(obj) |
+------------------+
^ ^
IS_EEN |" | IS_EEN
+----------------+ +-----------------+
| SessionStorage | | DatabaseStorage |
+----------------+ +-----------------+
| create(obj) | | create(obj) |
| read(obj) | | read(obj) |
| update(obj) | | update(obj) |
| delete(obj) | | delete(obj) |
+----------------+ | connect(...) |
+-----------------+
Ik het het zo gemaakt dat ik gewoon het object in de database opsla, dit doe ik met "serialize". Verder maak ik daarbij een cookie die voor het inloggen bijvoorbeeld "user" heet aan met een "hash". Deze "hash" staat ook weer opgeslagen in de database en op deze manier kan ik dus het user object ophalen. Maar zou ik andere dingen willen opslaan geen probleem, ik geef een andere naam mee aan de klasse en klaar.
Opslaan van een session:
Ophalen van een session:
Als ik dan toch even inga op de code. Mijn UserMapper of andere Mappers mogen nu geen PDO "commando's" meer gebruiken nu hé? Anders gaan we ons doel voorbij. Ik veronderstel dat de PDO "commando's" nu in de DatabaseStorage moeten en dat we die classe dan aanspreken in de Mappers zodat als we overschakelen op Mysqli dan we alleen de DatabaseStorage moeten aanpassen klopt?
Hoe zit dat nu bij de SessionStorage? Deze Class vervangt mijn class Session niet? Hoe zit dat nu concreet hoe moet ik de methode create bezien? Kan ik een volledig User Object in een Sessie steken? Nee dit moet zeker een int / string ofzo worden waarmee ik de User terug kan ophalen?
Dan nog even terugkomend op de UserMapper, je pijl loopt van de interface naar de Mapper, kan het zijn dat dat omgekeerd moet of interpreteer ik dat verkeerd?
We gaan door dat te doen afdwingen dat de UserMapper ook die methodes heeft?
@Tom, ik snap je manier van werken maar ik denk dat de methode van wouter abstracter is en dus dichter tegen het doel van OOP programmeren aanleunt dat jouw methode of zie ik dat verkeerd? Alleszins bedankt voor je inbreng, het deel van het opslaan in de database kan ik waarschijnlijk ook nog verwerken in wouter zijn manier. Dat is een kwestie van hoe je de functies vult denk ik...
Gewijzigd op 06/11/2012 22:54:37 door Jasper DS
Quote:
Ik veronderstel dat de PDO "commando's" nu in de DatabaseStorage moeten en dat we die classe dan aanspreken in de Mappers zodat als we overschakelen op Mysqli dan we alleen de DatabaseStorage moeten aanpassen klopt?
Kijk daar komt de OO denkende jasper voorbij gelopen! Dit heb je helemaal gelijk met als enige punt dat ik niet de DatabaseStorage zal aanpassen, maar gewoon een nieuwe klasse zou schrijven bijv. MySQLiDatabaseStorage. Je kan die PDODatabaseStorage altijd nog een keer gebruiken.
Merk overigens op dat de DatabaseStorage nog iets moet doen: verbinding maken met de db.
Quote:
Hoe zit dat nu bij de SessionStorage? Deze Class vervangt mijn class Session niet? Hoe zit dat nu concreet hoe moet ik de methode create bezien? Kan ik een volledig User Object in een Sessie steken? Nee dit moet zeker een int / string ofzo worden waarmee ik de User terug kan ophalen?
Lekker veel vragen:
2) Ja, dat klopt
3) Je kan hem zien zoals ik in het voorbeeld liet zien
4) Dit is de optie die ik liet zien
5) Dit kan je ook doen, is denk ik wat beter.
Quote:
ik snap je manier van werken maar ik denk dat de methode van wouter abstracter is en dus dichter tegen het doel van OOP programmeren aanleunt dat jouw methode of zie ik dat verkeerd?
Er zijn meerdere wegen naar Rome. Vooral voor het opslaan van gegevens kent OOP vele manieren, tom en ik leggen de manier uit die wij het best vinden, maar er zijn er nog veel meer (zoals ActiveRecord, ORMs, ect.).
Kan je als voorbeeld de methode create van de UserMapper geven? Ik veronderstel dat die een Object User binnen krijgt maar ik weet niet goed wat die moet returnen..
Quote:
Kan je als voorbeeld de methode create van de UserMapper geven? Ik veronderstel dat die een Object User binnen krijgt maar ik weet niet goed wat die moet returnen..
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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
<?php
class User
{
private $id = 0;
private $name;
// ...
public function __construct($name)
{
$this->setName($name);
}
public function setName($name)
{
$this->name = (string) $name;
}
public function setId($id)
{
$this->id = (int) $id;
}
public function getName()
{
return $this->name;
}
public function getId()
{
return $this->id;
}
}
class UserMapper implements DataMapperInterface
{
private $storage;
public function __construct(StorageInterface $storage)
{
$this->setStorage($storage);
}
protected function setStorage(StorageInterface $storage)
{
$this->storage = $storage;
}
protected function getStorage()
{
return $this->storage;
}
public function create(User $user)
{
$this->getStorage()->create('user', array(
'name' => $user->getName(),
));
$user->setId($this->getStorage()->getLastId());
return $user;
}
}
?>
class User
{
private $id = 0;
private $name;
// ...
public function __construct($name)
{
$this->setName($name);
}
public function setName($name)
{
$this->name = (string) $name;
}
public function setId($id)
{
$this->id = (int) $id;
}
public function getName()
{
return $this->name;
}
public function getId()
{
return $this->id;
}
}
class UserMapper implements DataMapperInterface
{
private $storage;
public function __construct(StorageInterface $storage)
{
$this->setStorage($storage);
}
protected function setStorage(StorageInterface $storage)
{
$this->storage = $storage;
}
protected function getStorage()
{
return $this->storage;
}
public function create(User $user)
{
$this->getStorage()->create('user', array(
'name' => $user->getName(),
));
$user->setId($this->getStorage()->getLastId());
return $user;
}
}
?>
Als ik een nieuwe User wil maken is de code zo dus:
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
<?php
// User Object aanmaken
$user = new User('Jasper');
$user->setEmail('[email protected]');
$user->setPassword();
// Steek in de database
$pdo = new PDO(...);
$pdoStorage = new PDODatabaseStorage($pdo);
$userMapper = new UserMapper($pdoStorage);
$userMapper->create($user);
?>
// User Object aanmaken
$user = new User('Jasper');
$user->setEmail('[email protected]');
$user->setPassword();
// Steek in de database
$pdo = new PDO(...);
$pdoStorage = new PDODatabaseStorage($pdo);
$userMapper = new UserMapper($pdoStorage);
$userMapper->create($user);
?>
Dan even concreet op de PDODatabaseStorage()
Die krijgt twee parameters binnen 'user' een een array. Ik veronderstel dat user naar de tabel in de database verwijst en de array naar de velden en de values verwijst?
Als ik tot nu toe nog correct aan het denken ben dan zit ik nu met een praktische vraag.
De parameter 'user' op de juiste plek te zetten zal nog wel lukken maar ik zit daar met een array die de velden als key hebben en de value als value. Hoe krijg ik die in mijn query? Heeft PDO daar een functie voor of moet ik de array uitlezen met een foreach?
Code (php)
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
<?php
$user = new User('Jasper');
$user->setEmail('[email protected]');
$userMapper = ...;
$userMapper->create($user);
$user->setEmail('[email protected]');
$userMapper->save($user);
?>
$user = new User('Jasper');
$user->setEmail('[email protected]');
$userMapper = ...;
$userMapper->create($user);
$user->setEmail('[email protected]');
$userMapper->save($user);
?>
Je ga merken dat het een beetje onpraktisch wordt om een UserMapper aan te maken:
Code (php)
1
2
3
4
5
2
3
4
5
<?php
$pdo = new PDO(...);
$pdoStorage = new PDODatabaseStorag($pdo);
$userMapper = new UserMapper($pdoStorage);
?>
$pdo = new PDO(...);
$pdoStorage = new PDODatabaseStorag($pdo);
$userMapper = new UserMapper($pdoStorage);
?>
Daarom ga je straks werken met een Service container:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
$container = new Container();
$container->set('storage.database.adapter', 'PDODatabaseStorage');
$container->set('storage.database.connection', function($c) {
return new PDO(...);
});
$container->set('storage.database', function($c) {
return new $c->get('storage.database.adapter')($c->get('storage.database.connection'));
});
$container->set('mapper.storage', 'storage.database');
$container->set('mapper.user', function($c) {
return new UserMapper($c->get('mapper.storage'));
});
// in gebruik
$userMapper = $container->get('mapper.user');
$userMapper->create($user);
?>
$container = new Container();
$container->set('storage.database.adapter', 'PDODatabaseStorage');
$container->set('storage.database.connection', function($c) {
return new PDO(...);
});
$container->set('storage.database', function($c) {
return new $c->get('storage.database.adapter')($c->get('storage.database.connection'));
});
$container->set('mapper.storage', 'storage.database');
$container->set('mapper.user', function($c) {
return new UserMapper($c->get('mapper.storage'));
});
// in gebruik
$userMapper = $container->get('mapper.user');
$userMapper->create($user);
?>
Maar daar zullen we straks naar kijken, eerst even de storage klassen:
Quote:
Hoe krijg ik die in mijn query? Heeft PDO daar een functie voor of moet ik de array uitlezen met een foreach?
Ik zou het zoiets doen:
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
class PDODatabaseStorage extends DatabaseStorage
{
// ...
public function create($table, array $fields)
{
$fieldNames = array_keys($fields);
$query = sprintf(
'INSERT INTO %s(%s) VALUES (',
$table,
implode(', ', $fieldNames)
);
foreach ($fields as $field) {
$query .= $field.',';
}
$query = substr($query, 0, -1).');';
// $query is nu een correcte query
}
}
?>
class PDODatabaseStorage extends DatabaseStorage
{
// ...
public function create($table, array $fields)
{
$fieldNames = array_keys($fields);
$query = sprintf(
'INSERT INTO %s(%s) VALUES (',
$table,
implode(', ', $fieldNames)
);
foreach ($fields as $field) {
$query .= $field.',';
}
$query = substr($query, 0, -1).');';
// $query is nu een correcte query
}
}
?>
Code (php)
1
2
3
4
5
6
7
2
3
4
5
6
7
<?php
public function __construct(PDO $db, string $table)
{
$this->db = $db;
$this->table = $table;
}
?>
public function __construct(PDO $db, string $table)
{
$this->db = $db;
$this->table = $table;
}
?>
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
public function create(array $fields)
{
$fieldNames = implode(', ',array_keys($fields));
foreach ($fields as $field)
{
$prepare .= '? ,';
}
$prepare = substr($prepare, 0, -1);
$qry = $this->db->prepare("INSERT INTO ".getTable()." " . $fieldNames . " VALUES (".$prepare.") ");
$i = 1;
foreach ($fields as $field)
{
$qry->bindParam($i, $field);
$i++;
}
$qry->execute();
}
?>
public function create(array $fields)
{
$fieldNames = implode(', ',array_keys($fields));
foreach ($fields as $field)
{
$prepare .= '? ,';
}
$prepare = substr($prepare, 0, -1);
$qry = $this->db->prepare("INSERT INTO ".getTable()." " . $fieldNames . " VALUES (".$prepare.") ");
$i = 1;
foreach ($fields as $field)
{
$qry->bindParam($i, $field);
$i++;
}
$qry->execute();
}
?>
Gewijzigd op 08/11/2012 11:07:14 door Jasper DS
Merk ook op dat we het nu als tabel gebruiken, maar dat hij met een sessie bijv. de array key kan voorstellen.
Toevoeging op 09/11/2012 09:56:21:
Mijn User class implements de StorableInterface en toch krijg ik deze error?
Code (php)
1
Fatal error: Declaration of Jds\User\UserMapper::create() must be compatible with Jds\Store\Storage\StorageInterface::create(Jds\Store\Storable\StorableInterface $object) in C:\wamp\www\login_system\lib\Jds\User\UserMapper.php on line 7
Toevoeging op 09/11/2012 10:09:12:
Hier is mijn code: https://github.com/JasperDS/login_system
Jasper, je DataMapper is geen StorageInterface. Je kijkt het nu weer verkeerd: De storage klassen en datamappers zijn 2 andere dingen. De UserMapper moet de DataMapperInterface implementeren en niet een StorageInterface.
Toevoeging op 09/11/2012 10:40:20:
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
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
<?php
namespace Jds\Store\Storage;
use \PDO;
use \Jds\Store\Storage\StorageInterface;
class PDODatabaseStorage implements StorageInterface
{
protected $db;
protected $table;
public function __construct(PDO $db, $table)
{
$this->db = $db;
$this->table = $table;
}
public function getTable()
{
return $this->table();
}
public function create(array $fields)
{
$fieldNames = implode(', ',array_keys($fields));
foreach ($fields as $field)
{
$prepare .= '? ,';
}
$prepare = substr($prepare, 0, -1);
$qry = $this->db->prepare("INSERT INTO ".getTable()." " . $fieldNames . " VALUES (".$prepare.") ");
$i = 1;
foreach ($fields as $field)
{
$qry->bindParam($i, $field);
$i++;
}
$qry->execute();
}
}
?>
namespace Jds\Store\Storage;
use \PDO;
use \Jds\Store\Storage\StorageInterface;
class PDODatabaseStorage implements StorageInterface
{
protected $db;
protected $table;
public function __construct(PDO $db, $table)
{
$this->db = $db;
$this->table = $table;
}
public function getTable()
{
return $this->table();
}
public function create(array $fields)
{
$fieldNames = implode(', ',array_keys($fields));
foreach ($fields as $field)
{
$prepare .= '? ,';
}
$prepare = substr($prepare, 0, -1);
$qry = $this->db->prepare("INSERT INTO ".getTable()." " . $fieldNames . " VALUES (".$prepare.") ");
$i = 1;
foreach ($fields as $field)
{
$qry->bindParam($i, $field);
$i++;
}
$qry->execute();
}
}
?>
PDODatabaseStorage->create($array) kan niet want $array is geen StorableInterface $object wat de StorageInterface wel afdwingt.