从其他 classes 访问 Silex 应用程序 class;如何注射?

Accessing Silex Application class from other classes; how to inject?

我目前正在处理我的第一个 Silex (2.0) 项目。我定义了一些 Pheasant 模型,我可以从我的控制器中访问它们:

// it is used both static
$p = \Model\Post::oneById(3);

// .. and with an instance
$p = new \Model\Post;
$p->title = 'foobar';
$p->save();

现在,在某些情况下,我想在我的模型中访问应用程序 class。例如,检查我们是否 运行 处于调试模式(或不是)。现在:

public function beforeSave() {
    global $app;
    if($app['debug']) {
        // ...
    }
}

但这不像 Silex。所以我想我需要某种东西可以自动将 $app class 注入我的模型:

class PheasantModelReflector {
    protected $app;

    public function __construct(\Silex\Application $app) {
        $this->app = $app;
    }

    public function __get($className) {
        $r = (new ReflectionClass(sprintf('Model\%s', $className)))->newInstance();
        $r->__invoke($this->app);
        return $r;
    }
}

$app['model'] = function ($app) {
    return new PheasantModelReflector($app);
};

这在一定程度上有效,但总是 returns Post 模型的新实例,如 $app['model']->Post.

有什么办法可以解决这个问题吗?

在没有使用 Pheasant 的情况下,我可能会尝试创建一个服务工厂,将 $app(或 $debug 变量)注入到您的实体 class 中,就像这样(这里的一个问题是您的实体必须扩展 \Pheasant\DomainObject 并且您不能将构造函数重写为 it's marked as final):

<?php

// in the example below I inject the whole $app, if you only need the
// debug var, inject only that, injecting the whole $app may
// tempt you to use it as a service locator

// creating a new instance
$app['model_post_factory'] = $app->factory(function ($app) {
  $post = new \Model\Post();
  $post->setApp($app); 

  return $post;
});

$post = $app['model_post_factory'];

// getting an instance by ID, here it gets a little tricky
$app['model_post_static_factory'] = function($app, $id) {
  $post = \Model\Post::oneById($id);
  $post->setApp($app);

  return $post;
}

$postById = $app->raw('model_post_static_factory');
$post = $postById($app, $id);  // $id comes from somewhere else

// depending on the PHP version you may try directly:
// $post = $app->raw('model_post_static_factory')($app, $id);

静态方法的问题在于传递 id 参数。您可以使用 use 关键字从外部范围将 $id 导入工厂范围,但恕我直言,这太神奇了(尽管我也不太喜欢替代方案)。可能还有一些更优雅的方式,但我暂时想不到。

为了避免每次需要创建共享服务时都生成一个新的模型实例(http://silex.sensiolabs.org/doc/services.html)。 例如:

$app['model'] = $app->share(function () {
    return new PheasantModelReflector();
});

无论如何注入$app不一定是个好主意,也许你可以注入每个对象的依赖?类似于:

new \Model\Post($app['debug']);