0

How do I load a database container using PHP DI? This is one of the variations I have tried up until now.

Settings.php

<?php 
use MyApp\Core\Database;
use MyApp\Models\SystemUser;

return [
    'Database'      => new Database(), 
    'SystemUser'    => new SystemUser()
];

init.php

$containerBuilder   = new \DI\ContainerBuilder(); 
$containerBuilder->addDefinitions('Settings.php');
$container          = $containerBuilder->build();

SystemUserDetails.php

<?php 
namespace MyApp\Models\SystemUser;

use MyApp\Core\Database;
use MyApp\Core\Config;
use MyApp\Helpers\Session;


/**
 *
 *  System User Details Class
 *
 */
class SystemUserDetails 
{

/*=================================
=            Variables            =
=================================*/

    private $db;


/*===============================
=            Methods            =
================================*/

    /**
     *
     *  Construct
     *
     */
    public function __construct(Database $db)
    {
        # Get database instance
        // $this->db           = Database::getInstance();
        $this->db           = $db;
    }


    /**

Too few arguments to function MyApp\Models\SystemUser\SystemUserDetails::__construct(), 0 passed in /www/myapp/models/SystemUser.php on line 54 and exactly 1 expected File: /www/myapp/models/SystemUser/SystemUserDetails.php

Shouldn't the database get loaded automatically?

Trace:

  1. Currrently, My main index.php file extends init.php which is the file where it create the container (pasted code part in the post).

  2. Then I call the App class, which fetches the URL(mysite.com/login/user_login) and instantiate a new controller class and run the mentioned method, in this case, it's the first page - MyApp/Contollers/Login.php.

    1. The user_login method fetches the credentials, validate them, and if they are valid, uses the SystemUser object to login.

SystemUser class:

namespace MyApp\Models;


class SystemUser
{

    public $id;

    # @obj SystemUser profile information (fullname, email, last_login, profile picture, etc')
    protected $systemUserDetatils;


    public function __construct($systemUserId = NULL)
    {
        # Create systemUserDedatils obj
        $this->systemUserDetatils   = new \MyApp\Models\SystemUser\SystemUserDetails();

        # If system_user passed
        if ( $systemUserId ) {

            # Set system user ID
            $this->id                   = $systemUserId;

            # Get SysUser data
            $this->systemUserDetatils->get($this);

        } else {

            # Check for sysUser id in the session:
            $systemUserId                   = $this->systemUserDetatils->getUserFromSession();

            # Get user data from session 
            if ( $systemUserId ) {

                # Set system user ID
                $this->id                   = $systemUserId;

                # Get SysUser data
                $this->systemUserDetatils->get($this);
            }
        }
    }
}
yivi
  • 42,438
  • 18
  • 116
  • 138
Imnotapotato
  • 5,308
  • 13
  • 80
  • 147
  • It looks like class `SystemUser` extends `SystemUserDetails` which require a DB instance as an argument as per constructor, try passing DB instance as an argument to the `SystemUser` class and see if it works – Shahin Jan 13 '19 at 11:21
  • @Shahin Why do you believe `SystemUser` extends `SystemUserDetails`? Where are you getting that? – yivi Jan 13 '19 at 11:24
  • @Shahin is right, but isn't that suppose to work directly by injecting the DB class to any class I want? Where might be something I do not understand about dependency injections and would like an explanation. – Imnotapotato Jan 13 '19 at 11:26
  • @yivi what is it that I am not showing? What do you want to see, I have no problem to add to my post.. – Imnotapotato Jan 13 '19 at 11:26
  • Do I need to run `$userManager = $container->get('SystemUserDetails');` to get – Imnotapotato Jan 13 '19 at 11:44
  • @Rick I do not understand the question. To get instances from the container, you need to call `get()`, of course. That's what the container is for. If you try to get these objects by calling `new` instead of `$container::get()`, you are not using DI. – yivi Jan 13 '19 at 12:15

1 Answers1

5

PHP-DI is working correctly.

In your SystemUser class you are doing:

$this->systemUserDetatils   = new \MyApp\Models\SystemUser\SystemUserDetails();

The constructor for SystemUserDetails requires a Database object, which you are not passing.

By calling new directly, you are not using PHP-DI. By doing this you are hiding the dependency, which is exactly what you are supposedly trying to avoid if you want to use a dependency injection system.

If SystemUser depends ("needs") SystemUserDetails, the dependency should be explicit (e.g. declared in its constructor).

Furthermore: You do not need a definitions file for a system like this. And the definitions file you show in your question doesn't follow the best practices recommended by PHP-DI.

Your design is far from perfect, and I'm not sure of your end-goals, but if you did something like this, it could work:

<?php
// src/Database.php

class Database {
    public function getDb() : string {
        return 'veryDb';
    }
}
<?php
// src/SystemUserDetails.php

class SystemUserDetails {

    protected $db;

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

    public function getDetails() {
       return "Got details using " . $this->db->getDb() . '.';
    }
}
<?php
// src/SystemUser.php
class SystemUser {

    protected $details;

    public function __construct(SystemUserDetails $details, $userId=null) {

        $this->details = $details;
    }

    public function getUser() {
       return "Found User. " .$this->details->getDetails();
    }
}
<?php
//init.php
require_once('vendor/autoload.php');

// build the container. notice I do not use a definition file.
$containerBuilder   = new \DI\ContainerBuilder();
$container          = $containerBuilder->build();

// get SystemUser instance from the container.
$userInstance = $container->get('SystemUser');

echo $userInstance->getUser(), "\n";

Which results in:

Found User. Got details using veryDb.
yivi
  • 42,438
  • 18
  • 116
  • 138
  • No, your confused. `SystemUser` class uses `SystemUserDetails` class. `SystemUser` is the main SystemUser class. – Imnotapotato Jan 13 '19 at 11:45
  • I'm sorry but I do not believe I'm confused. I'm looking at your code. `SystemUser` uses `SystemUserDetails`, but **that dependency is not declared anywhere**. You are trying to instantiate `SystemUserDetails` directly, without injecting the proper dependencies. – yivi Jan 13 '19 at 11:49
  • @Rick Try my code and you'll see that it works. The problem is that you were bypassing the DI layer by calling `new` directly. – yivi Jan 13 '19 at 12:02
  • 1
    Also having `Settings.php` contain `new Database()` is wrong, that means you create the `Database` instance directly in the config instead of letting PHP-DI create it and know about it. Read again http://php-di.org/doc/php-definitions.html#values – Matthieu Napoli Jan 13 '19 at 12:28