[OOP] Het uitdenken van een CMS
Ik zou alle delete() methods weghalen uit de view classes. In mijn ervaring gebruik je dat nooit. Je maakt de pagina pas op op het moment dat je alle gegevens hebt, dus je weet op dat moment al wat er op de pagina moet komen en wat niet.
Tevens heb je het nut van interfaces nog niet helemaal door. Een constructor is naar mijn mening totaal niet iets wat in een interface thuis hoort. Een interface voeg je toe om zeker te zijn dat een bepaalde klasse bepaalde methods heeft. 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
25
26
27
28
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
class Dog
{
public function playSound()
{
echo 'Woefwaf';
}
}
class Person
{
private $animal;
public function setAnimal(Dog $animal)
{
$this->animal = $animal;
}
public function makeAnimalHappy()
{
$this->animal->playSound();
}
}
$dog = new Dog();
$person = new Person($dog);
$person->makeAnimalHappy(); // >> 'woefwaf'
?>
class Dog
{
public function playSound()
{
echo 'Woefwaf';
}
}
class Person
{
private $animal;
public function setAnimal(Dog $animal)
{
$this->animal = $animal;
}
public function makeAnimalHappy()
{
$this->animal->playSound();
}
}
$dog = new Dog();
$person = new Person($dog);
$person->makeAnimalHappy(); // >> 'woefwaf'
?>
Nu typehinten we op de Dog klasse, dit zorgt ervoor dat de $animal property alleen een Dog klasse kan zijn. Hierdoor weten we zeker welke methods hij heeft.
Maar stel je voor dat onze persoon geen hond maar een kat heeft? Dan werkt deze methode dus niet. We moeten onze applicatie met het oog op flexibiliteit dus aanpassen. Hoe kunnen we er nou voor zorgen dat de method playSound() beschikbaar is in de Person klasse? Dat doen we door alle dieren een interface te laten implementeren die vastlegt dat deze klasse minimaal de method playSound heeft:
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
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
interface AnimalInterface
{
public function playSound();
}
class Dog implements AnimalInterface
{
public function playSound()
{
echo 'Woefwaf';
}
}
class Cat implements AnimalInterface
{
public funciton playSound()
{
echo 'Miau';
}
}
// we kunnen ook leuke dingen doen :)
abstract class SilentAnimal implements AnimalInterface
{
final public function playSound()
{
echo '';
}
}
// deze is nu stil, hij kan NOOIT geluid maken
class Ant extends SilentAnimal
{
}
class Person
{
// ...
public function setAnimal(AnimalInterface $animal)
{
$this->animal = $animal;
}
// ...
}
?>
interface AnimalInterface
{
public function playSound();
}
class Dog implements AnimalInterface
{
public function playSound()
{
echo 'Woefwaf';
}
}
class Cat implements AnimalInterface
{
public funciton playSound()
{
echo 'Miau';
}
}
// we kunnen ook leuke dingen doen :)
abstract class SilentAnimal implements AnimalInterface
{
final public function playSound()
{
echo '';
}
}
// deze is nu stil, hij kan NOOIT geluid maken
class Ant extends SilentAnimal
{
}
class Person
{
// ...
public function setAnimal(AnimalInterface $animal)
{
$this->animal = $animal;
}
// ...
}
?>
Offtopic:
Sorry, dit gaat weer alleen over interfaces. Ik kijk binnenkort nog wel eens naar het complete plaatje.
Omdat ik in alle gevallen een username en password wil weten van een gebruiker zal ik dit vast kunnen stellen in mijn interface.
Code (php)
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
+---------------------+
| User_Interface |
+---------------------+
| + getUsername() |
| + setUsername() |
| + getPassword() |
| + setPassword() |
+---------------------+
| User_Interface |
+---------------------+
| + getUsername() |
| + setUsername() |
| + getPassword() |
| + setPassword() |
+---------------------+
Vervolgens kan ik hier mijn userclasse op implenteren. Als ik dan de rollen erbij haal, moderator / administrator / klant extend ik deze van elkaar of de abstracte Role klasse.
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
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
+-------------------+
| Role | (abstract)
+-------------------+
| + getRights() |
| + setRights() |
+-------------------+
+-------------------+
| Customer | (extends Role)
+-------------------+
| + getRole() |
| + setRole() |
| + getAdress() |
| + setAdress() |
+-------------------+
+-------------------+
| Moderator | (extends Role)
+-------------------+
| + getRole() |
| + setRole() |
| eventuele extra |
| functies van |
| een moderator |
| ophalen |
+-------------------+
+-------------------+
| Admin | (extends Moderator)
+-------------------+
| + getRole() |
| + setRole() |
| eventuele extra |
| functies van |
| een admin |
| ophalen |
+-------------------+
| Role | (abstract)
+-------------------+
| + getRights() |
| + setRights() |
+-------------------+
+-------------------+
| Customer | (extends Role)
+-------------------+
| + getRole() |
| + setRole() |
| + getAdress() |
| + setAdress() |
+-------------------+
+-------------------+
| Moderator | (extends Role)
+-------------------+
| + getRole() |
| + setRole() |
| eventuele extra |
| functies van |
| een moderator |
| ophalen |
+-------------------+
+-------------------+
| Admin | (extends Moderator)
+-------------------+
| + getRole() |
| + setRole() |
| eventuele extra |
| functies van |
| een admin |
| ophalen |
+-------------------+
Is dit een beetje het princiepe wat jullie bedoelen? Of zou jij, Wouter, zeggen Roles moet ook een interface hebben om zo zijn punten vast te leggen? Door nu een abstract klasse ervan te maken heb ik toch altijd de benodigde functies maar kan ik toch (eventueel) oneindig uitbreiden.
@ Erwin,
Zo had ik hier nooit over nagedacht. Als je dat wel doet zie je inderdaad in dat de kans klein is dat jij met een klasse (bijvoorbeeld) je formulier veld weghaalt. Aan de andere kant stel nou dat het wel nodig is?
Gewijzigd op 19/01/2013 11:05:19 door Milo S
Ik ben nog steeds bezig met hetzelfde systeem, steeds probeer ik vraagstukken uit te sluiten en ben tot de conclusie gekomen dat ik het graag een stap voor stap wil doorlopen. Als de eerste basis maar goed is dan kan ik namelijk met een goede ondergrond verder borduren. Ik ga proberen minstens iedere avond een stap verder te komen, willen jullie mij hierbij helpen?
De eerste opzet voor de interfaces en klasse ziet er als volgt uit:
User_Interface
User implements User_Interface
Authentication implements User_Interface
Role_Interface
Role implements Role_Interface
Moderator extends Role
Administrator extends Role
User_Mapper_Interface
User_Mapper implements User_Interface
De rollen staan bij de gebruiker in de database. Hier hoef ik dan geen Role_Mapper voor aan te maken, want dat gaat dan via de User_Mapper, toch?
Page_Interface
Page implements Page_Interface
News extends Page
Page_Mapper_Interface
Page_Mapper implements Page_Interface
de nieuws klasse implenteer ik in de page klasse, omdat dit feitelijk hetzelfde is plus wat uitbreidingen. Voor de Page mapper geld dan dus ook dat hij ook data van de News klasse kan opslaan, of moet ik hier dan wel een extra mapper voor gebruiken?
Image_Interface
Image implements Image_Interface
Image _Mapper_Interface
Image_Mapper implements Image_Interface
Database_Interface
Database implements Database_Interface
MySQL extends Database
Wat vinden jullie, waar liggen de opmerkingen, graag hoor ik ze en wat dan de betere oplossing zou zijn.
Gewijzigd op 04/02/2013 22:03:18 door Milo S
de PSR-0 standaarden aan. Deze zijn opgezet door de PHP Fig (Framework Integration Group). Deze groep wil bereiken dat iedereen ong. dezelfde standaarden gebruikt, waardoor je erg makkelijk iemand anders library kan gebruiken.
Lijkt me niet correct. UserInterface zou de overkoppelende factor voor een User moeten zijn. Authentication is het kijken wie de huidige bezoeker is. Authentication gaat uit eindelijk een User maken.
Gewoon om even zeker te zijn, Role is abstract?
Allereerst zou ik geen specifieke mapper interfaces voor elke mapper maken. Ik zou 1 grote MapperInterface maken die sowieso de CRUD (create, read, update en delete) functies verplicht stelt.
Ten tweede moet de UserMapper ook totaal geen UserInterface implementeren. Overal waar je 'implements' hebt staan moet je kunnen veranderen in 'IS EEN'. Je kan niet zeggen UserMapper IS EEN User.
Exact
Als de News klasse andere waardes bevat dan de page klasse heb je hiervoor een aparte mapper nodig.
Weer hetzelfde als voor UserMapper en Authentication.
Ik zou je interface globaler maken, bijv. MediaInterface. Hierdoor heb je mooi alle typen media, youtube filmpje, afbeelding, ect. op gevangen. Tevens kun je alsnog een ImageInterface behouden als je vindt dat de afbeelding andere methods heeft dan een youtube filmpje. Je krijgt dan Image implements MediaInterface, ImageInterface
Zie andere Mapper opmerkingen
Om duidelijk te maken zou ik MySQL suffixen met 'Database', MySQLDatabase.
Allereerst zou ik eens gaan nadenken welke naming conventions je gaat gebruiken. Ik raad je Quote:
Authentication implements User_Interface
Lijkt me niet correct. UserInterface zou de overkoppelende factor voor een User moeten zijn. Authentication is het kijken wie de huidige bezoeker is. Authentication gaat uit eindelijk een User maken.
Quote:
Role implements Role_Interface
Gewoon om even zeker te zijn, Role is abstract?
Quote:
User_Mapper_Interface
User_Mapper implements User_Interface
User_Mapper implements User_Interface
Allereerst zou ik geen specifieke mapper interfaces voor elke mapper maken. Ik zou 1 grote MapperInterface maken die sowieso de CRUD (create, read, update en delete) functies verplicht stelt.
Ten tweede moet de UserMapper ook totaal geen UserInterface implementeren. Overal waar je 'implements' hebt staan moet je kunnen veranderen in 'IS EEN'. Je kan niet zeggen UserMapper IS EEN User.
Quote:
De rollen staan bij de gebruiker in de database. Hier hoef ik dan geen Role_Mapper voor aan te maken, want dat gaat dan via de User_Mapper, toch?
Exact
Quote:
de nieuws klasse implenteer ik in de page klasse, omdat dit feitelijk hetzelfde is plus wat uitbreidingen. Voor de Page mapper geld dan dus ook dat hij ook data van de News klasse kan opslaan, of moet ik hier dan wel een extra mapper voor gebruiken?
Als de News klasse andere waardes bevat dan de page klasse heb je hiervoor een aparte mapper nodig.
Quote:
Page_Mapper implements Page_Interface
Weer hetzelfde als voor UserMapper en Authentication.
Quote:
Image_Interface
Image implements Image_Interface
Image implements Image_Interface
Ik zou je interface globaler maken, bijv. MediaInterface. Hierdoor heb je mooi alle typen media, youtube filmpje, afbeelding, ect. op gevangen. Tevens kun je alsnog een ImageInterface behouden als je vindt dat de afbeelding andere methods heeft dan een youtube filmpje. Je krijgt dan Image implements MediaInterface, ImageInterface
Quote:
Image_Mapper implements Image_Interface
Zie andere Mapper opmerkingen
Quote:
Database implements Database_Interface
MySQL extends Database
MySQL extends Database
Om duidelijk te maken zou ik MySQL suffixen met 'Database', MySQLDatabase.
UserInterface
User extends UserInterface
Authentication
RoleInterface
Role abstract implements RoleInterface
Moderator extends Role
Administrator extends Role
UserMapper extends User
PageInterface
Page implements PageInterface
News extends Page
PageMapper extends Page
NewsMapper extends News
MediaInterface
Image implements MediaInterface
Image_Mapper extends Image
Bij de media interface kunnen altijd andere media klasse worden toegevoegd, bijvoorbeeld een music klasse...
Ik vraag mij af of ik wel een dergelijke database klasse nodig heb, kan ik hier niet PDO voor gebruiken? Nadeel zou zijn dat ik heel de zooi aan moet passen wanneer ik iets anders wil gebruiken. Hoe zien jullie dat?
Kloppen mijn aanpassingen nu wel? Ik kan niet wachten om verder te borduren...
Gewijzigd op 05/02/2013 17:39:23 door Milo S
Lees eens deze tutorial over relaties tussen objecten: http://wouterj.nl/php/oo-de-onbekende-kant/476/
Quote:
Page abstract implements PageInterface
Dit betekend dat Page niet op zichzelf staat, maar alleen als een overkoepelende klasse mag gebruikt worden. Vindt je dat echt?
Gewijzigd op 05/02/2013 15:16:47 door Wouter J
IS EEN = implements
HEEFT EEN = extens
Niet waar?
Wat betreft de page class ben ik dronken geweest denk ik.
Mijn gedachte gang was dat ik de methodes van page wilde vast stellen voor de news klasse, maar die extend al van page welke die functies al verplicht is door de pageInterface.
Snap je wat ik bedoel?
Ik voer de wijzigingen door en ben benieuwd of dit dan de sleutel is..
Quote:
Niet waar?
Inderdaad, dat is niet waar. :) Zowel extends als implements is een IS EEN relatie. Een HEEFT EEN relatie is niet iets wat je vastlegt bij het definiëren van een klasse. Een voorbeeld:
Quote:
Allereerst kan 'UserMapper implements User' natuurlijk nooit, User is een klasse en moet dus geëxtend worden
Dan hier van uitgaande ga ik de volgende gedurfde uitspraak doen... Je gebruikt extends zodra je een class IS EEN class relatie hebt en je gebruikt implements zodra je een class IS EEN interface relatie hebt.
Of ga ik nu helemaal stom brabbelen?
Nee, dat heb je helemaal goed.
Als ik mij dan niet vergis is dit wat je graag zou willen zien?
MapperInterface
UserInterface
User implements UserInterface
Authentication
RoleInterface
Role abstract implements RoleInterface
Moderator extends Role
Administrator extends Role
UserMapper implements MapperInterface
PageInterface
Page implements PageInterface
News extends Page
PageMapper implements MapperInterface
NewsMapper implements MapperInterface
MediaInterface
Image implements MediaInterface
ImageMapper implements MapperInterface
Bij de media interface kunnen altijd andere media klasse worden toegevoegd, bijvoorbeeld een music klasse...
Wat betreft de database denk ik dat het handig is om wel een database handler te maken. Zo kan ik makkelijk van database soort wisselen... Hier zal ik dan uit moeten gaan van de standaard functies. Connectie, Query, fetch_* etc.
Gewijzigd op 05/02/2013 22:41:53 door Milo S
Quote:
UserMapper extends User
Nee, UserMapper IS GEEN User. UserMapper MAAKT EEN User.
Quote:
Wat betreft de database denk ik dat het handig is om wel een database handler te maken. Zo kan ik makkelijk van database soort wisselen... Hier zal ik dan uit moeten gaan van de standaard functies. Connectie, Query, fetch_* etc.
Ja, of je gebruikt een al bestaande library, zoals Doctrine DBAL. Dit is zeg maar een erg mooie open source database klasse.
De waslijst die hier terecht gekomen is na een edit heb ik even in een word document geplaatst.
Gewijzigd op 06/02/2013 18:18:09 door Milo S
Een Authentication class is er voor om te kijken of de gebruiker wel is wie hij zegt dat hij is. Als ik een pagina ophaal door middel van een paginatitel, welke meegegeven is via de URL, moet ik de pagina dan niet ook nakijken met bijvoorbeeld een PageAuthentication? Dat ik dan huidige Authentication class hernoem tot een UserAuthentication?
Of behoort dit gewoon tot de fatsoenlijke foutafhandeling binnen de PageMapper?
Mocht het wel het geval zijn, dan heb ik er ook een nodig voor nieuws als ik bijvoorbeeld al het nieuws op wil halen van een bepaalde auteur?
De Authentication klasse werkt samen met de Mappers, maar om iets op te halen met een specifiek ID moet ik daarvoor dan nog een extra methode voor schrijven? Ik neem aan van niet, dat het dan geregeld wordt door te kijken of er een ID meegezonden is of niet...