php - 如何将逻辑插入模型而不是将其保留在 Controller 中?

标签 php codeigniter design-patterns model-view-controller separation-of-concerns

根据我对MVC的理解,逻辑驻留在模型中, Controller 处理协调部分, View 用于数据表示。

下面是从 Controller 中提取的代码。它是用 PHP 框架 codeigniter 编写的。下面发生的情况是,如果图像可用,它将被删除,否则用户将被重定向到删除图像表单。

if($this->records_model->check_img_availability($img_data))
{   
    //delete if the image is available
    $this->records_model->delete_img($img_data); 
}
else
{
    //redirected to delete image form
    $this->session->set_flashdata('errormsg', 'Img is not available'); 
    redirect(base_url() . 'records/deleteimg/');   
}

根据上面的 MVC 是错误的,因为逻辑驻留在 Controller 中。它正在检查图像是否可用,具体取决于该图像将被删除或用户重定向。

如何将逻辑插入模型而不是将其保留在 Controller 中?

最佳答案

使用服务层。您 Controller 中现在拥有的逻辑将进入您的一项服务。

服务通常处理大量相关的应用程序逻辑。

例如,如果您的应用程序销售产品,则购物车和客户结账的功能/逻辑必须转移到某个地方。由于功能相似,您可以将所有相关逻辑推送到 Services\Shopping 类中,并且它可以具有如下所示的 API:

interface Shopping {

    /**
     * @param int $productId
     * @param int $quantity
     * @return bool
     */
    public function addToCart($productId, $quantity);

    /**
     * @param int $productId
     * @return bool
     */
    public function removeItem($productId);

    /**
     * @param int $productId
     * @param int $quantity
     * @return bool
     */
    public function updateQuantity($productId, $quantity);

    /**
     * Customer checkout.
     *
     * @param string $firstName
     * @param string $lastName
     * @param string $emailAddress
     * @return bool
     */
    public function checkout($firstName, $lastName, $emailAddress, .....etc);

}

您基本上只需将类似的模型层功能分组到服务中,并为服务命名一个好名称,该名称可以广泛地解释它执行的工作/功能类型。

您的 Controller 现在只需从请求中提取数据并将其发送到服务来完成工作。

将商品添加到购物车的一些 Controller 方法:

$productId = (int)$this->request->post('productId');
$quantity = (int)$this->request->post('quantity');

// Now send the data to the shopping service to do the work.
$this->shopping->addToCart($productId, $quantity);

现在您的 Controller 可以变得精简且没有逻辑。如何实现服务取决于您。服务通常可以依赖其他服务来与模型层的不同功能进行交互。

编辑

对于您的场景,我无法为该服务提供一个描述性名称,因为我不知道有关您删除图像的原因、图像与什么相关等的足够信息,因此我将其称为 Services\Image 。对于服务来说,这可能被认为是一个不好的名称,因为它过于具体,但它仅用于演示目的。

class Image extends AbstractService {

    /**
      * This is assuming you have an ID for a row in the database containing data
      * about the image like the path to the image so it can be deleted from the server.
      *
      * @param int $id
      * @return bool
      */
    public function delete($id) {

        $mapper = $this->mapperFactory->build('Image');
        $image = $mapper->fetch($id);

        if( $image === null ) {
            return $this->fail('Image does not exist.');
        }

        try {
            $mapper->delete($image); // Delete the image row in the database.
            unlink( $image->getPath() ); // Delete the image file from the server.

            return $this->success('Image deleted successfully.');
        }
        catch(\RuntimeException $ex) {
            return $this->fail('Failed to delete the image.');
        }
    }

}

success()fail 方法位于父服务中。他们更新模型层的状态并设置一条消息以在需要时向用户显示。方法 failed() 来自父 AbstractService,如果您在任何子服务中调用 $this->fail(),该方法将返回 true。

然后在 View 中您可以看到类似的内容:

if( $this->imageService->hasState() ) {
    if( $this->imageService->failed() ) {
        // If deleting the image failed do whatever you want here.
        // Switch templates, redirect or whatever.
    }
    else {
        // The image was deleted successfully so do whatever you want here.
        // Again, switch templates, redirect or whatever. 
    }
    // Bind the message to the template.
}

正如你所看到的, View 有大脑并且可以执行逻辑。这是 CI 很糟糕的一个领域。 View 基本上是没有大脑的模板,因此应该在 View 中的逻辑被推送到 Controller 、服务层等中。

基本上 Controller 通过delete($id)方法与图像服务通信,该方法尝试删除图像并设置模型层的状态,然后 View 与图像对话服务并询问它处于什么样的状态,并根据模型层的状态继续执行它需要执行的任何逻辑。

在我看来,服务通常不应该知道任何响应或如何发送重定向,这是 View 的责任。

Symfony 可能是最好的 PHP 框架,但许多人说它是学习曲线最陡的框架之一。我在文档中写了几页,直到我开始看到我不同意的东西,所以我决定停止探索它。

PHP 的创建者 Rasmus Lerdorf 甚至说自己目前所有的 PHP 框架都很糟糕。

祝你好运。

关于php - 如何将逻辑插入模型而不是将其保留在 Controller 中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22169667/

相关文章:

php - foreach 复选框 PHP 中的 POST

PHP - 购买后从数据库中清除购物车

c# - 在 C# 中区分类名和属性的良好命名约定是什么?

php - 使用 CodeIgniters Active Record 库来操作 MySQL 数据库是个好主意还是我应该只使用 SQL?

php - 从不同路径访问 PHP session 变量

javascript - 如何将两个输入时间值相加并显示在另一个输入框中?

javascript - 打印二维阵列

java - 每个单元的自定义单元工厂 JavaFX

php - Codeigniter session 终止

php - 从 MySQL (Codeigniter) 中选择时缺少 HTML 标记