Простая иллюстрация шаблона MVC на PHP
Приложения по схеме MVC можно делать на любом языке, в том числе, конечно, и на PHP. Сначала скажем несколько слов о теории, не повторяя написанного ранее в статье по ссылке.
MVC — это, по сути, набор рекомендаций, описывающих, как отделить внешний вид приложения от его логики.
Если мы о говорим о web-приложении, то внешний вид будут определять разметка HTML и стилевое оформление CSS, а за логику будут отвечать клиентские и/или серверные скрипты, классы, функции и т.п.
Главные составляющие шаблона проектирования 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 просмотров]