Ik ben bezig met het ontwikkelen van een eigen MVC. Via htaccess stuur ik alles door naar mijn index. Mijn index bepaalt vervolgens welke controller moet worden aangeroepen. Stel mijn url is /nieuws/nieuwsitem. In dit geval moet de NewsitemController worden aangeroepen om het nieuwsitem te tonen. Stel mijn URL is /over-ons. Dan moet de ContentController worden aangeroepen en een contentpagina getoond worden. Maar nu mijn vraag.. Hoe weet index welke controller moet worden aangeroepen?


// index.php
$route = explode("/", $_SERVER["REQUEST_URI"]);
Dus als ik het goed begrepen heb..


<?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();
?>
@Roy:

Ja, lijkt me prima.

Je moet alleen nog wel het resultaat van getLatest() ergens in stoppen ...

<?php
$recent_news = $controller->getLatest();
?>
@Ozzie,
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...
Ja, precies: je kunt nu honderden of duizenden applicaties tegelijk verbouwen door alleen die ene controller te herschrijven.

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.
>> Hoe zit dit dan verder met de route?

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.
Routes:

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:
<?php
class NewsController
{
public function indexAction() {}
public function latestAction() {}
public function showAction($id) {}
}
?>

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:
<?php
$routes = array(
'latest_news' => new $route('news/latest'),
// en nog véél meer routes ...
);
?>
en in de view:

<a href="{{ path('latest_news') }}">Laatste nieuws</a>


Dit is dan even hoe het in Symfony & Twig gedaan wordt. path maakt dan van de route-label een echte route als

/news/latest
@Ozzie,
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.

Hoe zou ik de view dan kunnen koppelen?

@Frank,
Frank Nietbelangrijk op 24/11/2014 14:42:10


<?php
$routes = array(
    'latest_news' => new $route('news/latest'),
    // en nog véél meer routes ...
);
?>


en in de view:

<a href="{{ path('latest_news') }}">Laatste nieuws</a>


Dit is dan even hoe het in Symfony & Twig gedaan wordt. path maakt dan van de route-label een echte route als

/news/latest


Dus stel ik zet mijn routes in een array...

<?php
$routes = array(
    "nieuws" => new $route("nieuws'),
    ...
);
?>

Hoe weet de route dan welke controller moet worden aangeroepen?
En welke methode vervolgens moet worden aangeroepen?
>> Hoe zou ik de view dan kunnen koppelen?

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()?

<?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();
?>
>> En wat gebeurt er dan in render()?

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.

Reageren