Laravel 4 初级教程之视图、命名空间、路由


Posted in PHP onOctober 30, 2014

1. 视图分离与嵌套

在 learnlaravel 文件夹下运行命令:

php artisan generate:view admin._layouts.default

这时候generator插件帮我们创建了app/views/admin/_layouts/default.blade.php 文件,将内容修改为:

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>Learn Laravel 4</title>
  @include('admin._partials.assets')
</head>
<body>
<div class="container">
  <div class="navbar navbar-inverse navbar-fixed-top">
  <div class="navbar-inner">
    <div class="container">
      <a class="brand" href="{{ URL::route('admin.pages.index') }}">Learn Laravel 4</a>
      @include('admin._partials.navigation')
    </div>
  </div>
</div>
<hr>
  @yield('main')
</div>
</body>
</html>

这就是视图文件,MVC中的V。视图需要仔细讲一下。

views文件夹为视图文件夹,视图文件夹可以嵌套,就像我上面一样创建了admin/_layout嵌套文件夹,在里面创建了一个叫default.blade.php的文件,那么以后我们在Laravel内任何地方要用到这个视图的时候,他就叫admin._layouts.default。

我们看到,上面代码的第七行是“@include('admin._partials.assets')”,根据上面我们刚刚了解的知识,这表示载入了另外一个文件。blade是Laravel的模板引擎,此处的 @include 表示直接把那个文件的所有代码带入进来放到这里,变成当前视图的一部分。

注意看第25行“@yield('main')”,这表示什么呢?这个有点复杂,我们稍后再讲。

2. 权限验证

Laravel支持标准HTTP认证,但是在此处我们需要构建blog系统,所以我们将编写完善的管理员登陆系统,从页面登录。

用命令行创建app/views/admin/auth/login.blade.php文件,代码如下:

@extends('admin._layouts.default')
@section('main')
  <div id="login" class="login">
    {{ Form::open() }}
      @if ($errors->has('login'))
        <div class="alert alert-error">{{ $errors->first('login', ':message') }}</div>
      @endif
      <div class="control-group">
        {{ Form::label('email', 'Email') }}
        <div class="controls">
          {{ Form::text('email') }}
        </div>
      </div>
      <div class="control-group">
        {{ Form::label('password', 'Password') }}
        <div class="controls">
          {{ Form::password('password') }}
        </div>
      </div>
      <div class="form-actions">
        {{ Form::submit('Login', array('class' => 'btn btn-inverse btn-login')) }}
      </div>
    {{ Form::close() }}
  </div>
@stop

大家应该注意到了前两行:

@extends('admin._layouts.default')@section('main')

这代表什么?实际上,以后我们会了解到,在controller中调用view的时候,调用的只是这个login.blade.php文件,第一行表示,此视图是admin._layouts.default的子视图,这时blade引擎会把这个视图也载入进来,怎么组装呢?这时候下面那个@section('main')就该出场了,被它包裹的代码将会直接放到admin._layouts.default中的@yield('main')中。section和yield可以任意搭配,只要两个视图之间有调用关系,他们就可以这样用,非常灵活。

写到这里大家可能有个疑问,为什么示例代码里空行那么多?这一点就是个人经验了。blade引擎的所有标签都会在视图编译时用正则处理,引擎本身有一个问题,算不上bug,就是换行符会被处理掉,导致前后行和这一行都紧紧地挤在一起,在前端浏览器中“查看源代码”时,比较不清晰,前后加上空行可以解决这个问题。当然这可能是一个自动的“压缩”特性,不再深入讨论。

增加控制器文件app/controllers/admin/AuthController.php,这时候有人就说了,这我知道,哈哈,运行

“php artisan generate:controller admin.AuthController”

这个想法是对的,但你运行一下试试?会直接在app/controllers目录下创建一个“admin.AuthController.php”文件,有人又说,那我用“admin/AuthController”总行了吧,你试一下?也不行。所以我们要先在app/controllers 下手动创建 admin 文件夹,这时候,再命令行输入:

php artisan generate:controller admin/AuthController

这样就可以了。接下来改写AuthController.php 的内容为:

<?php
namespace App\Controllers\Admin;
use Auth, BaseController, Form, Input, Redirect, Sentry, View;
class AuthController extends BaseController {
  /**
   * 显示登录页面
   * @return View
   */
  public function getLogin()
  {
    return View::make('admin.auth.login');
  }
  /**
   * POST 登录验证
   * @return Redirect
   */
  public function postLogin()
  {
    $credentials = array(
      'email'    => Input::get('email'),
      'password' => Input::get('password')
    );
    try
    {
      $user = Sentry::authenticate($credentials, false);
      if ($user)
      {
        return Redirect::route('admin.pages.index');
      }
    }
    catch(\Exception $e)
    {
      return Redirect::route('admin.login')->withErrors(array('login' => $e->getMessage()));
    }
  }
  /**
   * 注销
   * @return Redirect
   */
  public function getLogout()
  {
    Sentry::logout();
    return Redirect::route('admin.login');
  }
}

这就是我们登录、注销的控制器,MVC中的C。接下来我将讲解命名空间,这是Laravel的基础,或者说是composer的基础,是整个Laravel教程中的重点、难点,希望大家锱铢必较,任何不懂都不要放过。可以到phphub论坛或者golaravel论坛相应帖子下面提问,或者直接发帖提问。

我们首先观察这个文件的位置,它位于 app/controllers/admin 目录下,这有什么不同呢?在其他框架如 CI 中,子文件夹直接加上文件夹名就可以直接调用到了,虽然最多只能有一层。而Laravel没有这么简单,涉及到了PHP的命名空间。

1. composer 支持 PSR-0 及 PSR-4 标准,标准规定 PHP 包以命名空间为区分,向外提供服务,所有暴露出来的类都应该在 \作者名\包名 命名空间下,例如 \lui\MFFC\Mail 类。这样,哪怕是名称一样的包只要是不同作者也可以在https://packagist.org/上共存,供大家使用。

2. 命名空间可以类比成 Linux 系统中的 目录,在任何目录下都可以直接使用文件名打开当前目录下的所有文件和可执行程序,如果需要打开其他目录下的文件,就需要使用绝对路径或者相对路径。

3. 大家可能在许多其他教程中见到过controller头部没有 namesapce 申明,更没有那一堆的 use xxx,像这个文件https://github.com/cecoo/laravel4demo/blob/master/app/controllers /BlogController.php。这个文件在第8行直接使用了 Blog 这个类,这是为什么呢?

因为他们都已经在 learnlaravel 这个 composer 应用的配置文件中声明为自动加载了,而他们没有在顶部声明他们所在的命名空间,这样就会被自动加为顶级命名空间。这个配置文件是 composer.json,对象配置项为autoload 下的classmap 项。这个声明会让 Composer 在生成自动载入文件的时候,自动扫描该文件下所有的类以及所有子文件夹中的类,只要没有声明特定的命名空间,将会被自动加载为顶级空间。【之前表述有误,特此更正!】

关于命名空间更多详情,可以参考 【PHP 命名空间 入门】。

OK,到目前为止我们的MVC三元素已经集齐了,那接下来该做什么了呢?配置路由。这里的路由并不是家里用的无线路由 :-D,而是 用户请求的URL到控制器某个方法的转换,function是PHP中代码段的最小单位,所以用户请求的一个路径,如 http://ooxx.com/fuck/me ,这条URL打给路由之后,路由就会去解析,应该调用哪个function,最终返回结果给用户。

Laravel的路由采用闭包的方式返回结果,在app/routes.php 中增加下列内容:

Route::get('admin/logout', array('as' => 'admin.logout', 'uses' => 'App\Controllers\Admin\AuthController@getLogout'));
Route::get('admin/login', array('as' => 'admin.login', 'uses' => 'App\Controllers\Admin\AuthController@getLogin'));
Route::post('admin/login', array('as' => 'admin.login.post', 'uses' => 'App\Controllers\Admin\AuthController@postLogin'));
Route::group(array('prefix' => 'admin', 'before' => 'auth.admin'), function()
{
    Route::any('/', 'App\Controllers\Admin\PagesController@index');
    Route::resource('articles', 'App\Controllers\Admin\ArticlesController');
    Route::resource('pages', 'App\Controllers\Admin\PagesController');
});

前三条的意思是hold住两个get请求和一个post请求,下面是一个路由组,规定了一个前缀admin,增加了一个过滤器,auth.admin,内部有一个能同时适应get和post请求的‘/'路径,其完整路径是 http://ooxx.com/admin/。剩下的两个资源控制器本质上只是一种简写,URL和控制器类中的方法名的对应表见 资源控制器。

上面说的那个过滤器 auth.admin,是Laravel提供的一个请求过滤器,这个文件就在路由文件的旁边,app/filters.php,在文件末尾增加:

Route::filter('auth.admin', function()
{
 if ( ! Sentry::check()) {
  return Redirect::route('admin.login');
 }
});

这样我们的权限验证就完成了。上面的代码意思是,在进入这个路由组中的任何一条路由之前,会先过一遍 auth.admin这个filter,这个filter会调用Sentry::check(),如果为false,将会进入if代码块,将用户的请求跳转到 命名路由‘admin.login',命名路由文档。从这个命名路由的名称大家也能看出来,就是跟访客说:傻逼,干啥呢,登录去~

这里的“命名路由”功能是为了模仿 Ruby On Rails 的 “link_to”到对象 的路由绑定功能,无奈PHP上传即部署无守护进程的特性,使得我们没法维护一个全量代码的路由表,没法像Rails那样实现 资源路由-资源对象-路由调用 三者绑定的功能,只能搞出一个半成品命名路由,人为地解决了当调整 /people 到 /human 时,要求名称改变而功能不变,同时要求代码自适应的需求。

这时候,我们就可以尝试访问我们的项目了。推荐配置Apache将一个端口指向learnlaravel这个项目的public目录下,即项目通过 http://127.0.0.1:8080 这样的地址访问,十分不建议从子文件夹访问。如果你不会,可以运行

php artisan serve

启动PHP5.4的内建HTTP服务器。地址将会是http://localhost:8000,注意此处 127.0.0.1 不可以访问。

下面,我们在浏览器中访问 /admin,注意URL会自动跳转到 /admin/login,这说明我们的filter起作用了,但你可能得到以下页面

Laravel 4 初级教程之视图、命名空间、路由

这说明代码出错了。接下来我们修改 app/config/app.php 第一项为:

'debug' => true,

刷新页面,错误提示出来了!有没有感觉Laravel4.2的错误提示很好看啊,确实不错,但我觉得没有4.1之前的好看 :-D。我得到了如下错误:

Laravel 4 初级教程之视图、命名空间、路由

说“App\Controllers\Admin\AuthController”这个类未找到,这是为什么呢?这个文件明明有啊。

这就涉及到了另一个问题,Laravel中的autoload问题。Laravel基于命名空间,它只会自动加载所有顶级命名空间的类,就是说我们新增的这个控制器类不是在顶级命名空间下,所以就需要告诉Laravel,我这个类是存在的,怎么告诉它呢?运行

composer dump-autoload

可以了,刷新页面,他告诉我

View [admin._partials.assets] not found.

这个确实是,我们还没建立这个文件呢。建立一个空文件即可,如果是用generator建的话,别忘了把里面默认的内容删掉哦。再刷新页面,如果还有问题,我相信这个问题你可以自己解决。

OK,一个丑的一逼的页面出现了,为什么它这么丑?(鸽子为什么这么大?)因为我们没有引入任何css和js文件,甚至连导航栏的html都不完整。这不要紧,来,按照我github上的代码,自己复制到相应文件中吧。另外,非常重要的一点,把我的项目中的public文件下的 js 和 css 两个文件夹完全复制到你们的public文件夹中。

再刷新,如果你看到以下页面,说明你成功了!

Laravel 4 初级教程之视图、命名空间、路由

3. 尝试登录

用seed新增一名管理员,顺便新增一个管理员组。新建app/database/seeds/SentrySeeder.php,内容为:

<?php
class SentrySeeder extends Seeder {
  public function run()
  {
    DB::table('users')->delete();
    DB::table('groups')->delete();
    DB::table('users_groups')->delete();
    Sentry::getUserProvider()->create(array(
      'email'      => 'oo@xx.com',
      'password'   => "ooxx",
      'first_name' => 'OO',
      'last_name'  => 'XX',
      'activated'  => 1,
    ));
    Sentry::getGroupProvider()->create(array(
      'name'        => 'Admin',
      'permissions' => ['admin' => 1],
    ));
    // 将用户加入用户组
    $adminUser  = Sentry::getUserProvider()->findByLogin('oo@xx.com');
    $adminGroup = Sentry::getGroupProvider()->findByName('Admin');
    $adminUser->addGroup($adminGroup);
  }
}

给app/database/seeds/DatabaseSeeder.php 新增一行:

$this->call('SentrySeeder');

然后运行:

php artisan db:seed

成功以后,进数据库就会发现,users、groups、users_groups表均新增了一行。但是,articles和pages表也分别新增了10行,对,seed就是这么蠢萌^_^

让我们来尝试登录!如果你得到:

Class App\Controllers\Admin\PagesController does not exist

这说明你成功了!

PHP 相关文章推荐
php中通过虚代理实现延迟加载的实现代码
Jun 10 PHP
实现获取http内容的php函数分享
Feb 16 PHP
php采用ajax数据提交post与post常见方法总结
Nov 10 PHP
PHP连接操作access数据库实例
Mar 30 PHP
PHP设计模式之适配器模式代码实例
May 11 PHP
PHP的Socket通信之UDP通信实例
Jul 02 PHP
PHP模拟asp.net的StringBuilder类实现方法
Aug 08 PHP
PHP实现的redis主从数据库状态检测功能示例
Jul 20 PHP
php实现支持中文的文件下载功能示例
Aug 30 PHP
简单实现php上传文件功能
Sep 21 PHP
在Laravel5.6中使用Swoole的协程数据库查询
Jun 15 PHP
yii 框架实现按天,月,年,自定义时间段统计数据的方法分析
Apr 04 PHP
Laravel 4 初级教程之安装及入门
Oct 30 #PHP
推荐几款用 Sublime Text 开发 Laravel 所用到的插件
Oct 30 #PHP
Thinkphp中数据按分类嵌套循环实现方法
Oct 30 #PHP
Thinkphp将二维数组变为标签适用的一维数组方法总结
Oct 30 #PHP
ThinkPHP模板中数组循环实例
Oct 30 #PHP
Laravel 4.2 中队列服务(queue)使用感受
Oct 30 #PHP
初识Laravel
Oct 30 #PHP
You might like
php分页函数
2006/07/08 PHP
用php来改写404错误页让你的页面更友好
2013/01/24 PHP
PHP函数getenv简介和使用实例
2014/05/12 PHP
Yii实现Command任务处理的方法详解
2016/07/14 PHP
PHP实现的AES双向加密解密功能示例【128位】
2018/09/03 PHP
FormValidate 表单验证功能代码更新并提供下载
2008/08/23 Javascript
javascript eval和JSON之间的联系
2009/12/31 Javascript
用jquery模仿的a的title属性(兼容ie6/7)
2013/01/21 Javascript
jquery div拖动效果示例代码
2013/12/08 Javascript
javascript动态控制服务器控件实例
2014/09/05 Javascript
js中的事件捕捉模型与冒泡模型实例分析
2015/01/10 Javascript
jQuery视差滚动效果网页实现方法经验总结
2016/09/29 Javascript
jQuery基于事件控制实现点击显示内容下拉效果
2017/03/07 Javascript
jQuery实现字体颜色渐变效果的方法
2017/03/29 jQuery
Vue.js实现在下拉列表区域外点击即可关闭下拉列表的功能(自定义下拉列表)
2017/05/30 Javascript
JavaScript变量声明var,let.const及区别浅析
2018/04/23 Javascript
js实现弹出框的拖拽效果实例代码详解
2019/04/16 Javascript
浅析微信小程序modal弹窗关闭默认会执行cancel问题
2019/10/14 Javascript
jQuery实现异步上传一个或多个文件
2020/08/17 jQuery
js删除指定位置超链接中含有百度与360的标题
2021/01/06 Javascript
[00:13]天涯墨客二技能展示
2018/08/25 DOTA
Python快速转换numpy数组中Nan和Inf的方法实例说明
2019/02/21 Python
python学生管理系统学习笔记
2019/03/19 Python
python画环形图的方法
2020/03/25 Python
Django Channel实时推送与聊天的示例代码
2020/04/30 Python
戴尔新加坡官网:Dell Singapore
2020/12/13 全球购物
怎样客观的做好自我评价
2013/12/28 职场文书
幼师求职信
2014/06/23 职场文书
中小学校园安全广播稿
2014/09/29 职场文书
2015年毕业生自荐信范文
2015/03/24 职场文书
电影开国大典观后感
2015/06/04 职场文书
2016年先进教师个人事迹材料
2016/02/26 职场文书
如何把新闻人物写得立体、鲜活?
2019/08/14 职场文书
golang如何去除多余空白字符(含制表符)
2021/04/25 Golang
Mysql数据库手动及定时备份步骤
2021/11/07 MySQL
flex弹性布局详解
2022/03/20 HTML / CSS