MVC Routing
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
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
<?php
// controllers/newsitem.php
class NewsitemController extends Controller
{
private _model;
public function __construct()
{
$this->_model = new NewsitemModel();
}
public function getLatest()
{
return $this->_model->getLatest();
}
}
// models/newsitem.php
class NewsitemModel extends Model
{
public function getLatest()
{
$select = "SELECT * FROM newsitems ORDER BY id DESC";
$query = $this->_database->query($select);
if($query->rowCount() > 0)
{
return $query->fetchAll();
}
}
}
$controller = new NewsitemController();
$newsitems = $controller->getLatest();
?>
// controllers/newsitem.php
class NewsitemController extends Controller
{
private _model;
public function __construct()
{
$this->_model = new NewsitemModel();
}
public function getLatest()
{
return $this->_model->getLatest();
}
}
// models/newsitem.php
class NewsitemModel extends Model
{
public function getLatest()
{
$select = "SELECT * FROM newsitems ORDER BY id DESC";
$query = $this->_database->query($select);
if($query->rowCount() > 0)
{
return $query->fetchAll();
}
}
}
$controller = new NewsitemController();
$newsitems = $controller->getLatest();
?>
Gewijzigd op 24/11/2014 13:14:13 door Roy B
Ja, lijkt me prima.
Je moet alleen nog wel het resultaat van getLatest() ergens in stoppen ...
Klopt, had ik al gezien. Heb mijn post al aangepast :)
Hoe zit dit dan verder met de route?
Stel mijn url is /nieuws, dan wil ik een overzicht tonen van alle nieuwsitems. Stel mijn url is /nieuws/dit-is-mijn-eerste-nieuwsitem, dan wil ik het specifieke nieuwsitem tonen. En zo verder...
Als je meerdere nieuwsitems hebt, wat niet onwaarschijnlijk klinkt, kun je er beter nog een controller naast zetten. De methode getLatest() heeft binnen één nieuwsitem zelf immers geen betekenis. Er is geen laatste nieuwsitem binnen één nieuwsitem.
Wel kunnen ze eventueel hetzelfde model delen: dáárin dient de methode getLatest() een duidelijk doel, net zoals bijvoorbeeld getPrevious() en getNext() om te bladeren.
Google anders maar eens op het data mapper pattern en het factory method pattern. Een factory is een oplossing voor wat je nu met getLatest() doet: je wilt niet zomaar een nieuwsitem, maar specifiek het laatste nieuwsitem. Dat is in alle opzichten identiek aan andere nieuwsitems, met één verschil: het is het item met de laatste publicatiedatum en -tijd.
Dat is weer een heel andere discussie. Ik heb je alleen even geholpen met het model-verhaal ;)
Een route koppel je aan een action (actie). Uit de route moet je afleiden om welke actie het gaat. Dus /nieuws koppel je bijvoorbeeld aan een showNewsAction() en /nieuws/item/1/mijn-eerste-nieuws-item koppel je aan showItemAction waarbij in dit geval 1 het ID van het item in de database is.
Toevoeging op 24/11/2014 13:38:24:
@Ward:
>> Er is geen laatste nieuwsitem binnen één nieuwsitem.
Ik snap wat je bedoelt. Wellicht is het dan handiger om de controller niet NewsItemController te noemen, maar simpelweg NewsController, met als actions bijvoorbeeld show($amount), showItem($id) en showRecent($amount).
Valt me nog even op nu dat Roy dit doet:
$newsitems = $controller->getLatest();
Eigenlijk is een MVC patroon bedoeld om direct dingen te tonen (de V staat voor View). In dat geval zou het dus logischer zijn om te kiezen voor:
$controller->showRecent();
De controller zorgt dan ook dat de recente items daadwerkelijk worden getoond in de browser.
Gewijzigd op 24/11/2014 13:40:54 door Ozzie PHP
stel je hebt
'http://mydomain.com/news/' // alle nieuwsitems moeten getoond worden (in een lijst met titels)
'http://mydomain.com/news/latest' // de laatste 20 nieuwsitems moeten getoond worden (in een lijst met titels)
'http://mydomain.com/news/show/12' // laat één nieuwsbericht zien met id=12
en je hebt een NewsController:
Code (php)
Dan moet de Dispatcher (Of hoe je dat geval ook wilt noemen) er voor zorgen dat bij de eerste route in mijn lijstje de indexAction methode wordt aangeroepen en dat bij de tweede route in mijn lijstje de latestAction methode wordt aangeroepen. Bij de derde wordt het extra interessant want daar zit een variabele waarde in.
Die 12 kan immers net zo goed een ander getal zijn. Leuke uitdaging :p
Je zult dus ergens een lijst, array, xml of whatever moeten opslaan waarin aangegeven staat welke controller aangeroepen moet worden bij welke route er opgevraagd wordt.
Als je het slim wilt doen dan zorg je ook dat je heel makkelijk linkjes (denk aan <a href="..." >blabla</a> kunt maken in je view.
DUS:
Code (php)
1
2
3
4
5
6
2
3
4
5
6
<?php
$routes = array(
'latest_news' => new $route('news/latest'),
// en nog véél meer routes ...
);
?>
$routes = array(
'latest_news' => new $route('news/latest'),
// en nog véél meer routes ...
);
?>
en in de view:
Dit is dan even hoe het in Symfony & Twig gedaan wordt. path maakt dan van de route-label een echte route als
Gewijzigd op 24/11/2014 15:01:11 door Frank Nietbelangrijk
Ozzie PHP op 24/11/2014 13:33:18:
Valt me nog even op nu dat Roy dit doet:
$newsitems = $controller->getLatest();
Eigenlijk is een MVC patroon bedoeld om direct dingen te tonen (de V staat voor View). In dat geval zou het dus logischer zijn om te kiezen voor:
$controller->showRecent();
De controller zorgt dan ook dat de recente items daadwerkelijk worden getoond in de browser.
$newsitems = $controller->getLatest();
Eigenlijk is een MVC patroon bedoeld om direct dingen te tonen (de V staat voor View). In dat geval zou het dus logischer zijn om te kiezen voor:
$controller->showRecent();
De controller zorgt dan ook dat de recente items daadwerkelijk worden getoond in de browser.
Hoe zou ik de view dan kunnen koppelen?
@Frank,
Frank Nietbelangrijk op 24/11/2014 14:42:10:
en in de view:
Dit is dan even hoe het in Symfony & Twig gedaan wordt. path maakt dan van de route-label een echte route als
Code (php)
1
2
3
4
5
6
2
3
4
5
6
<?php
$routes = array(
'latest_news' => new $route('news/latest'),
// en nog véél meer routes ...
);
?>
$routes = array(
'latest_news' => new $route('news/latest'),
// en nog véél meer routes ...
);
?>
en in de view:
Dit is dan even hoe het in Symfony & Twig gedaan wordt. path maakt dan van de route-label een echte route als
Dus stel ik zet mijn routes in een array...
Hoe weet de route dan welke controller moet worden aangeroepen?
En welke methode vervolgens moet worden aangeroepen?
Gewijzigd op 24/11/2014 15:32:30 door Roy B
Dat moet je dus gaan inbouwen. Vanuit de betreffende action ga je dan een view genereren. In je showNewsItemAction() komt dus aan het eind iets te staan als $this->render('newsitem.phtml').
Ozzie PHP op 24/11/2014 15:43:35:
Dat moet je dus gaan inbouwen. Vanuit de betreffende action ga je dan een view genereren. In je showNewsItemAction() komt dus aan het eind iets te staan als $this->render('newsitem.phtml').
Dan krijg ik dus zoiets?
En wat gebeurt er dan in render()?
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
class Controller
{
public function render($file)
{
}
}
class NewsitemController extends Controller
{
private _model;
public function __construct()
{
$this->_model = new NewsitemModel();
}
public function getLatest()
{
$newsitems = $this->_model->getLatest();
$this->render("nieuws.html");
}
}
// models/newsitem.php
class NewsitemModel extends Model
{
public function getLatest()
{
$select = "SELECT * FROM newsitems ORDER BY id DESC";
$query = $this->_database->query($select);
if($query->rowCount() > 0)
{
return $query->fetchAll();
}
}
}
$controller = new NewsitemController();
$controller->getLatest();
?>
class Controller
{
public function render($file)
{
}
}
class NewsitemController extends Controller
{
private _model;
public function __construct()
{
$this->_model = new NewsitemModel();
}
public function getLatest()
{
$newsitems = $this->_model->getLatest();
$this->render("nieuws.html");
}
}
// models/newsitem.php
class NewsitemModel extends Model
{
public function getLatest()
{
$select = "SELECT * FROM newsitems ORDER BY id DESC";
$query = $this->_database->query($select);
if($query->rowCount() > 0)
{
return $query->fetchAll();
}
}
}
$controller = new NewsitemController();
$controller->getLatest();
?>
Gewijzigd op 24/11/2014 16:02:26 door Roy B
Daarin roept de controller de view aan.
Die methode wordt ook vaak index() genoemd, omdat deze zich gedraagt als een index.html of index.php.
Die include de view die jij meegeeft, waarschijnlijk met daarbij ook nog een array, waarvan je de data in de view kunt gebruiken. De view laten we dan meestal eindigen op de extensie .phtml (een combinatie van html en PHP).
Kun je eens een voorbeeldje geven hoe dat precies werkt dan?
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
// in je news controller
public function getLatest()
{
$newsitems = $this->_model->getLatest();
$this->render("nieuws.phtml", array('newsitems' => $newsitems, 'title' => 'nieuws'));
}
// in je controller
public function render($view, $data) {
extract($data);
include 'path/to/views/' . $view;
}
// in je view
echo $title;
print_r($newsitems);
?>
// in je news controller
public function getLatest()
{
$newsitems = $this->_model->getLatest();
$this->render("nieuws.phtml", array('newsitems' => $newsitems, 'title' => 'nieuws'));
}
// in je controller
public function render($view, $data) {
extract($data);
include 'path/to/views/' . $view;
}
// in je view
echo $title;
print_r($newsitems);
?>
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
<?php
class Route
{
private $path;
private $controller;
private $action;
function __construct($path, $controller, $action = 'index') {
$this->path = $path;
$this->controller = $controller;
$this->action = $action;
}
function getPath()
{
return $this->path;
}
function getController()
{
return $this->$controller;
}
function getAction()
{
return $this->$action;
}
}
class Dispatcher
{
private $routes;
function __construct($routes) {
$this->routes = $routes;
}
public function matchRoute($path)
{
$pathParts = explode('/', $path);
foreach($this->routes as $route)
{
if($this->compareRouteParts(explode('/', $route->getPath()), $pathParts))
return $route;
}
return NULL;
}
private function compareRouteParts($routeParts, $pathParts)
{
// als de twee arrays ongelijk zijn van lengte dan zijn de routes sowiezo niet gelijk
if(count($routeParts) != count($pathParts))
return FALSE;
// loop dan nu door alle elementen in de array's
for($i = 0 ; $i < count($routeParts) ; $i++)
{
// variabelen zijn altijd geldig
if($routeParts[$i][0] == ':')
continue;
// indien dit deel geen variabele is dan moeten de elementen dus gelijk zijn
if($pathParts[$i] != $routeParts[$i])
return FALSE;
}
return TRUE;
}
}
$routes = array(
'news' => new Route('news', 'News'),
'news_latest' => new Route('news/latest', 'News', 'latest'),
'news_show' => new Route('news/show/:id', 'News', 'show'),
);
$dispatcher = new Dispatcher($routes);
$route = $dispatcher->matchRoute('news');
if($route) {
var_dump($route); echo '<br>';
} else
echo 'ongeldige route<br>';
$route = $dispatcher->matchRoute('news/latest');
if($route) {
var_dump($route); echo '<br>';
} else
echo 'ongeldige route<br>';
$route = $dispatcher->matchRoute('news/show/12');
if($route) {
var_dump($route); echo '<br>';
} else
echo 'ongeldige route<br>';
$route = $dispatcher->matchRoute('news/pannekoek');
if($route) {
var_dump($route); echo '<br>';
} else
echo 'ongeldige route<br>';
?>
class Route
{
private $path;
private $controller;
private $action;
function __construct($path, $controller, $action = 'index') {
$this->path = $path;
$this->controller = $controller;
$this->action = $action;
}
function getPath()
{
return $this->path;
}
function getController()
{
return $this->$controller;
}
function getAction()
{
return $this->$action;
}
}
class Dispatcher
{
private $routes;
function __construct($routes) {
$this->routes = $routes;
}
public function matchRoute($path)
{
$pathParts = explode('/', $path);
foreach($this->routes as $route)
{
if($this->compareRouteParts(explode('/', $route->getPath()), $pathParts))
return $route;
}
return NULL;
}
private function compareRouteParts($routeParts, $pathParts)
{
// als de twee arrays ongelijk zijn van lengte dan zijn de routes sowiezo niet gelijk
if(count($routeParts) != count($pathParts))
return FALSE;
// loop dan nu door alle elementen in de array's
for($i = 0 ; $i < count($routeParts) ; $i++)
{
// variabelen zijn altijd geldig
if($routeParts[$i][0] == ':')
continue;
// indien dit deel geen variabele is dan moeten de elementen dus gelijk zijn
if($pathParts[$i] != $routeParts[$i])
return FALSE;
}
return TRUE;
}
}
$routes = array(
'news' => new Route('news', 'News'),
'news_latest' => new Route('news/latest', 'News', 'latest'),
'news_show' => new Route('news/show/:id', 'News', 'show'),
);
$dispatcher = new Dispatcher($routes);
$route = $dispatcher->matchRoute('news');
if($route) {
var_dump($route); echo '<br>';
} else
echo 'ongeldige route<br>';
$route = $dispatcher->matchRoute('news/latest');
if($route) {
var_dump($route); echo '<br>';
} else
echo 'ongeldige route<br>';
$route = $dispatcher->matchRoute('news/show/12');
if($route) {
var_dump($route); echo '<br>';
} else
echo 'ongeldige route<br>';
$route = $dispatcher->matchRoute('news/pannekoek');
if($route) {
var_dump($route); echo '<br>';
} else
echo 'ongeldige route<br>';
?>
Gewijzigd op 24/11/2014 18:18:34 door Frank Nietbelangrijk
Het is even geleden, maar ik had niet eerder tijd ervoor.
Ik heb nu het volgende:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
// routes.php
$routes = array();
$routes[] = array("route" => "nieuws", "controller" => "Newsitem", "action" => "index");
$routes[] = array("route" => "nieuws/{titel}", "controller" => "Newsitem", "action" => "show");
// index.php
include "includes/config.php";
include "includes/routes.php";
$request = new Request();
// Save our request
$registry = new Registry();
$registry->set("request", $request);
// En hoe dan verder?
?>
// routes.php
$routes = array();
$routes[] = array("route" => "nieuws", "controller" => "Newsitem", "action" => "index");
$routes[] = array("route" => "nieuws/{titel}", "controller" => "Newsitem", "action" => "show");
// index.php
include "includes/config.php";
include "includes/routes.php";
$request = new Request();
// Save our request
$registry = new Registry();
$registry->set("request", $request);
// En hoe dan verder?
?>
Stel mijn route is /nieuws/dit-is-een-titel.
Dan moet er dus gekeken worden welke controller hier aan gekoppeld moet worden en welke action uitgevoerd moet worden. In dit geval zou dat de show action zijn van de NewsitemController. Maar hoe bepaal ik nu welke controller moet worden aangeroepen bij welke URL?
Gewijzigd op 20/01/2015 16:11:12 door Roy B