php项目不必强制用dao模式,但当出现sql硬编码、数据库耦合、测试难mock等问题时,dao就成为必要选择;它封装数据访问、隔离数据库变更、配合工厂实现依赖解耦与环境切换,并严格限定只做数据搬运,不涉业务逻辑。

PHP项目不需要强制用DAO模式,但当你开始遇到 mysqli_query 随处散落、SQL硬编码在控制器里、换MySQL为PostgreSQL时要全局搜索替换、或者单元测试根本没法 mock 数据库调用——这时候 DAO 就不是“要不要”,而是“拖不拖得起”。
DAO 在 PHP 里解决的不是设计感,是改 Bug 的时间
真实场景里,一个典型的坏味道是:某个业务函数里同时出现 new PDO()、拼接 WHERE 条件、手动处理 fetch() 结果、还要 try/catch 连接异常。这种写法让一次数据库字段重命名,可能波及十几个文件。
- DAO 把所有
SELECT/INSERT封装进方法名,比如findActiveOrdersByUserId($id),业务层只管“要什么”,不管“怎么拿” - 接口定义(如
OrderDaoInterface)和实现(如PdoOrderDao)分离,意味着你可以临时换成MockOrderDao来跑测试,完全绕过数据库 - 当项目从 MySQL 迁移到 SQLite 做本地开发时,只需重写 DAO 实现类,控制器、服务类一行不用动
工厂模式不是炫技,是控制 DAO 实例的唯一出口
直接 new PdoOrderDao($pdo) 看似简单,但很快你会在 5 个地方重复构造相同依赖,某天想加连接池或日志代理时,就得改 5 处。工厂模式在这里干一件具体的事:把“谁来创建 DAO”这件事收口。
- 写一个
DaoFactory类,提供createOrderDao(): OrderDaoInterface方法 - 工厂内部决定用
PdoOrderDao还是RedisOrderDao(比如缓存兜底),甚至根据环境变量切换:if (getenv('APP_ENV') === 'test') { return new MockOrderDao(); } - 业务代码永远只依赖工厂,不依赖具体 DAO 类,也就不会出现
use App\Dao\PdoOrderDao;这种强耦合引用
别在 DAO 里塞业务逻辑,那是最常踩的坑
DAO 的边界非常清晰:只做数据搬运。一旦你发现 DAO 方法里有 if ($user->isPremium()) { ... } 或者调用了 $this->sendNotification(),说明它已经越界了。
-
save()只负责把对象写入数据库,校验、状态变更、事件触发都该交给 Service 层 - DAO 方法返回值应尽量原始:数组、stdClass、或 DTO 对象(如
OrderData),而不是完整 Entity(含业务方法) - 事务控制不在 DAO 内部做,而由上层(如
OrderService)用$pdo->beginTransaction()统一管理,DAO 只暴露“可被事务包裹”的原子操作
DAO + 工厂的组合,真正难的不是写那几行接口和工厂类,而是每次新增一个查询时,克制住不把条件判断、格式化、关联加载全塞进 DAO 方法里——那个边界,才是后期维护成本的分水岭。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











