跳至主要内容

Package Development

備註

這裡以假設開發紀錄 Log 套件為例

開發流程

Git 版控前置作業(GitHub)

  1. 會在 GitHub 上先新增一個 repository
  2. 在開發用的 Laravel 專案中直接 clone 剛剛新增的 repositorypackages/ppcsite/record_log
提示

🌟資料夾建議使用 packages/<my-github-username[vendor]>/<package-name> 的結構

Composer 初始化

  1. Terminal cd 到剛剛新增的資料夾,透過 composer init 初始化。
提示

Terminal 指令:(若無使用版控,直接在 root 創一個資料夾)

mkdir -p packages/<my-github-username>/<package-name>
PS C:\xampp\htdocs\laravel-library> cd ./packages/ppcsite/record_log
# 初始化
PS C:\xampp\htdocs\laravel-library\packages\ppcsite\record_log> composer init


Welcome to the Composer config generator



This command will guide you through creating your composer.json config.
# 輸入套件命名
Package name (<vendor>/<name>) [user/test]: ppcsite/record_log
# 輸入套件描述
Description []: this package used for record log.
# 輸入作者訊息
Author [GavinLin512 <81368692+GavinLin512@users.noreply.github.com>, n to skip]:
# 輸入最低穩定版本,ex: stable, RC, beta, alpha, dev
Minimum Stability []: dev
# 輸入套件類型
Package Type (e.g. library, project, metapackage, composer-plugin) []: library
# 輸入授權類型
License []: MIT

Define your dependencies.

# 是否需要定義依賴套件
Would you like to define your dependencies (require) interactively [yes]? yes
# 以 php 為例
Search for a package: php
Info from https://repo.packagist.org: #StandWithUkraine
# 輸入此依賴套件最低需求版本(或留白取最新版本)
Enter the version constraint to require (or leave blank to use the latest version): >=8.0.2
# 如需多個依賴則重複上述步驟
Search for a package:
......

# 是否需要定義 require-dev
Would you like to define your dev dependencies (require-dev) interactively [yes]? no
# 是否支援 PSR-4 自動載入(對應命名空間名稱與檔案目錄名稱)
Add PSR-4 autoload mapping? Maps namespace "Packages\Test" to the entered relative path. [src/, n to skip]:

{
"name": "ppcsite/record_log",
"description": "this package used for record log",
"type": "library",
"require": {
"php": ">=8.0.2"
},
"license": "MIT",
"autoload": {
"psr-4": {
"Ppcsite\\RecordLog\\": "src/"
}
},
"authors": [
{
"name": "GavinLin512",
"email": "81368692+GavinLin512@users.noreply.github.com"
}
],
"minimum-stability": "dev"
}
# 是否照以上資訊生成 composer.json
Do you confirm generation [yes]?

# 是否要現在安裝依賴套件
Would you like to install dependencies now [yes]? no

PSR-4 autoloading configured. Use "namespace Ppcsite\RecordLog;" in src/
Include the Composer autoloader with: require 'vendor/autoload.php';

2. 初始化完成後會自動生成 src 資料夾及 composer.json

.
├── root
│ ├── packages
│ │ ├── ppcsite
│ │ │ ├── record_log
│ │ │ │ ├── "src"
│ │ │ │ └── "composer.json"

Service Provider 相關

1. 設定 Package Discovery,讓使用者在安裝套件時自動註冊 Service ProviderFacade

備註

關於 Service ProviderFacade

🌟透過 Service Provider 啟動 Laravel 應用程式,包括註冊 service container 綁定、EventListenerMiddleware,甚至 Route

🌟Facades 在應用程式的 service container 中為 Class 提供了一個 static 介面,它們提供了一個簡潔而有力的語法來讓你使用 Laravel 的功能。

{
"extra": {
"laravel": {
"providers": [
"Ppcsite\\RecordLog\\RecordLogServiceProvider"
],
"aliases": {
"RecordLog": "Ppcsite\\RecordLog\\RecordLog"
}
}
}
}

2. 在 src 資料夾新增 Service Provider,可以使用 php artisan make:provider XXXServiceProvider 指令新增。

.
├── record_log
│ ├── src
│ │ └── "RecordLogServiceProvider.php"
│ └── composer.json

信息

namespace 的設定很重要,必須符合 PSR 0/PSR 4 的規範。

<?php

namespace Ppcsite\RecordLog;

use Illuminate\Support\ServiceProvider;

class RecordLogServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* @return void
*/
public function register()
{
// 定義需要註冊的服務
// 讓 service container 連結自定義的 Facades
$this->app->bind('packages-record-log', function($app) {
return new \Ppcsite\RecordLog\Models\AdminLog();
});
// 合併使用者與系統預設 config 設定檔,讓使用者可以合併覆寫 config 設定檔
$this->mergeConfigFrom(
__DIR__.'/../config/record-log.php', 'record-log'
);
}

/**
* Bootstrap services.
*
* @return void
*/
public function boot(): void
{
// 發佈 config 設定檔
$this->publishes([
__DIR__.'/../config/record-log.php' => config_path('record-log.php'),
],'record-log-config');
// 發佈 migration
$timestamp = date('Y_m_d_His');
$this->publishes([
__DIR__.'/../database/migrations/create_admin_logs_table.php' => database_path('migrations/'.$timestamp.'_create_admin_logs_table.php'),
],'record-admin-logs-migration');
}
}

3. 在 src 資料夾新增 Facades/RecordLog.php,負責綁定套件的依賴注入方法。

.
├── record_log
│ ├── src
│ │ ├── Facades
│ │ │ └── "RecordLog.php"
│ │ └── RecordLogServiceProvider.php
│ └── composer.json

<?php

namespace Ppcsite\RecordLog\Facades;

use Illuminate\Support\Facades\Facade;

/**
* @see \Ppcsite\RecordLog\RecordLog
*/
class RecordLog extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*
*/
protected static function getFacadeAccessor():string
{
return 'packages-record-log';
}
}

套件內部相關

信息

🌟資料夾結構範例:

├── src
│ ├── Facades
│ ├── Contracts(interface)
│ ├── Models
│ ├── Views
│ ├── Commands
│ ├── Middlewares
│ ├── Exceptions
│ └── Traits
├── database
│ ├── factories
│ ├── seeders
│ └── migrations
├── resourses
│ └── views
├── config
└── tests

1. 在 src 資料夾新增 Contracts/AdminLog.php,定義 Class 的使用介面 interface

.
├── record_log
│ ├── src
│ │ ├── Facades
│ │ │ └── RecordLog.php
│ │ ├── Contracts
│ │ │ └── "AdminLog.php"
│ │ └── RecordLogServiceProvider.php
│ └── composer.json

<?php

namespace Ppcsite\RecordLog\Contracts;

use Illuminate\Database\Eloquent\Model;

interface AdminLog
{
/**
* 紀錄 Log
*
* @param string $function_name
* @param $action
* @param Model|null $data_model
* @param $comment
* @return void
*/
public static function Log(string $function_name, string $action, Model $data_model=null, string $comment=null): void;

/**
* 取得變更資料之 model 關聯
*
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function datatable(): \Illuminate\Database\Eloquent\Relations\MorphTo;
}

2. 在 src 資料夾新增 Models/AdminLog.php,定義此套件的核心 Model

.
├── record_log
│ ├── src
│ │ ├── Facades
│ │ │ └── RecordLog.php
│ │ ├── Contracts
│ │ │ └── AdminLog.php
│ │ ├── Models
│ │ │ └── "AdminLog.php"
│ │ └── RecordLogServiceProvider.php
│ └── composer.json

<?php

namespace Ppcsite\RecordLog\Models;

use Illuminate\Database\Eloquent\Model;

use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Request;

class AdminLog extends Model implements \Ppcsite\RecordLog\Contracts\AdminLog
{
// 操作類型
CONST ACTION_NEW = '新增';
CONST ACTION_EDIT = '編輯';
CONST ACTION_DELETE = '刪除';
CONST ACTION_SEARCH = '查詢';
CONST ACTION_POST = '送出';
CONST ACTION_DOWNLOAD = '下載';
CONST ACTION_UPLOAD = '上傳';
CONST ACTION_MAIL = '發信';

CONST ACTION_LOGIN = '登入';
CONST ACTION_LOGOUT = '登出';
CONST ACTION_REGISTER = '註冊';


protected $guarded = [];

public function datatable(): \Illuminate\Database\Eloquent\Relations\MorphTo
{
return $this->morphTo();
}

public static function Log(string $function_name, string $action, Model $data_model=null, string $comment=null): void
{
$ip = Request::ip();

$log = new self([
'function_name' => $function_name,
'user_id' => Auth::user()->id??0,
// 'user_type' => $user_type??'user',
'action' => $action,
'comment' => $comment,
'ip' => $ip,
]);

if($data_model) {
$log->datatable()->associate($data_model);
}

$log->save();
}
}

3. 在開發過程中如果要方便直接測試套件,需照以下步驟:

  • 在原根目錄 composer.json 設定 autoload 資料夾為 src
信息

🌟 AutoloadPHP 自動載入的機制,可以讓我們能夠在需要這個物件的時候才去真正的引入這個 Class

{
"autoload": {
"psr-4": {
"Ppcsite\\RecordLog\\": "src/"
}
}
}
  • config\app 中加入 Package Service ProvidersClass Aliases
[
'providers' => [
...
/*
* Package Service Providers...
*/
Ppcsite\RecordLog\RecordLogServiceProvider::class,
...
],
'aliases' => Facade::defaultAliases()->merge([
// 'ExampleClass' => App\Example\ExampleClass::class,
'RecordLog' => Ppcsite\RecordLog\Facades\RecordLog::class,
])->toArray(),
]
  • 在 Terminal 輸入 composer dump-autoload

套件發布

1. 以 Packagist 為例 (public)

Packagist 使用 GitHub 登入帳號,直接輸入 repository 的網址,然後 Submit Package 就完成了。

2. 以 Gitea 為例 (private)

將該套件資料夾打包成一個 .zip 檔,透過 postman 傳送一個 PUT 操作,將檔案傳到 Gitea Api 網址底下,範例如下:

https://gitea.example.com/api/packages/{owner}/composer
警告

打包時注意不要將 vendor 資料夾也打包進去。

提示

🌟傳送資料時,在 body 中選擇 binary,即可選擇打包後的檔案上傳。

🔆也可以加上版本資訊:

https://gitea.example.com/api/packages/{owner}/composer?version={x.y.z}

回傳結果如下:

HTTP 狀態代碼意義
201 Created此套件已經被發布.
400 Bad Request此套件的名稱 及/或 版本是無效的,或是已存在與此套件相同名稱及版本的套件.

📢設定套件登錄

需加入以下內容到本機 Composerconfig.json 內,通常位置會在 <user-home-dir>/.composer/config.json

{
"repositories": [{
"type": "composer",
"url": "https://gitea.example.com/api/packages/{owner}/composer"
}
]
}

若是使用身分驗證設定套件登錄,則需加入以下程式碼到 auth.json

{
"http-basic": {
"gitea.example.com": {
"username": "{username}",
"password": "{password}"
}
}
}
警告

若是在本機 Composer 資料夾找不到 config.jsonauth.json,請先輸入以下指令創建:

composer config --global
備註

🌟欄位說明如下:

參數名說明
owner套件的擁有者
usernameGitea 的使用者名稱
passwordGitea 的使用者密碼或是個人 token

套件安裝方法:

composer require {package_name}

或是根據指定版本安裝:

composer require {package_name}:{package_version}

參考連結

composer 相關

授權相關

架構相關

Laravel 相關

套件發布相關