-->

Inject Silex $app in my custom class

2019-07-21 15:57发布

问题:

I'm on a Silex project and I use classes to different treatments:

$connection = new Connection($app);
$app->match('/connection', function () use ($app, $connection) {
    $connexion->connectMember();
    return $app->redirect($app['url_generator']->generate('goHome'));
})->method('GET|POST')->bind('doConnection');

In the function 'connectMember()' of my class 'Connection', I have :

   [...]
if($isMember){
   [...]
}else{
   return $this->_app['twig']->render(
      'message.twig', 
      array('msg' => "This member does not exist.", 'class' => 'Warning'));
}
   [...]

But the render () method is not working. The error message I want to display is not displayed and "$ app-> redirect (...)" is launched instead.

How to make my class uses the current object Silex\Application ? Is there a better method to bind a custom class to the instance of the Silex application?

Thank you very much for your answers !


Edition : Add informations

If I use :

return $connexion->connectMember();

The error message is displayed. But it's not a good solution. The 'connection' class calls other classes that also use this code:

$this->_app['twig']->render(...). 

How to make $ this->_app (present in my classes) correspond to the variable $app created in my controller?

回答1:

Create a service for the Connection (or Connexion??) class and inject the application:

use Silex\Application;

class Connection
{
    private $_app;

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

    // ...
}
$app['connection'] = function () use ($app) {
    return new Connection($app); // inject the app on initialization
};

$app->match('/connection', function () use ($app) {
    // $app['connection'] executes the closure which creates a Connection instance (which is returned)
    return $app['connection']->connectMember();

    // seems useless now?
    return $app->redirect($app['url_generator']->generate('goHome'));
})->method('GET|POST')->bind('doConnection');

Read more about it in the documentation of silex and pimple (pimple is the container used by silex).



回答2:

if you are using $app->share(...) for dependency injection you can setup something like this (it's pseudo code) :

<?php
namespace Foo;
use Silex\Application as ApplicationBase;

interface NeedAppInterface {
   public function setApp(Application $app);
}

class Application extends ApplicationBase {
  // from \Pimple
  public static function share($callable)
  {
    if (!is_object($callable) || !method_exists($callable, '__invoke')) {
      throw new InvalidArgumentException('Service definition is not a Closure or invokable object.');
    }

    return function ($c) use ($callable) {
      static $object;

      if (null === $object) {
        $object = $callable($c);
        if ($object instanceof NeedAppInterface) {
          // runtime $app injection
          $object->setApp($c); // setApp() comes from your NeedAppInterface
        }
      }
      return $object;
    };
  }
}

Now doing this :

$app['mycontroller'] = $app->share(function() use ($app) {
   return new ControllerImplementingNeedAppInterface();
});

will automatically set $app when calling $app['mycontroller'] !

PS : if you don't want to use ->share() try using __invoke($app) because \Pimple::offsetGet() calls it :p