代表用户交互的页面、可以包含HTML界面、Smarty模板等和界面相关的元素。MVC设计模式对于视图的处理仅限于视图上数据的采集和处理,以及用户的点击、拖动等事件的处理,而不包括在视图上的业务流程处理。业务流程会交给模型层(Model)处理
模型层是对业务流程、状态的处理以及业务规则的指定。业务流程的处理过程对其他层来说是黑箱操作,模型接受视图的请求处理数据,返回最终的处理结果。业务模型还有一个很重要的模型–数据模型,数据模型主要指实体对象的数据保存(持久化)。比如将一张订单保存到数据库,从数据库获取订单,所有和数据库相关的操作限定在该模型中。
控制层是View层和Model层之间的一个桥梁,接收到用户的请求,将模型和视图匹配在一起,共同完成用户的请求。比如,用户点击一个链接,控制层接收到请求后,把信息传递给模型层,模型层处理完成之后返回视图给用户。
题目要求:
用MVC模式实现以下功能
首页导航栏中内置功能
查看数据库
点击Edit修改数据库内容
点击Delete后删除数据库内此记录,返回首页输出删除成功。
向数据库里增加数据
向搜索框输入查询的关键字
之后输出查询结果:
代码源文件链接
app/controller/BookingsController.php
include_once 'controller/Controller.php';
include_once 'model/BookingModel.php';
include_once 'Validation.php';
//用关键字extends 继承
class BookingsController extends Controller {
var $booking = null;
var $fields = [
'firstname' => 'alpha_spaces|true',
'lastname' => 'alpha_spaces|true',
'email' => 'email|true',
'mobile' => 'digits|true',
'photo' => 'photo|true',
];
//创建对象
function __construct() {
$this->booking = new BookingModel();
}
function index() {
include_once 'view/content.php';
}
function addBooking() {
if (isset($_POST['submit'])) {
$_POST['photo'] = $_FILES['photo']['name'];
$values = [$_POST['firstname'], $_POST['lastname'], $_POST['email'], $_POST['mobile'], $_FILES['photo']['name']];
$validation = new Validation();
$errors = $validation->validate_form_data($_POST, $this->fields);
if (count($errors) == 0) {
$success = $this->booking->insertRecord($values);
$filename = $_FILES["photo"]["name"];
$temp_file = $_FILES["photo"]["tmp_name"];
$destination = "assets/img/photos/$filename";
move_uploaded_file($temp_file, $destination);
if ($success) {
include_once "index.php";
} else {
echo "Insert ERROR!";
}
} else {
include_once 'view/registration_form.php';
}
} else {
include_once 'view/registration_form.php';
}
}
function deleteBooking() {
$id = $_GET['id'];
$success = $this->booking->deleteRecord($id);
if (!$success) {
echo "Delete Failed!";
} else {
$records = $this->booking->readRecords();
include_once("view/viewBookings.php");
}
return $success;
}
function editBooking() {
$id = $_GET['id'];
$records = $this->booking->readRecords();
for ($i = 0; $i < count($records); $i++) {
if ($records[$i]['id'] == $id) {
$firstname_temp = $records[$i]['first_name'];
$lastname_temp = $records[$i]['last_name'];
$email_temp = $records[$i]['email'];
$mobile_temp = $records[$i]['mobile'];
}
}
if (isset($_POST['submit'])) {
$_POST['photo'] = $_FILES['photo']['name'];
$validation = new Validation();
$errors = $validation->validate_form_data($_POST, $this->fields);
if (count($errors) == 0) {
$values = [$_POST['firstname'], $_POST['lastname'], $_POST['email'], $_POST['mobile'], $_FILES['photo']['name']];
$filename = $_FILES["photo"]["name"];
$temp_file = $_FILES["photo"]["tmp_name"];
$destination = "assets/img/photos/$filename";
move_uploaded_file($temp_file, $destination);
$success = $this->booking->updateRecord($id, $values);
if ($success) {
$firstname_temp = "";
$lastname_temp = "";
$email_temp = "";
$mobile_temp = "";
$photo_temp = "";
$records = $this->booking->readRecords();
include_once("view/viewBookings.php");
} else {
echo "Update Failed!";
}
} else {
include_once 'view/registration_form.php';
}
} else {
include_once 'view/registration_form.php';
}
}
function searchBookings() {
if (isset($_POST['Search'])) {
$keyword = $_POST['search'];
$search_record = $this->booking->searchRecord($keyword);
if (isset($search_record)) {
$records = $search_record;
include_once 'view/viewBookings.php';
} else {
echo "None";
}
} else {
include_once 'view/search_form.php';
}
}
function viewBookings() {
$records = $this->booking->readRecords();
include_once 'view/viewBookings.php';
}
}
app/controller/Controller.php
//定义一个抽象类
abstract class Controller {
abstract function index(); //抽象方法不用写函数体
}
?>
app/controller/HomeController.php
include_once 'controller/Controller.php';
class HomeController extends Controller {
public function index() {
include_once 'view/content.php';
}
}
app/database/dbconfig.json
{"DSN":"mysql:host=localhost:3306;dbname=contactsdb","USERNAME":"root","PASSWORD":""}
app/factory/PDOFactory.php
class PDOFactory {
const DB_CONFIG_FILE_PATH = __DIR__ . '/../database/dbconfig.json';
//static是不需要实例化就能使用的
static function getConnection() {
$f = fopen(PDOFactory::DB_CONFIG_FILE_PATH, "r");
$content = fread($f, filesize(PDOFactory::DB_CONFIG_FILE_PATH));
$json_data = json_decode($content);
$dsn = $json_data->DSN;
$username = $json_data->USERNAME;
$password = $json_data->PASSWORD;
$conn = new PDO($dsn, $username, $password);
return $conn;
}
}
include_once 'DataModel.php';
class BookingModel extends DataModel {
function __construct() {
parent::__construct('contacts');
}
function readRecords() {
$records = [];
$sql = 'select * from contacts';
$this->statement = $this->conn->query($sql);
$this->statement->setFetchMode(PDO::FETCH_ASSOC);
$records = $this->statement->fetchAll();
return $records;
}
function deleteRecord($id) {
$sql = "delete from contacts where id=$id";
$this->statement = $this->conn->query($sql);
$success = $this->statement->execute();
return $success;
}
function insertRecord($values) {
$sql = 'insert into contacts (
first_name,
last_name,
email,
mobile,
photo_filename)
values(?,?,?,?,?)';
$this->statement = $this->conn->prepare($sql);
$success = $this->statement->execute($values);
return $success;
}
function updateRecord($id, $values) {
$sql = "update contacts set first_name=?,
last_name=?,
email=?,
mobile=?,
photo_filename=?
where id=$id";
$this->statement = $this->conn->prepare($sql);
$success = $this->statement->execute($values);
return $success;
}
function searchRecord($keyword) {
$sql = "select * from contacts where first_name like '%$keyword%' or last_name like '%$keyword%' or email like '%$keyword%' or mobile like '%$keyword%' or photo_filename like '%$keyword%'";
$this->statement = $this->conn->query($sql);
$this->statement->setFetchMode(PDO::FETCH_ASSOC);
$booking_records = $this->statement->fetchAll();
return $booking_records;
}
}
app/model/Crudable.php
//接口使用关键字 interface 来定义,并使用关键字 implements 来实现接口中的方法,且必须完全实现。
//继承是父子关系,一个孩子只能有一个父亲。所以抽象类只能单继承,当一个子类需要实现的功能需要继承多个父类时,就必须使用接口
interface Crudable {
//在里面定义的方法,却不去实例化,而需要别的类去implements它
function readRecords();
function updateRecord($id, $values);
function deleteRecord($id);
function insertRecord($values);
function searchRecord($keyword);
}
app/model/DataModel.php
include_once 'Crudable.php';
include_once 'factory/PDOFactory.php';
abstract class DataModel implements Crudable {
var $conn = null;
function __construct() {
$this->conn = PDOFactory::getConnection();
}
function close() {
$this->conn = null;
}
}
app/view/content.php
<div class="slogan">
<h2><span class="text_color">CONTACTS DATABASE</span> </h2>
<h4>I AM A STUDENT IN THE WEB APPLICATION & SERVER MANAGEMENT UNIT</h4>
</div>
app/view/footer.php
</section>
<!-- /Section: intro -->
<footer>
<div class="container">
<div class="row">
<div class="col-md-12 col-lg-12">
<div class="wow shake" data-wow-delay="0.4s">
<div class="page-scroll marginbot-30">
</div>
<p>©SquadFREE. All rights reserved.</p>
<div class="credits">
Designed by <a href="https://bootstrapmade.com/">BootstrapMade</a>
</div>
</div>
</div>
</div>
</footer>
<!-- Core JavaScript Files -->
<script src="assets/js/jquery.min.js"></script>
<script src="assets/js/bootstrap.min.js"></script>
<script src="assets/js/jquery.easing.min.js"></script>
<script src="assets/js/jquery.scrollTo.js"></script>
<script src="assets/js/wow.min.js"></script>
<!-- Custom Theme JavaScript -->
<script src="assets/js/custom.js"></script>
</body>
</html>
app/view/header.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<title>CONTACTS DATABASE</title>
<!-- Bootstrap Core CSS -->
<link href="assets/css/bootstrap.min.css" rel="stylesheet" type="text/css">
<!-- Fonts -->
<link href="assets/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css">
<link href="assets/css/animate.css" rel="stylesheet" />
<!-- Squad theme CSS -->
<link href="assets/css/style.css" rel="stylesheet">
<link href="assets/color/default.css" rel="stylesheet">
</head>
<body id="page-top" data-spy="scroll" data-target=".navbar-custom">
<nav class="navbar navbar-custom navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header page-scroll">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-main-collapse">
<i class="fa fa-bars"></i>
</button>
<a class="navbar-brand" href="index.html">
<h1>CONTACTS DATABASE</h1>
</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse navbar-right navbar-main-collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="#intro">HOME</a></li>
<!-- <li><a href="#about">About</a></li>
<li><a href="#service">Service</a></li>
<li><a href="#contact">Contact</a></li>-->
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">E-CONTACTS<b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="?action=viewBookings">VIEW CONTACTS</a></li>
<li><a href="?action=addBooking">ADD CONTACT</a></li>
<li><a href="?action=research">SEARCH CONTACTS</a></li>
</ul>
</li>
</ul>
</div>
<!-- /.navbar-collapse -->
</div>
<!-- /.container -->
</nav>
<!-- Section: intro -->
<section id="intro" class="intro">
app/view/registration_form.php
<div class="container">
<div class="row">
<div class="col-sm-3"></div>
<div class="col-sm-6 box text-center">
Add Contact
</div>
<div class="col-sm-3"></div>
</div>
<div class="row">
<div class="col-sm-3"></div>
<div class="col-sm-6 jumbotron">
<form action="" method="post" novalidate="true" enctype="multipart/form-data">
<div class="form-group">
<label class="control-label">
First Name
<span class="alert-danger">
= isset($errors["firstname"]) ? $errors["firstname"] : "" ?>
</span>
</label>
<input class="form-control" type="text" name="firstname" value="= isset($_POST['firstname']) ? $_POST['firstname'] : (isset($firstname_temp) ? $firstname_temp : "") ?>"/>
</div>
<div class="form-group">
<label class="control-label">
Last Name
<span class="alert-danger">
= isset($errors["lastname"]) ? $errors["lastname"] : "" ?>
</span>
</label>
<input class="form-control" type="text" name="lastname" value="= isset($_POST['lastname']) ? $_POST['lastname'] : (isset($lastname_temp) ? $lastname_temp : "") ?>"/>
</div>
<div class="form-group">
<label class="control-label">
Email
<span class="alert-danger">
= isset($errors["email"]) ? $errors["email"] : "" ?>
</span>
</label>
<input class="form-control" type="email" name="email" value="= isset($_POST['email']) ? $_POST['email'] : (isset($email_temp) ? $email_temp : "") ?>"/>
</div>
<div class="form-group">
<label class="control-label">
Mobile
<span class="alert-danger">
= isset($errors["mobile"]) ? $errors["mobile"] : "" ?>
</span>
</label>
<input class="form-control" type="text" name="mobile" value="= isset($_POST['mobile']) ? $_POST['mobile'] : (isset($mobile_temp) ? $mobile_temp : "") ?>"/>
</div>
<div class="form-group">
<label class="control-label" for="photo">
Photo
<span class="alert-danger">
= isset($errors["photo"]) ? $errors["photo"] : "" ?>
</span>
</label>
<input class="form-control" type="file" name="photo" />
</div>
<br/>
<div class="form-group">
<input class="btn btn-primary btn-block" type="submit" name="submit" />
</div>
</form>
</div>
<div class="col-sm-3"></div>
</div>
</div>
app/view/search_form.php
<div class="container">
<div class="row">
<div class="col-sm-1"></div>
<div class="col-sm-10 box text-center">
Search Contacts
</div>
<div class="col-sm-1"></div>
</div>
<div class="row">
<div class="col-sm-1"></div>
<div class="col-sm-10 jumbotron">
<form action="" method="post" novalidate="true">
<div class="form-group">
<label class="control-label">
Keyword:
</label>
<input class="form-control" type="text" name="search"/>
</div>
<br/>
<div class="form-group">
<input class="btn btn-primary btn-block" type="submit" name="Search" />
</div>
</form>
</div>
<div class="col-sm-1"></div>
</div>
</div>
app/view/viewBookings.php
<br>
<br>
<div class="container">
<div class="row">
<div class="col-md-1 text-center"></div>
<div class="col-md-10 box text-center">View Contacts: = count($records) ?> results</div>
<div class="col-md-1 text-center"></div>
</div>
<div class="row">
<div class="col-md-1 text-center"></div>
<div class="col-md-10 jumbotron text-center">
<table class="table table-striped table-hover" >
<tr>
<th>Contact ID</th>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
<th>Mobile</th>
<th>Photo</th>
<th>Operation</th>
</tr>
foreach ($records as $row): ?>
<tr>
<td align="left">= $row['id'] ?></td>
<td align="left">= $row['first_name'] ?></td>
<td align="left">= $row['last_name'] ?></td>
<td align="left">= $row['email'] ?></td>
<td align="left">= $row['mobile'] ?></td>
<td align="left">
<img src="assets/img/photos/= $row['photo_filename'] ?>" width="40%">
</td>
<td align="left">
<a href="?action=editBooking&id== $row['id'] ?>" >Edit</a>
<a href="?action=deleteBooking&id== $row['id'] ?>" >Delete</a>
</td>
</tr>
endforeach; ?>
</table>
</div>
<div class="col-md-1 text-center"></div>
</div>
</div>
app/Validation.php
include "myfunctions.php";
class Validation {
function validate($value, $valid_type, $request) {
if (!$request)
return;
$msg = strlen($value) == 0 ? "Miss input" : "";
if (strlen($value) == 0)
return $msg;
$msg = $this->{$valid_type}($value);
if (strlen($value))
return $msg;
}
function time($value) {
return "";
}
function date($value) {
return validateDate($value) ? "" : "Invalid input";
}
function alpha_spaces($value) {//只允许空格与字母
$value = str_replace(" ", "", $value);
return ctype_alpha($value) ? '' : 'Alpha characters only';
}
function digits($value) {//只允许数字
return ctype_digit($value) ? "" : "Digit only";
}
function photo($value) {
return "";
}
function decimal($value) {//验证是否是浮点数
return is_float($value) ? "" : "Float only";
}
function email($value) {//验证邮件格式
return filter_var($value, FILTER_VALIDATE_EMAIL) ? "" : "Invaild email";
}
function validate_form_data($request, $field) {
$errors = [];
$validation = new Validation();
foreach ($field as $fieldname => $field_data) {
//验证其他:
$value = trim($request[$fieldname]); //photo在这里并没有被检测到
$tokens = explode("|", $field_data);
$valid_type = $tokens[0];
$require = $tokens[1];
$errorMessage = $this->validate($value, $valid_type, $request);
if (strlen($errorMessage) > 0) {
$errors[$fieldname] = $errorMessage;
}
}
return $errors;
}
}
app/myfunctions.php
/*
* To use these functions include this page in your PHP
*
* How to use these functions:
*
* $valid = validateDate($date);
*
* $valid = validateMonthYearDate($date);
*
* $valid = validateStreetAddress($street_address);
*
* @author Stefan Batsas
* @date 2017
*/
/**
* function validates date formats: dd/mm/yyyy, yyyy-mm-dd
* @param type $date
* @return boolean
*/
function validateDate($date) {
$valid = false;
// no support for HTML5 date picker user enters input for date
// any other date format will not pass
if (preg_match("/\d{4}\-\d{2}\-\d{2}/", $date)) {
// if we are here user has entered format of yyyy-mm-dd
$day = $month = $year = "";
// split up the pieces
list($year, $month, $day) = explode("-", $date);
$day = intval($day);
$month = intval($month);
$year = intval($year);
// now use PHP checkdate to verify it is a valid date
if (checkdate($month, $day, $year)) {
$valid = true;
}
// no support for HTML5 date picker user enters input for date
// any other date format will not pass
} else if (preg_match("/\d{2}\/\d{2}\/\d{4}/", $date)) {
// if we are here user has entered format of dd/mm/yyyy
$day = $month = $year = "";
// split up the pieces
list($day, $month, $year) = explode("/", $date);
$day = intval($day);
$month = intval($month);
$year = intval($year);
// now use PHP checkdate to verify it is a valid date
if (checkdate($month, $day, $year)) {
$valid = true;
}
}
return $valid;
}
// end function
/**
* function validates date formats: dd/yyyy, yyyy-mm
* eg. card expiry date
* @param type $date
* @return boolean
*/
function validateMonthYearDate($date) {
$valid = false;
if (preg_match("/\d{4}\-\d{2}/", $date)) {
// if we are here user has ebtered format of mm/yyyy
$month = $year = "";
// split up the pieces
list($year, $month) = explode("-", $date);
$month = intval($month);
$year = intval($year);
// now use PHP checkdate to verify it is a valid date - 1 is supplied for day
if (checkdate($month, 1, $year)) {
$valid = true;
}
// no support for HTML5 date picker user enters input for date
// any other date format will not pass
} else if (preg_match("/\d{2}\/\d{4}/", $date)) {
// if we are here user has ebtered format of mm/yyyy
$month = $year = "";
// split up the pieces
list($month, $year) = explode("/", $date);
$month = intval($month);
$year = intval($year);
// now use PHP checkdate to verify it is a valid date - 1 is supplied for day
if (checkdate($month, 1, $year)) {
$valid = true;
}
}
return $valid;
}
// end function
/**
* Street address example Unit 5/23-24 Page street
* Can contain one forward slash and one dash
* @param type $value
* @return boolean
*/
function validateStreetAddress($value) {
$valid = true;
// check for forward slashes - 1 is permitted
$numSlashes = $numDashes = 0;
// number of slashes replaced stored in $numSlashes
$temp = str_replace("/", "", $value, $numSlashes);
// number of dashes replaced stored in $numSlashes
$temp = str_replace("-", "", $value, $numDashes);
if ($numSlashes > 1 || $numDashes > 1) {
$valid = false;
} else {
// remove spaces
$temp = str_replace(" ", "", $value);
// remove slash
$temp = str_replace("/", "", $temp);
// remove dashes
$temp = str_replace("-", "", $temp);
// check for alpha numeric
if (!ctype_alnum($temp)) {
$valid = false;
} // end if
} // end if
return $valid;
}
// end validation of street address
/**
* Complex names eg Jon-Palo Jnr. the 3rd
* Allow alpha numeric, spaces, 1 hyphen, 1 period, 1 digit
* @param type $value
* @return boolean
*/
function validate_complex_name($value) {
$valid = true;
// remove the spaces
$temp = str_replace(' ', '', $value);
// remove dashes and count them
$temp = str_replace('-', '', $temp, $hyphen_count);
// remove periods and count them
$temp = str_replace('.', '', $temp, $fullstop_count);
// count the number of digits
$digit_count = preg_match_all("/[0-9]/", $temp);
// rules are 1 dash, 1 period, 1 digit and alpha permitted
if (!ctype_alnum($temp) || $hyphen_count > 1 ||
$fullstop_count > 1 || $digit_count > 1) {
$valid = false;
}
return $valid;
}
function validateDOB($value) {
$valid = false;
if (validateDate($value)) {
$today = date('Y-m-d');
$dob = date($value);
$time1 = strtotime($today);
$time2 = strtotime($dob);
if ($time2 < $time1) {
$valid = true;
}
}
return $valid;
}
?>
app/assets/config.php
set_include_path(__DIR__ . '/app');
//把app文件设置为include path
app/assets/index.php
include_once 'config.php'; //设置include path
include_once 'Validation.php';
include_once 'view/header.php';
include_once 'model/BookingModel.php';
include_once 'controller/BookingsController.php';
include_once 'controller/HomeController.php';
$homeController = new HomeController();
$bookings = new BookingModel();
$bookingsController = new BookingsController();
if (isset($_GET['action'])) {
$action = $_GET['action'];
if ($action == 'viewBookings') {
$bookingsController->viewBookings();
} else if ($action == 'addBooking') {
$bookingsController->addBooking();
} else if ($action == 'deleteBooking') {
$bookingsController->deleteBooking();
} else if ($action == 'editBooking') {
$bookingsController->editBooking();
} else if ($action == 'research') {
$bookingsController->searchBookings();
} else {
include_once "index.php";
}
} else {
$homeController->index();
}
include_once 'view/footer.php';