-
Notifications
You must be signed in to change notification settings - Fork 59
paozhu 框架 CRUD 教程
paozhu 做为 c++ web framework 框架,crud是核心功能,现在我我们看看怎么使用crud
前面经过orm入门,view入门,那么我们可以正式做一个演示,学完crud你就可以写业务代码,算新手毕业,去实现自己业务。
我们到controller目录 分别在include和src创建
testcrud.h
testcrud.cpp
两个文件
controller
- src
- testcrud.cpp
- include
- testcrud.h
#pragma once
#include <string>
#include <thread>
#include "httppeer.h"
namespace http
{
std::string articlelogin(std::shared_ptr<httppeer> peer);
std::string articleloginpost(std::shared_ptr<httppeer> peer);
std::string articlelist(std::shared_ptr<httppeer> peer);
std::string articleshow(std::shared_ptr<httppeer> peer);
std::string articleedit(std::shared_ptr<httppeer> peer);
std::string articleeditpost(std::shared_ptr<httppeer> peer);
std::string articleadd(std::shared_ptr<httppeer> peer);
std::string articleaddpost(std::shared_ptr<httppeer> peer);
std::string articledelete(std::shared_ptr<httppeer> peer);
}
这次方法有点多,大家看名字应该也猜到功能了,
articlelogin articleloginpost是模拟后台登录
articlelist articleshow是 文章列表和详细内容显示
articleedit articleeditpost 是文章编辑和接受编辑内容提交更新
articleadd articleaddpost 是添加新文章页面和接受提交内容
articledelete 就是删除文章
一个完整crud 就是这些,可以根据自己业务添加字段或添加其它功能。
接下来我们要实现业务代码了
std::string articlelogin(std::shared_ptr<httppeer> peer)
{
// step1 show login page
peer->view("login/login");
return "";
}
peer->view 就是显示登录页面 可以查看view视图教程,login/login 就是login目录下login.html文件,真实是 viewsrc/view/login/login.cpp文件。 我们使用了paozhu_cli 转化为cpp 文件了。
根目录运行
./bin/paozhu_cli
./bin/paozhu_cli model | view | viewtocpp | control
🎉 Welcome to use cli to manage your MVC files。
(m)model (v)view (f)viewtocpp or (c)control,x or q to exit[input m|v|f|c|]:
输入f
如果view目录下html文件有改动,那会显现出来,然后输入相应的数字就可以更新了,也可以输入a 全部更新
login.html 文件内容,里面有一个 include_sub("home/header",obj); 就是引用其他视图内容,home/header 表示home/header.html 文件内容,obj 就是控制器中peer->val的内容,home/header就是头部,我们每个页面可能都有,这样修改一处就可以全部修改了。
"/cms/loginpost" 就是articleloginpost 函数的urlpath挂载点,在reghttpmethod.hpp文件里面,后面会讲到。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors">
<meta name="generator" content="Hugo 0.101.0">
<title>Content Management System</title>
<link href="/assets/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="text-center">
<%c include_sub("home/header",obj); %>
<div class="container text-center">
<div class="row">
<div class="col-3"></div>
<div class="col-6">
<h2 id="horizontal-form">CMS Admin </h2>
<form action="/cms/loginpost" method="post">
<div class="row mb-3">
<label for="username" class="col-sm-2 col-form-label">Username</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="username" name="username" value="admin">
</div>
</div>
<div class="row mb-3">
<label for="password" class="col-sm-2 col-form-label">Password</label>
<div class="col-sm-10">
<input type="password" class="form-control" id="password" name="password" value="123456">
</div>
</div>
<button type="submit" class="btn btn-primary">Sign in</button>
</form>
</div>
</div>
</div>
</body>
</html>
std::string articleloginpost(std::shared_ptr<httppeer> peer)
{
// step2
// get login/login post field
httppeer &client = peer->getpeer();
std::string username = client.post["username"].to_string();
std::string password = client.post["password"].to_string();
auto users = orm::cms::User();
std::string md5string;
try
{
md5string = md5(password);
users.where("name=", username).whereAnd("password=", md5string).limit(1).fetch();
// view orm create sql
// client<<"<p>"<<users.sqlstring<<"</p>";
if (users.getUserid() > 0)
{
// save session,other page get int userid= client.session["userid"].to_int();
client.session["userid"] = users.getUserid();
client.save_session();
client.goto_url("/cms/list");
return "";
}
else
{
client.goto_url("/cms/login",3,"用户名或密码错误!");
return "";
}
}
catch (std::exception &e)
{
client << "<p>" << e.what() << "</p>";
return "";
}
return "";
}
httppeer &client = peer->getpeer(); //为取得原始指针方便重载
username password 是view/login/login.html里面的表单字段
to_string 表示转为字符串
std::string username = client.post["username"].to_string();
std::string password = client.post["password"].to_string();
auto users = orm::cms::User();
std::string md5string;
orm::cms::User(); 表示 数据模型表
std::string md5string;
md5string = md5(password);
就是md5加密
users.where("name=", username).whereAnd("password=", md5string).limit(1).fetch();
数据模型表操作数据库,使用链操作where("name=", username) 和 where("name", username)一样,后面会讲怎么拼接,不过要注意安全,防sql注入。
我们可以观察 users.sqlstring 拼接好的字符串
users.data 是一个struct 数据对象,里面成员跟数据库字段名字一一对应
users.getUserid() 和users.data.userid 同样的,目前设计为public
数据库表名 users 里面必须有userid这个字段,最好每个表有一个自增字段
client.session["userid"] = users.getUserid();
client.save_session();
client.session["userid"]保存到文件里面,有一个sessionid自带的cookie,这样下一个页面就自动获取session内容了。也可以实现页面权限,如果其他页面没有取到这个值,表示没有登录或没有权限。
client.save_session();是保存动作
gotu_url是表示跳转这个页面。
std::string articlelist(std::shared_ptr<httppeer> peer)
{
// step3 content list
httppeer &client = peer->getpeer();
int userid = client.session["userid"].to_int();
if (userid == 0)
{
// client.goto_url("/cms/login");
client.val["msg"] = "<a href=\"/cms/login\">Please login </a>";
}
auto articles = orm::cms::Article();
int page = client.get["page"].to_int();
if (page < 0)
{
page = 0;
}
page = page * 20;
articles.where("isopen=1").order(" aid desc ").limit(page, 20).fetch();
// 也可以直接返回OBJ_VALUE 对象; 不过正常业务会要处理下结果集
// You can also return the OBJ_VALUE object directly; but normal business process will need to process the result set
client.val["list"].set_array();
if (articles.size() > 0)
{
for (auto &bb : articles)
{
OBJ_ARRAY item;
item["aid"] = bb.aid;
item["title"] = bb.title;
item["createtime"] = bb.createtime;
item["summary"] = bb.summary;
// client<<"<p><a href=\"/cms/show?id="<<bb.aid<<"\">"<<bb.title<<"</a> "<<bb.createtime<<" </p>";
client.val["list"].push(std::move(item));
}
}
peer->view("cms/list");
return "";
}
client.session["userid"].to_int();
表示 上一个页面保存的userid 转为int类型
client.get["page"].to_int();
client.get 表示取得url地址的值 比如 /cms/list?page=2
client.get["page"].to_int();
那么就是 page等于2 表示第二页
articles.where("isopen=1").order(" aid desc ").limit(page, 20).fetch();
就是加上偏移20,每个页面20条内容标题,后面那个20表示在这个偏移上取回多少条内容
for (auto &bb : articles)
就是把 articles 取回的内容循环取出来处理,我们是显示到 视图里面。
client.val["list"].set_array();
保存到这个变量里面,是一个数组变量,视图里面再取这个值就可以了
#include "orm.h"
#include <chrono>
#include <thread>
#include "md5.h"
#include "func.h"
#include "httppeer.h"
#include "testcrud.h"
namespace http
{
std::string articlelogin(std::shared_ptr<httppeer> peer)
{
// step1 show login page
peer->view("login/login");
return "";
}
std::string articleloginpost(std::shared_ptr<httppeer> peer)
{
// step2
// get login/login post field
httppeer &client = peer->getpeer();
std::string username = client.post["username"].to_string();
std::string password = client.post["password"].to_string();
auto users = orm::cms::User();
std::string md5string;
try
{
md5string = md5(password);
users.where("name=", username).whereAnd("password=", md5string).limit(1).fetch();
// view orm create sql
// client<<"<p>"<<users.sqlstring<<"</p>";
if (users.getUserid() > 0)
{
// save session,other page get int userid= client.session["userid"].to_int();
client.session["userid"] = users.getUserid();
client.save_session();
client.goto_url("/cms/list");
return "";
}
else
{
client.goto_url("/cms/login",3,"用户名或密码错误!");
return "";
}
}
catch (std::exception &e)
{
client << "<p>" << e.what() << "</p>";
return "";
}
return "";
}
std::string articlelist(std::shared_ptr<httppeer> peer)
{
// step3 content list
httppeer &client = peer->getpeer();
int userid = client.session["userid"].to_int();
if (userid == 0)
{
// client.goto_url("/cms/login");
client.val["msg"] = "<a href=\"/cms/login\">Please login </a>";
}
auto articles = orm::cms::Article();
int page = client.get["page"].to_int();
if (page < 0)
{
page = 0;
}
page = page * 20;
articles.where("isopen=1").order(" aid desc ").limit(page, 20).fetch();
// 也可以直接返回OBJ_VALUE 对象; 不过正常业务会要处理下结果集
// You can also return the OBJ_VALUE object directly; but normal business process will need to process the result set
client.val["list"].set_array();
if (articles.size() > 0)
{
for (auto &bb : articles)
{
OBJ_ARRAY item;
item["aid"] = bb.aid;
item["title"] = bb.title;
item["createtime"] = bb.createtime;
item["summary"] = bb.summary;
// client<<"<p><a href=\"/cms/show?id="<<bb.aid<<"\">"<<bb.title<<"</a> "<<bb.createtime<<" </p>";
client.val["list"].push(std::move(item));
}
}
peer->view("cms/list");
return "";
}
std::string articleshow(std::shared_ptr<httppeer> peer)
{
// step4
httppeer &client = peer->getpeer();
auto articles = orm::cms::Article();
int aid = client.get["id"].to_int();
articles.where("isopen=1").where(" aid=", aid).limit(1).fetch();
client.val["title"] = articles.getTitle();
client.val["content"] = articles.getContent();
peer->view("cms/show");
return "";
}
std::string articleedit(std::shared_ptr<httppeer> peer)
{
// same the show content
httppeer &client = peer->getpeer();
auto articles = orm::cms::Article();
int aid = client.get["id"].to_int();
articles.where("isopen=1").where(" aid=", aid).limit(1).fetch();
client.val["title"] = articles.getTitle();
client.val["content"] = html_encode(articles.getRefContent());
client.val["aid"] = articles.getAid();
peer->view("cms/edit");
return "";
}
std::string articleeditpost(std::shared_ptr<httppeer> peer)
{
httppeer &client = peer->getpeer();
std::string title = client.post["title"].to_string();
std::string content = client.post["content"].to_string();
unsigned int aid = client.post["aid"].to_int();
auto articles = orm::cms::Article();
// articles.where("isopen=1").where(" aid=",aid).limit(1).fetch();
// articles.data.aid=aid;
// articles.data.title=title;
// articles.setAid(aid);
articles.setTitle(title);
// articles.setTitle("直接标题");
articles.setContent(content);
articles.where(" aid=", aid);
int effectnum = 0;
try
{
effectnum = articles.update("title,content");
}
catch (std::exception &e)
{
client << "<p>" << articles.sqlstring << "</p>";
client << "<p>" << e.what() << "</p>";
return "";
}
if (effectnum > 0)
{
client.goto_url("/cms/list", 3, "内容已经更新");
return "";
}
else
{
client.goto_url("/cms/list", 3, "更新出错(error)");
return "";
}
return "";
}
std::string articleadd(std::shared_ptr<httppeer> peer)
{
httppeer &client = peer->getpeer();
peer->view("cms/add");
return "";
}
std::string articleaddpost(std::shared_ptr<httppeer> peer)
{
httppeer &client = peer->getpeer();
std::string title = client.post["title"].to_string();
std::string content = client.post["content"].to_string();
unsigned int aid = 0;
auto articles = orm::cms::Article();
// articles.data.aid=aid;
// articles.data.title=title;
// articles.setAid(aid);
articles.setIsopen(1);
articles.setCreatetime(date("%Y-%m-%d %X")); // Y-m-d H:i:s
articles.setAddtime(timeid()); // unix timestamp
articles.setAddip(client.client_ip); // client ip
articles.setTitle(title);
// articles.setTitle("直接标题");
articles.setContent(content);
int effectnum = 0;
try
{
effectnum = articles.save();
aid = articles.getAid();
client << "<p>新(new)id " << aid << " 或 新(new)id " << effectnum << "</p>";
}
catch (std::exception &e)
{
client << "<p>" << articles.sqlstring << "</p>";
client << "<p>" << e.what() << "</p>";
return "";
}
if (effectnum > 0)
{
client.goto_url("/cms/list", 3, "内容已经添加");
return "";
}
else
{
client.goto_url("/cms/list", 3, "添加出错(error)");
return "";
}
return "";
}
std::string articledelete(std::shared_ptr<httppeer> peer)
{
httppeer &client = peer->getpeer();
unsigned int aid = client.get["id"].to_int();
auto articles = orm::cms::Article();
// 可以先查询是否存在或有权限之类
// articles.where("isopen=1").where(" aid=",aid).limit(1).fetch();
int effectnum = 0;
try
{
effectnum = articles.remove(aid);
}
catch (std::exception &e)
{
client << "<p>" << articles.sqlstring << "</p>";
client << "<p>" << e.what() << "</p>";
return "";
}
if (effectnum > 0)
{
client.goto_url("/cms/list", 3, "内容已经删除");
return "";
}
else
{
client.goto_url("/cms/list", 3, "删除出错(error)");
return "";
}
return "";
}
}
view/cms/list.html
视图文件内容,我们看看是怎么使用client.val["list"]的。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors">
<meta name="generator" content="Hugo 0.101.0">
<title>Content Management System</title>
<link href="/assets/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="text-center">
<%c include_sub("home/header",obj); %>
<h3>内容列表 <a href="/cms/add">添加内容</a></h3>
<p><%c echo<<obj["msg"].as_string(); %></p>
<div class="list-group w-auto">
<%c
for(auto &a:obj["list"].as_array()){
%>
<div class="list-group-item list-group-item-action d-flex gap-3 py-3" aria-current="true">
<img src="#" alt="twbs" class="rounded-circle flex-shrink-0" width="32" height="32">
<div class="d-flex gap-2 w-100 justify-content-between">
<div>
<h6 class="mb-0"><a href="/cms/show?id=<%c echo<<a.second["aid"].to_string(); %>"><%c echo<<a.second["title"].as_string(); %></a></h6>
<p class="mb-0 opacity-75"><%c echo<<a.second["summary"].as_string(); %></p>
</div>
<small class="opacity-50 text-nowrap"><%c echo<<a.second["createtime"].as_string(); %></small>
<small class="opacity-50 text-nowrap"><a href="/cms/edit?id=<%c echo<<a.second["aid"].to_string(); %>">编辑</a></small>
<small class="opacity-50 text-nowrap"><a href="/cms/delete?id=<%c echo<<a.second["aid"].to_string(); %>">删除</a></small>
</div>
</div>
<%c
}
%>
</div>
</body>
</html>
for(auto &a:obj["list"].as_array())
视图这里也是一个循环把值拿出来。
给一个a临时变量引用,然后循环一条一条显示出来
a.second["aid"].to_string()
是把数字转为字符
其它内容看看是不是有不明白的地方,可以提问。
别忘了,我们要做一个urlpath映射挂载
我们编辑挂载文件
#ifndef __HTTP_REGHTTPMETHOD_HPP
#define __HTTP_REGHTTPMETHOD_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
#pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "httppeer.h"
#include "testcrud.h"
namespace http
{
void _inithttpmethodregto(std::map<std::string, regmethold_t> &methodcallback)
{
struct regmethold_t temp;
temp.pre = nullptr;
temp.regfun = articlelogin;
methodcallback.emplace("cms/login", temp);
temp.regfun = articleloginpost;
methodcallback.emplace("cms/loginpost", temp);
temp.regfun = articlelist;
methodcallback.emplace("cms/list", temp);
temp.regfun = articleshow;
methodcallback.emplace("cms/show", temp);
temp.regfun = articleedit;
methodcallback.emplace("cms/edit", temp);
temp.regfun = articleeditpost;
methodcallback.emplace("cms/editpost", temp);
temp.regfun = articledelete;
methodcallback.emplace("cms/delete", temp);
temp.regfun = articleadd;
methodcallback.emplace("cms/add", temp);
temp.regfun = articleaddpost;
methodcallback.emplace("cms/addpost", temp);
}
}
#endif
记得包含文件 #include "testcrud.h"
你有其他挂载,可以在里面添加,不要跟我一样也行。
我们把所有函数都映射上去。
一切准备就绪了,我们开始编译
回到项目根目录进入build目录
cmake ..
make
然后在回到根目录,或打开新的命令窗口
执行
./bin/paozhu
用浏览器打开
可以看到视图内容了