@contra1

Как грамотно реализовать одно соединение с базой данных на все приложение, с помощью Dependency Injection Container?

Здравствуйте, изучаю паттерны проектирования. В данном случае Dependency Injection Container. Нужен совет от опытных коллег.

Есть базовая модель
abstract class Model
{
    protected $connection;

    public function __construct()
    {
        $this->connection = (new PDOMysqlConnection)->getConnection();
    }
}


Модель
namespace models;

class User extends Model
{
    public function getAll(): array
    {
        return $this->connection->executeQuery('SELECT * FROM users')->fetchAllAssociative();
    }
}


Контроллер
namespace controllers;

use models\User as UserModel;

class User extends Controller
{
    private $model;

    public function index()
    {
        $this->model = new UserModel;
    }
}


При каждом создании нового объекта модели, будет создаваться новое соединение с базой данных. Обычно соединение делал с помощью Singleton. Подскажите пожалуйста, есть ли возможность решить это с помощью DIC?

Хранить зависимости в статическом свойстве-массиве?

Код контейнера
class Container implements ContainerInterface
{
    private array $instances = [];

    public function set(string $id, callable $resolver): void
    {
        $this->instances[$id] = $resolver;
    }

    public function has(string $id): bool
    {
        return isset($this->instances[$id]);
    }

    public function get(string $id)
    {
        if (!$this->has($id)) {
            throw new NotFoundException("Dependency '$id' not found in the container.");
        }

        return $this->instances[$id]($this);
    }

    public function bind(string $id)
    {
        return $this->buildDependencies($id);
    }

    private function buildDependencies(string $className)
    {
        $reflector = new \ReflectionClass($className);

        if (empty($constructor = $reflector->getConstructor())
            || empty($parameters = $constructor->getParameters())) {
            return new $className;
        }

        $dependencies = [];

        foreach ($parameters as $parameter) {
            $dependency = $parameter->getType()?->getName();
            $dependencies[] = $this->get($dependency);
        }

        return $reflector->newInstanceArgs($dependencies);
    }
}
  • Вопрос задан
  • 65 просмотров
Пригласить эксперта
Ответы на вопрос 1
Henryh
@Henryh
Веб-программист
Без DI контейнера ты создаёшь какую-то конкретную реализацию подключения к БД и каждый раз к ней обращаешься, без возможности изменить или создать новую. То есть экземпляр объекта класса PDO создаёшь внутри.
При Dependency Injection, в сухом остатке, тебе не нужно привязываться к реализации, достаточно создать объект класса pdo снаружи и передать его.
DI-контейнер позволяет автоматизировать создание объектов и внедрение зависимостей. Это означает, что нам нужно иметь класс, который хранит все варианты созданных подключений к БД.
То есть, в любой момент мы можем положить в контейнер нужное подключение, а потом просто обращаться к уже готовому подключению. Но в отличие от Singleton, таких подключений может быть несколько и в коде можно выбирать разные, там где это нужно.

В твоём случае это будет что-то типа такого:

$container = new Container();

$container->set('primary_db', function () {
    return new PDO(
        'mysql:host=localhost;dbname= primary_db',
        'db_user',
        'password'
    );
});

$db = $container->get('primary_db');


А в той экземпляр Model нужно передавать в конструктор уже существующее подключение. Например, так:

class User {
    private $db;

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

$db = $container->get('primary_db');

$user = new User($db);

или через биндинг.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы