php 无法将父容器与slim一起使用

wrrgggsh  于 2023-08-02  发布在  PHP
关注(0)|答案(1)|浏览(128)

我试图建立一个项目使用SLIM和下面是我的HomeController

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,这样我就不想在每个控制器上重复构造代码。
例如:I want to move

$this->container = $container;

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

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


到Controller.php,所以我可以直接访问$this->view和$this->db,就像我可以访问$this->test一样
但是我可以看到Controller.php __construct没有执行。
我试图把parent::construct()放在HomeController __construct函数中,得到了Too few arguments错误。
什么是最好的方式,我可以用来访问控制器中的容器,所以我不想重复的数据库和视图代码。

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"
        }
    }
}

根据hakre答案更新代码

我将Controller重命名为BaseController,这更有意义。

家庭控制器

<?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');
    }

}

e5nqia27

e5nqia271#

一般来说,如果你将构造函数方法__construct()从子类移动到父类,比如从HomeController移动到Controller,你也必须从子类HomeController中删除它。
如果不删除它,子类构造函数将重写父构造函数,父构造函数将不会被调用。
正如你已经发现的,你需要调用parent::__construct()。(附适当的论据。)
这两种方法都可能是正确的,但是当你想只为初学者使用超类(你从其扩展的超类,parent/grandparent/grandgrand...)__construct()方法时,从子类中删除它。

class Controller {

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

    ...
}

个字符

更多信息

由于您将Controller作为模板代码的超类(或基类),以供其他控制器从中扩展,因此您可能希望通过使Controller类抽象来更清楚地传达这一点。
由于Controller类本身不能工作(它是不完整的,只包含通过扩展它来重用的代码),abstract 关键字将显示这一点,并确保永远不会单独创建Controller(没有new Controller($container))。

abstract class Controller {
    ...
}


此外,由于不再将__construct()方法视为实现细节,因此可以使用 final 关键字来防止任何子类重写构造函数。
例如,这可以防止你遇到的原始问题,PHP有了护栏,你可以保护你构建的对象层次结构不受在扩展上实现错误的子类的影响。

abstract class Controller {

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

    ...
}


此外,为了完成extends层次结构,子类也可以通过使它们成为final来密封。

final class HomeController extends Controller {

    ...

}


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

模板代码形式

你还问:
什么是最好的方法,我可以用来访问控制器中的容器,所以我不想重复数据库和视图代码。
使用抽象类进行扩展是处理模板代码的常用方法。它们被设计为通过从它们延伸来重复使用。
有些人说,当使用抽象基类,特别是超类(超类是最顶层的类,每个其他类都从例子中的Controller扩展而来)时,它们应该只有([abstract] protected)方法,不包含任何属性处理 * 也不提供它们。所以这个超类没有你现在在超类中拥有的属性。
也就是说,你为容器提供了一个方法接口,容器只是超类的细节,从它扩展的类只调用那些方法:

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 关键字,并实现所有的类final。
这会自动强制您永远不要使用继承,因此您需要使用组合。
你可以通过 traits 拉入模板代码。但是使用它们可能会再次破坏组合,只有 using traits才能在更细粒度的级别上为您提供某种继承(在加载期间复制和粘贴)。
通常情况下,您首先开始创建类层次结构,当事情解决后,您可以考虑组合更高级别的对象。然后通过重构代码来实现这一点。
在示例中,注入到构造函数中的DI $容器例如用于组合。控制器不从其延伸。
通过使用组合,您可以构建一个独立于类层次结构的对象层次结构。
组合通常更灵活,但很难正确实现。在所有类上使用 final 关键字并致力于所有类都有接口的要求通常可以很好地支持这种形式,因为这种方法强制您针对接口而不是具体的类抽象进行编程,因此您可以更容易地替换功能并更改实现,而不是抽象。
但是,extends也会丢失您正在寻找的模板。
这两种形式都需要你用它们来做实验,因此我建议你从控制器的低级类层次结构开始,例如。只有一个超类可以扩展,使子类直接成为final。
如果你发现你的应用程序中所有的控制器都在做同样的事情,你可能也想利用组合,通过提供一个服务,一个方法或一个网关对象来处理DI $容器的这些动作。

相关问题