Question

I like and use the Yii framework, particularly its "components", which are lazily-instantiated and you can swap them in or out in your configuration file. Kind of like a dependency injection-lite.

I try to keep the business logic of my code completely independent of the Framework, in case I ever want to repurpose that code, or even change frameworks.

Let's say I have a class in my service layer called AccountService, which implements IAccountService and has a one-argument constructor.

interface IAccountService
{
  function getUserById($id);
}

class AccountService implements IAccountService
{
  private $_userRepository;

  public function __construct(IUserRepository $userRepository) {
    $this->_userRepository = $userRepository;
  }

  public function getUserById($id) {
    return $this->_userRepository->getById($id);
  }
}

Great. So far, it's totally framework-free. Now I'd like to expose this as a Yii component, so it can be lazily-instantiated and easily used by Yii controllers and other Yii components.

But Yii components (which implement IApplicationComponent) must have exactly zero constructor arguments, while my class requires one!

Any ideas?

Here's what I've got. I'm not really happy with any of them; they both look over-engineered and I'm detecting a distinct smell from them.

Option 1 - compose: I create a class called "AccountServiceComponent" which implements Yii's IApplicationComponent. It cannot extend my AccountService class, because of the constructor, but it could instantiate one as a private member and wrap all of its methods, like so:

class AccountServiceComponent implements IApplicationComponent, IAccountservice
{
  private $_accountService;

  public __construct() {
    $this->_accountService = new AccountService(new UserRepository());
  }

  public getUserById($id) {
    return $this->_accountService->getUserById($id);
  }
}

Cons: I'll have to wrap every method like that, which is tedious and could lead to "baklava code." Especially considering that there'll be multiple service classes, each with multiple methods.

Option 2 - mixin: (Or behavior or trait or whatever it's called these days.)

Yii (having been written prior to PHP 5.4) offers "behaviors" in the form of a class which implements IBehavior. I could create a behavior class which extends my service, and attach it to a component:

class AccountServicesBehavior extends AccountService implements IBehavior
{
  // Implement the few required methods here
}

class AccountServiceComponent implements IApplicationComponent
{
  public function __construct() {
    $accountService = new AccountService(new UserRepository());
    $this->attachBehavior($accountService);
}

Cons: My component no longer officially implements IAccountService. Also seems to be getting excessive with the layering.

Option 3 - optional constructor parameters:

I could just make the constructor parameter to my service class optional, and then extend it into a component:

class AccountService implements IAccountService
{
  public $userRepository;

  public function __construct(IUserRepository $userRepository = null) {
    $this->userRepository = $userRepository;
  }

  public function getUserById($id) {
    return $this->_userRepository->getById($id);
  }
}

class AccountServiceComponent extends AccountService implements IApplicationComponent
{
}

Cons: The optional constructor parameter means this class coudld now be instantiated without supplying it with everything it needs.

...so, any other options I'm missing? Or am I just going to have to choose the one that disturbs me the least?

Was it helpful?

Solution

Option 3 but with an object as the optional argument sounds best imo:

public function __construct(IUserRepository $userRepository = new UserRepository()) {
    $this->userRepository = $userRepository;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top