简述

XinAdmin 后端采用 PHP 8.2 + Laravel 12 框架开发,遵循 MVC 设计模式并扩展为 Controller-Service-Repository 三层架构。系统具备现代化的注解路由系统、完善的认证机制以及灵活的数据操作层。

架构特点

  • 三层架构: Controller-Service-Repository 模式,职责分离明确

  • 注解路由: 类似 Spring 风格的注解路由系统 (AnnoRoute),简化路由定义

  • 认证系统: 基于 Laravel Sanctum 的 Token 认证,支持双用户提供者

  • 数据操作: 统一的 Repository 模式,提供标准化的数据访问接口

  • 自动验证: 内置表单验证和业务逻辑验证

开发流程概览

典型的后端功能开发流程如下:

  1. 数据库迁移: 创建数据表结构
  2. 模型定义: 定义 Eloquent 模型和关系
  3. 数据仓库: 实现 Repository 模式的数据操作
  4. 控制器开发: 创建控制器提供 API 接口

接下来我们将详细介绍每个开发环节的具体实现。

数据库迁移

数据库迁移 (Migration) 是 Laravel 提供的一种管理数据库结构变更的工具,它允许开发者使用 PHP 代码来定义数据库表结构,而不是直接执行 SQL 语句。迁移文件通常存储在 database/migrations/ 目录下。

创建迁移文件

使用 Artisan 命令创建迁移文件:

php artisan make:migration create_articles_table

迁移文件示例

以下是一个典型的迁移文件示例:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateArticlesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('articles', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('content');
            $table->unsignedBigInteger('user_id');
            $table->tinyInteger('status')->default(1);
            $table->timestamps();
            
            // 外键约束
            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('articles');
    }
}

执行迁移

# 执行所有未执行的迁移
php artisan migrate

# 回滚最后一次迁移
php artisan migrate:rollback

# 重置所有迁移
php artisan migrate:reset
INFO

在生产环境中执行迁移前,请务必备份数据库,避免数据丢失。

模型定义

Laravel 的 Eloquent ORM 提供了一个美观、简洁的 ActiveRecord 实现,让开发者可以用最少的代码与数据库进行交互。模型文件通常存储在 app/Models/ 目录下。

模型示例

以下是一个基于上面迁移创建的 Article 模型示例:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

/**
 * 文章模型
 */
class Article extends Model
{
    use HasFactory;

    // 指定表名(如果不符合Laravel约定)
    protected $table = 'articles';

    // 可批量赋值的字段
    protected $fillable = [
        'title',
        'content',
        'user_id',
        'status',
    ];

    // 不可批量赋值的字段
    protected $guarded = [];

    // 自动转换为PHP类型的字段
    protected $casts = [
        'status' => 'boolean',
        'published_at' => 'datetime',
        'created_at' => 'datetime',
        'updated_at' => 'datetime',
    ];

    // 关联用户模型
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class, 'user_id', 'id');
    }

    // 作用域查询 - 获取已发布的文章
    public function scopePublished($query)
    {
        return $query->where('status', 1);
    }
}

CRUD数据仓库

XinAdmin 采用 Repository 模式来抽象数据访问逻辑,实现了标准的 CRUD 操作接口。Repository 位于 app/Repositories/ 目录下,继承自 BaseRepository

Repository 示例

以下是一个 ArticleRepository 的实现示例:

<?php

namespace App\Repositories;

use App\Models\Article;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Validation\Rule;

/**
 * 文章数据仓库
 */
class ArticleRepository extends BaseRepository
{
    /** @var array|string[] 快速搜索字段 */
    protected array $quickSearchField = ['title', 'content'];

    /**
     * @inheritDoc
     */
    protected function model(): Builder
    {
        return Article::query();
    }

    /**
     * 验证规则
     */
    protected function rules(): array
    {
        if($this->isUpdate()) {
            $id = request()->route('id');
            return [
                'title' => 'required|string|max:255',
                'content' => 'required',
                'user_id' => 'required|exists:users,id',
                'status' => 'required|boolean',
            ];
        } else {
            return [
                'title' => 'required|string|max:255',
                'content' => 'required',
                'user_id' => 'required|exists:users,id',
                'status' => 'required|boolean',
            ];
        }
    }

    /**
     * 验证消息
     */
    protected function messages(): array
    {
        return [
            'title.required' => '文章标题不能为空',
            'title.max' => '文章标题长度不能超过255个字符',
            'content.required' => '文章内容不能为空',
            'user_id.required' => '作者不能为空',
            'user_id.exists' => '选择的作者不存在',
            'status.required' => '状态不能为空',
            'status.boolean' => '状态格式错误',
        ];
    }

    /**
     * 自定义查询 - 获取已发布文章
     */
    public function getPublishedArticles(): array
    {
        return $this->model()->where('status', true)->get()->toArray();
    }
}

控制器接口开发

XinAdmin 使用自定义的注解路由系统 (AnnoRoute) 来定义 API 接口,这种设计类似于 Spring Boot 的注解路由风格,使路由定义更加直观和便捷。

控制器示例

以下是一个 ArticleController 的实现示例:

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\BaseController;
use App\Providers\AnnoRoute\Attribute\Create;
use App\Providers\AnnoRoute\Attribute\Delete;
use App\Providers\AnnoRoute\Attribute\Query;
use App\Providers\AnnoRoute\Attribute\RequestMapping;
use App\Providers\AnnoRoute\Attribute\Update;
use App\Repositories\RepositoryInterface;
use App\Repositories\ArticleRepository;

/**
 * 文章管理
 */
#[RequestMapping('/articles', 'article.manage')]
#[Query, Create, Update, Delete]
class ArticleController extends BaseController
{
    /**
     * 注入数据仓库
     */
    protected function repository(): RepositoryInterface
    {
        return app(ArticleRepository::class);
    }

    /**
     * 额外权限配置
     */
    protected array $noPermission = ['publishedArticles'];

    /**
     * 获取已发布文章
     */
    #[GetMapping('/published')]
    public function publishedArticles(): \Illuminate\Http\JsonResponse
    {
        $articles = $this->repository()->getPublishedArticles();
        return $this->success($articles);
    }
}