БлогNot. Простая иллюстрация шаблона MVC на PHP

Простая иллюстрация шаблона MVC на PHP

Приложения по схеме MVC можно делать на любом языке, в том числе, конечно, и на PHP. Сначала скажем несколько слов о теории, не повторяя написанного ранее в статье по ссылке.

MVC — это, по сути, набор рекомендаций, описывающих, как отделить внешний вид приложения от его логики.

Если мы о говорим о web-приложении, то внешний вид будут определять разметка HTML и стилевое оформление CSS, а за логику будут отвечать клиентские и/или серверные скрипты, классы, функции и т.п.

Главные составляющие шаблона проектирования MVC - модель, вид и контроллер, классическая схема их взаимодействия показана на известном рисунке:

схема MVC
схема MVC

Модель отвечает за доступ к хранилищу данных для просмотра, отбора или записи данных.

По сути, она служит "мостом" между контроллером и видом, без неё невозможна связь между ними.

Модель "не знает", что происходит с данными во "внешнем мире", её задачи - поиск и подготовка данных, передаваемых прочим компонентам MVC, а также обработка их в постоянном хранилище.

Вид задаёт внешнее представление данным, запрашиваемым у модели.

В web-приложении за вид может отвечать модуль, в котором генерируется и отображается разметка HTML.

Вид не тождественен просто файлу шаблона, а данные не передаются непосредственно виду контроллером, так как они соединяются только посредством модели.

Контроллер занимается обработкой данных, которые вводятся пользователем, а также участвует в обновлении модели.

Он работает при взаимодействии с пользователем и не содержит другой логики, кроме той, что нужна для сбора входящих данных, передаваемых в модель для организации их хранения.

Контроллер получает задачи с целью выполнения тогда, когда пользователь осуществляет взаимодействие с видом.

Напишем небольшую модель, позволяющую хранить линейный список (ассоциативный массив) строк. Кодировка этого и последующих файлов должна быть utf-8, иначе не работают методы для JSON. Обратите внимание, что модель, по сути, и есть вся логика приложения. Она должна позаботиться об обновлении хранилища (в нашем случае - текстового файла), вернуть разумные результаты своих действий (у нас - логическая величина, показывающая, удалось ли последнее действие), а также предоставить другим частям приложения продуманный набор методов для извлечения и добавления данных. Контроллер и вид - это лишь возможные способы обращения к приложению за информацией.

Файл model.php
<?php
class Model {
 private $data;
 private $filename = 'data.txt';
 private function save () { //сохранить записи
  $encodedString = json_encode($this->data,JSON_UNESCAPED_UNICODE);
  if ($encodedString===false) return false;
  if (file_exists($this->filename) === false) {
   $f=fopen($this->filename, 'w') or die('Не могу открыть файл для записи!');
   fclose ($f);
   if (is_writeable($this->filename)==false) return false;
  }
  $r = file_put_contents ($this->filename, $encodedString);
  if ($r === false) return false;
  return true;
 }
 private function load () { //загрузить записи
  if (file_exists($this->filename) and is_readable($this->filename) and filesize($this->filename)) {
   $fileContents = file_get_contents ($this->filename) or die ('Не могу открыть файл для чтения!');
   $this->data = json_decode ($fileContents, true);
   if (json_last_error()!==JSON_ERROR_NONE) {
    $this->data = array (); 
    $this->data[json_last_error()] = 'Ошибка';
    return false;
   }
   return true;
  }
  else {
   $f=fopen($this->filename, 'w') or die('Не могу открыть файл для записи!');
   fwrite($f,'{}');
   fclose ($f);
   $this->data = array (); 
   return false;
  }
 }
 public function __construct () { //конструктор
  $this->data = array ();
  $this->load();
 }
 public function getData () { //получить все данные
  $this->load();
  return $this->data;
 }
 public function add ($key,$val) { //добавить/заменить запись $val с ключом $key
  $this->data[$key] = $val;
  return $this->save();
 }
 public function remove ($key) { //удалить запись с ключом $key
  if (isset($this->data[$key])) { unset ($this->data[$key]); $res = true; }
  else $res = false;
  $this->save();
  return $res;
 }
 public function find ($key) { //найти и вернуть запись с ключом $key или false
  return (isset($this->data[$key]) ? $this->data[$key] : false);
 }
}
?>

Проверить класс модели можно, например, таким кодом:

<?php
 require_once('model.php');

 $m = new Model();
 $m->add('str','String 1');
 $m->add(1,'Строка 2');
 print_r($m->getData()); //Array ( [str] => String 1 [1] => Строка 2 ) 
 echo $m->find(1).'<br>'; //Строка 2
 echo $m->find(2)===false; //1
?>

Проблем с кириллицей не должно быть.

В нашем случае вид будет получать в конструкторе ссылку на модель и предоставит два метода для отображения нужных данных - getPanel для панели управления и getHTML для табличного отображения данных хранилища.

За текущими данными вид, конечно, будет обращаться к модели, которой он проинициализирован.

Файл view.php
<?php
class View {
 private $model;
 public function __construct($model) {
  $this->model = $model;
 }
 public function getPanel() {
  $data = $this->model->getData();
  $str = '';
  foreach ($data as $key=>$val) {
   $str .= '<option value="'.($key).'">'.($val).'</option>'."\n";
  }
  return '
 <table><tr><td>
  <form method="post">
   Key=<input type="text" name="key" id="key" size="4" maxlength="3">
   Val=<input type="text" name="val" id="val" size="4" maxlength="3">
   <input type="hidden" name="action" value="add">
   <input type="submit" value="Добавить">
  </form>
  </td><td>
   <form method="post">
    <select size="1" name="key">
     <option value="false">Не выбрано</option>'.$str.
   '</select>
    <input type="hidden" name="action" value="remove">
    <input type="submit" value="Удалить">
   </form>
  </td>
  </tr></table>'."\n";
 }
 public function getHTML() {
  $data = $this->model->getData();
  $s = '<table>'."\n".'<tr><th>Key</th><th>Value</th></tr>'."\n";
  foreach ($data as $key=>$val) {
   $s .= '<tr><td>'.($key).'</td><td>'.($val).'</td></tr>'."\n";
  }
  return $s.'</table>';
 }
}

Наконец, контроллер займётся своей работой - передачей данных от пользователя к модели, в зависимости от обстоятельств, этим данным потребуется та или иная фильтрация, как у нас и сделано в методах add и remove.

Файл contoller.php
<?php
class Controller {
 private $model;
 public function __construct($model){
  $this->model = $model;
 }
 public function add ($key,$val) {
  $key = strip_tags(trim(htmlspecialchars($key,ENT_QUOTES,'UTF-8')));
  $val = strip_tags(trim(htmlspecialchars($val,ENT_QUOTES,'UTF-8')));
  if ($key===false or empty($key) or empty($val)) return false;
  else return $this->model->add($key,$val);
 }
 public function remove ($key) {
  $key = strip_tags(trim(htmlspecialchars($key,ENT_QUOTES,'UTF-8')));
  if ($key==false or empty($key)) return false;
  else return $this->model->remove($key);
 }
 public function getData() {
  return $this->model->getData();
 }
}

Чтобы всё заработало вместе, добавим главный файл, в котором настроим взаимодействие между компонентами и получим небольшое законченное приложение.

Файл index.php
<?php
 require_once('model.php');
 require_once('controller.php');
 require_once('view.php');

 $model = new Model();
 $controller = new Controller ($model);
 $view = new View ($model);

 $result = '';
 if (isset($_REQUEST['action']) && !empty($_REQUEST['action'])) {
  $action = strip_tags(trim($_REQUEST['action']));
  switch ($action) {
   case 'add':
    $result = $controller->add($_REQUEST['key'],$_REQUEST['val']);
   break;
   case 'remove':
    $result = $controller->remove($_REQUEST['key']);
   break;
   default: 
    $result = 'Неизвестное действие';
   break;
  }
 }

 echo $view->getPanel();
 if (is_bool($result)) $resstr = ($result==true ? 'true' : 'false');
 else if ($result=='') $resstr = 'нет действия';
 else $resstr = $result;
 echo '<p>Результат последнего действия: '.$resstr.'</p>';
 echo $view->getHTML();
?>
скриншот приложения
скриншот приложения

Я проверял этот код в XAMPP с PHP 7.4, все 4 файла, записанных в статье под заголовками, помещаются в одну папку на сервере в кодировке Юникода utf-8. Файл данных data.txt в этой же кодировке и в этой же папке скрипт должен создать себе сам.

17.04.2020, 15:40 [1086 просмотров]


теги: учебное список программирование php

К этой статье пока нет комментариев, Ваш будет первым