在PHP中设计服务层类

Jon*_*han 14 php model-view-controller service-layer

最近介绍Jani Hartikainen的服务层,讨论了如何在MVC应用程序中最好地处理表单数据.之后做一些阅读我真的可以看到这种做法的好处.我的问题是:

如何构建服务类?

  • 首先,user_service()我的user()模型是一个合适的类名还是另一个标准?
  • 由于我服务中的方法只会执行一项任务,因此认为这些方法总是可以的static function吗?服务类不代表数据,而是一系列动作,所以这似乎是合适的.
  • 服务方法应该只接受一个argument,这将是一个array

考虑表单已将数据发布到控制器以保存用户数据:

<?php

    class form_controller extends controller
    {

        public function process_submit()
        {
            if(user_service::update_preferences($_POST))
            {

                echo json_encode(array('success' => true));
            }
            else
            {
                echo json_encode(array('success' => false));
            }
        }

    }

    class user_service
    {

        // Accepts array()
        public static function update_preferences($fields)
        {

            // Check for required fields
            if((
                isset($fields['firstname']) and
                isset($fields['lastname']) and
                isset($fields['email'])
                ) == false
            {
                return false;
            }

            // Update user
            try
            {
                $s = new user();
                $s->set_firstname($fields['firstname']);
                $s->set_lastname($fields['lastname']);
                $s->set_email($fields['email']);
                $s->update();

                return true;
            }
            catch(Exception $e)
            {
                return false;
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

我觉得这是一个很好的方法,因为:

  • 我可以在我的表单中添加另一个字段,我不需要更新controller,只需要service.似乎对,控制器不应该关注传递的数据,只是它被传递.这使我的控制器变小,以及模型中的逻辑.
  • 如果我没有通过array,我可以设置多个参数的函数.例如,我的功能可能是update_preferences($firstname, $lastname, $email).然而,这可以使具有超过20个参数的函数(对于大型表格),并且顺序将变得非常糟糕.
  • 我可以通过object,但这有意义吗?如果我正在创建一个对象,它应该是它所代表的对象(在这种情况下是用户)吗?但控制器实例化用户对象是否有意义?这不是服务层的重点吗?
  • 也许有一个参数让一些方法有多个参数(当只有一到三个时)和一些接受数组的方法(当有很多字段时).这似乎可能是一场噩梦,因为你总是必须引用这个类来了解那个特定方法所要求的内容.

有没有人对这里做的正确的事情有什么看法?我是在正确的轨道上吗?你过去做了什么?非常感谢!

Jan*_*nen 23

你可以回复这个,因为你给我发了一封电子邮件;)

首先,user_service()是我的user()模型的适当类名还是另一个标准?

这是可以接受的.但是,您应该使用已建立的PHP编码约定之一,例如PEAR或ZF约定.在这两种情况下,类名UpperCamelCase和方法名称都是lowerCamelCase.使用它,这些类将是UserUserService

由于我服务中的方法只会执行一项任务,因此认为这些方法始终是静态函数是否正确?服务类不代表数据,而是一系列动作,所以这似乎是合适的.

没有.将方法设置为静态是一个糟糕的设计选择 - 这适用于大多数代码,而不仅仅是服务.服务情况的一个主要原因是,通常您的服务需要与数据存储或代表数据层的另一个类(存储库,数据访问对象,等等)进行交互.

当您的服务具有静态方法时,这意味着您需要在方法中实例化依赖项.这反过来意味着,除其他外,代码变得难以测试,因为您无法轻松替换依赖项.

例如,这里有一些很好的阅读(事实上​​,该博客上几乎所有内容都是对软件开发人员的良好解读)

服务方法是否只接受一个参数,即一个数组?

这取决于方法的作用.假设您处理表单结果集的示例,那么这可能会起作用.在其他一些情况下,它可能是一个糟糕的选择.

我可以在我的表单中添加另一个字段,我不必更新控制器,只需更新服务.[...]

如果我没有传递数组,我可以设置多个参数的函数.[...]

是的,在我看来,你对这两个案例的论证非常适合这个用例.

我可以传递一个物体,但这有意义吗?如果我正在创建一个对象,它应该是它所代表的对象(在这种情况下是用户)吗?但控制器实例化用户对象是否有意义?这不是服务层的重点吗?

这取决于.例如,如果您使用的框架允许您将表单表示为对象(例如Zend Framework和Zend_Form),则可以考虑将表单对象直接传递给服务.

也许有一个参数让一些方法有多个参数(当只有一到三个时)和一些接受数组的方法(当有很多字段时).这似乎可能是一场噩梦,因为你总是必须引用这个类来了解那个特定方法所要求的内容.

您通常应该根据方法的名称使参数至少是一半可猜测的.在我工作的东西中,我们有一个模型,例如企业和产品,企业可以赞助产品.在a中ProductService,我们有一个方法sponsorProduct,它将业务和产品作为参数.您几乎可以猜测它会占用这两个(如果您还熟悉代码库)

IDE通常也会帮助您 - 它们提供代码辅助,显示params函数所采用的内容.这是我认为IDE在大型项目中非常有用的主要原因之一,在这些项目中,您无法始终记住某个特定功能需要什么作为参数.

至于参数计数,我认为通常你应该尝试使用单独的参数.这样,任何人都可以通过查看函数的签名轻松查看所需的参数,并允许您非常轻松地定义类型提示和默认值.

但是有一点,当你得到这么多参数太多了.这可能是+5左右,具体取决于它是什么类型的方法.在这种情况下,您可以考虑使用数组或称为参数对象的东西,它本质上是一个包含调用所有参数的对象.更多关于参数对象的信息