MVC w praktyce – tworzymy system artykułów. cz. 1

Tworząc różnego rodzaju aplikacje natrafiamy na poważny problem utrzymania dobrej organizacji kodu – przejrzystej oraz łatwej w rozbudowie. Z pomocą przychodzą nam wzorce projektowe, które wymuszają na nas pewną organizację kodu aplikacji. W świecie aplikacji www najbardziej popularny jest wzorzec MVC. Jego ideę pokażę w praktyce – pisząc prosty system artykułów.

Uwaga. Pojawił się zaktualizowany cykl o MVC – przejdź


Żeby w pełni zrozumieć ideę tego wzorca projektowego czytelnik musi mieć solidne podstawy znajomości PHP oraz potrafić programować obiektowo.

Trochę teorii…

Model-View-Controller został zaprojektowany w 1979 roku przez norweskiego programistę Trygve Reenskaug pracującego wtedy nad językiem Smalltalk w laboratoriach Xerox i początkowo nosił nazwę Model-View-Editor.

Ideą tego wzorca jest rozdzielenie kodu odpowiedzialnego za przetworzenie danych od kodu odpowiedzialnego za ich wyświetlanie.

Model-View-Controller zakłada podział aplikacji na trzy główne części:

  • Model – jest pewną reprezentacją problemu bądź logiki aplikacji.
  • Widok – opisuje, jak wyświetlić pewną część modelu w ramach interfejsu użytkownika.
  • Kontroler – przyjmuje dane wejściowe od użytkownika i reaguje na jego poczynania, zarządzając aktualizacje modelu oraz odświeżenie widoków.

Brzmi strasznie, ale w praktyce okazuje się, że to wcale nie jest takie trudne …

No to zaczynamy!

Na samym początku stwórzmy szkielet katalogów:

config/
controller/
model/
view/
templates/

Mając hierarchię katalogów stwórzmy szkielet plików wzorca MVC:

controller/controller.php

<?php
/**
 * @author Łukasz Socha <kontakt@lukasz-socha.pl>
 * @version: 1.0
 * @license http://www.gnu.org/copyleft/lesser.html
 */

/**
 * This class includes methods for controllers.
 *
 * @abstract
 */
abstract class Controller{

    /**
     * It redirects URL.
     *
     * @param string $url URL to redirect
     *
     * @return void
     */
    public function redirect($url) {

    }
    /**
     * It loads the object with the view.
     *
     * @param string $name name class with the class
     * @param string $path pathway to the file with the class
     *
     * @return object
     */
    public function loadView($name, $path='') {

    }
    /**
     * It loads the object with the model.
     *
     * @param string $name name class with the class
     * @param string $path pathway to the file with the class
     *
     * @return object
     */
    public function loadModel($name, $path='') {

    }
}

model/model.php

<?php
/**
 * @author Łukasz Socha <kontakt@lukasz-socha.pl>
 * @version: 1.0
 * @license http://www.gnu.org/copyleft/lesser.html
 */

/**
 * This class includes methods for models.
 *
 * @abstract
 */
abstract class Model{
    /**
     * object of the class PDO
     *
     * @var object
     */
    protected $pdo;

    /**
     * It sets connect with the database.
     *
     * @return void
     */
    public function  __construct() {

    }
    /**
     * It loads the object with the model.
     *
     * @param string $name name class with the class
     * @param string $path pathway to the file with the class
     *
     * @return object
     */
    public function loadModel($name, $path='') {

    }
    /**
     * It selects data from the database.
     *
     * @param string $from Table
     * @param <type> $select Records to select (default * (all))
     * @param <type> $where Condition to query
     * @param <type> $order Order ($record ASC/DESC)
     * @param <type> $limit LIMIT
     * @return array
     */
    public function select($from, $select='*', $where=NULL, $order=NULL, $limit=NULL) {

    }
}

view/view.php

<?php
/**
 * @author Łukasz Socha <kontakt@lukasz-socha.pl>
 * @version: 1.0
 * @license http://www.gnu.org/copyleft/lesser.html
 */

/**
 * This class includes methods for views.
 *
 * @abstract
 */
abstract class View{

    /**
     * It loads the object with the model.
     *
     * @param string $name name class with the class
     * @param string $path pathway to the file with the class
     *
     * @return object
     */
    public function loadModel($name, $path='') {

    }
    /**
     * It includes template file.
     *
     * @return void
     */
    public function render() {

    }
    /**
     * It sets data.
     *
     * @param string $name
     * @param mixed $value
     *
     * @return void
     */
    public function set($name, $value) {

    }
    /**
     * It gets data.
     *
     * @param string $name
     *
     * @return mixed
     */
    public function get($name) {

    }
}

Pliki te zawierają zarys podstawowych metod, które wykorzystamy w tworzeniu systemu artykułów używając wzorca Model-View-Controller. Klasy oznaczyłem jako abstrakcyjne, gdyż będą one dziedziczone po bardziej specjalistycznych elementach – wykonujących już konkretne czynności. W dalszej części będziemy je sukcesywnie wypełniać kodem.

Tworzenie kodu aplikacji zacznijmy od controller/controller.php

<?php
/**
 * @author Łukasz Socha <kontakt@lukasz-socha.pl>
 * @version: 1.0
 * @license http://www.gnu.org/copyleft/lesser.html
 */

/**
 * This class includes methods for controllers.
 *
 * @abstract
 */
abstract class Controller{

    /**
     * It redirects URL.
     *
     * @param string $url URL to redirect
     *
     * @return void
     */
    public function redirect($url) {
        header("location: ".$url);
    }
    /**
     * It loads the object with the view.
     *
     * @param string $name name class with the class
     * @param string $path pathway to the file with the class
     *
     * @return object
     */
    public function loadView($name, $path='view/') {
        $path=$path.$name.'.php';
        $name=$name.'View';
        try {
            if(is_file($path)) {
                require $path;
                $ob=new $name();
            } else {
                throw new Exception('Can not open view '.$name.' in: '.$path);
            }
        }
        catch(Exception $e) {
            echo $e->getMessage().'<br />
                File: '.$e->getFile().'<br />
                Code line: '.$e->getLine().'<br />
                Trace: '.$e->getTraceAsString();
            exit;
        }
        return $ob;
    }
    /**
     * It loads the object with the model.
     *
     * @param string $name name class with the class
     * @param string $path pathway to the file with the class
     *
     * @return object
     */
    public function loadModel($name, $path='model/') {
        $path=$path.$name.'.php';
        $name=$name.'Model';
        try {
            if(is_file($path)) {
                require $path;
                $ob=new $name();
            } else {
                throw new Exception('Can not open model '.$name.' in: '.$path);
            }
        }
        catch(Exception $e) {
            echo $e->getMessage().'<br />
                File: '.$e->getFile().'<br />
                Code line: '.$e->getLine().'<br />
                Trace: '.$e->getTraceAsString();
            exit;
        }
        return $ob;
    }
}

Metoda redirect() służy do przekierowania strony na wskazany adres. Z kolei metody loadModel() i loadView() inicjują obiekty klas widoku oraz modelu.

Plik model/model.php będzie wyglądać tak:

<?php
/**
 * @author Łukasz Socha <kontakt@lukasz-socha.pl>
 * @version: 1.0
 * @license http://www.gnu.org/copyleft/lesser.html
 */

/**
 * This class includes methods for models.
 *
 * @abstract
 */
abstract class Model{
    /**
     * object of the class PDO
     *
     * @var object
     */
    protected $pdo;

    /**
     * It sets connect with the database.
     *
     * @return void
     */
    public function  __construct() {
        try {
            require 'config/sql.php';
            $this->pdo=new PDO('mysql:host='.$host.';dbname='.$dbase, $user, $pass);
            $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        }
        catch(DBException $e) {
            echo 'The connect can not create: ' . $e->getMessage();
        }
    }
    /**
     * It loads the object with the model.
     *
     * @param string $name name class with the class
     * @param string $path pathway to the file with the class
     *
     * @return object
     */
    public function loadModel($name, $path='model/') {
        $path=$path.$name.'.php';
        $name=$name.'Model';
        try {
            if(is_file($path)) {
                require $path;
                $ob=new $name();
            } else {
                throw new Exception('Can not open model '.$name.' in: '.$path);
            }
        }
        catch(Exception $e) {
            echo $e->getMessage().'<br />
                File: '.$e->getFile().'<br />
                Code line: '.$e->getLine().'<br />
                Trace: '.$e->getTraceAsString();
            exit;
        }
        return $ob;
    }
    /**
     * It selects data from the database.
     *
     * @param string $from Table
     * @param <type> $select Records to select (default * (all))
     * @param <type> $where Condition to query
     * @param <type> $order Order ($record ASC/DESC)
     * @param <type> $limit LIMIT
     * @return array
     */
    public function select($from, $select='*', $where=NULL, $order=NULL, $limit=NULL) {
        $query='SELECT '.$select.' FROM '.$from;
        if($where!=NULL)
            $query=$query.' WHERE '.$where;
        if($order!=NULL)
            $query=$query.' ORDER BY '.$order;
        if($limit!=NULL)
            $query=$query.' LIMIT '.$limit;

        $select=$this->pdo->query($query);
        foreach ($select as $row) {
            $data[]=$row;
        }
        $select->closeCursor();

        return $data;
    }
}

Konstruktor klasy Model ma nawiązać połączenie z bazą danych (używamy do tego PDO). Metoda loadModel() ma za zadanie stworzyć obiekt z klasą modelu. Natomiast select() będziemy używać do pobieranie danych.

Dodajmy jeszcze kod do pliku view/view.php:

<?php
/**
 * @author Łukasz Socha <kontakt@lukasz-socha.pl>
 * @version: 1.0
 * @license http://www.gnu.org/copyleft/lesser.html
 */

/**
 * This class includes methods for views.
 *
 * @abstract
 */
abstract class View{

    /**
     * It loads the object with the model.
     *
     * @param string $name name class with the class
     * @param string $path pathway to the file with the class
     *
     * @return object
     */
    public function loadModel($name, $path='model/') {
        $path=$path.$name.'.php';
        $name=$name.'Model';
        try {
            if(is_file($path)) {
                require $path;
                $ob=new $name();
            } else {
                throw new Exception('Can not open model '.$name.' in: '.$path);
            }
        }
        catch(Exception $e) {
            echo $e->getMessage().'<br />
                File: '.$e->getFile().'<br />
                Code line: '.$e->getLine().'<br />
                Trace: '.$e->getTraceAsString();
            exit;
        }
        return $ob;
    }
    /**
     * It includes template file.
     *
     * @param string $name name template file
     * @param string $path pathway
     *
     * @return void
     */
    public function render($name, $path='templates/') {
        $path=$path.$name.'.html.php';
        try {
            if(is_file($path)) {
                require $path;
            } else {
                throw new Exception('Can not open template '.$name.' in: '.$path);
            }
        }
        catch(Exception $e) {
            echo $e->getMessage().'<br />
                File: '.$e->getFile().'<br />
                Code line: '.$e->getLine().'<br />
                Trace: '.$e->getTraceAsString();
            exit;
        }
    }
    /**
     * It sets data.
     *
     * @param string $name
     * @param mixed $value
     *
     * @return void
     */
    public function set($name, $value) {
        $this->$name=$value;
    }
    /**
     * It gets data.
     *
     * @param string $name
     *
     * @return mixed
     */
    public function get($name) {
        return $this->$name;
    }
}

Metoda loadModel() działa tak samo jak w poprzednich plikach. Metodę render() będziemy wykorzystywać do dołączania plików z kodem HTML.

Tworzymy tabele bazy danych

Na zakończenie tej części stwórzmy tabele kategorii i artykułów w bazy danych:

CREATE TABLE categories(
id integer auto_increment,
name varchar(100),
primary key(id)
);

CREATE TABLE articles(
id integer auto_increment,
title varchar(100),
content text,
date_add datetime,
autor varchar(100),
id_categories integer,
primary key(id),
foreign key(id_categories) references categories(id)
);

W pliku config/sql.php zapiszmy dane dostępu do bazy danych.

<?php
$host='localhost';
$dbase='baza danych';
$user='user';
$pass='hasło';

W kolejnej części cyklu zaczniemy już dokładniej poznawać idee MVC. Stworzymy fragment aplikacji odpowiedzialny za dodawanie kategorii.

Powiązane tematy

Print Friendly, PDF & Email