【问题标题】:Is calling a Controller From Another Controller a good practice in Laravel?在 Laravel 中从另一个控制器调用控制器是一种好习惯吗?
【发布时间】:2016-06-09 07:49:55
【问题描述】:

我能够使用可重复使用的 postPayment() 方法实现 PaypalController,该方法接受商品及其价格,创建 Paypal 付款,并重定向到 Paypal 付款页面。

class PaypalController extends Controller {

    private static $_api_context;

    private static function initialize() {
        //initialize api context
    }

    public static function postPayment($items, $currency, $description) {
        self::initialize();

        //create item list, transaction, payment objects, etc

        $payment->create(PaypalController::$_api_context);
        ...
        return redirect()->away($redirect_url); // redirect to paypal
    }
}

PaypalController 被其他控制器静态调用。例如,AuthController 可能会在用户注册到我的网站后立即调用它来请求用户付款:

class AuthController extends Controller {
    public function postRegister(Request $request) {
        return PaypalController::postPayment($items, 'JPY', 'description');
    }
}

基本上,PaypalControllerRedirect 返回到AuthControllerAuthController 也返回它,以执行到 Paypal 支付页面的重定向。

我想知道这是否是一个好的设计 - 一个控制器调用另一个控制器,是吗?

如果没有,有什么更好的方法来做到这一点?也许将我的代码从 PaypalController 移动到自定义服务提供者、自定义 Helper 或其他东西?我是 Laravel 的新手,希望能得到一些指导。

【问题讨论】:

    标签: php laravel paypal laravel-5.1


    【解决方案1】:

    您的postPayment 是一个静态方法这一事实对我来说是一种代码味道,它告诉我“不,不在控制器内”。

    • 正如您所说,我认为服务会是一个更好的地方,如果您愿意,可以查看Omnipay

    • 您也可以将PaypalController 抽象化,AuthController 将扩展它(仅当您必须在多个控制器中使用postPayment 时)。

    • 您可以创建一个PaypalTrait 并在您的AuthController 中使用它(仅当您必须在多个控制器中使用postPayment 时)。

    • 当然,如果您认为第一个解决方案对您有意义,您可以将其与其他解决方案结合使用。

    这个问题有很多答案,我认为不存在完美的答案,这实际上取决于您正在构建什么以及您需要什么。

    【讨论】:

    • 其他人建议使用服务提供者,但我喜欢 Trait 的简单想法。我对这两个概念都很陌生,但是在我的情况下一个特征是否合适?如果我理解正确,一个特征将是实现多重继承,并且让 Paypal 处理被“继承”是不对的。你怎么看?
    • 是的,你可以做一个CanProcessPayment Trait 并在每个需要处理 PayPal 的方法的控制器中使用它。但不要将其设为静态并重命名您的 initialize 方法,以确保命名中没有冲突……如果您查看 Laravel 的 BaseController,它使用多个 Traits 来为每个控制器提供功能。抽象的 PaypalController 也不是一个糟糕的解决方案,就像 Trait 一样,你只需要继承你的控制器。
    【解决方案2】:

    不,这不是一个好习惯。您应该将业务逻辑抽象为服务/存储库类。比如:

    创建一个接口作为合约:

    namespace App\Services\Paypal;
    
    interface PaypalInterface {
    
         public function PostRegister(Array $array, /*More $params if necessary*/); 
    }
    

    然后执行合约:

    namespace App\Services\Paypal;
    
    class PaypalService implements PaypalInterface {
    
        // Must match the method signature declared in the interface
        public function PostRegister(Array $array, /*$More $params if necessary*/) {
    
            // Do the process here
        }
    }
    

    然后使用合约/接口作为依赖。因此,在您的 PaypalController 或任何其他控制器中,您可以像这样(重新)使用它:

    namespace App\Http\Controllers;
    
    use App\Http\Request;
    use App\Services\Paypal\PaypalInterface;
    
    class AuthController extends Controller {
        public function postPayment(Request $request, PaypalInterface $paypalService) {
            return $paypalService->postRegister($request->all());
        }
    }
    

    在这种情况下,register the binding(实现的接口)在服务提供者(基本上在AppServiceProvider)中。这就是基本的工作流程。为什么是接口,因为控制器(客户端/消费者类)应该与合同/接口而不是具体实现对话。

    This article of mine may help you 但请记住,这不是 100% 解耦,它仍然与Laravel 框架耦合,您可以进一步解耦服务。


    注意:这是一个最佳实践,但不要对每个项目/问题都盲目地遵循这种方法,只是明智地选择何时应该这样做,这实际上取决于上下文,但不要仅仅为它而死。当前的上下文可以遵循这一点。

    【讨论】:

    • 我为什么要使用接口?如果我在这种特殊情况下使用接口,恐怕我可能会过度设计。
    • 这是解耦业务逻辑的正确/最佳实践,也就是说:编程到接口,而不是具体实现。换句话说,对抽象类型(接口)的依赖使双方免于紧密解耦,即使不改变应用程序逻辑也可以改变依赖关系。但是就像我说的,做你认为对自己更好的事情,在你意识到需要并理解它之前,不要遵循任何规则。无论如何,它带来了话题。
    • 另外,Check This
    【解决方案3】:

    这不是正确的做法。请记住,控制器应该只接受请求并将其分派给正确的服务,而不是自己处理业务逻辑。

    此规则适用:当您需要从另一个控制器调用控制器方法时,它会散发出不良代码的味道

    相反,使用服务提供者和服务类来处理您的贝宝逻辑。服务类将处理 paypal 逻辑,稍后在控制器中需要它时使用:

    //SERVICE CLASS
    class PayPalService
    {
        public function processPayment(){ //... }
    }
    

    服务提供者用于在应用程序中注册服务类:你告诉 Laravel 当你需要 PayPalService 时,它应该为你构建并返回它

    //SERVICE PROVIDER: binds the creation of the service in the ioc container
    class PayPalServiceProvider extends ServiceProvider
    {
        public function register()
        {
            //use singleton or bind to bind the service in the ioc container
            $this->app->singleton( PayPalService::class, function()
            {
                return new PayPalService();
            });
        }
    }
    

    然后,当您需要 PayPalService 类时,您应该让 Laravel 自动将服务注入您的控制器:

    class AuthController extends Controller 
    {   
        public function postRegister(Request $request, PayPalService $paypal) {
            return $paypal->processPayment();
        }
    }
    

    如果你愿意,你可以通过使用服务类的接口来进一步改进这个设计

    【讨论】:

    • 在你的上一个例子中,你的意思是:return $paypal->processPayment(); ?
    猜你喜欢
    • 2021-08-24
    • 2019-05-03
    • 2011-10-31
    • 2014-08-27
    • 1970-01-01
    • 2014-09-22
    • 2018-02-25
    • 2014-03-09
    • 1970-01-01
    相关资源
    最近更新 更多