在 Slim Framework 3 中访问 class 中的应用程序

Access app in class in Slim Framework 3

当路由位于 class 而不是 index.php

时,我无法理解如何访问 Slim 的实例

在使用 Slim Framework 2 时,我总是使用以下内容,但它在 Slim 3 中不起作用:

$this->app = \Slim\Slim::getInstance();

我正在尝试访问我在容器中设置的数据库连接,但来自单独的 class。这是我目前在我的 index.php 中获得的启动 Slim 应用程序的内容:

require_once("rdb/rdb.php");
$conn = r\connect('localhost');
$container = new \Slim\Container;
$container['rdb'] = function ($c){return $conn;}
$app = new \Slim\App($container);

这是我的路线:

$app->get('/test','\mycontroller:test');

这就是我在我的 mycontroller.php class 中得到的,我的路由指向它,这显然不起作用,因为 $this->app 不存在:

class mycontroller{
public function test($request,$response){
$this->app->getContainer()->get('rdb');
}

错误消息如下,因为与 Slim 2 相比,getinstance 不是 Slim 3 的一部分:

Call to undefined method Slim\App::getInstance() 

感谢任何帮助,

问候 旦

重要

我对@mgansler 投了赞成票,如果你在处理 slim 3,你应该先阅读它,只有在对与 slim 2 的差异感兴趣时才阅读这篇文章。


更新

所以这些用法似乎只是旧代码,没有人清理过。

但是我将此 post 留在这里,因为它应该对使用 Slim 2 的任何人有所帮助(因为 slim 3 仍然是测试版),并作为帮助查看差异的参考点。


旧更新(见上文)

随着 OP 的更新,我查看了 github 源代码,发现 getInstance 仍然在那里,但可能有一些细微的差异......

https://github.com/slimphp/Slim/search?utf8=%E2%9C%93&q=getInstance

测试文件(可能已过时,但不太可能)显示如下内容:

public function testGetCallableAsStaticMethod()
{
    $route = new \Slim\Route('/bar', '\Slim\Slim::getInstance');

    $callable = $route->getCallable();
    $this->assertEquals('\Slim\Slim::getInstance', $callable);
}

但同时我们在一些文件中看到这样的调用,这些调用显然是上下文相关的并且 return diff object ($env) 或者在同一个静态文件中 (Slim.php)

$env = \Slim\Environment::getInstance(true);

static::getInstance();

但这确实表明静态函数仍然存在,因此请使用我下面的示例并尝试找出为什么不能以当前形式为您工作。

此外,这个 'maybe' 很有趣,作为 slim3 在使用中的唯一明显例子:https://github.com/akrabat/slim3-skeleton

尽管可能存在其他项目,如果仍有问题,请使用 github 过滤器进行搜索。



原回答内容

请在路线和其他方面提供更多详细信息class,但这里有 3 种方式,执行示例将在下面详细说明。

此信息确实与 Slim Framework 2 相关,而不是 Slim 3 beta,但 slim 3 beta 显示了类似的示例代码并且没有提及大修更改,实际上链接到 Slim 2 文档:http://docs.slimframework.com/configuration/names-and-scopes/

$this->app->getContainer()->get('rdb');

// Recommended approach, can be used in any file loaded via route() or include()
$app = \Slim\Slim::getInstance();

Slim::getInstance();

App::config('filename');

Slim3 Beta 只有一个代码示例,如下所示:

$app = new \Slim\App();

// which would by extension mean that this 'might' work too

$app = \Slim\App::getInstance();

// but be sure to try with slim2 naming just in case

$app = \Slim\Slim::getInstance()

虽然显然这不适合 index.php,但与显示 GetInstance 有效的 Slim2 doco 一致。


哪一个适合你?

我有多个文件使用了这些不同的方法,但我不能说哪种方法最合适,因为关于这个外部 class 如何适合以及它的组成的上下文太少了。


例如,我的控制器(这是我大部分路线的端点)使用相同的方法,通过基地 class 或直接:

class ApiBaseController /// extends \BaseController
{

    protected $app;
    protected $data;

    public function __construct()
    {

        $this->app = Slim\Slim::getInstance();
        $this->data = array();

    }

    //...

}


class VideoApiController extends \ApiBaseController
{

    // ... 


    public function embed($uid)
    {
        // trace($this->app->response->headers());
        $vid = \R::findOne('videos'," uid = ? ",array($uid));
        if(!empty($vid))
        {

            // embed logic

        }else{
            // see my baseclass
            $this->app->render('api/404.html', array(), 404);
        }
    }


    // ...




    // Returns the video file, keeping actual location obscured
    function video($uid)
    {
        require_once(APP_PATH.'helpers/player_helper.php');

        $data = \R::findOne('videos'," uid = ? ",array($uid));

        /// trace($_SERVER); die();

        if($data)
        {
            stream_file($data['filename']);
        }else{
            $app = \Slim\Slim::getInstance();
            $app->render('404.html');
        }

        /// NOTE - only same domain for direct /v/:uid call
        header('Access-Control-Allow-Origin : '.$_SERVER['HTTP_HOST']);
        // header('X-Frame-Options: SAMEORIGIN');

        // Exit to be certain nothing else returned
        exit();
    }


    //...
}


我的助手文件 显示如下代码:

function get_permissions_options_list($context = null)
{
    if(empty($context)) $context = 'user';
    return App::config('permissions')[$context];
}


我的中间件:

function checkAdminRoutePermissions($route)
{
    $passed = runAdminRoutePermissionsCheck($route);

    if($passed)
        return true;

    // App::notFound();
    // App::halt(403, $route->getPattern());

    if(!Sentry::check())
        App::unauthorizedNoLogin();
    else
        App::unauthorized();
    return false;
}


这是我如何访问各种文件的示例,尽管您共享的代码已经表明您已经使用了推荐的方法

$app = \Slim\Slim::getInstance();

尽管如此,需要更多信息来确定您的外部文件如何适合,但如果它位于路线的末端或 'include()',那么它应该可以工作。

你说你的旧方法虽然不起作用,但没有给出实际结果与预期结果的信息(错误消息等),所以如果这不起作用请更新 OP。

看看 Rob Allen 创建的 Slim 3 Skeleton

Slim 3 大量使用依赖注入,因此您可能也想使用它。

在您的 dependencies.php 中添加如下内容:

$container = $app->getContainer();

$container['rdb'] = function ($c) {
    return $conn;
};

$container['Your\Custom\Class'] = function ($c) {
    return new \Your\Custom\Class($c['rdb']);
};

在你的 Your\Custom\Class.php 中:

class Class {
    private $rdb;
    function __construct($rdb) {
        $this->rdb = $rdb;
    }

    public function test($request, $response, $args) {
        $this->rdb->doSomething();
    }
}

希望对您有所帮助,如果您还有其他问题,请随时提出。

更新:

当您这样定义路线时

$app->get('/test', '\mycontroller:test');

Slim 在您的容器中查找 \mycontroller:test

$container['\mycontroller'] = function($c) {
    return new \mycontroller($c['rdb']);
}

因此,当您在浏览器中打开 www.example.com/test 时,Slim 会自动创建 \mycontroller 的新实例,并使用参数 $request$response$args。 因为您接受数据库连接作为 mycontroller class 的构造函数的参数,您也可以在方法中使用它:)

使用 Slim 3 RC2 及以上版本的路线为:

$app->get('/test','MyController:test');

CallableResolver 将在 DIC 中寻找一个名为 'MyController' 的密钥,并期望它到 return 控制器,因此您可以像这样在 DIC 上注册:

// Register controller with DIC
$container = $app->getContainer();
$container['MyController'] = function ($c) {
    return new MyController($c->get('rdb'));   
}

// Define controller as:
class MyController
{
    public function __construct($rdb) {
        $this->rdb = $rdb;
    }

    public function test($request,$response){
        // do something with $this->rdb
    }
}

或者,如果您不向 DIC 注册,那么 CallableResolver 会将容器传递给您的构造函数,因此您可以像这样创建一个控制器:

class MyController
{
    public function __construct($container) {
        $this->rdb = $container->get('rdb');
    }

    public function test($request,$response){
        // do something with $this->rdb
    }
}

这是一场艰难的比赛。 @mgansler 的回答真的很有帮助,但在他的回答中他传递了一个数据库连接,而不是控制器内部的 $app

按照同样的想法,可以发送 $app。

首先,在您的 dependencies.php 中,您需要获取 $app 并将其放入容器中,以便稍后将其注入控制器。

$container['slim'] = function ($c) {
   global $app;
   return $app;
};

然后你必须注射它:

// Generic Controller
$container['App\Controllers\_Controller'] = function ($c) {
    return new _Controller($c->get('slim'));
};

现在在您的 controller.php 上:

private $slim;

/**
     * @param \Psr\Log\LoggerInterface       $logger
     * @param \App\DataAccess                $dataaccess
     * @param \App$app                      $slim
     */
    public function __construct(LoggerInterface $logger, _DataAccess $dataaccess, $slim)
    {       
        $this->logger = $logger;
        $this->dataaccess = $dataaccess;
        $this->slim = $slim;
    }

现在你可以这样调用它了:

$this->slim->doSomething();

我创建了以下基本控制器并从中扩展。才刚刚开始玩 Slim,但如果您需要访问控制器中的 DI,它就可以使用。

namespace App\Controllers;

use Interop\Container\ContainerInterface;

abstract class Controller
{
    protected $ci;

    /**
     * Controller constructor.
     *
     * @param ContainerInterface $container
     */
    public function __construct(ContainerInterface  $container)
    {
        $this->ci = $container;
    }

    /**
     * @param $name
     * @return mixed
     */
    public function __get($name)
    {
        if ($this->ci->has($name)) {
            return $this->ci->get($name);
        }
    }
}

然后在你的其他控制器中你可以像这样使用它。

namespace App\Controllers;

 /**
 * Class HomeController
 *
 * @package App\Controllers
 */
class HomeController extends Controller
{

    /**
     * @param $request
     * @param $response
     * @param $args
     * @return \Slim\Views\Twig
     */
    public function index($request, $response, $args)
    {
        // Render index view
        return $this->view->render($response, 'index.twig');
    }

}