Ik ga met iemand anders een projectje doen waarbij we een site gaan maken.
Aangezien we met z'n tweeën gaan werken leek het me handig om eens met OOP aan de slag te gaan, aangezien we al hebben ervaren dat de spaghetti code voor ons beiden vervelend kan zijn.
Ik heb redelijk wat over OOP gelezen maar ik kom heel moeilijk aan praktijk voorbeelden.
Al te vaak lees je over een auto classe of over een hond classe, die je kunt extenden naar een poedel bijvoorbeeld.
Ik heb in ieder geval geprobeert een beginnetje maken maar ik vraag me nu al af of ik wel op de goede weg zit en niet verkeerd aan het denken ben.
Ik heb op verschillende plekken gelezen dat setter en getter 'evil' zijn en ik betrap mezelf erop er al veelvuldig gebruik van te maken.
Ik was bezig met een Gebruiker classe voor de site, simpelweg omdat ik geen idee had waar ik anders moest beginnen.
Ik zal hieronder posten wat ik tot nu toe heb gedaan en zou graag commentaar hebben op het geheel (ook de foutafhandeling bijvoorbeeld, ik heb vrij veel gebruikt van jorendewit.nl)
Zoals je kunt zien gebruik ik in de Gebruikers classe al vrij veel setter en getters, en blijkbaar is dit niet goed maar ik vraag me af hoe ik dit anders moet aanpakken.
if(!is_int($id)){
throw new Exception('Aanmaken instantie van "'.__CLASS__.'" mislukt. $id is geen INT');
}
$this->set_id($id);
$this->db = $db;
}
// ID SETTER & GETTER
private function set_id($id){
// Deze moet nog verder uitgewerkt worden uiteraard
$this->id = $id;
}
public function get_id(){
// Deze moet nog verder uitgewerkt worden uiteraard
return $this->id;
}
public function set_voornaam($voornaam){
if(!is_string($voornaam)){
throw new Exception('Kon voornaam niet veranderen. Waarde is geen STRING');
}
$sql = "UPDATE tbl_gebruikers SET voornaam = :voornaam WHERE id = :id";
$stmt = $this->db->prepare($sql);
$stmt->bindParam(':voornaam', $voornaam);
$stmt->bindParam(':id', $this->id);
$stmt->execute();
if($stmt->rowCount() == 0)
{
throw new Exception('Er zijn geen rijen gewijzigd in de UPDATE query "'. $sql . '"');
}
}
public function get_voornaam(){
$sql = "SELECT voornaam FROM tbl_gebruikers WHERE id = :id";
$stmt = $this->db->prepare($sql);
$stmt->bindParam(':id', $this->id);
$stmt->execute();
Ik was bezig met een Gebruiker classe voor de site, simpelweg omdat ik geen idee had waar ik anders moest beginnen.
Zomaar ergens beginnen is vaak niet erg handig, zeker niet als OO wilt programmeren.
Zeker als je een hele site (applicatie) OO wilt programmeren, is het handig om met een design pattern te werken. Een die veel gebruikt wordt, is het Model-View-Controller, ofwel MVC design pattern. Ik zou je zeker aanraden om je daar eens over in te lezen.
Verder zou je kunnen overwegen om een framework te gebruiken om je website op te zetten. Een heel uitgebreid (en daarmee voor beginners vrij complex) framework is het Zend Framework. Er zijn ook eenvoudigere maar daarmee minder uitgebreide frameworks te vinden, zoals bijvoorbeeld CakePHP.
Kortom, voordat je echt kunt beginnen zul je eerst nog een paar (belangrijke) beslissingen moeten nemen en je waarschijnlijk nog iets verder in moeten lezen...
Succes in ieder geval!
ps. Wat betreft die setters en getters, bedenk vooraf altijd welke functionaliteit een klasse moet hebben. Ik zie nu een method set_tussenvoegsel(), maar ik vraag me echt af wanneer je ooit alleen een tussenvoegsel wilt wijzigen. Meestal worden er dan veel meer gegevens gewijzigd, iets dat prima in 1 database query en dus in bijvoorbeeld 1 method changeUserData() zou kunnen.
public function __construct($naam, $inhoud)
{
$this->naam = $naam;
$this->inhoud = $inhoud;
$this->geplaatst_op = new DateTime('now');
}
static public function restore(array $data)
{
$bericht = new self($data['naam'], $data['inhoud']);
$bericht->id = $data['id'];
$bericht->geplaatst_op = new DateTime($data['geplaatst_op']);
return $bericht;
}
public function id()
{
return $this->id;
}
public function naam()
{
return $this->naam;
}
public function inhoud()
{
return $this->inhoud;
}
public function geplaatst_op()
{
return $this->geplaatst_op;
}
}
/*
Dit is even intialisatie voor dit testscript. Database aanmaken, PHP instellingen
goed zetten, dat soort dingen
*/
error_reporting(E_ALL ^ E_STRICT);
ini_set('display_errors', true);
date_default_timezone_set('Europe/Amsterdam');
//$pdo = new PDO('sqlite::memory:');
$pdo = new PDO('sqlite:/tmp/gastenboek.db');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->query("
CREATE TABLE IF NOT EXISTS berichten (
id integer not null default 0,
naam varchar(100) not null,
inhoud text not null,
geplaatst_op datetime not null,
PRIMARY KEY(id)
)");
$berichten = new Berichten_Store($pdo);
$bericht = new Bericht('Jelmer', 'Testberichtje');
$berichten->insert($bericht);
foreach($berichten->all() as $bericht)
{
printf('"%s" door %s om %s' . "\n",
$bericht->inhoud(),
$bericht->naam(),
$bericht->geplaatst_op()->format('H:i'));
}
?>
Store is ook een zelfstandig naamwoord, engels voor winkel ;)
Het staat wat stom inderdaad, die mixin van Engels en Nederlands, maar het is zeg maar een conventie voor mezelf. Net als dat ik set_xxx en get_xxx zou schrijven in plaats van zet_xxx en verkrijg_xxx. Een ding dat dingen voor mij vasthoudt noem ik meestal een store. Repository, Provider, Table, Store... het maakt niet zoveel uit :)
Maar het is inderdaad zo dat je een zelfstandig naamwoord neemt als naam van een class.
Ik ben even in de weer geweest, en ik heb nu een klein MVC framework gemaakt.
Voordat ik hier mee verder ga, zou ik wel graag wat commentaar erop hebben.
Mijn excuses als deze post veel te lang word, maar ik denk dat ik niet zomaar dingen kan weglaten, anders klopt het geheel niet. :P
Zo ziet het er uit:
index.php
dir application
dir includes
dir model
dir views
dir controller
index.php
<?php
/* error reporting aan */
ini_set('display_errors', 'On');
error_reporting(E_ALL);
/*** check of het een dir is ***/
if (is_dir($path) == false)
{
throw new Exception ('Ongeldige controller path: `' . $path . '`');
}
/*** set path ***/
$this->path = $path;
}
/**
*
* @load controller
*
* @access public
*
* @return void
*
*/
public function loader()
{
/*** check route ***/
$this->getController();
/*** 404 geven als het bestand er niet is ***/
if (is_readable($this->file) == false)
{
$this->file = $this->path.'/error404.php';
$this->controller = 'error404';
}
/*** controller includen ***/
include $this->file;
/*** nieuw controller class instance ***/
$class = $this->controller . 'Controller';
$controller = new $class($this->registry);
/*** check of je de action kunt aanroepen ***/
if (is_callable(array($controller, $this->action)) == false)
{
$action = 'index';
}
else
{
$action = $this->action;
}
/*** run action ***/
$controller->$action();
}
/**
*
* private zodat je niet een nieuwe
* instance kan maken
*
*/
private function __construct() {
}
/**
*
* database instance returnen of nog maken
*
* @return object (PDO)
*
* @access public
*
*/
public static function getInstance() {
if (!self::$instance)
{
self::$instance = new PDO("mysql:host=localhost;dbname=****", '****', '****');;
self::$instance-> setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
return self::$instance;
}
/**
*
* private zodat je de instance niet kan klonen
*
*
*/
private function __clone(){
}
}
?>
dir views --> smarty templates
controller/error404.php
<?php
Class error404Controller Extends baseController {
public function index()
{
// show error met smarty
}
}
?>
controller/indexController.php
<?php
class indexController extends baseController {
public function index() {
/*** set template vars ***/
$this->registry->smarty->assign('title','Home');
$this->registry->smarty->assign('var1', 'Dt is var 1');
Goed begin. Het wordt vaak pas lastiger wanneer je iets serieus wilt bouwen. Bijvoorbeeld iets met een database, formulieren en terugkomende stukken pagina. Ik denk dat een gastenboek met navigatie (en dan in de navigatie aangeven of je op de bekijk- of de plaats-bericht-pagina bent) het makkelijkste probeersel is van die wat realistischere applicatie. Of een nieuwssysteem met categorieën.
Lijkt de basis je zo goed (genoeg)?
MVC is nogal nieuw voor me, maar ik begrijp wat er gebeurt maar ik vraag me af of dit goed genoeg is om mee te gaan werken.
Volgens mij klopt je implementatie tot nu toe wel. Misschien dat het logischer is om ook smarty en je router in init.php de instantiëren, zodat die configuratie niet in index.php komt (en daardoor verspreidt, ik zou alle configuratie in init.php doen. Dus ook je databasegegevens) maar dat is detail en persoonlijke voorkeur.
Volgens mij klopt je implementatie tot nu toe wel. Misschien dat het logischer is om ook smarty en je router in init.php de instantiëren, zodat die configuratie niet in index.php komt (en daardoor verspreidt, ik zou alle configuratie in init.php doen. Dus ook je databasegegevens) maar dat is detail en persoonlijke voorkeur.
Dat heb ik gedaan ;)
Ik zal binnenkort met user class hier posten aangezien er waarschijnlijk wel het een en ander aan op te merken is ;P
if($this->loggedIn == true)
{
throw new Exception('Je kan niet inloggen als je al ingelogd bent.');
}
/* Hash uit DB halen */
$sql = 'SELECT id, hash FROM tbl_gebruikers WHERE username = :username';
$stmt = $this->registry->db->prepare($sql);
$stmt->bindParam(':username', $username, PDO::PARAM_STR);
$stmt->execute();
if($stmt->rowCount() != 1)
{
throw new Exception('Combinatie gebruikersnaam/wachtwoord niet gevonden');
}
if($stmt->rowCount() == 0)
{
throw new Exception('Kon gebruikerinfo niet vinden');
}
return $result;
}
public function editProfileInfo($profileInfo){
if($this->loggedIn == false)
{
throw new Exception('Kan niet profileinfo aanpassen zonder ingelogd te zijn.');
}
/* USER INPUT CHECKEN*/
$errors = array();
(User_Validator::checkVoornaam($profileInfo[':voornaam'] = trim($profileInfo[':voornaam']))) ? NULL : $errors['voornaam'] = 'Een voornaam mag alleen uit letters bestaan en moet meer dan 1 letter en minder dan 20 letters bevatten';
(User_Validator::checkTussenvoegsel($profileInfo[':tussenvoegsel'] = trim($profileInfo[':tussenvoegsel']))) ? NULL : $errors['tussenvoegsel'] = 'Mag ook leeg zijn. Een tussenvoegsel mag alleen bestaan uit letters bestaan en moet minder dan 10 letters bevatten';
(User_Validator::checkAchternaam($profileInfo[':achternaam'] = trim($profileInfo[':achternaam']))) ? NULL : $errors['achternaam'] = 'Een achternaam mag alleen uit letters bestaan en moet meer dan 1 letter en minder dan 25 letters bevatten';
(User_Validator::checkEmail($profileInfo[':email'] = trim($profileInfo[':email']))) ? NULL : $errors['email'] = 'Het e-mailadres moet geldig zijn.';
if(User_Validator::checkGeslacht($profileInfo[':geslacht'] = intval($profileInfo[':geslacht']))){}else{throw new Exception('Geslacht moet een INT zijn en mag niet groter zijn dan het getal 2');}
if($this->loggedIn == true)
{
throw new Exception('Je kan niet registreren als je ingelogd bent.');
}
/* USER INPUT CHECKEN*/
$errors = array();
($registerInfo[':password'] == $registerInfo[':passwordrepeat']) ? NULL : $errors['passwordrepeat'] = 'Wachtwoorden moeten hetzelfde zijn';
(User_Validator::checkUsername($registerInfo[':username'] = trim($registerInfo[':username']))) ? NULL : $errors['username'] = 'Moet langer dan 3 en korter dan 16 tekens zijn. Mag alleen bestaan uit letters, cijfers, "_" en "-"';
(User_Validator::checkPassword($registerInfo[':password'])) ? NULL : $errors['password'] = 'Moet langer dan 6 en korter dan 18 tekens zijn. Mag alleen bestaan uit letters, cijfers, "_" en "-"';
(User_Validator::checkVoornaam($registerInfo[':voornaam'] = trim($registerInfo[':voornaam']))) ? NULL : $errors['voornaam'] = 'Een voornaam mag alleen uit letters bestaan en moet meer dan 1 letter en minder dan 20 letters bevatten';
(User_Validator::checkTussenvoegsel($registerInfo[':tussenvoegsel'] = trim($registerInfo[':tussenvoegsel']))) ? NULL : $errors['tussenvoegsel'] = 'Mag ook leeg zijn. Een tussenvoegsel mag alleen bestaan uit letters bestaan en moet minder dan 10 letters bevatten';
(User_Validator::checkAchternaam($registerInfo[':achternaam'] = trim($registerInfo[':achternaam']))) ? NULL : $errors['achternaam'] = 'Een achternaam mag alleen uit letters bestaan en moet meer dan 1 letter en minder dan 25 letters bevatten';
(User_Validator::checkEmail($registerInfo[':email'] = trim($registerInfo[':email']))) ? NULL : $errors['email'] = 'Het e-mailadres moet geldig zijn.';
if(User_Validator::checkGeslacht($registerInfo[':geslacht'] = intval($registerInfo[':geslacht']))){}else{throw new Exception('Geslacht moet een INT zijn en mag niet groter zijn dan het getal 2');}
public function changePassword($oldpassword, $newpassword, $newpasswordrepeat){
if($this->loggedIn == false)
{
throw new Exception('Je kunt je wachtwoord niet veranderen als je niet ingelogd bent');
}
if($newpassword != $newpasswordrepeat) { throw new Exception('Wachtwoorden moeten hetzelfde zijn'); }
if(User_Validator::checkPassword($newpassword)) { throw new Exception('Wachtwoord moet langer dan 6 en korter dan 18 tekens zijn. Mag alleen bestaan uit letters, cijfers, "_" en "-"'); }
/* Hash uit de database halen */
$sql = 'SELECT hash FROM tbl_gebruikers WHERE id = :id';
$stmt = $this->registry->db->prepare($sql);
$stmt->bindParam(':id', $this->id, PDO::PARAM_INT);
$stmt->execute();