2

I am learning how to use PDO and writing a Database class for the first time. I have read for a few hours about this subject and found some useful information here, here and here.

It seems clear that dependency injection is the right strategy, but I'm not sure on the best way to have the Database class access the configuration details for the database (host, dbname, user, pass).

In the first two examples above, this information is included inside the Database class. However, I usually keep all my global configuration variables inside an external file such as config.php.

In the last example, user 'prodigitalson' adds this comment to his getInstance() method:

// get the arguments to the constructor from configuration somewhere

Since the constructor for this Database class includes parameters for the database configuration, I assume that this information is not supposed to be stored in the Database class itself.

So what is the best strategy for accessing the database configuration (which I am currently storing in config.php along with other config variables) from the getInstance() method?

Someone asked a similar question here but none of the answers really addresses the question, imo.

Community
  • 1
  • 1
Chrysippus
  • 109
  • 1
  • 11
  • Are you using a dependency injection container? Because the answer might depend on this. – Matthieu Napoli Nov 08 '14 at 11:21
  • And if you have a `getInstance()` you are not doing dependency injection at all, this is the singleton pattern. – Matthieu Napoli Nov 08 '14 at 11:21
  • @MatthieuNapoli I have read about dependency injection containers but I don't quite understand them yet. You may be right that this is not the correct way to implement dependency injection. Would you please show me a better way to do it that includes an elegant way of handling configuration variables? – Chrysippus Nov 08 '14 at 17:23

3 Answers3

1

As I said in the comments, you are using the singleton pattern which is not dependency injection.

Here is an example using dependency injection:

class Database
{
    public function __construct($host, $user, $password) {
        // ...
    }
}

$db = new Database('foo', 'bar', 'baz');

Then you inject the database object in the classes where you need to use it:

$reportGenerator = new ReportGenerator($db);

Have a look at this tutorial if you are still lost.

Matthieu Napoli
  • 42,736
  • 37
  • 154
  • 239
0

While this is not what I would prefer, because \PDO does not lend itself to being configured after construction, it is the closest I've come to getting DI with \PDO.

<?php namespace b01\Database;

use Exception;
use b01\DatabaseException;
use PDO;
use PDOException;

/**
 * This class is responsible for connecting to a Database.
 * It wraps the PDO object, setting all the values needed in order to connect.
 * Also displays some human readable error messages when connecting fails.
 *
 * @package \b01\Database
 */
class Connection
{
    /**
     * string
     */
    const DSN_MYSQL = 'mysql:dbname=%s;host=%s;port=%s';

    /**
     * Initialized the PDO transport object.
     *
     * It was decided to pass the PDO class in as a string, as pseudo DI,
     * to make the class more testable.
     *
     * @param string $server server.
     * @param string $database Database name.
     * @param string $username Database username for reading.
     * @param string $password Database username password.
     * @param int $port Server port number.
     * @param string $pdoClass Defaults to \PDO, but is passed in this way to
     * make this class more testable.
     * @throws \b01\Exceptions\DatabaseException
     */
    public function __construct(
        $server,
        $database,
        $username,
        $password,
        $port = 3306,
        $pdoClass = '\PDO'
    ) {
        $dsn = sprintf(
            self::DSN_MYSQL,
            $database,
            $server,
            $port
        );

        $options = [
            \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
        ];

        try {
            $this->connection = new $pdoClass($dsn, $username, $password, $options);
        } catch (Exception $error) {
            throw new DatabaseException(
                DatabaseException::CODE_CONNECT,
                [$dsn . "\n\n", $error->getMessage()]
            );
        } catch (PDOException $error) {
            throw new DatabaseException(
                DatabaseException::CODE_CONNECT,
                [$dsn . "\n\n", $error->getMessage()]
            );
        }
    }
b01
  • 3,476
  • 1
  • 21
  • 26
  • The preferred alternative is to initialize the PDO and add it to a DI container and pass the PDO object around that way. The Symfony DI Component may work: http://symfony.com/doc/current/components/dependency_injection.html – b01 Aug 21 '16 at 11:20
-1

In your config.php file, set some constants for your database credentials, depending on the server you are running in:

switch($_SERVER['HTTP_HOST']){
    case 'your-live-domain.com':
        define('MYSQL_HOST', '<input>');
        define('DB_NAME', '<input>');
        define('MYSQL_USER', '<input>');
        define('MYSQL_PW', '<input>');
        break;
    default:
        // perhaps this could be your localhost db credentials
        define('MYSQL_HOST', '<input>');
        define('DB_NAME', '<input>');
        define('MYSQL_USER', '<input>');
        define('MYSQL_PW', '<input>');
}

Then, you can make calls to these constants within your database connection class(es).

visigoth
  • 208
  • 1
  • 8
  • Is there an advantage to using constants, as in your example, versus global variables as per @Adelphia's answer? – Chrysippus Nov 08 '14 at 17:25