2016年12月

laravel服务容器延迟加载

很简单,在provider文件夹下指定的服务当中这样写

<?php

namespace App\Providers;

use App\Repositories\RecommendRepository;
use Illuminate\Support\ServiceProvider;

class HotcastProvider extends ServiceProvider
{
    /**
     * 服务提供者加是否延迟加载.
     *
     * @var bool
     */
    protected $defer = true;

    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {

        $this->app->bind('App\Repositories\Contract\RecommendInterface','App\Repositories\RecommendRepository');

        $this->app->singleton('RecommendFacade',function (){
            return new RecommendRepository();
        });

    }

    /**
     * 获取由提供者提供的服务.
     *
     * @return array
     */
    public function provides()
    {
        return ['RecommendFacade','App\Repositories\Contract\RecommendInterface'];
    }
}

defer 改为 true, 添加一个 provides方法,然后命令行执行php artisan clear-compiled 重新编译一下服务文件就OK了

通过git rebase来合并commit提交记录

多人合作开发的时候就会因为本地的多个版本提交的很多杂乱的信息,导致git log查看的时候并不能很快的从一堆无意义的提交记录中找到我们想要的版本。
做完一个小功能随时提交是一个好习惯,但是这写小记录推送到远程就是个不大不小的麻烦

我们通过rebase命令将这些小版本合并成一个大的版本,然后推送到远程,前提是你的代码功能已经达到了。

  1. 查看log信息
commit 53097de5638d371da51bc6fef74a90ca1420d967
Author: GPF <5173180@qq.com>
Date:   Sun Dec 25 22:46:13 2016 +0800

    test3

commit 830026adb43506d0bc1172432f84639f84dae087
Author: GPF <5173180@qq.com>
Date:   Sun Dec 25 22:45:56 2016 +0800

    test2

commit 646f5be02ec285e30626f5682e6e7e9437762ac5
Author: GPF <5173180@qq.com>
Date:   Sun Dec 25 22:45:43 2016 +0800

    test1
...

比如我们就合并这前三个提交记录

  1. 开启rebase脚本
git rebase -i HEAD~3

HEAD~3的意思就是最近的san tiao三条提交信息

  1. 编辑脚本
    执行步骤2的时候就会进入一个编辑脚本如下:
pick 646f5be test1
pick 830026a test2
pick 53097de test3

# Rebase dbaf38a..53097de onto dbaf38a (3 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

带#号的都是注释内容,这里提供了很多命令信息。最上面的三行就是我们要执行的命令,pick 查看注释是 use commit 可以简写成 p , 而且你看排列commit记录的顺序是最早的排在最上面,我们这次是为了合并commit,因此我们要用的命令就是squash 简写成 s
将脚本修改成这样:

pick 646f5be test1
s 830026a test2
s 53097de test3

# Rebase dbaf38a..53097de onto dbaf38a (3 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

我们合并最早的两个到test1版本当中然后:wq退出,注意合并记录的时候,不能pick最近的一条,会产生报错,如果把报错的话就输入命令git rebase --abort终止这次执行就好

  1. 接下来会直接进入一个commit记录编辑界面,#都是注释过的,将非注释的内容编辑成我们想要的然后:wq保存就完毕了

laravel的错误与日志

laravel有一套方便的错误采集和报错机制,如果是开发api的时候我们不需要报错页面显示的那么详细,只想返回一些错误信息,而不用把所有的响应信息都返回到 controller 当中才行

之前的步骤是:

router->beforeMiddlware->controller->someService->controller->afterMiddleware-> yourInfo

我们想在这任何一个环节报错的时候都能停止掉运行流程直接返回信息 就需要接下来要说的一个类 Exception

我们自定义的错误文件也是都放在 app/Exception 当中,比如我自定义一个 TestException

<?php

namespace App\Exceptions;

class TestException extends \Exception
{
    public function responseJson(){
        $msg = config('errors.user.'.$this->getMessage());
        return \ApiResponse::error($msg);
    }
}

这里面我自定义了一个responseJson()的方法,为了处理我返回的信息

然后我在同文件夹下的 Handler.php 我们返回的错误在这里集中处理

这里的两个方法report()render()

report()会优先执行,用于将错误发送到第三方服务中去,render()就是最终要返回的结果,我们的目标就是这个函数

/**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Exception  $e
     * @return \Illuminate\Http\Response
     */
    public function render($request, Exception $e)
    {
        if($e instanceof TestException){
            return $e->responseJson();
        }
        return parent::render($request, $e);
    }

这里用了instanceof进行比较,如果是来自TestException的报错就会执行我们刚才自定义好的方法,内容当然也可以随便定义了

怎么使用呢?

只需要在控制器中任何你需要报错的地方

throw new TestException('system_busy');

括号内的内容可以通过它的getMessage()方法获得

参考文档

再来个facade吧

参照上一篇的文章laravel 的 Repository 模式 都已经注册了服务也可以使用 Facade

首先创建一个 Facade文件,还是在 app/Repositories这里,创建HelloFacade.php 名字无所谓,路径也是无所谓的,关键是能够正确引入文件就行

<?php
/**
 * Created by PhpStorm.
 * User: gpf
 * Date: 2016/12/19
 * Time: 下午10:48
 */

namespace App\Repositories;

use Illuminate\Support\Facades\Facade;
class HelloFacade extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'Hello';
    }

}

然后再在之前创建的namespace App\Providers\HelloProvider中作出一些改动

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Repositories\Hello;
use App\Repositories\HelloRepository;
class HelloProvider extends ServiceProvider
{
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind('App\Repositories\Hello','App\Repositories\HelloRepository');

        $this->app->singleton('Hello',function(){
            return new HelloRepository();
        });
    }
}

$this->app->singleton就是为了去绑定facade 现在已经成功了一半了,接下来去配置文件中注册一下就能正常使用了

config/app.php

'providers' => [
    ....
     App\Providers\HelloProvider::class,
    ....
],
'aliases' => [
    ...
    'Hello' => \App\Repositories\HelloFacade::class,
    ...
]

这样注册过后就能在文件中直接类似

use Hello;

Hello::say();

这一类的调用了

laravel 的 Repository 模式

原文出处

laravel版本5.1+

首先创建测试文件

php artisan make:controller TestController

修改路由文件route.php或者routes/web.php

Route::get('test', 'TestController@index');

创建服务提供者文件,这个文件的作用就是用来注册自定义的服务容器

php artisan make:provider HelloProvider

我们再创建一个容器文件夹app/Repositories这里存放相应的接口和应用类,现在上我们的主角

<?php
//app/Repositories/Hello.php
/**
 * Created by PhpStorm.
 * User: gpf
 * Date: 2016/12/18
 * Time: 下午9:30
 */

namespace App\Repositories;


interface Hello
{
    function say();
}
<?php
//app/Repositories/HelloRepository.php
/**
 * Created by PhpStorm.
 * User: gpf
 * Date: 2016/12/18
 * Time: 下午9:17
 */
 
 namespace App\Repositories;
 
class HelloRepository implements Hello
{
    public function say(){
        return 'hello baby';
    }

}

还记得刚才我们创建的HelloProvider吗?现在我们在这里注册一下服务

<?php
//app/Provider/HelloProvider.php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Repositories\Hello;
class HelloProvider extends ServiceProvider
{
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        //
   }

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind('App\Repositories\Hello','App\Repositories\HelloRepository');
    }
}

接下来是最重要的一步,那就是去注册这个服务,你发现每次安装一个依赖包的时候都会操作一个文件,那就是config/app.php,在providers这个数组中添加服务

'providers' => [
    //other providers...
    App\Providers\HelloProvider::class,
    ],

现在就可以使用这个啦!
进入我们的TestController

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Repositories\Hello;

class TestController extends Controller
{
    public $provider = null;

    public function __construct(Hello $hello)
    {
        $this->provider = $hello;
    }

    public function index(){
        return $this->provider->say();
    }
}

访问 http://your-domain/test就能看到效果了

这里要记住,我们在控制器中调用的是接口,而接口已经通过provider已经注册绑定了一个符合接口规范的实例,这样的话我们只调用接口提供的外部方法,这样一个好处就是在控制器中我们就专注流程,具体的实现交给服务,内部需要什么依赖就服务自己去解决,而且服务是通用的,每个控制器都能调用,代码的可复用性可维护性,这一点项目初期可能会感觉不到,但是一旦发生变动的时候,重复改动的地方就会非常多,项目经营时间越长这个变动的代价就会越高

试想一下,如果你是每个controller中都写相同的调度逻辑,有一天你的产品经理对你说:这里这里改一下,添加个字段而已很简单的……你会不会有砍死他的冲动?如果用上了服务仓库,你完全可以先看一下仓库中需要改动的方法到底怎么样再考虑砍人的事情 :)