php - mvc网站中的静态和非静态方法
问题描述
目前我有一个问题,不允许我继续向我的 mvc 网站添加功能而不做任何类型的意大利面条代码。
我有两个类,一个是 ModModel,另一个是 ModUploadModel。两者都使用 Model 类进行扩展。ModModel 包含有关“mods”的所有方法,如 ModModel->doesModNameExists()、ModModel->getModDetails() 等... ModUploadModel 包含所有上传 mod 的方法,如 ModUploadModel->upload()、ModUploadModel- >isModNameValid() 等...
在某些情况下,我必须从 ModUploadModel 调用一些 ModModel 方法,为此我必须在 ModUploadController 中创建一个新的 ModModel 实例,并将其作为参数传递给 ModUploadModel->upload()。例如:ModUploadController 创建两个新对象,$modModel = new ModModel() 和 $modUploadModel = new ModUploadModel(),然后调用 $modUploadModel->upload($modModel)。
这是 ModUploadController,它创建两个对象并调用 ModUploadModel->upload() 方法
class ModUploadController extends Mvc\Controller {
public function uploadMod(): void {
$modUploadModel = new ModUploadModel()
$modModel = new ModModel();
// $modModel needs to be passed because the ModUploadModel needs
// one of its methods
if ($modUploadModel->upload("beatiful-mod", $modModel)) {
// success
} else {
// failure
}
}
}
ModUploadModel->upload() 检查输入是否有效(如果尚未使用模组名称等),最后将模组数据上传到数据库中。显然,这一切都是在更多的子私有方法中出现的,如 ModUploadModel->isModNameValid() 和 ModUploadModel->insertIntoDb()。
问题是我没有使用所有静态方法来构建我的类,并且每次我必须将对象作为参数传递,就像 ModModel 一样(例如我需要它的 isModNameValid() 方法)。我想过让所有的 ModModel 方法都是静态的,但这并不像看起来那么简单,因为它的所有方法都查询数据库,并且它们使用 Model->executeStmt() 方法(请记住,所有 FooBarModel 类都使用 Model 扩展类,其中包含有用的常用方法,如 executeStmt() 和其他),并且从静态方法调用非静态方法在 php 中不是一个好习惯,所以我也应该将模型方法设为静态,因此也应该将 Dbh 方法用于db 连接(模型用 Dbh 扩展)。
ModModel 类:
class ModModel extends Mvc\Model {
// in reality it queries the db with $this->executeStmt(),
// which is a Model method
public function doesModNameExists($name) {
if (/* exists */) {
return true;
}
return false;
}
}
ModUploadModel 类:
class ModUploadModel extends Mvc\Model {
private $modName;
public function upload($modName, $modModel) {
$this->modName = $modName;
if (!$this->isModNameValid($modModel)) {
return false;
}
if ($this->insertIntoDb()) {
return true;
}
return false;
}
// this methods needs to use the non static doesModNameExists() method
// which is owned by the ModModel class, so i need to pass
// the object as an argument
private function isModNameValid($modModel) {
if ($modModel->doesModNameExists($this->modName)) {
return false;
}
// other if statements
return true;
}
private function insertIntoDb() {
$sql = "INSERT INTO blabla (x, y) VALUES (?, ?)";
$params = [$this->modName, "xxx"];
if ($this->executeStmt($sql, $params)) {
return true;
}
return false;
}
}
另一种方法是在 ModModel 方法中创建一个新的 Model 实例,例如 (new Model)->executeStmt()。问题是创建新对象不是模范工作,通常也不是我最喜欢的解决方案。
解决方案
一些观察和建议:
[a]您正在传递一个ModModel
对象以ModUploadModel
在上传前验证模组名称。ModUploadModel::upload()
如果具有提供的名称的模块已经存在,您甚至不应该尝试调用。因此,您应该遵循与此类似的步骤:
class ModUploadController extends Mvc\Controller {
public function uploadMod(): void {
$modUploadModel = new ModUploadModel()
$modModel = new ModModel();
$modName = 'beatiful-mod';
try {
if ($modModel->doesModNameExists($modName)) {
throw new \ModNameExistsException('A mod with the name "' . $modName . '" already exists');
}
$modUploadModel->upload($modName);
} catch (\ModNameExistsException $exception){
// ...Present the exception message to the user. Use $exception->getMessage() to get it...
}
}
}
[b]在类中创建对象是个坏主意(例如在 中ModUploadController
)。改用依赖注入。阅读此内容并观看此内容和此内容。所以解决方案看起来像这样:
class ModUploadController extends Mvc\Controller {
public function uploadMod(ModUploadModel $modUploadModel, ModModel $modModel): void {
//... Use the injected objects ($modUploadModel and $modModel ) ...
}
}
在一个项目中,所有需要注入其他对象的对象都可以通过一个“依赖注入容器”来创建。例如,PHP-DI(我推荐)或其他 DI 容器。因此,DI 容器负责您项目的所有依赖注入。例如,在您的情况下,注入ModUploadController::uploadMod
方法的两个对象将由 PHP-DI 自动创建。您只需要在用作应用程序入口点的文件中编写三行代码,可能index.php
:
use DI\ContainerBuilder;
$containerBuilder = new ContainerBuilder();
$containerBuilder->useAutowiring(true);
$container = $containerBuilder->build();
当然,DI 容器也需要配置步骤。但是,在几个小时内,您就可以了解如何以及在何处进行操作。
通过使用 DI 容器,您将能够专注于项目的逻辑,而不是应该如何以及在何处创建各种组件或类似任务。
[c]使用静态方法是个坏主意。我的建议是摆脱您已经编写的所有静态方法。看这个,读这个,这个和这个。因此,您遇到的注入问题的解决方案是上述问题:DI,由 DI 容器执行。根本不创建静态方法。
[d]您正在使用这两个组件来查询数据库(ModModel
withdoesModNameExists()
和ModUploadModel
with insertIntoDb()
)。您应该只使用一个组件来处理数据库。
[e]你根本不需要Mvc\Model
。
[f]你根本不需要Mvc\Controller
。
一些代码:
我写了一些代码,作为你的替代品(我以某种方式“推断”了任务)。也许它会帮助你,看看别人是如何编码的。它可以让你“在不做任何意大利面条代码的情况下向我的 mvc 网站添加功能”。该代码与我不久前写的答案中的代码非常相似。该答案还包含其他重要的建议和资源。
重要提示:请注意,应用程序服务,例如来自 的所有组件Mvc/App/Service/
,应仅与域模型组件通信,例如与来自Mvc/Domain/Model/
(主要是接口)的组件,而不是来自的组件Mvc/Domain/Infrastructure/
。反过来,您选择的 DI 容器将负责为应用程序服务使用Mvc/Domain/Infrastructure/
的接口注入适当的类实现。Mvc/Domain/Model/
注意:我的代码使用 PHP 8.0。祝你好运。
项目结构:
Mvc/App/Controller/Mod/AddMod.php:
<?php
namespace Mvc\App\Controller\Mod;
use Psr\Http\Message\{
ResponseInterface,
ServerRequestInterface,
};
use Mvc\App\Service\Mod\{
AddMod As AddModService,
Exception\ModAlreadyExists,
};
use Mvc\App\View\Mod\AddMod as AddModView;
class AddMod {
/**
* @param AddModView $addModView A view for presenting the response to the request back to the user.
* @param AddModService $addModService An application service for adding a mod to the model layer.
*/
public function __construct(
private AddModView $addModView,
private AddModService $addModService,
) {
}
/**
* Add a mod.
*
* The mod details are submitted from a form, using the HTTP method "POST".
*
* @param ServerRequestInterface $request A server request.
* @return ResponseInterface The response to the current request.
*/
public function addMod(ServerRequestInterface $request): ResponseInterface {
// Read the values submitted by the user.
$name = $request->getParsedBody()['name'];
$description = $request->getParsedBody()['description'];
// Add the mod.
try {
$mod = $this->addModService->addMod($name, $description);
$this->addModView->setMod($mod);
} catch (ModAlreadyExists $exception) {
$this->addModView->setErrorMessage(
$exception->getMessage()
);
}
// Present the results to the user.
$response = $this->addModView->addMod();
return $response;
}
}
Mvc/App/Service/Mod/Exception/ModAlreadyExists.php:
<?php
namespace Mvc\App\Service\Mod\Exception;
/**
* An exception thrown if a mod already exists.
*/
class ModAlreadyExists extends \OverflowException {
}
Mvc/App/Service/Mod/AddMod.php:
<?php
namespace Mvc\App\Service\Mod;
use Mvc\Domain\Model\Mod\{
Mod,
ModMapper,
};
use Mvc\App\Service\Mod\Exception\ModAlreadyExists;
/**
* An application service for adding a mod.
*/
class AddMod {
/**
* @param ModMapper $modMapper A data mapper for transfering mods
* to and from a persistence system.
*/
public function __construct(
private ModMapper $modMapper
) {
}
/**
* Add a mod.
*
* @param string|null $name A mod name.
* @param string|null $description A mod description.
* @return Mod The added mod.
*/
public function addMod(?string $name, ?string $description): Mod {
$mod = $this->createMod($name, $description);
return $this->storeMod($mod);
}
/**
* Create a mod.
*
* @param string|null $name A mod name.
* @param string|null $description A mod description.
* @return Mod The newly created mod.
*/
private function createMod(?string $name, ?string $description): Mod {
return new Mod($name, $description);
}
/**
* Store a mod.
*
* @param Mod $mod A mod.
* @return Mod The stored mod.
* @throws ModAlreadyExists The mod already exists.
*/
private function storeMod(Mod $mod): Mod {
if ($this->modMapper->modExists($mod)) {
throw new ModAlreadyExists(
'A mod with the name "' . $mod->getName() . '" already exists'
);
}
return $this->modMapper->saveMod($mod);
}
}
Mvc/App/View/Mod/AddMod.php:
<?php
namespace Mvc\App\View\Mod;
use Mvc\{
App\View\View,
Domain\Model\Mod\Mod,
};
use Psr\Http\Message\ResponseInterface;
/**
* A view for adding a mod.
*/
class AddMod extends View {
/** @var Mod A mod. */
private Mod $mod = null;
/**
* Add a mod.
*
* @return ResponseInterface The response to the current request.
*/
public function addMod(): ResponseInterface {
$bodyContent = $this->templateRenderer->render('@Templates/Mod/AddMod.html.twig', [
'activeNavItem' => 'AddMod',
'mod' => $this->mod,
'error' => $this->errorMessage,
]);
$response = $this->responseFactory->createResponse();
$response->getBody()->write($bodyContent);
return $response;
}
/**
* Set the mod.
*
* @param Mod $mod A mod.
* @return static
*/
public function setMod(Mod $mod): static {
$this->mod = $mod;
return $this;
}
}
Mvc/App/View/View.php:
<?php
namespace Mvc\App\View;
use Psr\Http\Message\ResponseFactoryInterface;
use SampleLib\Template\Renderer\TemplateRendererInterface;
/**
* A view.
*/
abstract class View {
/** @var string An error message */
protected string $errorMessage = '';
/**
* @param ResponseFactoryInterface $responseFactory A response factory.
* @param TemplateRendererInterface $templateRenderer A template renderer.
*/
public function __construct(
protected ResponseFactoryInterface $responseFactory,
protected TemplateRendererInterface $templateRenderer
) {
}
/**
* Set the error message.
*
* @param string $errorMessage An error message.
* @return static
*/
public function setErrorMessage(string $errorMessage): static {
$this->errorMessage = $errorMessage;
return $this;
}
}
Mvc/域/基础设施/Mod/PdoModMapper.php:
<?php
namespace Mvc\Domain\Infrastructure\Mod;
use Mvc\Domain\Model\Mod\{
Mod,
ModMapper,
};
use PDO;
/**
* A data mapper for transfering Mod entities to and from a database.
*
* This class uses a PDO instance as database connection.
*/
class PdoModMapper implements ModMapper {
/**
* @param PDO $connection Database connection.
*/
public function __construct(
private PDO $connection
) {
}
/**
* @inheritDoc
*/
public function modExists(Mod $mod): bool {
$sql = 'SELECT COUNT(*) as cnt FROM mods WHERE name = :name';
$statement = $this->connection->prepare($sql);
$statement->execute([
':name' => $mod->getName(),
]);
$data = $statement->fetch(PDO::FETCH_ASSOC);
return ($data['cnt'] > 0) ? true : false;
}
/**
* @inheritDoc
*/
public function saveMod(Mod $mod): Mod {
if (isset($mod->getId())) {
return $this->updateMod($mod);
}
return $this->insertMod($mod);
}
/**
* Update a mod.
*
* @param Mod $mod A mod.
* @return Mod The mod.
*/
private function updateMod(Mod $mod): Mod {
$sql = 'UPDATE mods
SET
name = :name,
description = :description
WHERE
id = :id';
$statement = $this->connection->prepare($sql);
$statement->execute([
':name' => $mod->getName(),
':description' => $mod->getDescription(),
]);
return $mod;
}
/**
* Insert a mod.
*
* @param Mod $mod A mod.
* @return Mod The newly inserted mod.
*/
private function insertMod(Mod $mod): Mod {
$sql = 'INSERT INTO mods (
name,
description
) VALUES (
:name,
:description
)';
$statement = $this->connection->prepare($sql);
$statement->execute([
':name' => $mod->getName(),
':description' => $mod->getDescription(),
]);
$mod->setId(
$this->connection->lastInsertId()
);
return $mod;
}
}
Mvc/域/模型/Mod/Mod.php:
<?php
namespace Mvc\Domain\Model\Mod;
/**
* Mod entity.
*/
class Mod {
/**
* @param string|null $name (optional) A name.
* @param string|null $description (optional) A description.
*/
public function __construct(
private ?string $name = null,
private ?string $description = null
) {
}
/**
* Get id.
*
* @return int|null
*/
public function getId(): ?int {
return $this->id;
}
/**
* Set id.
*
* @param int|null $id An id.
* @return static
*/
public function setId(?int $id): static {
$this->id = $id;
return $this;
}
/**
* Get the name.
*
* @return string|null
*/
public function getName(): ?string {
return $this->name;
}
/**
* Set the name.
*
* @param string|null $name A name.
* @return static
*/
public function setName(?string $name): static {
$this->name = $name;
return $this;
}
/**
* Get the description.
*
* @return string|null
*/
public function getDescription(): ?string {
return $this->description;
}
/**
* Set the description.
*
* @param string|null $description A description.
* @return static
*/
public function setDescription(?string $description): static {
$this->description = $description;
return $this;
}
}
Mvc/域/模型/Mod/ModMapper.php:
<?php
namespace Mvc\Domain\Model\Mod;
use Mvc\Domain\Model\Mod\Mod;
/**
* An interface for various data mappers used to
* transfer Mod entities to and from a persistence system.
*/
interface ModMapper {
/**
* Check if a mod exists.
*
* @param Mod $mod A mod.
* @return bool True if the mod exists, false otherwise.
*/
public function modExists(Mod $mod): bool;
/**
* Save a mod.
*
* @param Mod $mod A mod.
* @return Mod The saved mod.
*/
public function saveMod(Mod $mod): Mod;
}
推荐阅读
- sql - 为什么在表权限列表中看不到我的表权限?
- python - int 太大,无法在 python 中转换
- python - Python列表理解缺少信息
- html - 单击按钮后 5 秒显示 div
- flutter - 有没有办法为多个小部件共享相同的数据?
- ios - 删除 UIWebView 后,来自 App Store 的相同错误
- postman - 限制 Postman 上的一些 HTTP 方法
- python - 为什么使用 setWindowFlags 后子对话框无法显示?
- java - 将python PKCS1_v1_5加密代码移植到java生成不同的签名
- google-chrome-extension - Chrome 扩展程序无响应(browser_action 和后台脚本)