管理首页
Macaw::get('/admin', 'adminIndex@index');
//里面两个都是单引号,然后/admin斜杠在前,后面的路径,admin是命名空间,Index是类,index是方法,注意这里是反斜杠,否则会提示找不到类
思路是:先在index.php定义路径,然后按照路径去controller里面建类文件、类和方法,然后在方法里面调用views模板(即$this->display('index/index'))
自定义全局路径
获取不同项目的路径
/*
下面这个是为了能够自动获取项目路径而写的
主要就是为了根据不同的环境(https和端口)拼出来url,以使得所有的资源都可以使用相对路径
*/
function getCurUrl(){
$url = "http://";
if(isset($_SERVER['SERVER_HTTPS']) && $_SERVER['SERVER_HTTPS'] == 'on'){
$url = "https://";
}
//判断端口
if($_SERVER['SERVER_PORT'] != '80'){
$url .= $_SERVER['SERVER_NAME'].":".$_SERVER['SERVER_PORT'];
}else{
$url .= $_SERVER['SERVER_NAME'];
}
return $url;
}
获取了路径之后,在controller里面定义好几个路径的变量
$this->assign('url', $url.'/app/views/admin/resource'); //自己模板下的资源
$this->assign('public', $url.'/app/views/public'); //公共模板下的资源
$this->assign('res', $url.'uploads'); //文件上传资源
之后在模板文件里面的适当位置使用变量即可。
这一部分,在admin和home层面上是不同的url路径,所以在admin和home的基类里都重写display方法,在这个时候就定义好url。
admin\Index类添加以下方法
//设置url变量,重写父类里的display方法
protected function display($template) {
$url = getCurUrl();
$this->assign('url', $url.'/app/views/admin/resource'); //自己模板下的资源
$this->assign('public', $url.'/app/views/public'); //公共模板下的资源
$this->assign('res', $url.'uploads'); //文件上传资源
echo $this->twig->render($template.'.html', $this->data);
}
home\Index类添加以下方法:
//设置url变量,重写父类里的display方法
protected function display($template) {
$url = getCurUrl();
$this->assign('url', $url.'/app/views/'.TEMPNAME.'/resource'); //自己模板下的资源
$this->assign('public', $url.'/app/views/public'); //公共模板下的资源
$this->assign('res', $url.'uploads'); //文件上传资源
echo $this->twig->render($template.'.html', $this->data);
}
设置重写规则例外
在.htaccess里面,之前有这样一条规则:
RewriteRule ^(.*)$ index.php?$1 [QSA,L]
这条规则把所有的访问,都变成abc.cn/index.php/xxx/xxx了
这时候如果要访问资源的话,变成这样的路径是找不到资源的,所以要设置资源文件例外
添加一条规则就可以:
RewriteCond %{REQUEST_URI} !^.*(.css|.js|.png|.gif|.jpg|.jpeg)
这个应该也是正则表达式吧
根据controller传过来的字段来显示样式
<li {% if menumark == 'category' %} class="sel" {% endif %}><a href="/admin/category">商品分类</a></li>
//总觉得这个办法有点笨
在Index类中创建构建函数
function __construct(){
$this->assign('menumark', 'product');
parent::__construct();
}
知识点:在子类中建立构造函数,一定要使用parent::__construct()才能使用父类的构造函数,如果父类没有构造函数就不需要这个。
模板文件循环显示数据
{% for link in links %}
<tr>
<td>{{ link.id }}</td>
<td><input type="text" name="ord[{{ link.id }}]" value="1" class="inputtext input40" /></td>
<td>{{ link.name }}</td>
<td><a href="{{ link.url }}" target="_blank">{{ link.url }}</a></td>
<td>
<a href="/admin/link/mod/{{ link.id }}" class="admin_edit mar3">修改</a>
<a href="/admin/link/del/{{ link.id }}" class="admin_del" onclick="return confirm('你确定要删除链接{{ link.name }}吗?')">删除</a>
</td>
</tr>
{% endfor %}
(坑)数据库读出来的中文乱码
这个原因是charset没有设置的原因,在baseDao.php里medoo的设置里面加上
'charset' => 'utf8"
就好了
(坑)读数据库的时候使用的命令
我用$db->select()读单条数据,结果显示不正常,出来是一个二维数组
后来跟教程对比了一下,使用$db->get()才是一个一位数组
以后这种情况很多,一定要注意啊
(坑)Medoo组件在连不上网的时候会报错
报错却是找不到Class,你说坑不坑,还以为什么地方写错了。
列表页修改顺序
如果第一行name是a,value是1,第二行name是b,value是2,第三行name是c,value是3,那么post出去,就会获得数组:
{a:1, b:2, c:3}
如果第一行name是a[1],value是1,第二行name是a[2],value是2,第三行name是a[3],value是3,那么post出去,就会获得数组:
'a' =>
array (size=4)
1 => string '1' (length=2)
2 => string '2' (length=2)
3 => string '3' (length=2)
这样就可以用for loop每条每条去修改顺序了。
$num = 0;
foreach($_POST['ord'] as $id=>$ord){
$num += $db->update('link', ['ord'=>$ord], ['id'=>$id]);
}
if($num>0){
$this->success('/admin/link', '修改成功');
}else{
$this->success('/admin/link', '修改失败');
}
有两个知识点:
第一: foreach($_POST['ord'] as $id=>$ord) 是把每一条值里面的key和value赋值到变量$id和$ord里了
第二:使用$num=0和$num += $db返回的1,最后判断$num是不是大于0来判断整体是不是执行了
这一点挺有意思的。
提交表单提示404
今天(10.19)自己写增加管理员,发现提交表单后移一直显示404,我看了我的路径都没有错啊,找了几乎半个小时。
最后无奈按照老师的方法,把link的路径复制一遍改,一提交居然就可以了。对比了发现,原来我用的是get方法,而提交表单需要也接受post,所以应该写Macaw::any();
就这个事情又浪费了半个小时,哎。
安全问题如何解决
现在这些方法,都是可以直接访问的,即使看不到前台,也可以通过伪造url直接访问,增删改查其实都可以,这是非常大的安全隐患,应该怎么解决呢?后面有没有相应的教程呢?
(10.21 这个问题在后面使用session判断是否登录的时候解决了,如果没有登录,是访问不到这些控制器的,因为在父类控制器admin里面就已经加入了判断。)
用户名已经存在是不是没有判断
自己添加了判断在里面。
如何制作验证码
首先定义一个路由 /admin/login/vcode,指向到Login类的vcode方法。
到Packagist.org寻找Captcha组件
然后进入项目根目录使用composer安装,composer require gregwar/captcha
安装完后,在login.php use它,然后在vcode方法里这么写:
function vcode(){
//生成验证码
$builder = new CaptchaBuilder;
$builder->build();
//获得验证码并保存到session,以备后续校验
$_SESSION['code'] = strtoupper($builder->getPhrase());
//输出验证码图片
header('Content-type: image/jpeg');
$builder->output();
}
别忘了还需要在入口文件Index.php里面开启session_start;
另外还需要有一段js,让用户点击的时候刷新图片,后面加一个time参数,避免由于浏览器缓存导致图片不刷新。
$(".authcode").click(function(){
$(this).attr("src", $(this).attr("src") + '?time=' + new Date().getTime());
});
讲输入的字母变成大写
<input class="fl" type="text" maxlength="6" onkeyup="if(this.value!=this.value.toUpperCase()) this.value=this.value.toUpperCase()" name="code" />
验证用户登录后如何处理session
首先用户登录验证后,讲获取到的用户id和name信息存入session
$_SESSION = $user;
然后在某个控制器里面,讲$_SESSION assign到前端页面,在Admin的构造方法里添加。
$this->assign('session', $_SESSION);
之后就可以在模板文件里使用{{session.id}}来使用数据了。
判断是否登录
首先创建一个token,因为直接用id判断不安全:
$_SESSION['admin_token'] = md5($user['id'].$_SERVER['HTTP_HOST']);
在Helpers.php里判断这个,返回1/0
function ew_login($uType){
return true;
//return md5($_SESSION['id'].$_SERVER['HTTP_HOST']) == $_SERVER[$uType.'_token'] ? 1 : 0;
}
在admin.php的构建函数里,调用ew_login('admin'),如果是假就返回登录页,提示请先登录。
if(!ew_login('admin')){
$this->error('/admin/login', '请先登录');
}
由于login也集成了admin类,加了这个判断之后,连login页也打不开了,所以要把login类改成集成BaseControllers(admin的父类)。
在BaseControllers的display方法里,添加:
$url = getCurUrl();
$this->assign('uri', $url);
同时在__construct里由于路径只是“/app/views”,所以在login.php的index()里面调用模板的路径要变成'admin/login/index'。
这里面有一个坑,我才发现__construct里面的内容,BaseControllers里面有,Admin里面也有,我之前把BaseControllers里的构造函数直接剪切到Admin里面去了,这次改的时候才发现的这个错误。
用户退出
固定的写法,记下来。
function logout(){
//先将session设置为空值
$_SESSION = array();
//如果cookie里面还有session_name,设置成空值,时间设置为之前以便过期,/表示从根目录开始删除
if(isset($_COOKIE[session_name()])){
setcookie(session_name(), '', time()-3600, '/');
}
//销毁session
session_destroy();
//提示
$this->success('/admin/login', '退出成功');
}
HTML中的固定列表处理
现在controller文件里面assign一个数组:
$this->assign('position', [
'1'=>'首页顶部广告(980*80)',
'2'=>'首页底部广告(980*80)',
'3'=>'所有页面顶部广告(980*80)',
'4'=>'所有页面底部广告(980*80)',
'5'=>'首页焦点图广告(730*300)'
]);
再在html文件里面循环显示标签就可以了。
{% for k,v in position %}
<option value="{{k}}">{{v}}</option>
{% endfor %}
一个神奇的事情
居然call了两次模板文件,都不知道是什么原因:
查到原因是路由文件里写了两次mod方法的路由。
处理图片上传
安装slince/upload组件
composer require slince/upload
组件使用教程在这里,用以下代码来处理文件上传:
if($_POST['do_submit']){
unset($_POST['do_submit']);
//处理图片上传
$path = TEMPDIR.'/uploads/ad';
$builder = new UploadHandlerBuilder();
$handler = $builder
->allowExtensions(['jpg', 'gif', 'png'])
->allowMimeTypes(['image/*'])
->saveTo($path)
->getHandler();
$files = $handler->handle();
$filename = $files['logo']->getUploadedFile()->getClientOriginalName();
$newfilename = date('Y-md').'-'.uniqid().'.'.$files['logo']->getUploadedFile()->getClientOriginalName();
rename($path.'/'.$filename, $path.'/'.$newfilename);
$_POST['logo'] = $newfilename;
//添加数据
$db = new BaseDao();
if($db->insert('ad', $_POST)){
$this->success('/admin/ad', '添加成功');
}else{
$this->error('/admin/ad/add', '添加失败');
}
}
坑
input是file的时候,提交过去会存在$_FILES里,我在$_POST里找了半天,其实是没有这个值的,需要在$_FILES里面有,而且里面包含了图片的name, type, size等信息。
删除广告的时候把图片删除
在删除了记录之后,把相对应的图片也删除掉,使用@unlink命令。
$path = TEMPDIR.'/uploads/ad';
@unlink($path.'/'.$logo);
更新广告图片
把update的代码拿过来即可,需要注意的是:
- 获取当前图片名称,更新成功后,删除原有图片
- 如果图片的后缀是大写,那么需要在->allowExtensions(['jpg', 'gif', 'png'])添加'JPG',否则会出错。
获取设置信息并存储在array里
最新回复