我正在try 使用SLIM和下面的是我的家庭控制器来构建一个项目

HomeController.php

<?php

namespace Controllers;

use DI\Container;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Views\Twig;


class HomeController extends Controller{

   private $db;

   private $view;

   private $container;

    public function __construct(Container $container){
    
      $this->container = $container;

       $this->db = $container->get('db');

       $this->view = $container->get('view');
    }

    public function index(Request $request, Response $response, $args){

      echo $this->test; // 'hello world'; This is from Controller.php

      echo $this->container->get('container-test'); // I am from container is the string i pass is container and i am getting output.

      echo $this->container_from_controller?'Captured':'Not Captured'; // Not Captured i.e the container in Controller.php is empty
      
      return $this->view->render($response, 'home/index.html', [

          'name' => 'from home controller'

      ]);
    }
}

Controller.php

<?php

namespace Controllers;

use DI\Container;



class Controller{

    protected $container_from_controller;

    protected $test = 'hello world';

    public function __construct(Container $container){
      
        $this->container_from_controller = $container;
    }


}

代码运行得很好.作为改进,我考虑将HomeController构造函数移到Controller.php,这样我就不想在每个控制器上重复构造代码.

也就是说我想搬家

   $this->container = $container;

   $this->db = $container->get('db');

   $this->view = $container->get('view');

到Controller.php,因此我可以直接访问$This-&>视图和$This-&>db,因为我可以访问$This-&>test

但我可以看到Controller.php__构造没有执行.

我try 将Parent::Construct()放入HomeController__Construct函数,但得到的参数太少错误.

访问控制器中的容器最好的方法是什么,所以我不想重复db和view代码.

composer.json

{
    "require": {
        "slim/slim": "4.*",
        "slim/psr7": "^1.6",
        "php-di/slim-bridge": "^3.4",
        "dcblogdev/pdo-wrapper": "^2.0",
        "slim/twig-view": "^3.3"
    },
    "autoload": {
        "psr-4": {
            "Controllers\\": "src/Controllers"
        }
    }
}

Update of codes as per hakre answer

我将控制器重命名为BaseController,这更有意义.

HomeController

<?php

namespace Controllers;

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;


class HomeController extends BaseController{



    public function index(Request $request, Response $response, $args){

      $sql = "SELECT * FROM list";

      $rows = $this->db->rows($sql);

      var_dump($rows);
      
      return $this->view->render($response, 'home/index.html', [

          'name' => 'from home controller'

      ]);
    }
}

BaseController

<?php

namespace Controllers;

use DI\Container;



abstract class BaseController{

    protected $db;

    protected $view;

    public function __construct(Container $container){
      
        $this->db = $container->get('db');

        $this->view = $container->get('view');
    }


}

推荐答案

通常,如果将构造函数方法__Construction()从子类移动到父类,如从HomeController移动到控制器,则也必须将其从子类HomeController中删除.

如果不删除它,子类构造函数将覆盖父类构造函数,不会调用父类构造函数.

正如您已经发现的,然后需要调用Parent::__Construct().(使用适当的论据.)

这两种方法都是正确的,但由于您只想对启动器使用超类(从其扩展的超类,父/祖父/祖父...)__Construction()方法,因此应将其从子类中删除.

class Controller {

    public function __construct(Container $container)
    {
        ...
    }

    ...
}
class HomeController extends Controller {

    # deleted:
    # public function __construct(Container $container) 
    # { 
    #  ... 
    # }

    ...
}

更多信息

由于您将控制器作为其他控制器的模板代码的超类(或基类)来进行扩展,因此您可能希望通过使控制器类抽象来更清楚地传达这一点.

由于控制器类本身不工作(它是不完整的,并且只包含通过扩展它来重用的代码),所以关键字abstract将显示这一点,并确保永远不能单独创建控制器(不是new Controller($container)).

abstract class Controller {
    ...
}

此外,由于您不再将__Construction()方法视为实现细节,因此可以使用关键字final来防止任何子类覆盖该构造函数.

例如,这可以防止您遇到的原始问题,然后PHP就有了护栏,您可以保护您构建的对象层次 struct 不受在扩展上实现错误的子类的影响.

abstract class Controller {

    final public function __construct(Container $container)
    {
        ...
    }

    ...
}

此外,为了完成扩展层次 struct ,还可以通过将子类设为最终类来封闭子类.

final class HomeController extends Controller {

    ...

}

https://www.php.net/manual/en/language.oop5.basic.php

模板代码的形式

你还可以进一步问:

访问控制器中的容器的最佳方式是什么,所以我不想重复db和view代码.

使用抽象类进行扩展是处理模板代码的一种常见方式.它们的设计目的是通过扩展它们来重复使用.

有人说,当使用抽象基类,尤其是超类(超类是最顶层的类,每个其他类都从示例中的控制器扩展而来)时,它们应该只具有([抽象]受保护的)方法,并且不包含任何提供它们的属性处理nor.因此,超类没有您当前在超类中拥有的属性.

也就是说,您为容器提供了一个方法接口,而容器只是超类的细节,从它扩展的类只调用那些方法:

abstract class Controller
{
    final public function __construct(private Container $container)
    {
    }

    protected function db() : Db {
       return $this->container->get('db');
    }

    private function view() : View {
       return $this->container->get('view');
    }

    protected function render(...$args)
    {
        return $this->view()->render(...$args);
    }

    ...
}

然后,子类仅通过(受保护的)方法、协议与超类通信.

此外,超类本身可以将协议的一部分定义为抽象的,这将使任何子类实现此类方法(此处未显示).

final class HomeController extends Controller
{
     ...

     public function index(Request $request, Response $response, $args)
     {
        ...

        $db = $this->db();

        ...

        return $this->render($response, 'home/index.html', [
          'name' => 'from home controller'
        ]);
     }

     ...
}

使用该协议只允许编写更好的模板,因为扩展类不必处理模板类的实现细节.

这有助于更好地进行交流,并允许更好地确定您需要哪些模板代码以及您拥有哪些抽象.

模板的组合与继承

您可以采取的另一种方法是永远不要使用关键字extends,并最终实现您的所有类.

这会自动强制您永远不要使用继承,因此您需要使用组合.

不过,您可以通过traits引入模板代码.但使用它们可能会再次 destruct 组合,只有using个特征在更细粒度的级别上为您提供了某种继承(加载过程中复制和粘贴).

通常情况下,您首先开始创建您的类层次 struct ,当事情稳定下来后,您会考虑使用更高级别的对象进行组合.然后,您可以通过重构代码来做到这一点.

例如,在本例中,注入构造函数的DI$容器用于组合.控制器不是从它延伸出来的.

通过使用组合,您可以构建独立于类层次 struct 的对象层次 struct .

组合通常更灵活,但更难实现正确.在所有类上使用关键字final并满足与所有类具有接口的要求通常可以很好地支持这种形式,因为这种方法强制您针对接口而不是具体的类抽象进行编程,因此您可以更轻松地替换功能和更改实现,而不是抽象.

但是,通过扩展,您也失go 了您正在寻找的模板.

这两种形式都需要您使用它们进行实验,因此我建议您从控制器的低级类层次 struct 开始,例如,只有一个超类可以从扩展而来,并直接使子类成为最终类.

如果您发现应用程序中的某些区域中所有控制器只是执行相同的操作,那么您可能也希望通过提供一个服务、方法或网关对象来处理DI$容器的这些操作,从而利用组合.

Php相关问答推荐

PHP DOMDocument忽略第一个表S结束标记

在函数内部获取一致的单选按钮值以更新WordPress用户数据

根据在WooCommerce购物车和 checkout 中 Select 的送货方式显示快捷代码内容

如何在函数中定位WooCommerce产品循环

在Laravel Eloquent中实现一对多关系

WooCommerce我的帐户:从帖子作者处获取自定义帖子类型帖子ID

在WooCommerce管理中 for each 运输方法设置添加自定义字段

如何从Laravel中的Spatie\Dns\Records\A对象中提取IP地址

从两个日期创建日期范围,然后为范围中的每个日期复制子数组

WooCommerce在购物车项目中复制产品而不增加产品数量

模式 bootstrap 未显示

HTTPPost请求在从php脚本调用时返回404,但在从node.js脚本调用时有效.终结点有效

添加不存在的订单元数据以扩展WooCommerce针对特定产品的管理订单搜索

允许在WooCommerce管理订单列表中使用计费邮箱进行搜索

wp_insert_post() 和 WordPress 中查询字符串的多个帖子

Laravel 10 中的自定义类未找到

为WooCommerce中的运输方式ID设置必填的 checkout 订单备注字段

Laravel使用另一张表进行关联查询

如何在 WooCommerce 中显示每个自定义选项卡的内容

为什么 ECDSA 384 签名验证在 GO 中失败,但在 PHP 中却没有?