MVC w praktyce – tworzymy system artykułów. cz. 3
W ostatniej części artykułu o wzorcu MVC stworzymy pozostałe elementy prostego systemu artykułów.
Uwaga. Pojawił się zaktualizowany cykl o MVC – przejdź
Dobrą praktyką przy budowaniu aplikacji z użyciem wzorca MVC jest „rozbicie” całego kodu na poszczególne, mniejsze moduły. W poprzedniej części stworzyliśmy fragmenty kodu do obsługi kategorii, teraz zajmiemy się artykułami.
Tworzymy kontroler artykułów
Na początek tworzymy kontroler controllers/articles.php:
<?php /** * @author Łukasz Socha <kontakt@lukasz-socha.pl> * @version: 1.0 * @license http://www.gnu.org/copyleft/lesser.html */ include 'controller/controller.php'; class ArticlesController extends Controller{ public function index() { $view=$this->loadView('articles'); $view->index(); } public function one() { $view=$this->loadView('articles'); $view->one(); } public function add() { $view=$this->loadView('articles'); $view->add(); } public function insert() { $model=$this->loadModel('articles'); $model->insert($_POST); $this->redirect('?task=articles&action=index'); } public function delete() { $model=$this->loadModel('articles'); $model->delete($_GET['id']); $this->redirect('?task=articles&action=index'); } }
I tu także przeanalizujmy reakcje dla następujących adresów URL:
- ?task=articles&action=index – zostanie wywołana metoda index(), która inicjuje obiekt widoku articles, następnie zostaje wywołana metoda index()
- ?task=articles&action=one – zostanie wywołana metoda one(), która inicjuje obiekt widoku articles, następnie zostaje wywołana metoda one()
- ?task=articles&action=add – zostanie wywołana metoda add(), która inicjuje obiekt widoku articles, następnie zostaje wywołana metoda add()
- ?task=articles&action=insert – zostanie wywołana metoda insert(), która inicjuje obiekt modelu articles, następnie zostaje wywołana metoda insert()
- ?task=articles&action=delete – zostanie wywołana metoda delete(), która inicjuje obiekt modelu articles, następnie zostaje wywołana metoda delete()
Musimy jeszcze zmodyfikować plik index.php, by skrypt wywoływał kontroler Articles:
<?php if($_GET['task']=='categories') { include 'controller/categories.php'; $ob = new CategoriesController(); $ob->$_GET['action'](); } else if($_GET['task']=='articles') { include 'controller/articles.php'; $ob = new ArticlesController(); $ob->$_GET['action'](); } else { include 'controller/articles.php'; $ob = new ArticlesController(); $ob->index(); }
Mamy już kontroler. Teraz przejdźmy do modelu.
Tworzymy model artykułów
model/articles.php:
<?php /** * @author Łukasz Socha <kontakt@lukasz-socha.pl> * @version: 1.0 * @license http://www.gnu.org/copyleft/lesser.html */ include 'model/model.php'; class ArticlesModel extends Model{ public function getAll() { $query="SELECT a.id, a.title, a.date_add, a.autor, c.name FROM articles AS a LEFT JOIN categories AS c ON a.id_categories=c.id"; $select=$this->pdo->query($query); foreach ($select as $row) { $data[]=$row; } $select->closeCursor(); return $data; } public function getOne($id) { $query="SELECT a.id, a.title, a.date_add, a.autor, c.name, a.content FROM articles AS a LEFT JOIN categories AS c ON a.id_categories=c.id where a.id=".$id; $select=$this->pdo->query($query); foreach ($select as $row) { $data[]=$row; } $select->closeCursor(); return $data; } public function insert($data) { $ins=$this->pdo->prepare('INSERT INTO articles (title, content, date_add, autor, id_categories) VALUES ( :title, :content, :date_add, :autor, :id_categories)'); $ins->bindValue(':title', $data['title'], PDO::PARAM_STR); $ins->bindValue(':content', $data['content'], PDO::PARAM_STR); $ins->bindValue(':date_add', $data['date_add'], PDO::PARAM_STR); $ins->bindValue(':autor', $data['author'], PDO::PARAM_STR); $ins->bindValue(':id_categories', $data['cat'], PDO::PARAM_INT); $ins->execute(); } public function delete($id) { $del=$this->pdo->prepare('DELETE FROM articles where id=:id'); $del->bindValue(':id', $id, PDO::PARAM_INT); $del->execute(); } }
Tak jak w przypadku kategorii, metody w ArticlesModel dodają, usuwają oraz poobierają dane z bazy danych.
Tworzymy widok artykułów
Pozostaje nam jeszcze stworzenie widoku. Plik view/articles.php:
<?php /** * @author Łukasz Socha <kontakt@lukasz-socha.pl> * @version: 1.0 * @license http://www.gnu.org/copyleft/lesser.html */ include 'view/view.php'; class ArticlesView extends View{ public function index() { $art=$this->loadModel('articles'); $this->set('articles', $art->getAll()); $this->render('indexArticle'); } public function one() { $art=$this->loadModel('articles'); $this->set('articles', $art->getOne($_GET['id'])); $this->render('oneArticle'); } public function add() { $cat=$this->loadModel('categories'); $this->set('catsData', $cat->getAll()); $this->render('addArticle'); } }
Metoda index() generuje szablon HTML z listą wszystkich artykułów. Metoda one() wyświetla tylko jeden artykuł. Z kolei add() ma za zadanie wygenerować formularz dodawania artykułu. Warto zauważyć, że metoda ta pobiera listę kategorii z modelu CategoriesModel.
A wiec poszczególne warstwy aplikacji mogą wykorzystywać instancje wielu modeli. W dobrze zaprojektowanej aplikacji manipulacja danymi jest bardzo prosta – wystarczy załadować odpowiedni model i wykorzystywać jego publiczne metody.
Do pełni działającego skryptu brakuje nam jeszcze tylko plików szablonu.
Aktualizujemy plik templates/header.html.php:
<html> <head> <title>System newsów</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link href="css/style.css" rel="stylesheet" type="text/css" /> </head> <body> <ul> <li><a href="?task=categories&action=add">Dodaj kategorię</a></li> <li><a href="?task=categories&action=index">Lista kategorii</a></li> <li><a href="?task=articles&action=add">Dodaj artykuł</a> <li><a href="?task=articles&action=index">Lista artykułów</a> </ul>
templates/addArticle.html.php:
<? include 'templates/header.html.php'; ?> <h1>Dodaj artykuł</h1> <form action="?task=articles&action=insert" method="post"> Tytuł: <input type="text" name="title" /><br /> Autor: <input type="text" name="author" /><br /> Data dodania: <input type="text" name="date_add" value="<?= date("Y:m:d"); ?>" /><br /> Treść:<br /> <textarea name="content"></textarea><br /> Kategoria: <select name="cat" size="0"> <? foreach($this->get('catsData') as $cats) { ?> <option value="<?= $cats['id'] ;?>"><?= $cats['name']; ?></option> <? } ?> </select><br /> <input type="submit" value="Dodaj" /> </form> <? include 'templates/footer.html.php'; ?>
templates/indexArticle.html.php:
<? include 'templates/header.html.php'; ?> <h1>Lista artykułów</h1> <table> <tr> <td>Tytuł</td> <td>Data dodania</td> <td>Autor</td> <td>Kategoria</td> <td> </td> </tr> <? foreach($this->get('articles') as $articles) { ?> <tr> <td><a href="?task=articles&action=one&id=<?= $articles['id']; ?>"><?= $articles['title']; ?></a></td> <td><?= $articles['date_add']; ?></td> <td><?= $articles['autor']; ?></td> <td><?= $articles['name']; ?></td> <td><a href="?task=articles&action=delete&id=<?= $articles['id']; ?>">usuń</a></td> </tr> <? } ?> </table> <? include 'templates/footer.html.php'; ?>
templates/oneArticles.php:
<? include 'templates/header.html.php'; ?> <? foreach($this->get('articles') as $articles) { ?> <h1><?= $articles['title']; ?></h1> autor: <?= $articles['autor']; ?>, data dodania: <?= $articles['date_add']; ?><br /> Kategoria: <?= $articles['name']; ?> <p><?= $articles['content']; ?></p> <? } ?> <? include 'templates/footer.html.php'; ?>
Zakończenie
Tak stworzyliśmy działający skrypt oparty na wzorcu MVC. Dzięki rozdzieleniu warstwy logiki od formy prezentacji dalsza rozbudowa staje się znacznie prostsza. Możemy teraz dodać szablon HTML, bez potrzeby zaglądania w kontrolery oraz modele. Jest to szczególnie przydatne przy pracy nad większymi aplikacjami – osoby odpowiedzialne za wygląd mogą pracować praktycznie bez pomocy programistów.
Cały kod można pobrać stąd.