简介

MVC(Model-View-Controller) 是 PHP 开发中常用的架构模式,大部分项目构建都是基于这种模式。

  • 模型 Model – 连接和操作数据库数据。
  • 控制器 Controller - 负责响应用户请求、准备数据,以及决定如何展示数据。
  • 视图 View – 负责渲染数据,通过 HTML 方式呈现给用户。
    1. Model 操作数据
    2. Controller 控制器
    3. View 视图

    本文 PHP 演示均使用 ThinkPHP6.0

模型 Model 使用习惯

项目中一般设置 后台(Admin)、Web端(Index)、接口(Api) 三个大的模块。往往 Web端(Index) 和 接口(Api) 会共用一个 Model 层,而 后台(Admin) 因为涉及大量增删改查的动作,我一般会给 后台(Admin) 单独创建一个 Model 层。

  1. # 后台使用
  2. namespace app\Model\admin;
  3. # web、Api使用
  4. namespace app\Model;

在 ProductModel 中创建几个常用方法:

  1. <?php
  2. namespace app\Model\admin;
  3. use think\Db;
  4. class ProductModel
  5. {
  6. /**
  7. * Get Product
  8. * 获取单个产品
  9. * @param array $params
  10. * @return array
  11. */
  12. public function getProduct($params = []) {}
  13. /**
  14. * Get Products
  15. * 获取多个产品
  16. * @param array $params
  17. * @return array
  18. */
  19. public function getProducts($params = []) {}
  20. /**
  21. * Create Product
  22. * 创建产品
  23. * @param array $params
  24. * @return array
  25. */
  26. public function create($params = []) {}
  27. /**
  28. * Update Product
  29. * 更新产品
  30. * @param array $params
  31. * @return array
  32. */
  33. public function update($params = [], $id) {}
  34. }

需要注意,方法中使用传递数组 $params = [] 的方式,这样在项目后期可以提升代码的观赏度,避免出现以下情况:

  1. public function getProducts($id, $category_id = '', $paginate = 1, $barand_id = '', ...) {}

当存在少量参数时,在当前方法中处理参数:

  1. public function getProduct($params = [])
  2. {
  3. $query = Db::name('product');
  4. if (isset($params['id'])) $query->where('id', $params['id']);
  5. if (isset($params['status'])) $query->where('status', $params['status']);
  6. $product = $query->find();
  7. return $product;
  8. }

当存在大量参数时,可使用 private 辅助方法处理参数:

  1. public function getProducts($params = [])
  2. {
  3. $query = Db::name('product');
  4. $this->setGetProductsParams($query, $params);
  5. $product = $query->find();
  6. return $product;
  7. }
  8. private function setGetProductsParams($query, $params = [])
  9. {
  10. if (isset($params['category_id'])) $query->where('category_id', $params['category_id']);
  11. if (isset($params['brand_id'])) $query->where('brand_id', $params['brand_id']);
  12. if (isset($params['status'])) $query->where('status', $params['status']);
  13. if (isset($params['type'])) $query->where('type', $params['type']);
  14. }

有时会需要在某个方法中调取不同类型的数据,即需要支持数组、又需要支持对象,或是框架带分页数据。这里我常使用多个方法处理,并不是在一个方法中通过不同参数返回不同类型的数据。

  1. /**
  2. * Get Products
  3. * 获取多个产品
  4. * @param array $params
  5. * @return array
  6. */
  7. public function getProducts($params = [])
  8. {
  9. $products = Db::name('product')->select()->toArray();
  10. return $products;
  11. }
  12. /**
  13. * Get Products
  14. * 获取多个产品
  15. * @param array $params
  16. * @return object
  17. */
  18. public function getProductsObject($params = [])
  19. {
  20. $products = Db::name('product')->select();
  21. return $products;
  22. }
  23. /**
  24. * Get Products
  25. * 获取多个产品
  26. * @param array $params
  27. * @return object 分页
  28. */
  29. public function getProductsPage($params = [])
  30. {
  31. $products = Db::name('product')->paginate();
  32. return $products;
  33. }

控制器 Controller 使用习惯

增删改查常用于后台,我习惯定义的方法名:

  1. # 列表页
  2. public function index() {}
  3. # 新增页
  4. public function create() {}
  5. # 保存数据
  6. public function store() {}
  7. # 编辑页
  8. public function edit() {}
  9. # 更新数据
  10. public function update() {}
  11. # 非物理删除数据
  12. public function delete() {}
  13. # 物理删除数据
  14. public function destory() {}
  15. # 其它方法
  16. ......

MVCR

有些项目一般选择在模型 Model 与控制器 Controller 之间加一层逻辑处理的模块,我习惯使用 R(Repository:仓库、储藏室)命名(网上也有说使用 I 命名)。
通俗点讲,就是保持了 Model 的共用性、整洁性和 Controller 的整洁性,在 Repository 中处理较为复杂的逻辑。下面我们用两个示例来简单说明下 Repository 的作用。
1,查询产品,并修改结果数据格式:
在 ProductModel 中创建查询多个产品的方法:

  1. <?php
  2. namespace app\Model\admin;
  3. use think\Db;
  4. class ProductModel
  5. {
  6. public function getProducts() {}
  7. }

通常逻辑我们会在控制器中直接调用该方法。这里我们改变下思路使用 ProductRepository 来获取数据,并修改产品创建时间格式:

  1. <?php
  2. namespace app\Repository\admin;
  3. use app\Model\admin\ProductModel;
  4. class ProductRepository
  5. {
  6. public function getProducts()
  7. {
  8. $products = app(ProductModel::class)->getProducts();
  9. foreach ($products as $key => $value) {
  10. $products[$key]['create_time'] = date('Y-m-d', $value['create_time']);
  11. }
  12. }
  13. }

最后,在控制器中使用 ProductRepository 中的 getProducts 方法。
2,用户登录: UserModel 中查询,更新方法:

  1. <?php
  2. namespace app\Model\admin;
  3. use think\Db;
  4. class UserModel
  5. {
  6. public function getUser($params = []) {}
  7. public function update($params = [], $id) {}
  8. }

UserRepository 中登录方法:

  1. <?php
  2. namespace app\Repository\admin;
  3. use app\Model\admin\UserModel;
  4. class UserRepository
  5. {
  6. public function login($username, $password)
  7. {
  8. // 验证信息
  9. $user = app(UserModel)->getUser(['username' => $username, 'password' => $password]);
  10. if (!$user) return arrayFailed('login failed');
  11. // 登录成功后续操作
  12. app(UserModel)->update(['last_login_time' => time()], $user->id);
  13. }
  14. }

UserController 中使用 UserRepository 中的 login 方法:

  1. <?php
  2. namespace app\Repository\api;
  3. use think\Request;
  4. use app\Model\admin\UserRepository;
  5. class UserController
  6. {
  7. public function login(Request $request)
  8. {
  9. $res = app(UserRepository::class)->login($request->username, $request->password);
  10. }
  11. }

上面两个示例简单展示了在 Repository 中,处理结果数据与操作业务逻辑。这里也建议项目中加入 Repositoy 层,在保持代码整洁的同时,也有灵活的扩展性。