PHP保姆级入门练手商品后台维护项目详解

目录

项目成果

商品后端维护功能

前端功能

数据库表

 项目效果图

项目的收获

WEBAPP开发总体思想

前端开发

后端开发

后端代码的分层作用

前端开发

首页核心代码

添加商品页面核心代码

商品维护页面核心代码

后端开发

数据库工具类

实体

DAO层

Service层

Controller层

PHP项目运行遇到的坑


文章初衷就是学习PHP开发后端项目的过程,推荐一个模块一个模块做,项目代码有没有放文末,这是个随机事件:) ,如果觉得文章不错麻烦动动小手指点个赞吧哈哈。

项目成果

商品后端维护功能

  1. 添加商品(需要有商品图片,在服务器保存)
  2. 商品的上架(status字段的取值为“新增”“已上架”“已下架”)
  3. 商品的基本信息修改(状态为已上架商品不能修改)
  4. 商品的删除(状态为已上架商品不能删除)

前端功能

  1. 商品列表页面
  2. 添加商品页面
  3. 商品维护页面

数据库表

       商品Product(主键ID,商品名称Name,图片Pic,价格Price,状态Status)    

 项目效果图

商品列表首页 

添加商品页面

商品维护页面

 前端好麻烦,这里偷懒了。。。

项目的收获

  1. 熟悉PHP开发后端的流程
  2. 熟悉前后端数据交互的流程
  3. 后端连接数据库和增删改查的流程
  4. 前端如何把本地图片上传并实时显示
  5. 上传图片到服务器的流程

WEBAPP开发总体思想

WEBAPP就是网页应用(WEB application)

前端开发

        负责渲染网页、编写Ajax传输请求数据与获得响应数据。

后端开发

        负责与服务器的数据库打交道(DAO层)、业务逻辑(Service层)、编写响应前端的方法(Controller层)。

        一开始没经验开发小项目中代码分层就会让我觉得很麻烦很愚蠢,写了很多没必要的代码。

后端代码的分层作用

        使我们的项目工程化,而在大项目中代码量很多这个时候我们的大脑就容易混乱注意力不集中,分层就能很好集中我们的注意力开发特定的模块,使项目开发有规律可循并稳定前进。

        在计算机网络操作系统计算机组成原理这些知识体系中分层思想出现较为频繁。

前端开发

网页样式使用BootStrap框架的样式,使得前端页面开发变得简单高效,我代码部分就不贴出引用部分了。

首页核心代码

这里HTML部分我就简单贴出body

<body>
<h1>你好,世界!</h1>
<p class="addBlock"><a href="addGoods.html" class="btn btn-primary addBtn" role="button">添加商品</a></p>
<!-- 商品列表容器 -->
<div class="row">

</div>
</body>

JS部分使商品模块渲染的关键,这里是发送请求到后端,后端响应商品数据回来的ajax。

window.onload = function () {
    var rowDiv = document.getElementsByClassName('row')[0];
    //请求获得商品数据,并渲染页面
    $.ajax({
        url:'../controller/GoodsController.php',
        type:'POST',
        data:'Action=queryAllGoods',
        dataType: 'json',
        // processData: false,
        // contentType: false,
        success: function (response) {
            console.log(response);
            var htmlstr = '';
            var status = ["已下架","已上架","新增"];
            for(var i = 0; i < response.length; i++){
                var goods = response[i];
                var lable;
                if(goods.status == 0) lable = "label label-default";
                else if(goods.status == 1) lable = "label label-success";
                else if(goods.status == 2) lable = "label label-danger";
                // console.log(goods.status);
                htmlstr = "<div id=\"goods"+goods.id+"\" class=\"col-xs-6 col-md-2 \">\n" +
                    "        <div class=\"thumbnail \">\n" +
                    "            <span class=\"goodsStatus "+ lable+"\">"+ status[goods.status]+ "</span>\n" +
                    "            <img src=\""+goods.pic+"\" class=\"goodsImg\">\n" +
                    "            <span class=\"goodsPrice label label-info\">¥"+ goods.price+ "</span>\n" +
                    "\n" +
                    "            <div class=\"caption\">\n" +
                    "                <h3 class=\"goodsName\">"+ goods.name+ "</h3>\n" +
                    "                <p class=\"goodsDetail\">"+goods.detail+ "</p>\n" +
                    "                <p><a href=\"updateGoods.html?id="+goods.id+"\" class=\"btn btn-primary\" role=\"button\">商品维护</a> <a onclick=\"delGoods("+goods.id+","+goods.status+")\" class=\"btn btn-default\" role=\"button\">删除商品</a></p>\n" +
                    "            </div>\n" +
                    "        </div>\n" +
                    "    </div>";
                rowDiv.innerHTML += htmlstr;
            }
        },
        error: function (jqXHR, textStatus, errorMessage) {
            console.log(errorMessage); // Optional
        }
    });
};

响应js的后端代码(大概看过一下,具体在后面)

$Action = '';
//当有post请求到这个php时可以使用$_POST取出请求信息
if(isset($_POST['Action'])){
    $Action = $_POST['Action'];
}
if($Action == 'queryAllGoods'){//获得所有商品信息
    $goodsService = new GoodsService();
    echo $goodsService->getAllGoods();
} 

添加商品页面核心代码

HTML结构,CSS自己写写吧。。

<body>
    
    <div class="container">
        <p  class="returnBtn"><a href="index.html" class="btn btn-primary" role="button">返回</a></p>
        <div class="leftDiv">
            <!-- <form> -->
                <li class="list-group-item list-group-item-info">商品名称</li>
                <div class="panel panel-info">
                    <input id="goodsName" type="text" class="form-control"  aria-describedby="basic-addon1">
                </div>
                <li class="list-group-item list-group-item-info">商品价格</li>
                <div class="panel panel-info">
                    <input id="goodsPrice" type="text" class="form-control" placeholder="输入数字" onkeyup="value=value.replace(/[^\d{1,}]/g,'')" aria-describedby="basic-addon1">
                </div>
                <li class="list-group-item list-group-item-info">商品状态</li>
                <div class="panel panel-info">
                    <!-- <input type="number" class="form-control" placeholder="0已下架 1已上架 2新增" min="0" max="2" aria-describedby="basic-addon1"> -->
                    <select id="goodsStatus" class="form-control">
                        <option value="0">已下架</option>
                        <option value="1">已上架</option>
                        <option value="2">新增</option>
                      </select>
                </div>
                <li class="list-group-item list-group-item-info">商品基本信息</li>
                <div class="panel panel-info">
                    <input id="goodsDetail" type="text" class="form-control" aria-describedby="basic-addon1">
                </div>

                <button class="btn btn-primary subBtn" onclick="addGoods()">添加商品</button>
            <!-- </form> -->
        </div>

        <div class="rightDiv">
            <img id="goodsImg" src="./img/default.jpg" class="goodsImg">
            <div align="center">
                <span class="btn btn-success fileinput-button">
                    <span>上传商品图片</span>
                    <input type="file" name="pic" id="pic" onchange="showImg()" accept="image/gif, image/jpeg, .png">
                </span>
            </div>
        </div>
    </div>

</body>

JS部分,这个也比较关键,图片上传这个需要多留意,比如,如何将本地图片上传到页面上实时显示,并将图片发送到后端。前端图片上传实时显示传送门

这里是模仿form表单的提交,使用了FormData类,其中AJAX默认关闭数据序列化,这里需要设置processData: false,contentType: false这两个属性就可以开启数据序列化。

function showImg(){
    //获取上传文件的信息
    var upfile = document.getElementById('pic').files[0];
    var goodsimg = document.getElementById('goodsImg');
    //在pic的文件里抓取该文件用于显示二进制信息
    var sr = window.URL.createObjectURL(upfile);
    // alert(sr);
    goodsimg.src = sr;
}

function addGoods(){
    // var goodsName = document.getElementById('goodsName');
    var goodsName = $("#goodsName");
    var goodsPrice = $("#goodsPrice");
    var goodsStatus = $("#goodsStatus");
    var goodsDetail = $('#goodsDetail');
    var goodsImg = $('#goodsImg');
    var upfile = document.getElementById('pic').files[0];
    var formData = new FormData();
    if(goodsName.val()==''){
        alert('添加失败:请填写商品名称');
        return;
    } else if(goodsPrice.val()==''){
        alert('添加失败:请填写商品价格');
        return;
    } else if(goodsDetail.val()==''){
        alert('添加失败:请填写商品明细');
        return;
    }
    formData.append('Action','addGoods');
    formData.append('status',goodsStatus.val());
    formData.append('name',goodsName.val());
    formData.append('price',goodsPrice.val());
    formData.append('detail',goodsDetail.val());
    formData.append('upfile',upfile);

    $.ajax({
        url: "../controller/GoodsController.php",
        type: "POST",
        data: formData,
        dataType: 'json', //返回的数据类型
        processData: false,
        contentType: false,
        success: function (response) {
            alert(response);
        },
        error: function (jqXHR, textStatus, errorMessage) {
            console.log(errorMessage); // Optional
        }
    });

}

商品维护页面核心代码

        这个就跟添加商品差不多,我就不贴出来了,我这里就只贴出如何或获得首页穿过来的参数,疯狂偷懒。。。

//获得当前页面地址
var url = location.href;
//因为就一个参数这里我偷懒,按=切开字符串
var id = parseInt(url.split('=')[1]);

后端开发

代码逻辑都比较简单,这里代码我就只贴出关键部分,其他的看自己需要补充。

代码贴出顺序是由底向上。

数据库工具类

class DBHelper
{
    static function getConnect(){
        $conn = mysqli_connect(
            "localhost",
            "root",
            "1234",
            "goods_php",//数据库名
            8806);//数据库端口
        //防止中文乱码
        mysqli_query($conn,"set names 'utf8'");
        return  $conn;
    }
}

实体

setter和getter我就不贴出来了,这我用private封装后会

class Goods
{
    var $id; //商品id
    var $name; //商品名称
    var $pic; //商品图片地址
    var $price; //商品价格
    var $status; //商品状态 0已下架 1已上架 2新增
    var $detail;
}

DAO层

主要与本地数据库数据打交道的层,这里我代码就只贴出关键部分

class GoodsDao
{

    //添加商品信息
    function addGoods($goods){
        $Name = $goods->getName();
        $Pic = $goods->getPic();
        $Price = $goods->getPrice();
        $Status = $goods->getStatus();
        $Detail = $goods->getDetail();
        $conn = DBHelper::getConnect();
        $conn->query(
            "insert into product(Name,Pic,Price,Status,Detail) values('$Name','$Pic','$Price','$Status','$Detail')"
        );
        $result = mysqli_affected_rows($conn);
        mysqli_close($conn);
        return $result;
    }

    //删除商品 状态为已上架商品不能删除
    function deleteGoods($id){
        $conn = DBHelper::getConnect();
        $conn->query("delete from product where id=$id");
        $result = mysqli_affected_rows($conn); //受影响行数
        mysqli_close($conn);
        return $result;
    }

    function updateGoods($id,$goods){
        $Name = $goods->getName();
        $Pic = $goods->getPic();
        $Price = $goods->getPrice();
        $Status = $goods->getStatus();
        $Detail = $goods->getDetail();
        $conn = DBHelper::getConnect();
        $conn->query(
            "update product set Name='$Name',Pic='$Pic',Price='$Price',Status='$Status',Detail='$Detail' WHERE id='$id'"
        );
        $result = mysqli_affected_rows($conn); //受影响行数
        mysqli_close($conn);
        return $result;
    }

    //获得所有商品
    function queryAllGoods(){
        $conn = DBHelper::getConnect();
        $result = $conn->query("select * from product");
        mysqli_close($conn);
        return $result;
    }

    //获得单个商品
    function querySingleGoods($id){
        $conn = DBHelper::getConnect();
        $result = $conn->query("select * from product where id=$id");
        mysqli_close($conn);
        return $result;
    }

}

Service层

DAO与Controller之间的层

class GoodsService{
     private $goodsDao;

    /**
     * GoodsService constructor.
     * @param $goodsDao
     */
    public function __construct()
    {
        $this->goodsDao = new GoodsDao();
    }

    //添加商品
    function addGoods($goods){
        return $this->goodsDao->addGoods($goods);
    }

    //删除商品
    function deleteGoods($id){
        return $this->goodsDao->deleteGoods($id);
    }

    //获得所有商品信息
    function getAllGoods(){
        $result = $this->goodsDao->queryAllGoods();
        $arr = array();
        if ($result->num_rows > 0) {
            // 输出数据
            while($row = $result->fetch_assoc()) {
                //大坑!!!goods定义在外面,arr取的都是相同的(arr应该都是只插入对象地址)
                $goods = new Goods();
                $goods->setId($row["id"]);
                $goods->setName($row["Name"]);
                $goods->setPic($row["Pic"]);
                $goods->setPrice($row["Price"]);
                $goods->setDetail($row["Detail"]);
                $goods->setStatus($row["Status"]);
                $arr[] = $goods;
            }
        } else {
            return json_encode("0结果");
        }
        return json_encode($arr,JSON_UNESCAPED_UNICODE);
    }

    //获得单个商品信息
    function getGoods($id){
        $result = $this->goodsDao->querySingleGoods($id);
        if($result->num_rows > 0){
            $goods = new Goods();
            $row = $result->fetch_assoc();
            $goods->setId($row["id"]);
            $goods->setName($row["Name"]);
            $goods->setPic($row["Pic"]);
            $goods->setPrice($row["Price"]);
            $goods->setDetail($row["Detail"]);
            $goods->setStatus($row["Status"]);
            return json_encode($goods,JSON_UNESCAPED_UNICODE);
        } else {
            return json_encode('null');
        }
    }

    //商品信息修改 状态为已上架商品不能修改
    function updateGoods($id,$goods){
        return $this->goodsDao->updateGoods($id,$goods);
    }
}

Controller层

这一层比较关键哦,是个前后端打交道的层,决定了如何与前端合作,好好看。

tips:有兴趣研究$_POST和$_FILES的小伙伴可以用print_r函数打印出来。

$Action = '';
if(isset($_POST['Action'])){
    $Action = $_POST['Action'];
}
if($Action == 'addGoods'){//添加商品

    $goods = new Goods();
    $goods->setName($_POST['name']);
    $goods->setPrice($_POST['price']);
    $goods->setDetail($_POST['detail']);
    $goods->setStatus($_POST['status']);

    //判断是否存在文件
    if (isset($_FILES["upfile"])){
        //iconv将utf-8转为gbk,由于本地编码是GBK
        $source = iconv("UTF-8","GBK//IGNORE",$_FILES['upfile']['tmp_name']);
        $realPath = iconv("UTF-8","GBK//IGNORE",'../resource/img/'.$_FILES['upfile']['name']);
        //print_r(mb_detect_encoding($realPath, array("ASCII",'UTF-8',"GB2312","GBK",'BIG5'))); //获得$realPath编码 GBK
        //图片存入服务器
        move_uploaded_file($source, $realPath);
         //设置$realPath会乱码(此时$realPath编码为gbk)
        $goods->setPic('../resource/img/'.$_FILES['upfile']['name']);
    }else {
        $goods->setPic('null');
        echo json_encode("添加失败:图片未上传");
        return;
    }

    //商品信息持久化
    $goodsService = new GoodsService();
    $result = $goodsService->addGoods($goods);
    if($result > 0) echo json_encode("添加商品成功");
    else echo json_encode("添加商品失败");

} else if($Action == 'queryAllGoods'){//获得所有商品信息

    $goodsService = new GoodsService();
    echo $goodsService->getAllGoods();

} else if($Action == 'deleteGoods'){ //删除商品

    $goodsService = new GoodsService();
    $result = $goodsService->deleteGoods($_POST['id']);
    echo json_encode($result);
} else if($Action == 'queryGoods'){

    $goodsService = new GoodsService();
    echo $goodsService->getGoods($_POST['id']);

} else if($Action = 'updateGoods'){ //修改商品信息

    $goods = new Goods();
    $goods->setName($_POST['name']);
    $goods->setPrice($_POST['price']);
    $goods->setDetail($_POST['detail']);
    $goods->setStatus($_POST['status']);
    $goods->setPic($_POST['pic']);

    //判断内存中是否存在文件,存在的话就不用再保存到服务器了
    if (isset($_FILES["upfile"])){
        //图片存入服务器
        $source = iconv("UTF-8","GBK//IGNORE",$_FILES['upfile']['tmp_name']);
        $realPath = iconv("UTF-8","GBK//IGNORE",'../resource/img/'.$_FILES['upfile']['name']);
        move_uploaded_file($source, $realPath);
        $goods->setPic('../resource/img/'.$_FILES['upfile']['name']);
    }

    //商品信息持久化
    $goodsService = new GoodsService();
    $result = $goodsService->updateGoods($_POST['id'],$goods);
    if($result > 0){
        echo json_encode("success");
    } else {
        echo json_encode('unchanged');
    }
}

至此后端代码完结!!!

PHP项目运行遇到的坑

        php表单提交时获取不到post数据的解决方法_hys__handsome的博客-CSDN博客

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是在Ubuntu上部署PHPThink5项目保姆教程: 1. 安装Web服务器、PHP和MySQL 在Ubuntu上安装Web服务器、PHP和MySQL是部署PHPThink5项目的第一步。具体的安装过程可以使用以下命令: ``` sudo apt update sudo apt install apache2 php mysql-server php-mysql ``` 安装过程中会提示你设置MySQL的root账户密码。 2. 下载PHPThink5的压缩包并解压 在Ubuntu上下载PHPThink5的压缩包并解压到你想要部署的目录中,可以使用以下命令: ``` wget https://github.com/top-think/think/archive/5.1.52.tar.gz tar -zxvf 5.1.52.tar.gz ``` 其中`https://github.com/top-think/think/archive/5.1.52.tar.gz`是PHPThink5的压缩包下载链接,可以根据自己的需要选择不同的版本。 3. 创建MySQL数据库并配置 在MySQL中创建一个新的数据库,并将数据库信息配置到`application/database.php`文件中,具体的步骤如下: - 登录MySQL: ``` sudo mysql -u root -p ``` - 创建一个新的数据库: ``` CREATE DATABASE your_database_name; ``` - 创建一个新的MySQL用户并授权: ``` CREATE USER 'your_username'@'localhost' IDENTIFIED BY 'your_password'; GRANT ALL PRIVILEGES ON your_database_name.* TO 'your_username'@'localhost'; ``` - 刷新MySQL权限: ``` FLUSH PRIVILEGES; ``` - 退出MySQL: ``` exit; ``` - 将数据库信息配置到`application/database.php`文件中: ``` // 数据库类型 'type' => 'mysql', // 服务器地址 'hostname' => 'localhost', // 数据库名 'database' => 'your_database_name', // 数据库用户名 'username' => 'your_username', // 数据库密码 'password' => 'your_password', // 数据库编码默认采用utf8 'charset' => 'utf8', ``` 4. 安装Composer依赖 在终端中切换到PHPThink5目录下,运行以下命令安装Composer依赖: ``` cd think-5.1.52 sudo php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" sudo php composer-setup.php sudo php -r "unlink('composer-setup.php');" sudo php composer.phar install ``` 这个过程可能需要一些时间,取决于你的网络速度和计算机性能。 5. 生成应用目录 运行以下命令生成应用目录: ``` php think build ``` 这个命令会在当前目录下生成一个`runtime`目录和一个`application`目录。其中`application`目录是你的应用程序目录,`runtime`目录是PHPThink5的运行时目录。 6. 配置Web服务器 配置Web服务器,使其指向PHPThink5的`public`目录。具体的配置方式取决于你使用的Web服务器,下面以Apache为例: - 打开Apache的配置文件: ``` sudo nano /etc/apache2/sites-available/000-default.conf ``` - 在`VirtualHost`标签中添加以下内容: ``` DocumentRoot /path/to/think-5.1.52/public <Directory /path/to/think-5.1.52/public> Options Indexes FollowSymLinks AllowOverride All Require all granted </Directory> ``` 其中`/path/to/think-5.1.52`是你解压PHPThink5的目录。 - 重启Apache: ``` sudo systemctl restart apache2 ``` 7. 访问应用程序 访问你的Web服务器,应该能够看到PHPThink5的欢迎界面。如果你使用的是Apache Web服务器,在浏览器中输入以下URL: ``` http://your_server_ip/ ``` 其中`your_server_ip`是你的服务器IP地址。 注意事项: - 在部署之前,确保你已经正确地配置了Web服务器、PHP和MySQL,否则你的应用无法正常运行; - 在部署之前,建议先在本地环境中测试你的应用,确保没有问题再进行部署; - 在部署之后,建议关闭调试模式,以提高应用的安全性和性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值