Skip to content

模型与数据库

DuxLite 基于 Eloquent ORM 提供强大的数据库操作功能。本文档重点介绍模型定义、数据库迁移和高级查询技巧。

💡 提示: 基础的 CRUD 操作请参考 API 开发 文档中的 Resource 控制器部分。

🚀 模型定义

自动迁移模型

基于 app/System/Models/SystemUser.php 的实际代码:

php
<?php

namespace App\YourModule\Models;

use Core\Database\Eloquent\Model;
use Core\Database\Attribute\AutoMigrate;
use Illuminate\Database\Schema\Blueprint;

#[AutoMigrate]
class User extends Model
{
    public $table = "users";

    // 数据库迁移定义
    public function migration(Blueprint $table)
    {
        $table->id();
        $table->string('username')->unique()->comment('用户名');
        $table->string('nickname')->comment('昵称');
        $table->string('password')->comment('密码');
        $table->string('avatar')->nullable()->comment('头像');
        $table->string('tel')->nullable()->comment('电话');
        $table->string('email')->nullable()->comment('邮箱');
        $table->boolean('status')->default(true)->comment('状态');
        $table->timestamps();
    }

    // 可批量赋值字段
    protected $fillable = [
        'username',
        'nickname',
        'password',
        'avatar',
        'tel',
        'email',
        'status'
    ];

    // 隐藏字段
    protected $hidden = [
        'password'
    ];

    // 类型转换
    protected $casts = [
        'status' => 'boolean',
        'created_at' => 'datetime',
        'updated_at' => 'datetime'
    ];
}

## 📋 模型定义

### 基础模型

```php
<?php

namespace App\YourModule\Models;

use Core\Database\Eloquent\Model;

class Article extends Model
{
    // 表名(可选,默认为类名的复数形式)
    protected $table = 'articles';

    // 主键(可选,默认为 'id')
    protected $primaryKey = 'id';

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

    // 隐藏字段(不会出现在数组或 JSON 中)
    protected $hidden = [
        'deleted_at'
    ];

    // 字段类型转换
    protected $casts = [
        'published_at' => 'datetime',
        'is_featured' => 'boolean',
        'meta' => 'array'
    ];

    // 时间戳字段(可选)
    public $timestamps = true;

    // 软删除(可选)
    use SoftDeletes;
}

模型关联

php
<?php

namespace App\YourModule\Models;

use Core\Database\Eloquent\Model;

class Article extends Model
{
    /**
     * 文章属于用户(一对一)
     */
    public function user()
    {
        return $this->belongsTo(SystemUser::class, 'user_id');
    }

    /**
     * 文章属于分类(一对一)
     */
    public function category()
    {
        return $this->belongsTo(Category::class, 'category_id');
    }

    /**
     * 文章有多个评论(一对多)
     */
    public function comments()
    {
        return $this->hasMany(Comment::class, 'article_id');
    }

    /**
     * 文章有多个标签(多对多)
     */
    public function tags()
    {
        return $this->belongsToMany(Tag::class, 'article_tags', 'article_id', 'tag_id');
    }
}

class User extends Model
{
    /**
     * 用户有多篇文章(一对多)
     */
    public function articles()
    {
        return $this->hasMany(Article::class, 'user_id');
    }

    /**
     * 用户资料(一对一)
     */
    public function profile()
    {
        return $this->hasOne(UserProfile::class, 'user_id');
    }
}

🔧 高级查询

复杂条件查询

php
// 多条件查询
$articles = Article::where('status', 1)
    ->where('published_at', '<=', now())
    ->whereIn('category_id', [1, 2, 3])
    ->orderBy('created_at', 'desc')
    ->limit(10)
    ->get();

// 或条件查询
$articles = Article::where('status', 1)
    ->orWhere('is_featured', true)
    ->get();

// 子查询
$popularArticles = Article::whereIn('id', function ($query) {
    $query->select('article_id')
        ->from('article_views')
        ->where('views', '>', 1000);
})->get();

// 原生 SQL 查询
$results = DB::select('SELECT * FROM articles WHERE status = ?', [1]);

关联查询

php
// 预加载关联数据(避免 N+1 问题)
$articles = Article::with(['user', 'category', 'tags'])->get();

// 条件预加载
$articles = Article::with(['comments' => function ($query) {
    $query->where('status', 1)->orderBy('created_at', 'desc');
}])->get();

// 关联条件查询
$articles = Article::whereHas('user', function ($query) {
    $query->where('status', 1);
})->get();

// 统计关联数据
$users = User::withCount('articles')->get();

聚合查询

php
// 基础聚合
$totalArticles = Article::count();
$avgViews = Article::avg('views');
$maxViews = Article::max('views');
$minViews = Article::min('views');
$sumViews = Article::sum('views');

// 分组聚合
$categoryStats = Article::select('category_id')
    ->selectRaw('COUNT(*) as article_count')
    ->selectRaw('AVG(views) as avg_views')
    ->groupBy('category_id')
    ->get();

🎯 实际应用示例

用户管理服务

php
<?php

namespace App\System\Service;

use App\System\Models\SystemUser;
use Core\Database\DB;

class UserService
{
    /**
     * 获取用户列表
     */
    public static function getUsers(array $filters = [], int $page = 1, int $limit = 10): array
    {
        $query = SystemUser::query();

        // 应用过滤条件
        if (!empty($filters['keyword'])) {
            $query->where(function ($q) use ($filters) {
                $q->where('username', 'like', '%' . $filters['keyword'] . '%')
                  ->orWhere('email', 'like', '%' . $filters['keyword'] . '%');
            });
        }

        if (!empty($filters['status'])) {
            $query->where('status', $filters['status']);
        }

        if (!empty($filters['role_id'])) {
            $query->whereHas('roles', function ($q) use ($filters) {
                $q->where('id', $filters['role_id']);
            });
        }

        // 分页查询
        $total = $query->count();
        $users = $query->with(['roles', 'department'])
            ->orderBy('created_at', 'desc')
            ->offset(($page - 1) * $limit)
            ->limit($limit)
            ->get();

        return [
            'data' => $users,
            'total' => $total,
            'page' => $page,
            'limit' => $limit
        ];
    }

    /**
     * 创建用户
     */
    public static function createUser(array $data): SystemUser
    {
        return DB::transaction(function () use ($data) {
            // 创建用户
            $user = SystemUser::create([
                'username' => $data['username'],
                'email' => $data['email'],
                'password' => password_hash($data['password'], PASSWORD_DEFAULT),
                'status' => $data['status'] ?? 1,
                'department_id' => $data['department_id'] ?? null
            ]);

            // 分配角色
            if (!empty($data['role_ids'])) {
                $user->roles()->attach($data['role_ids']);
            }

            return $user;
        });
    }

    /**
     * 更新用户
     */
    public static function updateUser(int $id, array $data): bool
    {
        return DB::transaction(function () use ($id, $data) {
            $user = SystemUser::findOrFail($id);

            // 更新基本信息
            $updateData = array_filter([
                'username' => $data['username'] ?? null,
                'email' => $data['email'] ?? null,
                'status' => $data['status'] ?? null,
                'department_id' => $data['department_id'] ?? null
            ]);

            if (!empty($data['password'])) {
                $updateData['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
            }

            $user->update($updateData);

            // 更新角色
            if (isset($data['role_ids'])) {
                $user->roles()->sync($data['role_ids']);
            }

            return true;
        });
    }

    /**
     * 删除用户
     */
    public static function deleteUser(int $id): bool
    {
        return DB::transaction(function () use ($id) {
            $user = SystemUser::findOrFail($id);
            
            // 删除关联数据
            $user->roles()->detach();
            
            // 删除用户
            return $user->delete();
        });
    }

    /**
     * 获取用户统计
     */
    public static function getUserStats(): array
    {
        return [
            'total_users' => SystemUser::count(),
            'active_users' => SystemUser::where('status', 1)->count(),
            'inactive_users' => SystemUser::where('status', 0)->count(),
            'new_users_today' => SystemUser::whereDate('created_at', today())->count(),
            'new_users_this_month' => SystemUser::whereMonth('created_at', now()->month)->count()
        ];
    }
}

文章管理服务

php
<?php

namespace App\Content\Service;

use App\Content\Models\Article;
use Core\Database\DB;

class ArticleService
{
    /**
     * 获取文章列表
     */
    public static function getArticles(array $filters = []): array
    {
        $query = Article::with(['user', 'category']);

        // 状态过滤
        if (isset($filters['status'])) {
            $query->where('status', $filters['status']);
        }

        // 分类过滤
        if (!empty($filters['category_id'])) {
            $query->where('category_id', $filters['category_id']);
        }

        // 关键词搜索
        if (!empty($filters['keyword'])) {
            $query->where(function ($q) use ($filters) {
                $q->where('title', 'like', '%' . $filters['keyword'] . '%')
                  ->orWhere('content', 'like', '%' . $filters['keyword'] . '%');
            });
        }

        // 时间范围
        if (!empty($filters['start_date'])) {
            $query->where('created_at', '>=', $filters['start_date']);
        }
        if (!empty($filters['end_date'])) {
            $query->where('created_at', '<=', $filters['end_date']);
        }

        return $query->orderBy('created_at', 'desc')->paginate(10);
    }

    /**
     * 发布文章
     */
    public static function publishArticle(array $data): Article
    {
        return DB::transaction(function () use ($data) {
            $article = Article::create([
                'title' => $data['title'],
                'content' => $data['content'],
                'excerpt' => $data['excerpt'] ?? '',
                'status' => 1,
                'user_id' => $data['user_id'],
                'category_id' => $data['category_id'],
                'published_at' => now()
            ]);

            // 关联标签
            if (!empty($data['tag_ids'])) {
                $article->tags()->attach($data['tag_ids']);
            }

            return $article;
        });
    }

    /**
     * 获取热门文章
     */
    public static function getPopularArticles(int $limit = 10): array
    {
        return Article::with(['user', 'category'])
            ->where('status', 1)
            ->orderBy('views', 'desc')
            ->limit($limit)
            ->get()
            ->toArray();
    }
}

💡 最佳实践

1. 使用事务

php
use Core\Database\DB;

// 数据库事务
DB::transaction(function () {
    // 多个数据库操作
    User::create($userData);
    Profile::create($profileData);
    Log::create($logData);
});

// 手动事务控制
DB::beginTransaction();
try {
    // 数据库操作
    DB::commit();
} catch (\Exception $e) {
    DB::rollback();
    throw $e;
}

2. 避免 N+1 查询

php
// ❌ 会产生 N+1 查询
$articles = Article::all();
foreach ($articles as $article) {
    echo $article->user->name; // 每次都会查询数据库
}

// ✅ 使用预加载
$articles = Article::with('user')->get();
foreach ($articles as $article) {
    echo $article->user->name; // 不会产生额外查询
}

3. 合理使用索引

php
// 在经常查询的字段上建立索引
Schema::table('articles', function (Blueprint $table) {
    $table->index('status');
    $table->index('category_id');
    $table->index(['status', 'published_at']);
});

4. 数据验证

php
public static function createUser(array $data): SystemUser
{
    // 验证数据
    $validator = validator($data, [
        'username' => 'required|unique:system_users',
        'email' => 'required|email|unique:system_users',
        'password' => 'required|min:6'
    ]);

    if ($validator->fails()) {
        throw new ValidationException($validator->errors());
    }

    return SystemUser::create($data);
}

🎉 总结

DuxLite 数据库操作的特点:

  • 🚀 简单易用:基于 Eloquent ORM,语法简洁
  • 🔧 功能强大:支持复杂查询、关联、事务等
  • ⚡ 高性能:查询优化、预加载、索引支持
  • 🛡️ 安全可靠:SQL 注入防护、事务支持
  • 📊 灵活扩展:支持原生 SQL、自定义查询

合理使用数据库操作可以让你的应用更加高效和稳定!