实体类必须用private属性,这是防止orm框架或业务逻辑被意外破坏的硬性边界;public会导致绕过验证、加密等关键逻辑,private配合getter/setter才能保障数据安全与一致性。

PHP实体类该用 private 还是 public 属性?
实体类必须用 private 属性,这是硬性边界。它不是“推荐”,而是防止 ORM 框架或业务逻辑被意外破坏的底线。
比如用 Laravel Eloquent 时,如果把 $id 或 $password 设为 public,外部代码可直接赋值:$user->password = '123',跳过模型的加密逻辑、验证钩子(如 boot 中的 creating),甚至绕过事件监听器。这种写法在测试或调试中看似方便,上线后极易引发数据不一致或安全漏洞。
正确做法是:所有字段声明为 private string $name;,通过 getName()/setName() 控制读写,并在 setter 中嵌入校验、格式化或副作用逻辑(如密码哈希)。
-
private是默认且唯一安全的选择;protected仅在需被子类(如AdminUser extends User)扩展行为时谨慎使用 - 不要依赖注释或命名约定(如
$name_private)代替访问控制——PHP 不会强制执行 - Eloquent 的
$fillable和$casts是补充机制,不能替代属性封装
DTO 类要不要加 getter/setter?
DTO 在 PHP 中通常不需要手写 getter/setter,尤其当它只用于 JSON 序列化或请求参数绑定时。
PHP 7.4+ 支持类型化属性,DTO 可以简洁定义为:
class UserDTO
{
public string $username;
public string $email;
public ?string $avatarUrl;
}
这种写法满足绝大多数场景:Laravel 的 request()->validate()、response()->json()、API 文档生成工具(如 OpenAPI)都能直接识别。加了 getter/setter 反而增加维护成本,且无实际收益——DTO 本就不该包含逻辑。
例外情况只有两个:
- 需要运行时字段转换(如把
$createdAtint 时间戳转成 ISO8601 字符串),此时才加getCreatedAtString()方法 - DTO 要复用部分实体逻辑(极少见),才考虑继承或组合,但这时它已偏离 DTO 原意
DTO 字段命名该和数据库字段一致,还是和前端 API 一致?
DTO 字段名必须和前端 API 一致,和数据库字段无关。
这是 DTO 存在的根本理由:解耦。实体类字段名由数据库设计决定(如 user_name、created_at),而前端需要的是 username、createdAt。如果 DTO 直接照搬数据库字段名,等于把存储细节暴露给调用方,后续改表结构就得同步改前端,丧失灵活性。
常见错误是让 DTO 继承实体类(如 class UserDTO extends User)——这会导致敏感字段($password_hash)、内部字段($remember_token)意外透出,也违背单一职责。
- DTO 字段名应由接口契约驱动,例如 OpenAPI spec 定义的字段名就是唯一依据
- 字段映射靠显式赋值或工具(如
spatie/data-transfer-object),不靠继承或魔术方法 - 日期格式、布尔值命名(
is_active→isActive)等转换必须在 DTO 构造或 toArray() 中完成
什么时候该拆出 VO,而不是全用 DTO?
当同一实体要以不同形态展示给不同端时,VO 就该出现了——DTO 解决“传什么”,VO 解决“怎么展”。
比如用户数据:管理后台需要完整字段(含最后登录 IP、失败次数),App 端只需头像+昵称+等级,H5 页面还要加一个“距离上次登录 X 天”的计算字段。这三个场景共用一个 UserDTO 必然臃肿,且任一端改字段都会影响其他端。
正确分层是:
-
UserDTO:用于 Controller → Service 数据输入,字段与 API 请求体严格对应(如注册时的password_confirmation) -
UserResponse(DTO):用于 Service → Controller 输出,字段与 API 响应体一致(不含敏感字段,含格式化时间) -
UserDashboardVO、UserProfileVO(VO):仅用于视图层,可含计算属性、本地化文案、前端专用状态字段
VO 和 DTO 最大区别在于生命周期和作用域:VO 生命周期绑定到一次 HTTP 响应渲染,DTO 可能跨多次调用(如缓存序列化)。忽略这点,容易把 VO 写成带 DB 查询的“伪 DTO”,导致 N+1 查询或缓存失效。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











