Во-первых, это никакой не DatabaseManager , а CRUDManager. Работа с БД далеко не ограничивается этими 4 примитивными функциями.
Отсюда мы делаем логичный вывод, что
соединение с БД никаким местом не должно создаваться в конструкторе менеджера крудов. А должно точно так же передаваться в него в качестве зависимости. Это может быть либо ванильная ПДО, либо инстанс
реального MySQLDatabase (но поскольку мы пока не знаем, как он должен выглядеть, то лучше остановиться на PDO).
Сам по себе DatabaseManager выглядит избыточным. Непонятно, зачем он нужен, если любой потребитель DatabaseManager-а может просто написать
public function __construct(CRUDInterface $crud) {
}
и получить
тот самый полиморфизм, которого мы изначально и добивались. Передадим другую реализацию - будет другая, но с тем же публичным контрактом, то есть всё будет работать.
В-четвёртых, хоть это и не относится напрямую к теме SOLID, но для меня является очень важным: собственно, реализация методов CRUD-а. Что в них передаётся? Откуда берутся названия таблиц, полей? Передаются в параметрах методов? Это прямая дорога к SQL инъекции, не говоря уже о нарушении инкапсуляции. Поэтому, отвечая на вопрос "Как вы реализуете работу с базой данных", лично я всё больше в последнее время от развесистых ORM-ов склоняюсь к простым TableGateway-ам. Да, кода писать больше, но он строже и понятнее. И не встаёт колом в нестандартных ситуациях. Тем более что приведённый пример кода как раз очень и похож на этот паттерн. То есть
abstract class MysqlTableGateway implements CrudInterface
{
protected $db;
protected $table;
protected $fields;
protected $primary = 'id';
public function __construct(\PDO $db)
{
$this->db = $db;
}
public function read($id): ?array
{
$stmt = $this->db->prepare("SELECT * FROM `$this->table` WHERE `$this->primary`=?");
$stmt->execute([$id]);
return $stmt->fetch();
}
// ну и так далее
}
И дальше уже классы по работе с отдельными табличками наследовать от него,
final class UserGateway extends MysqlTableGateway {
protected $table = 'users';
protected $fields = ['email', 'password','phone'];
}
Соответственно, если мы захотим перейти с мускуля на какой-нибудь редис с джейсоном внутре, то надо будет создать новый абстрактный класс с тем же интерфейсом, и от него отнаследовать реализации. Соответственно, в интерфейсе надо нормально прописать входные и выходные параметры:
interface CRUDInterface {
public function create(array $data):int;
public function read(int $id):?array;
public function update(array $data);
public function delete(int $id);
}
Другое дело, что в реальности такой шалтай-болтай будет сделать довольно сложно, поскольку классы для работы с отдельными таблицами будут расширяться запросами, специфичными для данной таблицы - то есть все их придется дописывать во все драйверы. То есть в реальности с D будут проблемы. Но чисто с теоретической точки зрения примерно вот так оно будет выглядеть.