Waar gebruik ik het Response object?
In mijn MVC maakte in eerst geen gebruik van een Response object, maar wel van een Request object. Ik ben dus nu een Response object aan het inbouwen. Maar waar doe ik dat eigenlijk?
Het zit zo in elkaar:
index.php
Doet de routing. Hier wordt een router object gemaakt etc. De router krijgt een Request object. De router include de controller die bij de route hoort en de actie van de controller wordt uitgevoerd. Aan de actie wordt het Request object meegegeven als parameter.
In controller
Worden model objecten gemaakt.
Bij mij hebben models nooit een verband met views of controllers.
De controller vraagt met methods aan een model on gegevens, of geeft aan het model een opdracht om gegevens aan te passen.
Wel geef ik bijna altijd een Database object mee aan een model object via de constructor.
De controller include daarna een view bestand, en een array met variabelen voor de view wordt ge-extract.
Bij het includen van de view is eigenlijk alles uitgevoerd, dus wordt er vanzelf een response gegeven, omdat PHP gewoon klaar is (callstack is klaar, of hoe noem je dat?) doordat het view bestand is ge-include wordt dus ook dat bestand weergeven.
Waar doe ik een Response object inbouwen, en wat moet dat object eigenlijk doen?
Kort samengevat: je moet de view (die je al hebt) nog in een response verpakken.
Daar zijn verschillende redenen voor te verzinnen. De lastigste: één response kan meerdere views bevatten als er meerdere controllers aan te pas kwamen.
(niks moet...) je deze meegeven aan een variable en het response object, welke door de controller wordt gereturned en door de front-controller wordt gerendered.
Dit omdat je naast gewoon outputten je je controller meer vrijheid wil geven. Zo moet de response vast niet altijd 200 OK zijn, maar bijv. ook wel eens 404 Not Found. Ook dit is de taak van de Response object.
Om nog een ander voorbeeld te noemen, de Content-Type is niet altijd text/html. Zo kan het voorkomen dat een controller een JSON response terug geeft, Content-Type: application/json. Ook dit is de taak van de Response.
Eigenlijk heeft Response dus praktisch dezelfde taken als Request. Waar de Request een wrapper om de request headers (uri, method, parameters, etc.) is is de Response een wrapper om de response headers (content-type, status, body, etc.).
I.p.v. dat de controller de view direct op het scherm rendered "moet" Dit omdat je naast gewoon outputten je je controller meer vrijheid wil geven. Zo moet de response vast niet altijd 200 OK zijn, maar bijv. ook wel eens 404 Not Found. Ook dit is de taak van de Response object.
Om nog een ander voorbeeld te noemen, de Content-Type is niet altijd text/html. Zo kan het voorkomen dat een controller een JSON response terug geeft, Content-Type: application/json. Ook dit is de taak van de Response.
Eigenlijk heeft Response dus praktisch dezelfde taken als Request. Waar de Request een wrapper om de request headers (uri, method, parameters, etc.) is is de Response een wrapper om de response headers (content-type, status, body, etc.).
Gewijzigd op 17/05/2015 15:07:28 door Wouter J
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
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
public function loginPage() {
$form = new Form("POST");
$form->addElement(new EmailInput("email", "Geen emailadres ingevuld", "Ongeldig emailadres"));
$form->addElement(new PasswordInput("password", "Geen wachtwoord ingevuld"));
$form->setValidator(new LoginFormValidator());
$form->handleRequest($this->request);
if($form->isSubmit()) {
if($form->isValid()) {
$this->login();
} else {
$data = array("email" => htmlentities($this->request->postVar("email")));
$this->loadView("views/login.php", $data);
}
} else {
$data = array("email" => "");
$this->loadView("views/login.php", $data);
}
}
private function login() {
// when login form is submitted and valid
}
$form = new Form("POST");
$form->addElement(new EmailInput("email", "Geen emailadres ingevuld", "Ongeldig emailadres"));
$form->addElement(new PasswordInput("password", "Geen wachtwoord ingevuld"));
$form->setValidator(new LoginFormValidator());
$form->handleRequest($this->request);
if($form->isSubmit()) {
if($form->isValid()) {
$this->login();
} else {
$data = array("email" => htmlentities($this->request->postVar("email")));
$this->loadView("views/login.php", $data);
}
} else {
$data = array("email" => "");
$this->loadView("views/login.php", $data);
}
}
private function login() {
// when login form is submitted and valid
}
Ik laad nu steeds een pagina met loadView(). loadView() is een method van de parent (base) controller.
In plaats van dat ik loadView gebruik, kan ik dus beter een method in de base controller maken zoals response(Response $responseObject).
En dat response object, daar stel ik dan headers op in, zet ik views in etc.
Gewijzigd op 17/05/2015 15:36:04 door jan terhuijzen
Denk dat je wel even bij Symfony mag spieken: http://symfony.com/doc/current/components/http_foundation/introduction.html#response
Code (php)
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
<?php
loadView($template, $data)
{
$response = new Response();
//...
return $response;
}
?>
loadView($template, $data)
{
$response = new Response();
//...
return $response;
}
?>
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 DemoController
{
public function helloAction(Request $request)
{
return new Response('<h1>Hallo!</h1>');
// of met een status
return new Response('<h1>No product found!</h1>', 404);
// of een template renderen en die als body meegeven
return new Response($this->templating->render('static/hello.html.twig'));
// of iets compleet anders
$response = new Response(json_encode($data));
$response->headers->set('Content-Type', 'application/json');
return $response;
// ... etc
}
}
?>
class DemoController
{
public function helloAction(Request $request)
{
return new Response('<h1>Hallo!</h1>');
// of met een status
return new Response('<h1>No product found!</h1>', 404);
// of een template renderen en die als body meegeven
return new Response($this->templating->render('static/hello.html.twig'));
// of iets compleet anders
$response = new Response(json_encode($data));
$response->headers->set('Content-Type', 'application/json');
return $response;
// ... etc
}
}
?>
Zoals je ziet krijgt de controller op deze manier veel meer vrijheid. De front controller krijgt dan het resultaat en handelt het af (stelt de juiste response headers in, output de body, etc.).
dan plaats?
Dus waar wordt de content dan daadwerkelijk output (in symfony) ?
Code (php)
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
<?php
$kernel = new AppKernel(...);
// ...
$response = $kernel->handle(Request::createFromGlobals());
$response->send(); // het daadwerkelijke versturen van de content
// ... nog een terminate call om alles weer terug te zetten in de goede staat
?>
$kernel = new AppKernel(...);
// ...
$response = $kernel->handle(Request::createFromGlobals());
$response->send(); // het daadwerkelijke versturen van de content
// ... nog een terminate call om alles weer terug te zetten in de goede staat
?>
Dus hier: https://github.com/symfony/HttpFoundation/blob/master/Response.php#L373-385
Zoals je ziet is sendContent slechts een echo'tje en de rest een paar header() calls.
En dus inderdaad onderdeel van het Response Object :-) Dank je Wouter.
Frank Nietbelangrijk op 17/05/2015 16:00:17:
Ik zou loadView zelf een response object terug laten geven.
Denk dat je wel even bij Symfony mag spieken: http://symfony.com/doc/current/components/http_foundation/introduction.html#response
Denk dat je wel even bij Symfony mag spieken: http://symfony.com/doc/current/components/http_foundation/introduction.html#response
Code (php)
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
<?php
loadView($template, $data)
{
$response = new Response();
//...
return $response;
}
?>
loadView($template, $data)
{
$response = new Response();
//...
return $response;
}
?>
Als ik dus die methode loadView een Response object laat returnen, waar wordt dan een include gedaan ofzo?
Ik lees trouwens wel eens codes van Symfony, dat framework kijk ik veel van af.
Gewijzigd op 21/05/2015 16:42:42 door jan terhuijzen
Code (php)
1
2
3
4
2
3
4
<?php
// of een template renderen en die als body meegeven
return new Response($this->templating->render('static/hello.html.twig'));
?>
// of een template renderen en die als body meegeven
return new Response($this->templating->render('static/hello.html.twig'));
?>
Even een stukje van Wouter zijn voorbeeld gepakt.
Hier wordt Twig gebruikt om een pagina te genereren.
Een heel erg simpel voorbeeldje van een Twig template:
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
// views/customer/show.html.twig
<html>
<head>
<title>blabla</title>
</head>
<body>
<h1>Klantgegevens</h1>
<table>
<tr>
<th>naam:</th>
<td>{{ customer.name }}</td>
</tr>
<tr>
<th>email:</th>
<td>{{ customer.email }}</td>
</tr>
</table>
</body>
</html>
<html>
<head>
<title>blabla</title>
</head>
<body>
<h1>Klantgegevens</h1>
<table>
<tr>
<th>naam:</th>
<td>{{ customer.name }}</td>
</tr>
<tr>
<th>email:</th>
<td>{{ customer.email }}</td>
</tr>
</table>
</body>
</html>
Let dan vooral op de variabelen: {{ customer.name }} en {{ customer.email }}
Twig doet dus geen include maar een file_get_contents() !!!!
Ik hoop dat je het verschil weet hiertussen? anders gaan we daar dieper op in.
Twig vervangt de variabelen voor de daadwerkelijke naam en mailadres van de klant in dit voorbeeld.
Je hebt de controller, een response object (een storage met "het antwoord aan de client") en een class die dat antwoord in elkaar moet sleutelen. Hiervoor kunnen zoals Wouter al aangaf verschillende classes ingezet worden.
Zo heb je behalve een HTML response ook een JSON response, een Image response of een PDF response (etc)
Gewijzigd op 21/05/2015 18:02:28 door Frank Nietbelangrijk
Waarbij ik niet zeg dat het niet loont om af te kijken hoe andere frameworks dat doen.
Dit stukje code
Code (php)
1
2
3
4
2
3
4
<?php
// of een template renderen en die als body meegeven
return new Response($this->templating->render('static/hello.html.twig'));
?>
// of een template renderen en die als body meegeven
return new Response($this->templating->render('static/hello.html.twig'));
?>
Daar krijgt het response object de uiteindelijke output meegegeven in zijn constructor.
$this->templating->render('static/hello.html.twig') dat zorgt ervoor dat de inhoud van een template wordt opgehaald, met bijvoorbeeld file_get_contents(), en dat de inhoud van dat bestand wordt gelezen door een templating engine. Die templating engine vervangt dan bepaalde patterns zoals {{ customer.name }} door gegevens die hij ook op een of andere manier weet en dus kan invullen?
Gewijzigd op 22/05/2015 12:14:52 door jan terhuijzen