Pregunta

Hello guys I was creating a package and i was trying to implement a dependency injection on my class without success. I followed all the instruction for do it work. I'm getting crazy on it. When i try to call

Player::team_players(2);

it throw me an error:

Argument 1 passed to Team\Player\Player::__construct() must be an instance of Team\Player\StatusPlayerInterface, none given, called in C:\wamp\www\ultima\workbench\team\player\src\Team\Player\PlayerServiceProvider.php on line 35 and defined

I created my class Player.php

<?php namespace Team\Player;

use Team\Player\Models\User;
use Team\Player\Models\Team;
use Team\Player\Models\Fighter;
use Team\Player\StatusPlayerInterface;
use DB;

class Player {

  protected $player;

  function __construct(StatusPlayerInterface $player) {
    $this->player = $player;
  } 

  public function team_players($team_id) {
    return $player->team($team_id);
  }
}

StatusPlayerInterface.php

<?php namespace Team\Player;

interface StatusPlayerInterface {

    public function team($team_id); // active - retired - injured by team id

}

Active.php

<?php namespace Team\Player;

use Team\Player\Models\User;
use Team\Player\Models\Team;
use Team\Player\Models\Fighter;

/**
* 
*/
class Active implements StatusPlayerInterface
{

    protected $user;
    protected $team;
    protected $fighter;

    function __construct(User $user,Team $team,Fighter $fighter)
    {
        $this->user = $user;
        $this->team = $team;
        $this->fighter = $fighter;
    }

    public function team($team_id) 
    {
        return $fighters = $this->fighter->with('user')->where('team_id',$team_id)->active()->Confirmed()->get();
    }

}

PlayerServiceProvider.php

public function register()
    {

         $this->app->bind('Team\Player\StatusPlayerInterface','Team\Player\Player'); // bind the interface
         $this->app['player'] = $this->app->share(function($app)
          {
            return new Player; // line 35
          });

         $this->app->booting(function()
        {
          $loader = \Illuminate\Foundation\AliasLoader::getInstance();
          $loader->alias('Player', 'Team\Player\Facades\Player');
        });
    }

EDIT:

What i'm trying to do is to follow a principle that Jeffrey Way suggested to follow. It say Entities should be open for extension but close for modification.

I 2 others classes that implements StatusPlayerInterface and of course change only the query on the function team()

  • Active // on the example
  • Retired
  • Injured

Then i have the main class Player and with the method team_players it should automatically call the function team of the instance called. this method is used for don't do

class Player {

 ....

  function team_player($team_id,$status) {
      if (is_a($status) == "Active") {
          $fighters = $this->fighter->with('user')->where('team_id',$team_id)->active()->Confirmed()->get();
      } elseif(is_a($status) == "Retired") {
           $fighters = $this->fighter->with('user')->where('team_id',$team_id)->retired()->Confirmed()->get();
      } 
   // ecc
  }

}

but i can throw pass the interface to the constructor and return just the function team of the interface, because the interface is like a contract so it can trust that exist that function. But the problem is that i cannot find a way for pass that interface on the constructor.

¿Fue útil?

Solución

Your constructor here is waiting for a $player:

class Player {

  ...

  function __construct(StatusPlayerInterface $player) {
    $this->player = $player;
  } 

}

So your ServiceProvider should be passing one to it in line 35:

return new Player; // line 35

I can see you tried to use IoC to do that for you:

$this->app->bind('Team\Player\StatusPlayerInterface','Team\Player\Player');

But you have two problems,

1) Team\Player\Player doesn't implements Team\Player\StatusPlayerInterface and it must. But Active class does implements, shouldn't you be using it?

2) I'm not sure that the IoC will be effective at this point of the code, would have to ask Taylor Otwell himself.

But this is something you can do:

public function register()
{
     $this->app['player'] = $this->app->share(function($app)
      {
        return new Player(new Team\Player\Player);

        //// OR

        return new Player(new Team\Player\Active);
      });

     $this->app->booting(function()
    {
      $loader = \Illuminate\Foundation\AliasLoader::getInstance();
      $loader->alias('Player', 'Team\Player\Facades\Player');
    });
}

Your Player class would have to implement the StatusPlayerInterface:

class Player implements StatusPlayerInterface {

}

But I'm not sure if it is supposed to, so, look, those are suggestions, I not aware of what you're doing exactly with your package, so I'm just pointing what I'm seeing is wrong on it, okay?

EDIT

For example, you are constructing your Player class already passing a Player Status, right? But how would you swap different statuses if the constructor, the way you're building, will only receive the one you are passing via your ServiceProvider? In this case the IoC container will not help you, because you should be able to instantiate that same class with 3 differents statuses: active, retired and injured.

You can create a setPlayerStatus() method, to change it in the during a request, of course, but as I hope you can see, before building the whole package, you have first to think a lot about your architecture and then write your code based on it, always remembering that the IoC container has its boundaries and there are some resolutions it will not solve, just because they are problems on your architecture.

EDIT 2

You don't really pass an interface to a constructor. You pass a concrete object of a concrete class that implemented that interface.

Look at the error again, it says 3 important things

Argument 1 passed to Team\Player\Player::__construct() 

must be an instance of Team\Player\StatusPlayerInterface, 

none given

So you need to instantiate Player

return new Player;

with something:

return new Player(new Active);

That's all you need to make it work, really. The error will go away. But you need this package to work too and I'm afraid this is not enough.

As I said before, if the IoC could work here, how could you make it send the correct implementation of Active, Retired or Injured, at the time you need? I see two options:

1) Call

$this->app->bind('Team\Player\StatusPlayerInterface','Team\Player\Active');
$this->app->bind('Team\Player\StatusPlayerInterface','Team\Player\Retired');
$this->app->bind('Team\Player\StatusPlayerInterface','Team\Player\Injured');

every time you need one of them, which is bad.

2) Change the architecture to keep you in the SOLID track, in the case the Open Closed Principle.

Take a read on the Factory Design Pattern, it might help you out with this achitecture. This is an answer about it: What is a Factory Design Pattern in PHP?.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top