如何在 Laravel 5.1 中创建服务 Service Provider

澳门新葡亰赌995577 1

本文由码农网 –
任琦磊原创翻译,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划!

1. 视图分离与嵌套

这是一份面向初学者的 Laravel 5.1 中构建 Service Provider 的教程。

在 learnlaravel 文件夹下运行命令:

我在自己过去的博客中提到了我喜欢 Laravel
5.1 的架构,尤其是它引入了Service
Provider,从而使你模块化的构建应用成为了可能。应用的配置常常可能成为棘手的任务,完全取决于你正在使用的框架,但幸运的是,我们正在使用的 Laravel 让这件事变得相当简单。

php artisan generate:view admin._layouts.default

澳门新葡亰赌995577 1

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

所以让我们开始创建一个用于演示的路由(route)。到 app/Http/routes.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="{{%20URL::route('admin.pages.index')%20}}">Learn Laravel 4</a>
 @include('admin._partials.navigation')
 </div>
 </div>
</div>
<hr>
 @yield('main')
</div>
</body>
</html>
Route::resource('demo', 'DemoController');

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

通过使用 Route::resource,我们就获得了预定义好的 index,show,create,edit,update,store 和 destroy 路由。

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

为了实现良好的对称性,现在我们可以使用 artisan 命令行工具来为我们创建对应的控制器(controller)。键入如下指令:

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

php artisan make:controller DemoController

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

让我们打开创建好的文件,将 index 方法修改为如下内容:

2. 权限验证

public function index()
{
    return view('demo.index');
}

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

现在让我们继续在 app/Resources/views 目录下创建一个名为 Demo 的文件夹,并在文件夹中创建一个名为 index.blade.php 的视图(view)文件,内容如下:

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

@extends('layouts.master')

@section('content')
<h1>Demo Page</h1>
@endsection
@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

澳门新葡亰赌995577,这个例子中我们正在调用一个我已经在 layouts 文件夹中创建了的 master 页面master.blade.php。如果你的 master 用了另一个名字,那么这里你得替换掉。如果你没有 master 页面,那么就删掉第一行 extends 的全部内容,包括 @sectioin 申明。

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

假设你已经配置好了你的开发环境并解析了你的域名,那么当你访问路由yourapplication.com/demo,你应该可以看到内容 Demo
Page 了。

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

好的,那么现在就让我们来创建一个Service Provider。这个Service
Provider不会做太多特别有用的事情。它只是用来向你展示如何搭建它。

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

让我们在 app 目录下创建一个 Helpers 文件夹。然后在 Helpers 文件夹里,创建一个 Contracts 文件夹。在 Contracts 文件夹里,创建文件RocketShipContract.php 并写入下面的内容:

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

<?php

namespace AppHelpersContracts;

Interface RocketShipContract
{

    public function blastOff();

}

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

正如你所知,接口(interface)是一种用来强化架构的契约(contract)。为了定义类的接口,它必须包含名为 blastOff 的公共函数(public
function)。

php artisan generate:controller admin.AuthController

所以为什么要费力地创建一个契约呢?其实,Laravel 有一个神奇的功能是你可以类型提示契约,Service
Provider会返回一个受它约束的具体类的实例。这实现了无与伦比的灵活性和松耦合的结构,因为你的工作将可以轻松地通过一行代码来完成。我们即将看到这是如何工作的。

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

首先,让我们创建一个具体类。在 app/Helpers 文件夹中,创建RocketShip.php,代码如下:

php artisan generate:controller admin/AuthController
<?php

namespace appHelpers;

use AppHelpersContractsRocketShipContract;

class RocketShip implements RocketShipContract
{

    public function blastOff()
    {

        return 'Houston, we have ignition';

    }

}

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

你可以看到我们的具体类没有做很多事,但我们则对如何配合在一起更感兴趣。你可以自己决定你想给你的应用提供什么服务。

<?php
namespace AppControllersAdmin;
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');
 }
}

好的,现在我们要来创建一个符合契约和具体类的Service
Provider了。在命令行中键入下面的指令:

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

php artisan make:provider RocketShipServiceProvider

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

回车确认,它就会为你创建好一个类。

  1. composer 支持 PSR-0 及 PSR-4 标准,标准规定 PHP
    包以命名空间为区分,向外提供服务,所有暴露出来的类都应该在
    作者名包名 命名空间下,例如 luiMFFCMail
    类。这样,哪怕是名称一样的包只要是不同作者也可以在

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

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

新文件位于 app/Providers。前往这个文件,修改为如下内容:

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

<?php

namespace AppProviders;

use IlluminateSupportServiceProvider;
use AppHelpersRocketLauncher;

class RocketShipServiceProvider extends ServiceProvider
{
    protected $defer = true;

    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind('AppHelpersContractsRocketShipContract', function(){

            return new RocketShip();

        });
    }

    /**
     * Get the services provided by the provider.
     *
     * @return array
     */
    public function provides()
    {
        return ['AppHelpersContractsRocketShipContract'];
    }

}

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

让我们看一下这一段:

OK,到目前为止我们的MVC三元素已经集齐了,那接下来该做什么了呢?配置路由。这里的路由并不是家里用的无线路由
:-D,而是
用户请求的URL到控制器某个方法的转换,function是PHP中代码段的最小单位,所以用户请求的一个路径,如

,这条URL打给路由之后,路由就会去解析,应该调用哪个function,最终返回结果给用户。

<?php

namespace AppProviders;

use IlluminateSupportServiceProvider;
use AppHelpersRocketShip;

class RocketShipServiceProvider extends ServiceProvider
{

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

简单粗暴。我们有了命名空间,use 申明和 class 申明。当你创建Service
Provider时,你要导入(import)具体类,像这里我在 use 申明中导入了 RocketShip。

Route::get('admin/logout', array('as' => 'admin.logout', 'uses' => 'AppControllersAdminAuthController@getLogout'));
Route::get('admin/login', array('as' => 'admin.login', 'uses' => 'AppControllersAdminAuthController@getLogin'));
Route::post('admin/login', array('as' => 'admin.login.post', 'uses' => 'AppControllersAdminAuthController@postLogin'));
Route::group(array('prefix' => 'admin', 'before' => 'auth.admin'), function()
{
 Route::any('/', 'AppControllersAdminPagesController@index');
 Route::resource('articles', 'AppControllersAdminArticlesController');
 Route::resource('pages', 'AppControllersAdminPagesController');
});

接下来是:

前三条的意思是hold住两个get请求和一个post请求,下面是一个路由组,规定了一个前缀admin,增加了一个过滤器,auth.admin,内部有一个能同时适应get和post请求的‘/’路径,其完整路径是

资源控制器。

protected $defer = true;

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

发表评论

电子邮件地址不会被公开。 必填项已用*标注

相关文章

网站地图xml地图