使用Laravel开发自己的Package
Service Provider
最主要的功能就是让我们写package,本文以一个简单的 Hello World
的功能,介绍一下package的开发流程。
包含route
、controller
与view
,然后会打包成package传到github与packagist,最后在其他的laravel项目中直接使用 composer require vendor/package
下载执行我们上传的package。
Version
- Laravel 5.5
- php 7.1.12
建立项目
composer create-project laravel/laravel MyPackage --prefer-dist
建立一个开发package的项目。
建立目录
在项目根目录下串建立packages
目录,关于该package的源代码都会放在它里面。
在packages
目录下建立vendor/package/src
子目录,本package会建立curder/helloworld/src
。
设定PSR-4命名空间
由于目前Laravel预设的root namespace
是在app
目录下,并且无法法得知我们新加的packages/curder/helloworld/src
目录,必须在MyPackage
项目的composer.json
加入对应新的root namespace
。
{
"name": "laravel/laravel",
"description": "The Laravel Framework.",
"keywords": ["framework", "laravel"],
...
"autoload": {
"classmap": [
"database/seeds",
"database/factories"
],
"psr-4": {
"App\\": "app/",
"Curder\\HelloWord\\": "packages/curder/helloworld/src/"
}
},
...
}
注意我们添加的行:"Curder\\HelloWorld\\": "packages/curder/helloworld/src/"
的命名空间和路径的映射关系。
修改完上面的设定后,需要执行下面的composer命令更新一下自动加载。
composer dump-autoload
建立Service Provider
使用下面的命令建立Service Provider
php artisan make:provider HelloWorldServiceProvider
产生HelloWorldServiceProvider.php
默认在app/Providers
目录下,因为我们是要写package,所以将此文件移到packages/curder/helloworld/src
目录下。
因为文件的目录已经移动,需要重新修改一些它的namespace。
<?php
namespace Curder\HelloWorld;
use Illuminate\Support\ServiceProvider;
class HelloWorldServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot()
{
//
}
/**
* Register the application services.
*
* @return void
*/
public function register()
{
//
}
}
注意文件的命名空间是:Curder\HelloWorld
,使用命令生成的Provider默认会建立boot()
和register()
。
注册ServiceProvider
在config/app.php
中註冊HelloWorldServiceProvider
。
return [
...
'providers' => [
...
/*
* Package Service Providers...
*/
Curder\HelloWord\HelloWorldServiceProvider::class,
/*
* Application Service Providers...
*/
...
],
...
];
注册刚刚建立的HelloWorldServiceProvider
。
建立Router
把路由文件放到src/routes/
下,并命名为web.php
,内容如下:
<?php
$namespace = 'Curder\HelloWorld\Http\Controllers';
Route::group([
'namespace' => $namespace,
'prefix' => 'helloworld',
], function () {
Route::get('/', 'HelloWorldController@index');
});
建立View
把视图文件建立在src/resources/views
下,将其命名为welcome.blade.php
。
<!doctype html>
<html lang="{{ app()->getLocale() }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css?family=Raleway:100,600" rel="stylesheet" type="text/css">
<!-- Styles -->
<style>
html, body {
background-color: #fff;
color: #636b6f;
font-family: 'Raleway', sans-serif;
font-weight: 100;
height: 100vh;
margin: 0;
}
.full-height {
height: 100vh;
}
.flex-center {
align-items: center;
display: flex;
justify-content: center;
}
.position-ref {
position: relative;
}
.top-right {
position: absolute;
right: 10px;
top: 18px;
}
.content {
text-align: center;
}
.title {
font-size: 84px;
}
.links > a {
color: #636b6f;
padding: 0 25px;
font-size: 12px;
font-weight: 600;
letter-spacing: .1rem;
text-decoration: none;
text-transform: uppercase;
}
.m-b-md {
margin-bottom: 30px;
}
</style>
</head>
<body>
<div class="flex-center position-ref full-height">
@if (Route::has('login'))
<div class="top-right links">
@auth
<a href="{{ url('/home') }}">Home</a>
@else
<a href="{{ route('login') }}">Login</a>
<a href="{{ route('register') }}">Register</a>
@endauth
</div>
@endif
<div class="content">
<div class="title m-b-md">
{{ $message }}
</div>
<div class="links">
<a href="https://laravel.com/docs">Documentation</a>
<a href="https://laracasts.com">Laracasts</a>
<a href="https://laravel-news.com">News</a>
<a href="https://forge.laravel.com">Forge</a>
<a href="https://github.com/laravel/laravel">GitHub</a>
</div>
</div>
</div>
</body>
</html>
创建Migration
将数据库迁移文件创建在src/publishable/databases/migrations/
下,我们按照当前日期将其命名为:2017_12_17_000000_create_package_demo_table.php
,内容如下:
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreatePackageDemoTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('package_demo', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->unsignedInteger('user_id');
$table->unsignedInteger('checklist_id');
$table->boolean('is_published');
$table->boolean('is_archived');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('package_demo');
}
}
创建配置文件
将配置文件创建在src/publishable/config/
下,我们将其命名为helloworld.php
,内容如下:
<?php
return [
"message" => "Hello World",
];
配置文件的文件名不允许随意定义,不要与Laravel已有的配置文件文件名冲突,后期再项目的其他地方需要引用。
建立Controller
根据上面路由的定义,把控制器文件放到src/Http/Controllers
下。
<?php
namespace Curder\HelloWorld\Http\Controllers;
use App\Http\Controllers\Controller;
class HelloWorldController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$message = config("helloworld.message");
return view('HelloWorld::welcome', compact('message'));
}
}
修改Service Provider
<?php
namespace Curder\HelloWorld;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
class HelloWorldServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot()
{
Schema::defaultStringLength(191);
$this->loadRoutesFrom(__DIR__ . '/routes/web.php' .
'');
$this->loadViewsFrom(__DIR__ . '/resources/views', 'HelloWorld');
}
/**
* Register the application services.
*
* @return void
*/
public function register()
{
$this->registerPublishables();
}
private function registerPublishables()
{
$basePath = __DIR__;
$arrPublishable = [
'migrations' => [
"$basePath/publishable/databases/migrations" => database_path('migrations'),
],
'config' => [
"$basePath/publishable/config/helloworld.php" => config_path('helloworld.php'),
],
];
foreach ($arrPublishable as $group => $paths) {
$this->publishes($paths, $group);
}
}
}
执行发布命令
php artisan vendor:publish
执行完上面的命令后,就能看到如下的界面
输入Curder\HelloWorld\HelloWorldServiceProvider
的内容进行发布。
另外对于已经发布的内容如果需要覆盖,在上面的发布命令中加入
--force
参数。但是在执行这个命令之前请确保对修改过的数据已有备份。
访问效果
上传到GitHub
设定package的命名空间
目前在本地开发的这个package由于是搭配MyPackage项目测试,所以将PSR-4
的root namespace
设定在MyPackage
项目的composer.json
。
但发布package
之后,不可能要使用者也手动在自己项目的composer.json
加上命名空间,所以我们要将命名空间设定在package
自己的composer.json
。
我们来到项目的目录下packages/curder/helloworld
下执行如下命令,生成composer.json
文件:
composer init
下面是我们从上面的命令中生成的composer.json
,添加上PSR-4
的自动加载。
{
"name": "curder/helloworld",
"description": "A Laravel Package For Demo.",
"license": "MIT",
"authors": [
{
"name": "curder",
"email": "[email protected]"
}
],
"minimum-stability": "dev",
"require": {},
"autoload": {
"psr-4": {
"Curder\\HelloWorld\\": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"Curder\\HelloWorld\\HelloWorldServiceProvider"
]
}
}
}
建立本地Git仓库
cd packages/curder/helloworld
git init
git add .
git commit -m "Initial commit."
建立远程git仓库
推送到远程仓库
git remote add origin [email protected]:curder/helloworld.git
git push -u origin master
git tag -a 1.0.0 -m "First version"
git push --tags
上传Packagist
登入Packagist
登录至Packagist,按右上角Submit上传package。
提交Github网址
贴上package在Github Repository的网址。
确认提交
因为Packagist已经有很多package名字叫做helloworld,Packagist让你确认是否要上传。
上传成功
测试Package
建立测试项目
composer create-project laravel/laravel MyTestPackage --prefer-dist
安裝Package
cd MyTestPackage
composer require oomusou/helloworld
由于Laravel5.5之后新增了包的自动发现功能,所以我们并不需要在
config/app.config
中手动引入ServiceProvider
。
发布配置和迁移文件
php artisan vendor:publish