mcpField
T
thinkphp

thinkphp

github.com/youlaitech開発62122 回インストール5 日前に更新
リポジトリ

thinkphp について

REST API やモジュール型アプリケーションを開発する PHP エンジニア向けの ThinkPHP 8.x バックエンド開発ガイドです。プロジェクト構成やコーディング規約に加え、レスポンス、検証、認証、例外処理の方針を示します。

主な機能

  1. 01ThinkPHP 8.x のプロジェクト構成
  2. 02PSR-4 の命名規則
  3. 03RESTful API のルーティング指針
  4. 04統一レスポンスと例外処理
  5. 05JWT 認証の規約
  6. 06バリデーターとモデルの指針

活用シーン

  1. 01ThinkPHP プロジェクトの規約策定
  2. 02モジュール型バックエンドの構成
  3. 03REST API エンドポイントの実装
  4. 04JWT 認証と入力検証の設計

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/youlaitech/youlai-skills --skill thinkphp

インストール後、Claude Code で/thinkphpを入力して読み込まれたか確認します。

詳細

ThinkPHP バックエンド開発規約

参考:ThinkPHP 8.0 開発規約 | PSR-4 自動読み込み

適用条件

  • ThinkPHP プロジェクトを開発する
  • REST API を実装する
  • モデルを使用したデータアクセスを行う
  • JWT 認証を実装する
  • 権限制御を実装する

Part 1: 技術スタック

層選定技術説明
実行環境PHP 8.1+ファイバー、列挙型、読み取り専用プロパティ
フレームワークThinkPHP 8.xMVC アーキテクチャ、ORM 内蔵
データベースMySQL 8.xInnoDB エンジン
キャッシュRedis 7.x分散キャッシュ
認証firebase/php-jwtJWT の発行/検証
API ドキュメントSwagger UI (swagger.json)静的ドキュメント

Part 2: ディレクトリ構成

基本ディレクトリは ThinkPHP 8 公式ディレクトリ構成 に準拠します。app/ はこれをベースに業務モジュール(auth、system など)ごとに分割し、各モジュールに controller/model/service/validate を含めます。

app/
├── BaseController.php              # 基础控制器
├── ExceptionHandle.php             # 全局异常处理
├── common.php, middleware.php       # 公共函数 / 中间件定义
│
├── auth/                           # 认证模块
│   ├── controller/{AuthController, WxMaAuthController}
│   └── service/{AuthService, WxMaAuthService}
│
├── system/                         # 系统管理
│   ├── controller/{UserController, RoleController, MenuController, ...}
│   ├── model/{User, Role, Menu, ...}
│   ├── service/{UserService, RoleService, RolePermService, DataPermissionService, ...}
│   ├── validate/{UserValidate, RoleValidate}
│   └── enums/{ActionType, LogModule}
│
├── codegen/                        # 代码生成器
├── file/                           # 文件管理
│
├── common/                         # 公共代码
│   ├── constants/                  # RedisConstants, RedisKey
│   ├── enums/                      # DataScopeEnum
│   ├── exception/                  # BusinessException
│   ├── middleware/                  # Auth, Perm, DataScope, Cors, Log, RateLimit, ConvertCase
│   ├── model/BaseModel.php
│   ├── traits/{AuthTrait, ParamsTrait}
│   ├── util/{CaseConverter, IdStringify, PageUtil, VerifyCodeHelper}
│   └── web/                        # Result, PageResult, ResultCode, IResultCode
│
├── controller/BaseController.php   # 基础控制器
│
extend/                             # 扩展类库
├── jwt/{JwtTokenManager, TokenManager, ...}
├── redis/{RedisClient, KeyFormatter}
├── sse/{SseEmitter, SseService, ...}
└── http/HttpClient.php

config/   route/   public/   sql/

設計原則:

  • common/ はすべてのモジュールで共有する共通コンポーネントです
  • extend/ はサードパーティ/フレームワークの拡張(JWT/Redis/SSE)で、業務には依存しません
  • app/{module}/ はモジュールごとに構成し、各モジュールに controller/model/service/validate を含めます

Part 3: 命名規約

ThinkPHP 8.0 開発規約 と PSR-4 に準拠します。

3.1 ファイルとクラス
規則例
ディレクトリ名は小文字+アンダースコアを使用controller/, user_service/
クラス名はキャメルケース(先頭大文字)UserController, UserValidate
クラス名とファイル名を一致させるUserController → UserController.php
名前空間をディレクトリパスと一致させるapp\system\controller → app/system/controller/
3.2 定数と設定
種類規約例
定数大文字とアンダースコアAPP_PATH, HAS_ONE
設定パラメーター小文字とアンダースコアurl_route_on
環境変数大文字とアンダースコアAPP_DEBUG, DB_HOST
3.3 データテーブルとフィールド
規則例
データテーブル名は小文字+アンダースコアを使用sys_user, sys_role_menu
フィールド名は小文字+アンダースコアを使用user_name, create_time
キャメルケースと中国語による命名は禁止❌ userName, ❌ 用户表
3.4 関数と変数
種類規約例
メソッドキャメルケース(先頭小文字)getUserName()
グローバル関数小文字とアンダースコアget_client_ip()
プロパティキャメルケース(先頭小文字)$tableName

Part 4: RESTful API 規約

4.1 標準 CRUD パス
操作メソッドパス
ページネーション付き一覧GET/api/v1/users/page
詳細GET/api/v1/users/:id
新規作成POST/api/v1/users
更新PUT/api/v1/users
削除DELETE/api/v1/users/:id
一括削除DELETE/api/v1/users/batch
ドロップダウン選択肢GET/api/v1/users/options
4.2 Controller テンプレート
declare(strict_types=1);
namespace app\system\controller;

use app\BaseController;
use app\system\model\User;
use app\system\validate\UserValidate;
use app\common\exception\BusinessException;
use app\common\web\ResultCode;

final class UserController extends BaseController
{
    protected bool $requireAuth = true;

    /** 用户分页列表 */
    public function page()
    {
        $params = $this->request->get();
        $result = User::where(function ($q) use ($params) {
            if (!empty($params['keywords']))
                $q->whereLike('username|nickname', $params['keywords']);
            if (isset($params['status']))
                $q->where('status', $params['status']);
        })->order('create_time', 'desc')
          ->paginate(['page' => $params['pageNum'] ?? 1, 'list_rows' => $params['pageSize'] ?? 20]);
        return $this->successPaginate($result->items(), $result->total());
    }

    /** 新增用户 */
    public function create()
    {
        $params = $this->request->post();
        $this->validate($params, UserValidate::class, 'create');
        if (User::where('username', $params['username'])->find())
            throw new BusinessException(ResultCode::USER_ERROR, '用户名已存在');

        $user = new User();
        $user->username = $params['username'];
        $user->password = password_hash($params['password'], PASSWORD_BCRYPT);
        $user->save();
        return $this->success(['id' => $user->id]);
    }
}

Part 5: レスポンス形式と例外処理

5.1 統一レスポンス
class Result
{
    public static function success(mixed $data = null, string $msg = '成功'): array
    {
        return ['code' => '00000', 'msg' => $msg, 'data' => $data];
    }

    public static function page(array $list, int $total): array
    {
        return ['code' => '00000', 'msg' => '成功', 'data' => ['list' => $list, 'total' => $total]];
    }

    public static function failedWith(ResultCode $code, string $msg = ''): array
    {
        return ['code' => $code->value, 'msg' => $msg ?: $code->getMsg(), 'data' => null];
    }
}
5.2 基本コントローラーのレスポンスメソッド
abstract class BaseController
{
    protected function success(mixed $data = null, string $msg = ''): Json
    {
        return json(Result::success($data, $msg ?: ResultCode::SUCCESS->getMsg()));
    }

    protected function successPaginate(array $list, int $total): Json
    {
        return json(Result::page($list, $total));
    }
}
5.3 業務例外
final class BusinessException extends RuntimeException
{
    private ResultCode $resultCode;

    public function __construct(ResultCode $resultCode, string $message = '')
    {
        $this->resultCode = $resultCode;
        parent::__construct($message ?: $resultCode->getMsg());
    }

    public function getResultCode(): ResultCode { return $this->resultCode; }
}
5.4 グローバル例外処理
class ExceptionHandle extends Handle
{
    public function render($request, Throwable $e): Response
    {
        if ($e instanceof BusinessException)
            return json(Result::failedWith($e->getResultCode(), $e->getMessage()));
        if ($e instanceof ValidateException)
            return json(Result::failedWith(ResultCode::PARAM_ERROR, $e->getError()));
        return parent::render($request, $e); // 生产环境隐藏详情
    }
}
// config/app.php — 注册异常处理器
return ['exception_handle' => \app\ExceptionHandle::class];

Part 6: モデル規約

class BaseModel extends Model
{
    protected $autoWriteTimestamp = true;
    protected $createTime = 'create_time';
    protected $updateTime = 'update_time';

    use \think\model\concern\SoftDelete;
    protected $deleteTime = 'delete_time';
    protected $defaultSoftDelete = 0;
}

final class User extends BaseModel
{
    protected $name = 'sys_user';
    protected $pk = 'id';
    protected $hidden = ['password', 'delete_time'];

    public function dept()
    {
        return $this->belongsTo(Dept::class, 'dept_id');
    }

    public function roles()
    {
        return $this->belongsToMany(Role::class, UserRole::class, 'role_id', 'user_id');
    }
}

Part 7: ルーティング規約

use think\facade\Route;

// 认证接口(无需登录)
Route::group('api/v1/auth', function () {
    Route::post('login', 'auth.controller.AuthController/login');
    Route::post('logout', 'auth.controller.AuthController/logout');
    Route::post('refresh-token', 'auth.controller.AuthController/refreshToken');
})->allowCrossDomain();

// 需要登录的接口
Route::group('api/v1', function () {
    Route::group('users', function () {
        Route::get('page', 'system.controller.UserController/page');
        Route::get(':id', 'system.controller.UserController/detail');
        Route::post('', 'system.controller.UserController/create');
        Route::put('', 'system.controller.UserController/update');
        Route::delete(':id', 'system.controller.UserController/delete');
        Route::delete('batch', 'system.controller.UserController/batchDelete');
    });
})->middleware(\app\middleware\Auth::class)->allowCrossDomain();

Part 8: 認証規約

8.1 JWT サービス
final class JwtService
{
    public static function generateAccessToken(array $payload): string
    {
        $payload['exp'] = time() + Config::get('jwt.access_ttl', 7200);
        $payload['iat'] = time();
        $payload['iss'] = Config::get('jwt.issuer', 'youlai-think');
        return JWT::encode($payload, Config::get('jwt.secret'), 'HS256');
    }

    public static function parseToken(string $token): ?array
    {
        try {
            return (array) JWT::decode($token, new Key(Config::get('jwt.secret'), 'HS256'));
        } catch (\Exception) { return null; }
    }
}
8.2 認証ミドルウェア
final class Auth
{
    public function handle(Request $request, Closure $next): Response
    {
        $token = str_replace('Bearer ', '', $request->header('Authorization', ''));
        if (empty($token)) return json(Result::failedWith(ResultCode::ACCESS_TOKEN_INVALID));

        $payload = JwtService::parseToken($token);
        if (!$payload) return json(Result::failedWith(ResultCode::ACCESS_TOKEN_INVALID));

        $request->userId = $payload['sub'];
        $request->username = $payload['username'] ?? '';
        return $next($request);
    }
}

Part 9: バリデーター規約

final class UserValidate extends Validate
{
    protected $rule = [
        'username' => 'require|max:50',
        'password' => 'require|min:6|max:20',
        'nickname' => 'require|max:50',
        'mobile' => 'mobile',
        'status' => 'in:0,1',
    ];

    protected $scene = [
        'create' => ['username', 'password', 'nickname', 'mobile', 'status'],
        'update' => ['id', 'nickname', 'mobile', 'status'],
    ];
}

Part 10: コメント規約

  • すべての public メソッドに PHPDoc /** */ コメントを付ける
  • メソッドのコメントには @param/@return/@throws を含める
  • declare(strict_types=1) をファイルの先頭行に記述する(PHP 8 の厳密な型チェック)
/**
 * 用户管理控制器。
 */
final class UserController extends BaseController
{
    /**
     * 创建新用户。
     *
     * @return Json {"code":"00000","data":{"id":1}}
     * @throws BusinessException 用户名已存在
     */
    public function create()
    {
        // ...
    }
}

Part 11: コード品質チェックリスト

  • ディレクトリ名は小文字+アンダースコア、クラス名はキャメルケースを使用する
  • データテーブル名とフィールド名は小文字+アンダースコアを使用する
  • RESTful API のパス規約に準拠する
  • 統一レスポンス形式(Result / ResultCode)を使用する
  • グローバル例外処理(ExceptionHandle)を実装する
  • 業務例外には BusinessException + ResultCode を使用する
  • モデルは BaseModel を継承し、論理削除を使用する
  • UserValidate バリデーターでパラメーターを検証する
  • ルートを明示的にマッピングする
  • declare(strict_types=1) をファイルの先頭行に記述する
よくあるアンチパターン
アンチパターン正しい方法
return json(['code' => 0]) が各所に散在するResult::success() で統一的にラップする
Controller 内で直接 SQL クエリーを実行するModel / Service 層に委譲する
if (!$user) { exit('用户不存在'); }BusinessException をスローし、グローバル例外処理に任せる
catch (\Exception $e) { } で例外を空のまま捕捉するログを記録するか、BusinessException を再スローする
テーブル名にキャメルケースを使用するsys_user(小文字+アンダースコア)を使用し、SysUser は禁止

コメント