Lekcja 12 – Wzorce projektowe w PHP – wprowadzenie do wzorca MVC

Wprowadzenie

W miarę jak aplikacje internetowe rosną w złożoności, organizacja kodu staje się kluczowa dla jego łatwego utrzymania i rozwoju. Jednym z najczęściej używanych wzorców projektowych w aplikacjach webowych jest MVC (Model-View-Controller). MVC dzieli aplikację na trzy główne części: Model, View i Controller, co ułatwia zarządzanie kodem, wprowadzenie zmian i utrzymanie aplikacji w dłuższym czasie. W tej lekcji omówimy dokładnie, czym jest wzorzec MVC, jak go zaimplementować w PHP oraz jakie korzyści oferuje.

Co to jest MVC?

MVC (Model-View-Controller) to wzorzec projektowy, który dzieli aplikację na trzy główne komponenty:

  • Model: Odpowiada za logikę biznesową oraz operacje na danych (np. interakcje z bazą danych).
  • View: Odpowiada za prezentację danych, czyli to, co użytkownik widzi (interfejs użytkownika).
  • Controller: Odpowiada za przyjmowanie żądań użytkownika, kontrolowanie przepływu danych między modelem a widokiem oraz decydowanie, jakie dane powinny być wyświetlane.

Dzięki podziałowi aplikacji na te trzy komponenty, kod jest bardziej modularny, co ułatwia jego zarządzanie i rozbudowę. MVC pozwala na oddzielenie logiki biznesowej od prezentacji, co jest szczególnie ważne w dużych projektach.

Struktura aplikacji w MVC

W klasycznej aplikacji PHP pliki PHP, HTML i inne zasoby są często ze sobą wymieszane, co prowadzi do trudności w utrzymaniu kodu. MVC wprowadza porządek w strukturze aplikacji, organizując ją w odpowiednie foldery dla każdego z trzech komponentów.

Przykładowa struktura projektu MVC:

/app
  /controllers
    HomeController.php
    UserController.php
  /models
    User.php
  /views
    /home
      index.php
    /user
      login.php
      register.php
  /core
    Router.php
    Controller.php
    Model.php
  /config
    config.php
/public
  index.php

Implementacja wzorca MVC w PHP

1. Model – Warstwa logiki biznesowej

Model odpowiada za dostęp do danych oraz logikę aplikacji. Modele są klasami, które komunikują się z bazą danych i dostarczają dane do kontrolera.

Przykład modelu User:

<?php
class User {
  private $conn;

  public function __construct($db) {
    $this->conn = $db;
  }

  public function getUserById($id) {
    $stmt = $this->conn->prepare("SELECT * FROM users WHERE id = :id");
    $stmt->bindParam(':id', $id, PDO::PARAM_INT);
    $stmt->execute();
    return $stmt->fetch(PDO::FETCH_ASSOC);
  }

  public function createUser($username, $email, $password) {
    $stmt = $this->conn->prepare("INSERT INTO users (username, email, password) VALUES (:username, :email, :password)");
    $stmt->bindParam(':username', $username);
    $stmt->bindParam(':email', $email);
    $stmt->bindParam(':password', password_hash($password, PASSWORD_DEFAULT));
    return $stmt->execute();
  }
}
?>

W powyższym przykładzie model User zajmuje się operacjami związanymi z użytkownikami, takimi jak pobieranie danych użytkownika z bazy danych oraz tworzenie nowych użytkowników.

2. View – Warstwa prezentacji

Widoki są odpowiedzialne za wyświetlanie danych. Widoki są plikami HTML lub PHP, które są renderowane, aby użytkownik mógł zobaczyć wyniki działania aplikacji.

Przykład widoku login.php:

<!DOCTYPE html>
<html lang="pl">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Logowanie</title>
</head>
<body>
  <h1>Zaloguj się</h1>
  <form action="/user/login" method="post">
    <label for="username">Nazwa użytkownika:</label>
    <input type="text" id="username" name="username" required><br>
    <label for="password">Hasło:</label>
    <input type="password" id="password" name="password" required><br>
    <input type="submit" value="Zaloguj">
  </form>
</body>
</html>

Widok odpowiada za prezentację i nie powinien zawierać logiki biznesowej ani bezpośrednich interakcji z modelem. Otrzymuje dane z kontrolera, które są następnie wyświetlane.

3. Controller – Warstwa kontrolera

Kontroler odpowiada za przyjmowanie żądań od użytkownika i interakcję z modelem oraz widokiem. Kontroler przetwarza żądania, pobiera dane z modelu i przekazuje je do odpowiedniego widoku.

Przykład kontrolera UserController.php:

<?php
class UserController {
  private $model;

  public function __construct($model) {
    $this->model = $model;
  }

  public function login() {
    if ($_SERVER["REQUEST_METHOD"] == "POST") {
      $username = $_POST['username'];
      $password = $_POST['password'];
      
      $user = $this->model->getUserByUsername($username);
      
      if ($user && password_verify($password, $user['password'])) {
        $_SESSION['user'] = $user['username'];
        header("Location: /home");
      } else {
        echo "Nieprawidłowa nazwa użytkownika lub hasło.";
      }
    } else {
      require 'app/views/user/login.php';
    }
  }

  public function register() {
    if ($_SERVER["REQUEST_METHOD"] == "POST") {
      $username = $_POST['username'];
      $email = $_POST['email'];
      $password = $_POST['password'];
      
      $this->model->createUser($username, $email, $password);
      header("Location: /user/login");
    } else {
      require 'app/views/user/register.php';
    }
  }
}
?>

Kontroler UserController odpowiada za obsługę logowania i rejestracji użytkownika. Otrzymuje dane od użytkownika (np. z formularza logowania), przekazuje je do modelu, a następnie decyduje, który widok powinien zostać wyświetlony.

Tworzenie routera (Routing)

W MVC potrzebujemy mechanizmu, który zarządza żądaniami użytkownika i przekierowuje je do odpowiedniego kontrolera i metody. W prostym przykładzie możemy stworzyć router, który interpretuje URL i przekierowuje żądanie do odpowiedniej akcji kontrolera.

Przykład prostego routera:

<?php
class Router {
  public static function route($url) {
    $parts = explode('/', trim($url, '/'));

    $controller = !empty($parts[0]) ? ucfirst($parts[0]) . 'Controller' : 'HomeController';
    $action = !empty($parts[1]) ? $parts[1] : 'index';

    $controllerFile = "app/controllers/" . $controller . ".php";

    if (file_exists($controllerFile)) {
      require_once $controllerFile;
      $controllerInstance = new $controller();
      
      if (method_exists($controllerInstance, $action)) {
        $controllerInstance->$action();
      } else {
        echo "Akcja nie istnieje.";
      }
    } else {
      echo "Kontroler nie istnieje.";
    }
  }
}
?>

Router analizuje URL (np. /user/login) i odpowiednio przekierowuje żądanie do kontrolera UserController i metody login().

Strona główna aplikacji – Plik index.php

Główne wejście do aplikacji, plik index.php, może wyglądać tak:

<?php
session_start();
require_once 'app/core/Router.php';
require_once 'app/models/User.php';

$url = $_SERVER['REQUEST_URI'];
Router::route($url);
?>

Ten plik inicjuje sesje i uruchamia router, który obsługuje wszystkie żądania użytkownika.

Zalety wzorca MVC

  1. Separacja logiki biznesowej od prezentacji: Ułatwia zarządzanie kodem, ponieważ logika aplikacji i warstwa widoku są od siebie oddzielone.
  2. Łatwość rozbudowy: Wzorzec MVC sprawia, że rozbudowa aplikacji jest łatwiejsza, ponieważ możemy dodawać nowe funkcje bez zmieniania istniejących modułów.
  3. Wydajność zespołowa: Dzięki oddzieleniu warstw, programiści mogą pracować nad modelem, kontrolerem i widokiem niezależnie od siebie.
  4. Zwiększona czytelność kodu: Kod jest bardziej zorganizowany, co poprawia jego czytelność i ułatwia jego zrozumienie innym programistom.

Wyzwania związane z MVC

  1. Początkowa złożoność: W porównaniu do prostych skryptów PHP, struktura MVC może wydawać się bardziej złożona, co wymaga nauki i zrozumienia, jak działają poszczególne komponenty.
  2. Przesadne rozdzielenie kodu: Przy małych projektach MVC może wprowadzać nadmierną złożoność, która nie jest potrzebna.

Podsumowanie

W tej lekcji dowiedzieliśmy się, czym jest wzorzec MVC, jak dzieli aplikację na trzy warstwy (Model, View, Controller) oraz jak zbudować prostą aplikację w PHP z wykorzystaniem MVC. MVC pozwala na lepszą organizację kodu, co jest kluczowe dla rozwoju i utrzymania złożonych aplikacji. Wzorzec ten nie tylko pomaga w rozwoju dużych aplikacji, ale także sprzyja pracy zespołowej, gdzie różne osoby mogą pracować nad różnymi częściami aplikacji.

Gratulacje! Ukończyłeś lekcję 12.
Przejdź teraz do lekcji 13 >> Zaawansowane techniki pracy z bazami danych w PHP


Spis Treści - darmowy kurs PHP

Wprowadzenie: Instalacja środowiska PHP
Lekcja 1: Podstawy składni PHP
Lekcja 2: Funkcje i instrukcje warunkowe w PHP
Lekcja 3: Pętle w PHP
Lekcja 4: Tablice w PHP
Lekcja 5: Dodatkowe podstawy funkcji w PHP
Lekcja 6: Praca z formularzami HTML w PHP
Lekcja 7: Obsługa plików w PHP
Lekcja 8: Sesje i ciasteczka w PHP
Lekcja 9: Podstawy operacji na bazach danych MySQL z PHP
Lekcja 10: Prepared Statements w PHP i bezpieczeństwo aplikacji
Lekcja 11: Zarządzanie użytkownikami – rejestracja, logowanie i autoryzacja w PHP
Lekcja 12: Wzorce projektowe w PHP – wprowadzenie do wzorca MVC
Lekcja 13: Zaawansowane techniki pracy z bazami danych w PHP
Lekcja 14: Testowanie jednostkowe w PHP z PHPUnit
Lekcja 15: Tworzenie i korzystanie z API RESTful w PHP
Lekcja 16: Obsługa plików JSON i XML w PHP

Dodatki
- Spis najważniejszych funkcji PHP