Skip to content

最佳实践

DuxLite 框架开发最佳实践指南。

项目结构规范

目录组织

app/
├── Controllers/        # 控制器
│   ├── Api/           # API 控制器
│   └── Web/           # Web 控制器
├── Models/            # 数据模型
├── Services/          # 业务服务
├── Middleware/        # 中间件
├── Jobs/              # 队列任务
└── Events/            # 事件处理

命名约定

php
// 类名使用 PascalCase
class UserController extends Resources {}
class OrderService {}

// 方法名使用 camelCase
public function getUserById(int $id): ?User {}
public function createOrder(array $data): Order {}

// 变量名使用 camelCase
$userId = 123;
$orderData = [];

// 常量使用 UPPER_CASE
const MAX_UPLOAD_SIZE = 1024 * 1024;
const DEFAULT_CACHE_TTL = 3600;

控制器最佳实践

资源控制器

php
// 使用资源控制器简化 CRUD 操作
#[Resource(route: '/api/users')]
class UserController extends Resources
{
    protected string $model = User::class;

    // 自动提供:index、show、store、update、destroy

    // 自定义验证规则
    protected function rules(): array
    {
        return [
            'name' => 'required|string|max:100',
            'email' => 'required|email|unique:users',
            'password' => 'required|min:6'
        ];
    }

    // 自定义数据转换
    protected function transform($item): array
    {
        return [
            'id' => $item->id,
            'name' => $item->name,
            'email' => $item->email,
            'created_at' => $item->created_at->format('Y-m-d H:i:s')
        ];
    }
}

普通控制器

php
class AuthController
{
    public function login(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
    {
        try {
            $data = $request->getParsedBody();

            // 验证数据
            $validator = App::validator($data, [
                'email' => 'required|email',
                'password' => 'required'
            ]);

            if ($validator->fails()) {
                throw new ExceptionValidator('验证失败', $validator->errors());
            }

            // 业务逻辑
            $token = App::auth()->attempt($data);
            if (!$token) {
                throw new ExceptionBusiness('登录失败');
            }

            return send($response, '登录成功', ['token' => $token]);

        } catch (ExceptionValidator $e) {
            return send($response, $e->getMessage(), $e->getErrors(), 422);
        } catch (ExceptionBusiness $e) {
            return send($response, $e->getMessage(), null, 400);
        }
    }
}

模型最佳实践

基础模型

php
class User extends Model
{
    protected $fillable = ['name', 'email', 'password'];
    protected $hidden = ['password'];
    protected $casts = [
        'email_verified_at' => 'datetime',
        'created_at' => 'datetime',
        'updated_at' => 'datetime'
    ];

    // 关联关系
    public function posts()
    {
        return $this->hasMany(Post::class);
    }

    public function profile()
    {
        return $this->hasOne(UserProfile::class);
    }

    // 访问器
    public function getAvatarUrlAttribute(): string
    {
        return $this->avatar ? url('uploads/' . $this->avatar) : url('images/default-avatar.png');
    }

    // 修改器
    public function setPasswordAttribute($value): void
    {
        $this->attributes['password'] = password_hash($value, PASSWORD_DEFAULT);
    }
}

模型作用域

php
class Post extends Model
{
    // 本地作用域
    public function scopePublished($query)
    {
        return $query->where('status', 'published');
    }

    public function scopeByCategory($query, $categoryId)
    {
        return $query->where('category_id', $categoryId);
    }

    // 使用示例
    // Post::published()->byCategory(1)->get();
}

服务层设计

业务服务

php
class UserService
{
    public function createUser(array $data): User
    {
        // 数据验证
        $validator = App::validator($data, [
            'name' => 'required|string|max:100',
            'email' => 'required|email|unique:users',
            'password' => 'required|min:6'
        ]);

        if ($validator->fails()) {
            throw new ExceptionValidator('数据验证失败', $validator->errors());
        }

        // 事务处理
        return App::db()->transaction(function () use ($data) {
            $user = User::create($data);

            // 创建用户配置
            $user->profile()->create([
                'avatar' => 'default.png',
                'bio' => ''
            ]);

            // 发送欢迎邮件
            App::queue()->push(new SendWelcomeEmailJob($user->email));

            return $user;
        });
    }

    public function updateUser(int $id, array $data): User
    {
        $user = User::findOrFail($id);

        // 移除空值
        $data = array_filter($data, fn($value) => $value !== null);

        $user->update($data);

        // 清除相关缓存
        App::cache()->delete("user_{$id}");

        return $user;
    }
}

数据库最佳实践

查询优化

php
class PostService
{
    // ✅ 好的查询
    public function getPostsWithAuthors(int $page = 1): array
    {
        return Post::select(['id', 'title', 'content', 'user_id', 'created_at'])
            ->with('user:id,name') // 预加载关联
            ->where('status', 'published')
            ->orderBy('created_at', 'desc')
            ->paginate(20, ['*'], 'page', $page)
            ->toArray();
    }

    // ❌ 避免的查询
    public function getBadPosts(): array
    {
        $posts = Post::all(); // 加载所有数据
        $result = [];

        foreach ($posts as $post) {
            $result[] = [
                'title' => $post->title,
                'author' => $post->user->name // N+1 查询问题
            ];
        }

        return $result;
    }
}

事务使用

php
class OrderService
{
    public function createOrder(array $orderData, array $items): Order
    {
        return App::db()->transaction(function () use ($orderData, $items) {
            // 创建订单
            $order = Order::create($orderData);

            // 创建订单项
            foreach ($items as $item) {
                $order->items()->create($item);

                // 更新库存
                Product::where('id', $item['product_id'])
                    ->decrement('stock', $item['quantity']);
            }

            return $order;
        });
    }
}

缓存策略

查询缓存

php
class ProductService
{
    public function getProduct(int $id): ?Product
    {
        $cacheKey = "product_{$id}";

        return App::cache()->remember($cacheKey, 3600, function () use ($id) {
            return Product::with(['category', 'images'])->find($id);
        });
    }

    public function updateProduct(int $id, array $data): Product
    {
        $product = Product::findOrFail($id);
        $product->update($data);

        // 清除缓存
        App::cache()->delete("product_{$id}");

        return $product;
    }
}

标签缓存

php
class CategoryService
{
    public function getCategories(): array
    {
        return App::cache()->tags(['categories'])->remember('all_categories', 3600, function () {
            return Category::orderBy('sort')->get()->toArray();
        });
    }

    public function updateCategory(int $id, array $data): Category
    {
        $category = Category::findOrFail($id);
        $category->update($data);

        // 清除标签缓存
        App::cache()->tags(['categories'])->flush();

        return $category;
    }
}

队列任务

基础队列任务

php
class SendEmailJob extends QueueMessage
{
    public function __construct(
        private string $email,
        private string $subject,
        private string $content
    ) {}

    public function handle(): void
    {
        try {
            // 发送邮件逻辑
            mail($this->email, $this->subject, $this->content);

            App::log()->info('邮件发送成功', ['email' => $this->email]);
        } catch (\Exception $e) {
            App::log()->error('邮件发送失败', [
                'email' => $this->email,
                'error' => $e->getMessage()
            ]);

            throw $e; // 重新抛出以触发重试
        }
    }
}

批量处理任务

php
class ProcessUsersJob extends QueueMessage
{
    public function __construct(private array $userIds) {}

    public function handle(): void
    {
        // 分批处理,避免内存问题
        $chunks = array_chunk($this->userIds, 100);

        foreach ($chunks as $chunk) {
            User::whereIn('id', $chunk)->chunk(10, function ($users) {
                foreach ($users as $user) {
                    $this->processUser($user);
                }
            });
        }
    }

    private function processUser(User $user): void
    {
        // 处理单个用户
    }
}

错误处理

异常分类

php
// 业务异常
class UserNotFoundException extends ExceptionBusiness
{
    public function __construct(int $userId)
    {
        parent::__construct("用户不存在: {$userId}", 404);
    }
}

// 验证异常
class InvalidEmailException extends ExceptionValidator
{
    public function __construct(string $email)
    {
        parent::__construct('邮箱格式无效', ['email' => $email]);
    }
}

统一错误处理

php
class BaseController
{
    protected function handleException(\Throwable $e): ResponseInterface
    {
        // 记录错误日志
        App::log()->error('控制器异常', [
            'message' => $e->getMessage(),
            'file' => $e->getFile(),
            'line' => $e->getLine()
        ]);

        // 根据异常类型返回不同响应
        if ($e instanceof ExceptionValidator) {
            return send(new Response(), $e->getMessage(), $e->getErrors(), 422);
        }

        if ($e instanceof ExceptionBusiness) {
            return send(new Response(), $e->getMessage(), null, $e->getCode() ?: 400);
        }

        // 默认错误响应
        $message = App::config('app.debug') ? $e->getMessage() : '服务器内部错误';
        return send(new Response(), $message, null, 500);
    }
}

配置管理

环境配置

toml
# config/use.toml
[app]
name = "我的应用"
debug = true
env = "development"

[database]
driver = "mysql"
host = "localhost"
database = "myapp"
username = "root"
password = ""

[cache]
default = "file"

[queue]
default = "database"

配置访问

php
// 获取配置
$appName = App::config('app.name');
$dbHost = App::config('database.host');

// 设置默认值
$cacheDriver = App::config('cache.default', 'file');

// 检查配置存在
if (App::config()->has('redis.host')) {
    // Redis 配置存在
}

测试建议

单元测试

php
class UserServiceTest extends TestCase
{
    public function testCreateUser(): void
    {
        $userData = [
            'name' => 'Test User',
            'email' => 'test@example.com',
            'password' => 'password123'
        ];

        $service = new UserService();
        $user = $service->createUser($userData);

        $this->assertInstanceOf(User::class, $user);
        $this->assertEquals($userData['name'], $user->name);
        $this->assertEquals($userData['email'], $user->email);
    }
}

安全建议

输入验证

php
// 始终验证用户输入
$validator = App::validator($request->getParsedBody(), [
    'email' => 'required|email',
    'password' => 'required|min:8'
]);

if ($validator->fails()) {
    throw new ExceptionValidator('验证失败', $validator->errors());
}

SQL 注入防护

php
// ✅ 使用参数绑定
User::where('email', $email)->first();

// ❌ 避免直接拼接 SQL
// User::whereRaw("email = '{$email}'")->first();

XSS 防护

php
// 输出时转义
echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');

// 或使用模板引擎自动转义

性能建议

数据库优化

php
// 使用索引
// 在迁移中添加索引
$table->index('user_id');
$table->index(['status', 'created_at']);

// 预加载关联
$posts = Post::with(['user', 'category'])->get();

// 只选择需要的字段
$users = User::select(['id', 'name', 'email'])->get();

缓存使用

php
// 缓存查询结果
$popularPosts = App::cache()->remember('popular_posts', 3600, function () {
    return Post::orderBy('views', 'desc')->limit(10)->get();
});

// 缓存计算结果
$userCount = App::cache()->remember('user_count', 1800, function () {
    return User::count();
});

通过遵循这些最佳实践,可以编写出更加健壮、可维护和高性能的 DuxLite 应用。

基于 MIT 许可证发布