在 Laravel 的文档中有Package Development,对于入门开发人员来说还是比较抽象,因为开发一个包需要了解 Service Providers, Facade 已经够抽象的了对刚接触 Laravel 的开发人员来说,所以我来写一个简单的 Laravel 包开发的实例教程吧。

toastr.js是一个很方便的通知效果,最近刚发布了 laravel 5.2,所以就来开发一个 toastr for laravel 5 的包吧,主要用 toastr 结合 laravel 的flash session来实现页面的一次性消息提醒,其实这个在我们日常开发中页面操作提醒还是很常用到的业务。

一般的 laravel 包开发过程是这样的,开发好以后打包 push 到 gitlab,然后在packagist上提交,下面我们就来一步一步实现这个过程。

1.在新建的 laravel 项目中建立如下目录 packages/yuansir/toastr/src ,packages 目录和 app 目录同级。我们开发包的代码都放在这个 src 目录中,yuansir 和 toastr 完全自定义。

2.修改项目的 composer.json,设定 PSR-4 命名空间:

"autoload": {
        "classmap": [
            "database"
        ],
        "psr-4": {
            "App\\": "app/",
            "Yuansir\\Toastr\\": "packages/yuansir/toastr/src/"
        }
    },

别忘了执行 autoload

$ composer dump-autoload

3.为我们的包初始化一个 composer.json 文件,熟悉 composer 的应该都知道这玩意是干嘛的了

$ cd pacages/yuansir/toastr/src
$ composer init

按照提示填写相关信息,有些信息可以不用填写,后面自己在 composer.json 中添加就可以了,生成的示例如下:

{
    "name": "ryan/toastr-for-laravel",
    "description": "toastr.js for laravel5",
    "authors": [
        {
            "name": "Ryan",
            "email": "yuansir@live.cn"
        }
    ],
    "require": {}
}

4.开始开发,新建 Service Provider

$ php artisan make:provider ToastrServiceProvider

将生成的app/Providers/ToastrServiceProvider.php文件移动到我们的packages/yuansir/toastr/src目录下面,并注册ToastrServiceProviderconfig/app.phpproviders 中。

 'providers' => [

        /*
         * Laravel Framework Service Providers...
         */
         ......

        /*
         * Application Service Providers...
         */
         ......
        Yuansir\Toastr\ToastrServiceProvider::class,
    ],

5.新建 packages/yuansir/toastr/src/config/toastr.php 来保存toastr.js的 options,options 配置还蛮多的,具体可以看它的 demo。

<?php
return [
    'options' => []
];

关于这个配置在我们包中如何调用的,我们过会来说.

6.新建 Toastr 类,来实现 toastr 的 info,success,error,warning 的相关实现,代码还是很简单的,packages/yuansir/toastr/src/Toastr.php:

<?php namespace Yuansir\Toastr;

use Illuminate\Session\SessionManager;
use Illuminate\Config\Repository;

class Toastr
{
    /**
     * @var SessionManager
     */
    protected $session;

    /**
     * @var Repository
     */
    protected $config;

    /**
     * @var array
     */
    protected $notifications = [];

    /**
     * Toastr constructor.
     * @param SessionManager $session
     * @param Repository $config
     */
    public function __construct(SessionManager $session, Repository $config)
    {
        $this->session = $session;
        $this->config = $config;
    }

    public function render()
    {
        $notifications = $this->session->get('toastr:notifications');

        if(!$notifications) {
            return '';
        }

        foreach ($notifications as $notification) {
            $config = $this->config->get('toastr.options');
            $javascript = '';
            $options = [];
            if($config) {
                $options = array_merge($config, $notification['options']);
            }

            if($options) {
                $javascript = 'toastr.options = ' . json_encode($options) . ';';
            }

            $message = str_replace("'", "\\'", $notification['message']);
            $title = $notification['title'] ? str_replace("'", "\\'", $notification['title']) : null;
            $javascript .= " toastr.{$notification['type']}('$message','$title');";
        }

        return view('Toastr::toastr', compact('javascript'));
    }

    /**
     * Add notification
     * @param $type
     * @param $message
     * @param null $title
     * @param array $options
     * @return bool
     */
    public function add($type, $message, $title = null, $options = [])
    {
        $types = ['info', 'warning', 'success', 'error'];
        if(!in_array($type, $types)) {
            return false;
        }

        $this->notifications[] = [
            'type' => $type,
            'title' => $title,
            'message' => $message,
            'options' => $options
        ];
        $this->session->flash('toastr:notifications', $this->notifications);
    }

    /**
     * Add info notification
     * @param $message
     * @param null $title
     * @param array $options
     */
    public function info($message, $title = null, $options = [])
    {
        $this->add('info', $message, $title, $options);
    }

    /**
     * Add warning notification
     * @param $message
     * @param null $title
     * @param array $options
     */
    public function warning($message, $title = null, $options = [])
    {
        $this->add('warning', $message, $title, $options);
    }

    /**
     * Add success notification
     * @param $message
     * @param null $title
     * @param array $options
     */
    public function success($message, $title = null, $options = [])
    {
        $this->add('success', $message, $title, $options);
    }

    /**
     * Add error notification
     * @param $message
     * @param null $title
     * @param array $options
     */
    public function error($message, $title = null, $options = [])
    {
        $this->add('error', $message, $title, $options);
    }

    /**
     * Clear notifications
     */
    public function clear()
    {
        $this->notifications = [];
    }
}

7.我们看到 view(‘Toastr::toastr’, compact(‘javascript’));,那么就是需要一个视图文件了,关于 Toastr::toastr 是什么鬼我们过会来说,新建 packages/yuansir/toastr/src/views/toastr.blade.php 视图文件:

<link href="http://cdn.bootcss.com/toastr.js/latest/css/toastr.min.css" rel="stylesheet">
<script src="http://cdn.bootcss.com/toastr.js/latest/js/toastr.min.js"></script>
<script type="text/javascript">{!! $javascript !!}</script>

就是引入了 tastr 插件,输出我们 render 方法中的$javascript

8.建立 Facade,新建 packages/yuansir/toastr/src/Facades/Toastr.php

<?php namespace Yuansir\Toastr\Facades;

use Illuminate\Support\Facades\Facade;

class Toastr extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'toastr';
    }
}

9.修改 ToastrServiceProvider:

<?php namespace Yuansir\Toastr;

use Illuminate\Support\ServiceProvider;

class ToastrServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        $this->loadViewsFrom(__DIR__ . '/views', 'Toastr');

        $this->publishes([
            __DIR__.'/views' => base_path('resources/views/vendor/toastr'),
            __DIR__.'/config/toastr.php' => config_path('toastr.php'),
        ]);
    }

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app['toastr'] = $this->app->share(function ($app) {
            return new Toastr($app['session'], $app['config']);
        });
    }

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

$this->loadViewsFrom(__DIR__ . ‘/views’, ‘Toastr’);就是表示 Toastr 命名空间的视图文件从当前目录的 views 目录中渲染,所以我们上面用return view(‘Toastr::toastr’, compact(‘javascript’));

$this->publishes 在执行php artisan vendor:publish时会将对应的目录和文件复制到对应的位置

10.测试下是否可行,修改 config/app.php 添加如下:

/*
    |--------------------------------------------------------------------------
    | Class Aliases
    |--------------------------------------------------------------------------
    |
    | This array of class aliases will be registered when this application
    | is started. However, feel free to register as many as you wish as
    | the aliases are "lazy" loaded so they don't hinder performance.
    |
    */

    'aliases' => [
        ......

        'Toastr' => Yuansir\Toastr\Facades\Toastr::class,

    ],

写个控制器放进去试试:

<?php

namespace App\Http\Controllers;

use App\Http\Requests;
use Illuminate\Http\Request;
use Toastr;

class HomeController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        //略
    }

    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Http\Response
     */
    public function index(Request $request)
    {
        Toastr::error('你好啊','标题');
        return view('home');
    }
}

反正我测试是 OK 了,就不截图了!!

11.修改命名空间到包的 composer.json,因为别人安装这个包的时候不可能也去改项目 composer.json 的 PSR-4 的 autoload,所以我们把 PSR-4 的命名空间加到这个包的 composer.json 中去,修改packages/yuansir/toastr/src/composer.json 如下:

{
    "name": "ryan/toastr-for-laravel",
    "description": "toastr.js for laravel5",
    "authors": [
        {
            "name": "Ryan",
            "email": "yuansir@live.cn"
        }
    ],
    "require": {},
    "autoload": {
        "psr-4": {
            "Yuansir\\Toastr\\": "src/"
        }
    }
}

12.建立 Github 项目

EE32D501-A968-4D6C-9F77-0661137F736

将代码 push 到项目中去:

$ cd packages/yuansir/toastr/
$ git init
$ git add .
$ git commit -m "add package source files."
$ git remote add origin git@github.com:yuansir/toastr-for-laravel5.git
$ git push -u origin master
$ git tag -a 1.0.0 -m "version 1.0.0"
$ git push --tags

13.提交到 Packagist,打开到packagist.org,登陆后点击右边上角的 submit,并填入 git 的项目地址 git@github.com:yuansir/toastr-for-laravel5.git 点击check 就 OK 了

D9AB1D5A-C1BC-4A01-8A78-D1EDB859822

705850F3-B2AA-453F-B0DB-6196BE9C0CB

到此结束,大功告成,这样一个 Laravel 的 composer 包就开发完成了。 这个教程的源码和这个包的安装使用方法详见 github https://github.com/yuansir/toastr-for-laravel5 如有问题欢迎指正!

转载请注明: 转载自Ryan 是菜鸟 | LNMP 技术栈笔记

如果觉得本篇文章对您十分有益,何不 打赏一下

谢谢打赏

本文链接地址: Laravel Composer Package 开发实战 toastr-for-laravel5

知识共享许可协议 本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可