1

I am novice in OOP programming in php and trying to understand and implement the dependency injection feature in my MVC project. In the following I am explaining a super simple example of the feature where I am struggling applying the dependency injection. The actual application is lot more complex than this, however, this should be enough to demonstrate the problem I am having.

I have created a Model called “user” that is responsible for managing a single user. Data handled by the class (data about a user) is also saved in the database table. The “user” class has method to load from and save/update the data to the database table. The user class can be initiated with data loaded from the database (by using user id) or load from the array supplied to the constructor.

The project deals with multiple users at a time. So, I have created a container class called “users”. This class has an array of “user” objects. However, this class also have method to load data for multiple user objects from the database (based on criteria such as all paid users), then create the object array with the data. The number of object is created is depends on the number of users returned from the database.

The following is a sample code for the classes

class user
{
    private $data;  
    function __construct ($arData=””)
    {       
         $this->dbTable        ="user";
         if(!is_array($ar))
         {
              if($ar!="")
              {
                    $ar = $this->getDataFromDB($ar);
              }
              else
              {
                  $ar = array();
              }
        }
        $this->data = $ar;
    }

    function getDataFromDB($id_user){  … data base implementation … }
    ....
    Other methods
    ....
}

class users // the container class
{
    private $objUsers;
    function __ construct(){
        $this->objUsers = array();
    }

    function loadUsers($type){
        $userDataArray = $this->getUsersFromDatabase($type);

        foreach($useDataArray as $userData){
            $this->objUsers[] = new user($userData);
        }
    }
    function getUsersFromDatabase($userType) { …… database …… }
    …… other methods …..
}

My concern is the container class (container may not be the right word to say). I want to know the best practice to create this type of container class and what is the recommend for this. In addition, this is clearly evident that this container class is tightly coupled with “user” class and cannot be tested separately. How can I implement the dependency injection for a class like this?

5
  • You would make users more generic and break any tight coupling you have with the Database in the Users class. There are a few ways to do this but most of them are kind of ugly, It really depends if you plan to use this container for anything else. I have the reverse where I never call user directly but instead create users and load them all through the users class, which returns either a user object, an array of user objects or some other data. Commented Oct 29, 2017 at 2:33
  • So a quick example for mine would be instead of going new User($data) I do $Users->createUser($data) And the user has no connection to the DB it's just a set of properties defining/wrapping the DB table data. In other words, User is responsible for just the Data, and Users is responsible for getting and creating a User or a set of Users. If that makes sense. Commented Oct 29, 2017 at 2:34
  • That said, I don't think this is a good use case for Dependency injection, The main reason is that you wont typically have many types of Users, you could have permission levels etc. but a User is always a User. There is not a huge need to have that level of separation, some PPL might disagree, but I don't like coding things for the sake of coding them. So basically doing dependency injection, for the sake of doing it, when there really is no need. Sort of if the shoe fits, or not. Commented Oct 29, 2017 at 2:57
  • Thanks @ArtisticPhoenix . I like your approach. Commented Oct 29, 2017 at 3:07
  • Sure, always try to keep in mind what a given class should be concerned with. The more you can limit that the more maintainable and simpler your code will be. Commented Oct 29, 2017 at 3:47

2 Answers 2

3

As I said, I don't think this is a good fit for dependency injection. And I wouldn't set it up that way just for the sake of saying it uses dependency injection.

The main reason it's not a good fit, is that a User is always a User. So you always have a concrete contract between the wrapper, Users, and the User. You can count on User having certain methods. And you don't have some weird 3rd class that your adding into these collections, it's just a collection of a known and well defined object.

That said, I would go with a more factory style wrapper, Where the User is the simpler of the 2 classes. ( note, I didn't test any of this, so just look at it like psudo code )

class users {

    public function createUser( array $data = []){

        if( $data['id'] ){
            $User = $this->getUser( $data['id'] );
            if( $User )
                return $User;  
        }
        //depending what you want you could search on other things
        //like the email, (unique)  and return the user.
        //you could make this as complex, or as simple as you want
        //it could simply create a new user, or check for an existing one first.

        return new User($data); //dependency
    }

    public function getUser( $id ){
        $stmt = $this->DB->prepare( "SELECT * FROM users WHERE id=:id" );
        $stmt->FetchMode(PDO::FETCH_CLASS, 'User');
        return $stmt->fetch(); //fetch right into the class
    }

    public function saveUser( User $User ){
        //I just added this to show how you can type hint the class
        // so that it only accepts Objects of the type User
    }

}

class user{

    protected $id;
    protected $username;
    protected $email;


    public function __construct(array $data = []){
        foreach( $data as $field=>$value){
            //this could be done better, with error checking etc.
            //but I just wanted to show how you can use the setters
            //dynamically when constructing with an array
            //this is useful because we are not hard coding the schema of User in any files
            //-note- PHP method calls are case insensitive.
            $this->set{$field} = $value;
        }
    }

    public function setId( $id ){ $this->id = $id; }

    public function getId(){ return $this->id; }

    public function setUsername( $username ){ $this->username = $username; }

    public function getUsername(){ $this->username; }

    public function setEmail( $email ){ $this->email = $email; }

    public function getEmail(){ return $this->email; }  
}

Then you can worry about dependency injection for things like the Database. This could be represented by having the users constructor accept a PDO or Database object. Like this

class Users{
     protected $DB;

     public function __construct( $DB ){
         $this->DB = $DB;
     }
}

The Users class doesn't care about the DB credentials, or even the particular DB driver your using. To some extent it does have some coupling with the driver based on the SQL syntax, which may be specific to a particular database. If we wanted to make this a "truer" form of dependency injection we should use an ORM like Doctrine, or some kind of Query builder ( instead of PDO itself ). Then we would have another layer of abstraction between our code and the database.

Sign up to request clarification or add additional context in comments.

1 Comment

Aside form not extending this is basically as i had in mind and thinking it over now that im fully awake, there really was no reason to link users and user by extending.
-2

If you need user to have access to users and they cant be separated extend the class.

class users {

}

class user extends users {

}

Child user can then access the parent users properties.

1 Comment

I would do it the opposite, it makes more sense, to have Users be a factory class and User be the widget produced by the factory. IMO>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.