3

I have a problem described in subject. Let me explain. I use basic application template. I have simple model Search, which uses application component GoogleCustomSearch for search through Google Custom Search API. GoogleCustomSearch has to be configured with Google API key and Search Engine ID. I specifying Google API key and Search Engine ID via application config config/web.php. I would like to inject configured instance of GoogleCustomSearch into an instance of the Search model. Is it possible to reach my goal in a cleaner way (workaround below)?

File: models/Search.php

namespace app\models;

use app\components\GoogleCustomSearch;
use yii\base\Model;

class Search extends Model
{
    /** @var GoogleCustomSearch */
    protected $googleCustomSearch;

    public function __construct(GoogleCustomSearch $googleCustomSearch, array $config = [])
    {
        $this->googleCustomSearch = $googleCustomSearch;
        parent::__construct($config);
    }
    ....
}

File: components/GoogleCustomSearch.php

namespace app\components;

use yii\base\Component;
use yii\base\InvalidValueException;

class GoogleCustomSearch extends Component
{
    public $searchEngineId;
    public $apiKey;
    ...
}

My current workaround below

File: config/bootstrap.php

use app\models\Search;
use yii\di\Container;
use yii\web\Application;

\Yii::$container->set('search', function (Container $container, $params, $config) {
    $googleCustomSearch = $container->get(Application::class)->googleCustomSearch;
    array_unshift($params, $googleCustomSearch);
    return $container->get(Search::class, $params, $config);
});

File: web/index.php

use yii\web\Application;

....

require (__DIR__ . '/../config/bootstrap.php');
$config = require(__DIR__ . '/../config/web.php');

\Yii::$container
    ->setSingleton(Application::class, [], [$config])
    ->get(Application::class)
    ->run()
;

And then call

/** @var Search $model */
$model = \Yii::createObject('search');

Is there cleaner way to inject configured component to an object instance?

glagola
  • 2,142
  • 3
  • 17
  • 20

1 Answers1

1
  • Your model part is good. It knows nothing about how dependency is injected and that's correct way of doing it.
  • In the bootstrap I'd set GoogleCustomSearch instead of search model:

    Yii::$container->set(GoogleCustomSearch::class, function (Container $container, $params, $config) { return Yii::$app->googleCustomSearch; });

  • Not sure about the purpose of modified index.php.
Sam Dark
  • 5,291
  • 1
  • 34
  • 52
  • 1
    ```Yii::$container->set(GoogleCustomSearch::class, function () { return Yii::$app->googleCustomSearch; });``` I think it would cause a loop in initialization – glagola Oct 23 '16 at 11:51
  • Yes. It is likely. Then your approach is good except `index.php` modifications. – Sam Dark Oct 23 '16 at 21:44
  • Why you think the index.php modifications is bad? \Yii::$app is singleton, why it should not be stored and accessed via DI-container? I'm not arguing, just want to know maybe I miss something. – glagola Oct 24 '16 at 10:48
  • There's no point in putting everything into container. In this particular case nothing is achieved but calls are becoming a bit more complicated. – Sam Dark Oct 24 '16 at 12:07