Model 标准示例 — Article
源文件:
server/app/model/article/Article.php(生成代码必须模仿本示例的结构与风格)本示例为手写风格范本,用于学习结构与分层;经 make:crud 生成的骨架方法名以骨架为准,做业务增量时不要为对齐本示例而改名。
完整代码
php
<?php
/* ============================================================
* 项目:元点Admin
* 官网:https://www.dev007.cn
* Slogan:提供高质量行业系统源码,帮助中小企业快速搭建专属应用
* Author:mashanglai Team
* ============================================================ */
declare(strict_types=1);
namespace app\model\article;
use core\base\Model;
class Article extends Model
{
protected $name = 'articles';
protected $fillable = [
'category_id', 'title', 'cover', 'summary', 'content',
'tags', 'author', 'view_count', 'status', 'publish_at', 'admin_id',
];
// 状态常量
const STATUS_DRAFT = 0;
const STATUS_PUBLISHED = 1;
protected $type = [
'category_id' => 'integer',
'status' => 'integer',
'admin_id' => 'integer',
'view_count' => 'integer',
'tags' => 'json',
];
protected $append = ['status_text'];
/**
* 所属分类
*/
public function category(): \think\model\relation\BelongsTo
{
return $this->belongsTo(ArticleCategory::class, 'category_id', 'id');
}
/**
* 状态文本获取器
*/
public function getStatusTextAttr($value, $data): string
{
$statusMap = [
self::STATUS_DRAFT => '草稿',
self::STATUS_PUBLISHED => '已发布',
];
return $this->getStatusText((int) ($data['status'] ?? 0), $statusMap);
}
}要点注解
- 第 14 行
class Article extends Model(core\base\Model):核心基类已统一use SoftDelete、$deleteTime = 'deleted_at'、$createTime = 'created_at'、$updateTime = 'updated_at'、$autoWriteTimestamp = true,子类 Model 不需要也不应该重复声明这些时间戳字段配置,直接继承即可。 - 第 35 行
protected $append = ['status_text'];与第 48-55 行getStatusTextAttr($value, $data)成对出现:定义了getXxxAttr()访问器就必须同时在$append数组里登记对应的下划线字段名(status_text),否则该访问器不会出现在toArray()/toJson()输出中,前端拿不到这个字段。 - 第 48-55 行访问器内部调用
$this->getStatusText((int) ($data['status'] ?? 0), $statusMap)复用了core\base\Model提供的getStatusText()辅助方法,而不是自己手写$statusMap[$status] ?? '未知'的映射逻辑;基类还提供了默认的getStatusTextAttr()实现(1=启用/0=禁用),本类因为语义是"草稿/已发布"而非通用启禁用,所以在子类中 override 了这个方法,符合基类注释里"若业务语义不同则子类 override"的约定。 - 第 40-43 行
category(): \think\model\relation\BelongsTo定义关联关系,方法名用小写单数业务名词(category)而非getCategory,返回类型显式声明为\think\model\relation\BelongsTo,belongsTo(ArticleCategory::class, 'category_id', 'id')显式传入外键与主键字段名,不依赖框架的默认推断。 - 第 16 行
protected $name = 'articles';显式声明表名(复数形式,无表前缀),与类名Article(单数)区分,避免依赖 ThinkPHP 默认的类名转表名推断规则。 - 第 18-21 行
protected $fillable = [...]白名单式声明允许批量赋值的字段,未列出的字段(如id、created_at)不会被create()/save()批量写入,防止越权字段注入。 - 第 27-33 行
protected $type = [...]声明字段类型转换,其中'tags' => 'json'触发基类getJsonAttr()/setJsonAttr()(在core\base\Model中定义)自动做 JSON 编解码,Model 层不需要在访问器里手写json_decode/json_encode。 - 第 24-25 行
const STATUS_DRAFT = 0; const STATUS_PUBLISHED = 1;在 Model 中定义状态常量,供 Service/Repository 层通过Article::STATUS_PUBLISHED引用,避免在业务代码中出现魔法数字。