一个简单的留言本
创建APP和入口文件
一. 创建app目录
进入app
目录, 创建文件夹guestbook
, 并在guestbook
文件夹下创建controllers
, templates
, views
目录, 分别用于存放控制器, 模板, 和视图控制器, 创建好以后的结构如下:
├─admin
├─api
├─cli
├─web
└─guestbook
├─controllers
├─templates
└─views
二. 创建app配置文件
直接拷贝app/web/init.php
文件到app/guestbook
目录, 作为app配置文件
三. 创建入口文件
在htdocs
下创建guestbook
文件夹, 并创建存放静态资源的static
文件夹
├─admin
├─api
├─guestbook
│ └─static
└─web
然后在guestbook
目录下新建入口文件index.php
<?php
require __DIR__ . '/../../crossboot.php';
Cross\Core\Delegate::loadApp('guestbook')->run();
创建好入口文件后, 通过浏览器访问http://domain/htdocs/guestbook/
, 这时会显示CP
的异常页面, 提示我们Main
控制器不存在, 默认控制器可以通过app配置文件指定
创建控制器
在guestbook/controllers
目录下新建控制器文件Main.php
, 输入以下内容:
<?php
namespace app\guestbook\controllers;
use Cross\MVC\Controller;
class Main extends Controller
{
function index()
{
echo 'im guestbook';
}
}
在控制器的第一行我们指定控制器的命名空间. cp
会根据命名空间自动加载类文件, 具体请查看文档模块及命名空间
然后刷新网页http://domain/htdocs/guestbook/
, 如果网页显示im guestbook
, 那就证明创建控制器成功了.
使用视图控制器及布局文件
在guestbook/views
目录下创建文件MainView.php
, 输入以下内容
<?php
namespace app\guestbook\views;
use Cross\MVC\View;
class MainView extends View
{
function index($data = array())
{
echo $data['title'];
}
}
然后改写控制器, 通过$data
数组把数据传给视图控制器, 把输出内容的工作交给视图控制器来处理
<?php
namespace app\guestbook\controllers;
use Cross\MVC\Controller;
class Main extends Controller
{
function index()
{
$data['title'] = 'im guestbook';
$this->display($data);
}
}
控制器中调用$this->display()
来使用视图控制器. 完成后刷新页面http://domain/htdocs/guestbook/
, 会提示我们默认的布局文件不存在
app\guestbook\templates\default\default.layer.php layer Not found!
根据提示我们在app\guestbook\templates
目录下新建文件夹default
, 并在default
中新建布局文件default.layer.php
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title><?php echo isset($title)?$title:'guestbook' ?></title>
</head>
<body>
<?php echo $content; ?>
</body>
</html>
在布局文件中, 通过视图控制器处理过的内容都会被赋值给$content
变量
我们在布局中只要在适当的位置输出$content
变量就OK了, 再此刷新网页, 就可以看到我们通过视图控制器输出的内容了, 查看网页源码可以看到视图控制器生成的内容放到了default.layer.php
中指定的位置, 并且输出了默认的标题guestbook
, 关于布局文件的详细介绍请查看文档使用布局
创建留言表单模板
在app/guestbook/templates/default
下创建文件夹main
, 并在文件夹下创建模板文件guest.tpl.php
, 我们创建一个表单用于发布留言
<div class="col-md-12">
<form class="form-horizontal" action="" method="post">
<div class="form-group">
<label for="name" class="col-sm-2 control-label">昵称</label>
<div class="col-sm-3">
<input type="text" class="form-control" name="name" id="name" placeholder="昵称">
</div>
</div>
<div class="form-group">
<label for="url" class="col-sm-2 control-label">个人主页</label>
<div class="col-sm-5">
<input type="text" class="form-control" name="url" id="url" placeholder="个人主页">
</div>
</div>
<div class="form-group">
<label for="content" class="col-sm-2 control-label"></label>
<div class="col-sm-8">
<textarea name="content" class="form-control" id="content" cols="30" rows="10"></textarea>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">提交</button>
</div>
</div>
</form>
</div>
我们在模板中使用了bootstrap, 所以我们还需要在布局文件default.layer.php
中引入bootstrap前端框架所需的css和js.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title><?php echo isset($title)?$title:'guestbook' ?></title>
<link href="http://cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
<link href="http://cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" rel="stylesheet">
<style>
#headerwrap {
background-color: #FFFAF0;
margin-top: -20px;
padding-top:50px;
background-attachment: relative;
background-position: center center;
min-height: 150px;
width: 100%;
-webkit-background-size: 100%;
-moz-background-size: 100%;
-o-background-size: 100%;
background-size: 100%;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
text-align: center;
}
#headerwrap img{
width:120px;
padding:30px;
}
</style>
</head>
<body>
<div id="headerwrap">
<img src="http://www.crossphp.com/static/images/logo.png" alt="cp logo">
</div>
<div class="container" style="padding-top: 30px;">
<?php echo $content; ?>
</div>
<script src="http://cdn.bootcss.com/jquery/1.12.1/jquery.min.js"></script>
<script src="http://cdn.bootcss.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
</body>
</html>
这里直接使用第三方CDN提供的在线版本, 当然你可以下载到本地, 放到入口的static目录, 在相应位置使用$this->res('css/bootstrap.css')
来获取资源文件的绝对路径, 为了让教程更加简明, 我们直接把自定义的css写在了布局文件中. 最后修改视图控制器代码来加载模板.
<?php
namespace app\guestbook\views;
use Cross\MVC\View;
class MainView extends View
{
function index($data = array())
{
$this->renderTpl('main/guest', $data);
}
}
在视图控制器中调用模板所在文件夹和模板名称(不带后缀), 就可以了, 这时刷新浏览器, 访问http://domain/htdocs/guestbook/
就可以看到带表单的留言发布页面了.
在控制器中接收表单数据
创建好表单后, 我们就可以在控制器中接收表单数据了
<?php
namespace app\guestbook\controllers;
use Cross\MVC\Controller;
class Main extends Controller
{
function index()
{
if ($this->is_post()) {
print_r($_POST);
}
$data['title'] = 'im guestbook';
$this->display($data);
}
}
我们在index
方法中加一个判断, 如果是POST
请求, 那么就打印$_POST
数组中的值.
在使用Module
之前我们先修改数据库配置, 并创建相应的表, 用来保存留言本数据. 打开config/db.config.php
修改数据库配置
/**
* mysql
*/
$mysql_link = array(
'host' => '127.0.0.1',
'port' => '3306',
'user' => 'root',
'pass' => '123456',
'prefix' => '',
'charset' => 'utf8',
);
#默认数据库配置
$db = $mysql_link;
$db['name'] = 'test';
return array(
'mysql' => array(
'db' => $db,
)
);
不要忘记修改$db['name']
来指定数据库名称, 然后创建存储留言的表cp_guestbook
, 结构如下:
CREATE TABLE `cp_guestbook` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`url` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '访客主页地址',
`name` VARCHAR(255) NOT NULL COMMENT '昵称',
`ip` INT(10) UNSIGNED NOT NULL COMMENT '访客IP地址',
`content` TEXT NOT NULL COMMENT '留言内容',
`t` INT(11) NOT NULL COMMENT '留言时间',
PRIMARY KEY (`id`)
)
ENGINE=InnoDB;
创建Module
创建好表结构以后, 我们在modules
目录下新建文件夹guestbook
, 并在之下创建GuestBookModule.php
文件, 用于处理留言数据. 当然你可以把GuestBookModule
创建在app/guestbook
中, 只要遵循命名规则约定就可以了
<?php
namespace modules\guestbook;
use Cross\Module\SQLModule;
class GuestBookModule extends SQLModule
{
protected $t = 'cp_guestbook';
}
GuestBookModule
从SQLModule
继承, 这样我们可以直接使用SQLModule
中提供的方法, 来进行简单的数据操作.
使用Module
创建好Module以后, 我们在控制器中调用GuestBookModule
来处理数据
<?php
namespace app\guestbook\controllers;
use Cross\Core\Helper;
use Cross\MVC\Controller;
use modules\guestbook\GuestBookModule;
class Main extends Controller
{
function index()
{
$GuestBook = new GuestBookModule();
if ($this->is_post()) {
$data = array();
//简单过滤, 在项目中请自定义一个类来处理这类问题
$data['name'] = htmlentities(strip_tags($_POST['name']));
$data['url'] = htmlentities($_POST['url']);
$data['content'] = htmlentities($_POST['content']);
//调用Helper来获取客户端IP
$data['ip'] = Helper::getLongIp();
$data['t'] = TIME;
if (empty($data['name']) || empty($data['content'])) {
die('昵称或内容不能为空');
}
//data中的key和数据库中的字段名对应, 直接调用add方法来保存留言数据
$ret = $GuestBook->add($data);
if ($ret) {
//发表留言失败重定向到首页
$this->to();
return;
} else {
die('发表留言失败, 请联系管理员');
}
}
$this->display($data);
}
}
查看数据表, 如果数据库中有数据, 那么证明我们发表留言成功了, 下一步我们从数据库中读取数据. 依然修改控制器代码:
<?php
namespace app\guestbook\controllers;
use Cross\Core\Helper;
use Cross\MVC\Controller;
use modules\guestbook\GuestBookModule;
class Main extends Controller
{
/**
* @cp_params p=1
*/
function index()
{
$GuestBook = new GuestBookModule();
//如果是POST请求, 那么保存留言
if ($this->is_post()) {
$data = array();
//简单过滤, 在项目中请自定义一个类来专门处理这类
$data['name'] = htmlentities(strip_tags($_POST['name']));
$data['url'] = htmlentities($_POST['url']);
$data['content'] = htmlentities($_POST['content']);
//调用Helper来获取客户端IP
$data['ip'] = Helper::getLongIp();
$data['t'] = TIME;
if (empty($data['name']) || empty($data['content'])) {
die('昵称或内容不能为空');
}
//data中的key和数据库中的字段名对应, 直接调用add方法来保存留言数据
$ret = $GuestBook->add($data);
if ($ret) {
//发表留言失败重定向到首页
$this->to();
return;
} else {
die('发表留言失败, 请联系管理员');
}
//其他请求(一般是GET)获取留言数据
} else {
//分页数据
//$page变量是以引用的形式传入find()
//find()方法执行后会添加总条数到$page变量, 返回到控制器
$page = array(
'p' => (int)$this->params['p'], //当前页数 我们用@cp_param来还原参数的key
'limit' => 10, //每页取多少条
'link' => array('main:index', array()) //分页参数链接配置 第一个为控制器:方法, 第二个参数为附加参数
);
//按id倒排
$order = 'id DESC';
//默认查询所有数据, 条件为空
$condition = array();
$guest = $GuestBook->find($condition, $page, $order);
$data['page'] = $page;
$data['guest'] = $guest;
}
$this->display($data);
}
}
我们把查询结果和分页数据分别放到$data
中, 然后一起传给视图控制器来处理.
输出留言列表
从控制器中获取到数据后, 再修改视图控制器输出留言列表, 并扩展视图控制器, 增加一个方法输出分页标签.
<?php
namespace app\guestbook\views;
use Cross\MVC\View;
class MainView extends View
{
function index($data = array())
{
//判断是否有guest数据, 有的话载入main/guest_list模板
if (!empty($data['guest'])) {
$this->renderTpl('main/guest_list', $data);
}
$this->renderTpl('main/guest', $data);
}
//分页方法
function page($data = array())
{
list($controller, $params) = $data['link'];
//上一页
$pre_params = $params;
$pre_params['p'] = max(1, $data['p'] - 1);
//下一页
$next_params = $params;
$next_params['p'] = min($data['p'] + 1, $data['total_page']);
?>
<nav>
<ul class="pagination">
<li>
<a href="<?php echo $this->url($controller, $pre_params) ?>" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<?php
for($i=1; $i<=$data['total_page']; $i++) {
$params['p'] = $i;
if ($data['p'] == $i) {
//当前页
printf('<li class="active"><a href="%s">%d</a></li>', $this->url($controller, $params), $i);
} else {
printf('<li><a href="%s">%d</a></li>', $this->url($controller, $params), $i);
}
}
?>
<li>
<a href="<?php echo $this->url($controller, $next_params) ?>" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
<?php
}
}
最后创建main/guest_list
模板
<div class="col-md-12">
<div class="panel">
<div class="panel-heading">
<h3>
留言列表
</h3>
</div>
<div class="panel-body">
<?php foreach($data['guest'] as $d) : ?>
<div class="media">
<div class="media-left">
<a href="<?php echo $this->e($d, 'url', 'http://www.crossphp.com') ?>">
<?php echo $d['name'] ?>
</a>
</div>
<div class="media-body">
<h4 class="media-heading">
<?php echo $d['content'] ?>
</h4>
<?php printf('留言时间: %s', date('Y-m-d H:i:s', $d['t'])) ?>
</div>
</div>
<?php endforeach ?>
</div>
<div class="panel-footer">
<?php $this->page($data['page']) ?>
</div>
</div>
</div>
总结
到此我们的留言本例子完成了. 我们一共创建了八个文件
控制器
app/guestbook/controllers/Main.php
在控制器中处理url参数, 从Module中获取数据, 最后把数据交给视图控制器来处理.
视图控制器及模板
app/guestbook/views/MainView.php
视图控制器和控制器默认是一一对应的, 专职处理数据输出, 根据配置输出不同格式的数据给浏览器
app/guestbook/templates/default/main/guest.tpl.php app/guestbook/templates/default/main/guest_list.tpl.php
两个模板, 分别用于留言发布和列表, 发布和列表分开的原因是发布留言的模板, 可以重用于留言编辑, 只是我们在demo中没实现 :)
app/guestbook/templates/default/default.layer.php
布局文件用来整体控制页面结构, 管理css, js等. 通过布局文件, 可以很方便的做skin. 视图控制器方法的输出最后会赋值给布局中一个叫
$content
的变量, 灵活的使用布局, 我们可以处理项目中各种复杂的页面结构.模块
modules/guestbook/GuestBookModule.php
模块是项目的数据中心, 数据在模块中处理, 在控制器中调用传递给视图控制器. 模块使数据来源统一, 便于各app重用.
一个app配置文件
app/guestbook/init.php
一个入口文件
app/htdocs/guest/index.php
以上.
这个例子实现了一个简单的留言本的发布和查看功能, 也大致梳理了基于cp
的项目结构. 由于目的只是讲解cp
的使用, 所以功能并不完善, 编辑, 删除功能都大同小异, 有兴趣的同学可以自己完善, 如有问题或建议请加入我们的QQ群讨论~
最后附上截图