is dit oop of niet?
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
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
<?php
class actions{
//query
public $set_query;
//resultaat mysql_query
protected $execute_query;
protected $result;
//bericht succes
public $set_message_true;
//bericht mislukt
public $set_message_false;
//het aantal beinvloede rijen na het uitvoeren van de query
public $set_affected_rows;
//mysql_query uitvoeren
public function execute_query(){
$this->result = mysql_query($this->set_query);
}
//check succes of niet
public function check(){
global $smarty;
if($this->result && mysql_affected_rows() == $this->set_affected_rows){
$smarty->assign('message',$this->set_message_true);
}
else{
$smarty->assign('message',$this->set_message_false);
}
}
}
$delete = new actions();
$delete->set_query = "DELETE FROM content WHERE paginaid =$id";
$delete->set_message_true = 'succes';
$delete->set_message_false = 'mislukt';
$delete->set_affected_rows = 1;
$delete->execute_query();
$delete->check();
unset($delete);
?>
class actions{
//query
public $set_query;
//resultaat mysql_query
protected $execute_query;
protected $result;
//bericht succes
public $set_message_true;
//bericht mislukt
public $set_message_false;
//het aantal beinvloede rijen na het uitvoeren van de query
public $set_affected_rows;
//mysql_query uitvoeren
public function execute_query(){
$this->result = mysql_query($this->set_query);
}
//check succes of niet
public function check(){
global $smarty;
if($this->result && mysql_affected_rows() == $this->set_affected_rows){
$smarty->assign('message',$this->set_message_true);
}
else{
$smarty->assign('message',$this->set_message_false);
}
}
}
$delete = new actions();
$delete->set_query = "DELETE FROM content WHERE paginaid =$id";
$delete->set_message_true = 'succes';
$delete->set_message_false = 'mislukt';
$delete->set_affected_rows = 1;
$delete->execute_query();
$delete->check();
unset($delete);
?>
Ik hoor graag op of aanmerkingen!!
Gewijzigd op 19/11/2012 22:01:00 door Tim S
Vandaar ook de naam object oriënted programming.
Iedere class die je maakt mag maar één verantwoordelijkheid hebben. Ook moet dit een object zijn (= zelfstandig naamwoord).
Is 'actions' (=acties) een zelfstanding naamwoord (=object)? Nee. Dat is het niet. Ik raad je aan om een goede OOP beginnerstutorial te volgen op http://phptuts.nl.
Veel succes!
- Raoul - op 19/11/2012 22:28:13:
Iedere class die je maakt mag maar één verantwoordelijkheid hebben.
Raoul, wat versta je precies onder één "verantwoordelijkheid"?
De verantwoordelijkheid van deze class is volgens mij alleen een query uitvoeren en dan kijken of dit is gelukt.
Is het dan de bedoeling dat ik deze klass nog verder opsplits?
Ik was bezig met een simpel cms systeem, daarvoor moet je update verwijderen en toevoegen. Daarom heb ik dit gemaakt. Het is de bedoeling dat ik dat ook voor andere dingen kan gebruiken, stel ik heb een gastenboek dan moet ik hier ook items verwijderen.
Quote:
De verantwoordelijkheid van deze class is volgens mij alleen een query uitvoeren en dan kijken of dit is gelukt.
Als er een 'EN' in je zin voor komt dan weet je dat je al meer dan 1 ding doet.
Verder zou ik beginnen met iets simpelers maken. Dit om de volgende redenen.
- Een class maken die alle acties voor jou makkelijk maakt omtrent queries is gewoon moeilijk om na te maken zelfs voor goede OOP programmeurs
- Wil je zoiets om echt te gebruiken alleen gebruik dan een ORM ( Object Relational Mapping ) een paar goede hiervan zijn Doctrine of Propel.
verder moet je in OO als het volgende denken
Elk object is een class. Deze class kan verschillende methoden hebben. bijvoorbeeld de User class die Ozzie als voorbeeld al gebruikt.
dit is een User ( persoon ) die verschillende dingen kan doen.
leven, lopen, eten, slapen, doodgaan. dit zijn dan de methoden van de User class. Je kan bijvoorbeeld in de User class geen methode meegeven die aangeeft welke ranking een User heeft. De oplossing hiervoor is bijvoorbeeld om de User class te extenden. zo krijg je dan bijvoorbeeld
Admin extends User waarbij Admin de User class overneemt en waarbij je nieuwe methoden kan meegeven die alleen de Admin class krijgt. Zo kan je verder weer een Employee class maken en noem maar op. Dit worden doorgaans Child classes genoemd. de User class is hierbij een Super class.
Ik zou de volgende tutorials doornemen om het een en ander goed te begrijpen:
http://www.phphulp.nl/php/tutorial/overig/het-princiepe-oop/302/
http://www.phphulp.nl/php/tutorial/overig/oop-beginnershandleiding-php5/701/
en deze is wel oud maar volgens mij nog wel goed
http://www.phphulp.nl/php/script/overig/database-class/478/
Ik denk dat dit wel genoeg leesvoer is voor nu :)
Toevoeging op 19/11/2012 23:41:00:
@Reshad F
wat ik probeer te maken is inderdaad een db class, wat ik alleen niet begrijp is dat in de laatste link ook meerderen dingen gebeuren, dat database connectie wordt gemaakt daarna worden er ook query's uitgevoerd.
Is het dan wel oop als ik een db class heb met daarin een functie delete en hierin een bericht zet gelukt of mislukt??
Zoals ik al zei een (goede) Databaseclass is niet iets wat je zomaar even maakt. Zelfs voor een ervaren developer. zoals ik al zei is die laatste link behoorlijk oud al. dus het kan zijn dat het niet correct is meer. link is namelijk al 8 jaar oud. wil je zien hoe een goede DB class er echt uitziet dan zou ik de source van Doctrine bijvoorbeeld bestuderen.
Aleen bij een mens is het simpel een mens loopt praat etc. Maar een pagina update delete enzovoort. Aleen geeft een pagina een reactie nee, dus ik vraag me af of ik daar een andere class voor moet maken of dat je wel een bericht mag geven als de pagina succesvol is geupdate.
Ook had ik nu $delete->setquery =
Hiervoor kan ik beter doen function settable($table){
$this->table = $table;
}
En dan $page->settable('content');
Een class page zou je gebruiken om bijvoorbeeld een header, een footer, een content gedeelte en een pagina gedeelte in te stellen, maar niet zoals jij het in gedachten hebt.
Ga eerst wat basistutorials lezen, want ik merk dat je anders helemaal de verkeerde kant op gaat. En ik, en velen met mij, zijn fout begonnen. Daarom adviseer ik je echt om eerst het principe door te krijgen.
Een van onze forumleden, Wouter J, heeft ook een mooi artikel geschreven over het denken in objecten. Begin daar maar eens mee: http://wouterj.nl/php/eens-goed-nadenken-in-objecten/354/
Nee klopt ik zit even hardop te denken. Wat ik wel heb gemerkt is dat ik erg slecht ben in het bedenken van namen. Het basis idee van bijvoorbeeld een class pet met eigenschappen van een dier begrijp ik. Ik vindt het alleen moeilijk om het update delete in een class te zetten die daarvoor verantwoordelijk is. We gaan weer verder denken....
Dus in jouw voorbeeldje met Pet:
Volgens mij heb je dan ook weer een datamapper nodig, maar dat kan Wouter of iemand anders beter uitleggen dan ik.
Gewijzigd op 20/11/2012 14:05:09 door Ozzie PHP
Misschien is het idee nu iets duidelijker??
Alleen stel dat dit juist is dan zit je nog met delete en update kan dit dan ook in dezelfde class?
wat je dan krijgt is bijvoorbeeld
Code (php)
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
<?php
$page = new Page(); // je maakt het object page aan.
$page->setName('home'); // geeft attributen mee
$page->setId('1234');
// slaat de page op dmv de storage class
$storage = new PageStorage();
$storage->save($page);
?>
$page = new Page(); // je maakt het object page aan.
$page->setName('home'); // geeft attributen mee
$page->setId('1234');
// slaat de page op dmv de storage class
$storage = new PageStorage();
$storage->save($page);
?>
nog mooier zou zijn om een interface te maken voor de CRUD methodes. bijvoorbeeld een interface genaamd StorageInterface deze kan door elke klas dan geimplementeerd worden.
Gewijzigd op 20/11/2012 16:39:41 door Reshad F
Tevens kan de manier van Tim ook, je zit dan meer te denken aan een Active Record pattern.
Toevoeging op 20/11/2012 16:51:31:
Wast iets te snel, hoe zou jij het doen wouter?
de interface zou ik natuurlijk geen interface noemen. je krijgt dan iets van
Het verschil tussen een DataMapper en een Active Record pattern is dat je bij een DataMapper door een externe class je queries afhandelt. bij een Active Record pattern zorgt deze zelf voor zijn CRUD methodes. Voor simpele dingen zou ik zeker voor een Active Record Pattern gaan maar wil je echt iets heel uitgebreids creëren dan zou ik toch kijken naar een DataMapper. De betere ORM's gebruiken deze manier ook. (@wouterJ correct me if im wrong :) ben ook nog niet zo lang bezig met OOP ;) dus kan ik gelijk zien of ik het goed heb begrepen )
Toevoeging op 20/11/2012 17:18:41:
edit: misschien handig om te lezen over Active Record Patterns
http://buggy.zoombug.com.au/wp-content/uploads/2007/03/activerecord.pdf
Allemaal leuke termen, maar dat gaat compleet voorbij aan het feit dat de TS daar zo te zien nog niet aan toe is.
Even terug naar de basis; waarom OOP gebruiken. Als het alleen is omdat het leuk klinkt.... niet doen.
Het idee van OOP is dat je applicaties opbreekt in simpele, functionele componenten die eenvoudig, zonder aanpassing, zijn te hergebruiken. Ook in andere applicaties. Dit scheelt ontwikkel tijd, scheelt test tijd en maakt je applicatie robuster en beter onderhoudbaar.
Om dat voor elkaar te krijgen is echter wel enig denkwerk nodig. Met je eerste twee zinnen geef je al dat niet voldoende te hebben gedaan:
Tim Slootweg op 19/11/2012 22:00:10:
Ik ben sinds dit weekend begonnen met oop. Ik heb nu een klasse gemaakt
Met OOP is het van belang dat je eerst een structuur ontwerpt waarbinnen je alle verschillende componenten kunt onderscheiden en daarvoor kun je classes ontwerpen en bouwen. Dat doe je niet in een weekend, zeker niet als je net begint.
Begin je die klasses dan te bouwen, dan zijn ook daar een paar heel belangrijke zaken om aan te denken. Zonder compleet te willen zijn staat er 1 voorop:
- zorg dat je elke verandering altijd maar op 1 plek hoeft door te voeren.
Dit is overigens ook de reden dat elke klasse altijd maar 1 functionaliteit (verantwoordelijkheid) zou moeten hebben. Heeft een klasse er meer, dan kom je al gauw in de situatie dat je dezelfde code in een andere klasse zal zien opduiken.
In jouw geval blijkt bovenstaande ook nog niet goed doorgewerkt te zijn. Neem als voorbeeld het property $set_affected_rows. Aangezien het nu een public property is kan elke andere klasse die zo aanroepen en er iets in zetten. Dat kan een goed waarde zijn, maar kan ook een negatief getal zijn, een letter of een ander object. Op zeker moment zal je zien dat dat een keer fout gaat en zal je er dus een test aan willen toevoegen, om ervoor te zorgen dat de waarde ook bruikbaar is. Dat doe je dan via een setter, alleen al die andere klasses die nu al het public property gebruiken moet je dan ook aanpassen. Niet doen dus, NOOIT public properties gebruiken, altijd via een setter laten lopen, desnoods via een magic method. Wil je dan eens zo'n test tovoegen is er niets aan de hand.
Een ander probleem wat je krijgt is als je ooit eens deze klasse wil gaan gebruiken in een gezamenlijk project met een ander. Jij gaat dan de database kant doen (het model in MVC termen) terwijl die ander de output kant gaat doen (view in MVC termen). Is de applicatie goed opgebouwd en zijn de componenten goed opgebouwd, dan zou dat los van elkaar moeten kunnen gebeuren. Het model heeft geen weet van de view en andersom. Alleen..... in jouw geval gaat dat al mis. Je gebruikt namelijk een global $smarty in je database klasse. Dat is dus echt een hele grote NO-NO. NOOIT globals gebruiken in OOP. Hiermee bepaal je namelijk al dat de view gebruik moet gaan maken van smarty en veeg je in 1 keer een hele berg goeds van het gebruik van OOP van tafel.
Dus, eerst nog eens even terug naar de tekentafel. Ga je applicatie structuur uitdenken, bepaal welke componenten er nodig zijn, welke functionaliteiten nodig zijn en ontwerp aan de hand daarvan je klasses.
Active Record Pattern
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
<?php
class User
{
private $id;
private $name;
private static $db;
public function __construct($name)
{
$this->name = $name;
}
public static function setDatabase(PDO $db)
{
self::$db = $db;
}
public function setId($id)
{
$this->id = (int) $id;
}
public static function getDatabase()
{
return self::$db;
}
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function save()
{
if (null === $this->getId()) {
// user moet aangemaakt worden
$query = "INSERT users(name) VALUES ('".$this->getName()."');";
// voer de query uit
$db = self::getDatabase();
$db->query($query);
// stel het id in
$this->setId($db::lastInsertId());
} else {
// update de user in de tabel
$query = "UPDATE users SET name = '".$this->getName()."' WHERE id = ".$this->getId().';';
// voer de query uit
self::getDatabase()->query($query);
}
}
/**
* Gets an User by his ID.
*
* @param int $id
*/
public static function getById($id)
{
$query = "SELECT name FROM users WHERE id = ".(int) $id.';';
// voer de query uit
$result = self::getDatabase()->query($query);
$result = $result->fetch(PDO:FETCH_ASSOC);
// maak een nieuw user object aan
$user = new static($result['name']);
$user->setId($result['id']);
$user::setDatabase(self::getDatabase());
return $user;
}
}
/************************\
HET GEBRUIK
\************************/
$user = new User('Wouter');
$user->save(); // maak een nieuwe user aan in de db
$user = User::getById(2); // krijg de user bij id = 2
$user->setName('foo'); // verander de naam
$user->save(); // update de user in de db
?>
class User
{
private $id;
private $name;
private static $db;
public function __construct($name)
{
$this->name = $name;
}
public static function setDatabase(PDO $db)
{
self::$db = $db;
}
public function setId($id)
{
$this->id = (int) $id;
}
public static function getDatabase()
{
return self::$db;
}
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function save()
{
if (null === $this->getId()) {
// user moet aangemaakt worden
$query = "INSERT users(name) VALUES ('".$this->getName()."');";
// voer de query uit
$db = self::getDatabase();
$db->query($query);
// stel het id in
$this->setId($db::lastInsertId());
} else {
// update de user in de tabel
$query = "UPDATE users SET name = '".$this->getName()."' WHERE id = ".$this->getId().';';
// voer de query uit
self::getDatabase()->query($query);
}
}
/**
* Gets an User by his ID.
*
* @param int $id
*/
public static function getById($id)
{
$query = "SELECT name FROM users WHERE id = ".(int) $id.';';
// voer de query uit
$result = self::getDatabase()->query($query);
$result = $result->fetch(PDO:FETCH_ASSOC);
// maak een nieuw user object aan
$user = new static($result['name']);
$user->setId($result['id']);
$user::setDatabase(self::getDatabase());
return $user;
}
}
/************************\
HET GEBRUIK
\************************/
$user = new User('Wouter');
$user->save(); // maak een nieuwe user aan in de db
$user = User::getById(2); // krijg de user bij id = 2
$user->setName('foo'); // verander de naam
$user->save(); // update de user in de db
?>
DataMapper Pattern
Hierdoor verplaats je alles wat te maken heeft met de db in een nieuwe klasse, dit wordt over het algemeen meer 'OO' gezien dan Active Record. Een uitleg + voorbeeld hiervan: http://www.phphulp.nl/php/forum/topic/oop-gedachtengang/85017/1/#606620
Een ORM
Object Relation Mapper, deze zorgt ervoor dat de db up to date blijft met het object:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
$doctrine = ...;
$em = $doctrine->getManager();
$user = new User('Wouter');
$em->persists($user); // voeg de user toe aan de lijst die Doctrine bij houdt
$em->flush(); // hou alles up to date
$user->setName('foo'); // verander iets
$em->flush(); // maak elk object weer up to date
$repo = $doctrine->getRepository('User');
$user = $repo->find(2); // krijg het object bij id = 2
$user->setName('foo'); // verander weer wat
$em->flush(); // en maak alles weer up to date
?>
$doctrine = ...;
$em = $doctrine->getManager();
$user = new User('Wouter');
$em->persists($user); // voeg de user toe aan de lijst die Doctrine bij houdt
$em->flush(); // hou alles up to date
$user->setName('foo'); // verander iets
$em->flush(); // maak elk object weer up to date
$repo = $doctrine->getRepository('User');
$user = $repo->find(2); // krijg het object bij id = 2
$user->setName('foo'); // verander weer wat
$em->flush(); // en maak alles weer up to date
?>