I’ve been dabbling with learning Laravel recently, but think I’m going to prefer Yii 2.0. But while looking things over in Laravel, I noticed one item that looked pretty nice.
The basic idea is that you can specify an interface as part of the constructor for a controller and then have the IoC container load up an instantiated version of the model. This makes it far easier to mock data that would generally be retrieved from the DB (and would mean that functional testing would be far simpler).
I’ve been looking through things in Yii2 and from what I can tell, I don’t see that ability. Would love to know that I’m wrong though!
You can accomplish this in Yii with setter injection. A common pattern of mine is to declare setters/getters for the dependencies, with a fallback to an application component if the dependency isn’t explicitly declared.
(Here Yii::$app functions as the service locator/container.)
Here’s an example with a controller that depends on a “customerFinder” to locate customer records. In production you’d probably interact with a database, but in tests you can inject a mock that returns fixture/other data and just use that.
Does this accomplish what you’re suggesting? It’s less magical, but note that in Laravel or Symfony you’d ultimately have to declare the interface => instance binding somewhere as well.
/**
* @var string ID of the application component containing the customer finder.
* If [[_customerFinder]] is not set, it is pulled from the app component with this ID.
*/
public $customerFinderID = 'customerFinder';
/**
* @var CustomerFinderInterface the object used for finding customers
*/
private $_customerFinder;
/**
* Sets the [[_customerFinder]]
* @param CustomerFinderInterface $finder
*/
public function setCustomerFinder(CustomerFinderInterface $finder)
{
$this->_customerFinder = $finder;
}
/**
* Returns the [[_customerFinder]]
* If the finder is not set, it is pulled from the [[customerFinderID]] application component.
* @return CustomerFinderInterface
*/
public function getCustomerFinder()
{
if ($this->_customerFinder === null) {
$this->setCustomerFinder(Yii::$app->getComponent($this->customerFinderID));
}
return $this->_customerFinder;
}