sp1se
@sp1se
IT manager

Как правильно реализовать ООП класс базы данных с PDO?

Доброе время суток.
Сильно не пинайте, но никак не получается правильно написать класс базы данных с PDO, так чтоб мне самому он понравился.
Предупреждаю, я не программист, только учусь.
Задача:
Написать класс базы данных для безопасного соединения и работы с ним.
Есть 3 таблицы, users, product, order(sample)
Я хочу написать один класс DB в котором будет происходить соединение и где будут методы для работы со ВСЕМИ таблицами, так например чтоб методом select я мог бы запросить как пользователей в таблице users, так и product..
На сегодняшний день, код мой выглядит вот так ужасно: DB_Class.php
class DB_Class
{
    protected $db;
    function __construct(PDO $db = null){
        $dsn ='mysql:host='.DB_HOST.';dbname='.DB_NAME;
        $this->db = $db;

    try{
        if($this->db === null)
        {
            $this->db = new PDO($dsn, DB_LOGIN, DB_PASS);
        }
      } catch(PDOException $e){
        echo $e->getMessage();
      }
    }
/// Всякие методы и деструктор.
function __destruct(){

      $this->db = null;
    }


В классе users вот такая вот порнография:
public function viewUser($where=0,$param = null){
        try{
         $sql = "";
         $select = "SELECT *
                        FROM users ";

            if(!$where=0)
                    {
                        $wh ="WHERE id =$param";
                        $sql = $select.$wh;
                    }else
            {
                $sql .= $select;
            }
          $query = $this->db->prepare($sql);
          $query->bindParam(':id',$where);
           // var_dump($sql);
           // var_dump($where);
          $query->execute();
          return  $query->fetchAll();
        } catch(PDOException $e){
            $e->getMessage();
        }
    }


Мне не нравится что сам запрос у меня не в классе ДБ, я хотел бы чтоб все методы подключения были в нем, а классы юзер, продукты только дергали нужные методы и передавали необходимые параметры..
Буду признателен за правильное наставление..
  • Вопрос задан
  • 15098 просмотров
Пригласить эксперта
Ответы на вопрос 2
FanatPHP
@FanatPHP
Чебуратор тега РНР
Я уже отвечал как-то на подобный вопрос. И не один раз. И не два.
Поскольку мозги всех пользователей пхп ходят по одним и тем же рельсам, не сворачивая. Впрочем, не всех. 85% всю жизнь продолжают писать mysl_query, которую выучили из видеоурока, и не видят в этом проблем. И только у самых талантливых 15-и процентов в какой-то момент возникает мысль ВСЁ АВТОМАТИЗИРОВАТЬ. Это, на самом деле, хороший знак. Такое желание как раз и отличает потенциального программиста от клепальщика гуано-кода.

Но всё портит недостаток знаний в SQL. Искренне полагая SQL не более чем key-value хранилищем, они всерьез уверены в том, что функция select() с двумя аргументами - это все что им надо.

Настоятельно рекомендую прочесть аргументацию по ссылке выше.

После этого понять, что существует ТРИ класса классов для работы с БД:

1. DB-хелпер. Класс, берущий на себя всю грязную работу по исполнению запросов. В случае с ПДО не сильно-то и нужен. Позволяет исполнять любые запросы. НИКАКИХ функций типа select(), ограничивающих функциональность, в нем быть не должно ни в коем случае.
2. Query builder. Функция типа select() может быть только в квери билдере, который маскирует SQL в функции РНР. Заведомо ущербен по сравнению с первым, но зато позволяет использовать запросы более сложные, чем ORM.
3. ORM. То, что начинающему пользователю на самом деле нужно, но он об этом просто не догадывается. Как раз та самая волшебная палочка, которая делает примитивное доставание данных из базы по первичному ключу столь маняще единообразным.

Cамое главное, что надо понять:
Все вышеперечисленное - это РАЗНЫЕ типы классов, не имеющие между собой ничего общего.
И не пытаться под видом первого городить нежизнеспособное второе, имея в виду третье. Надо очень четко понимать, что сначала делаем первое, а потом, на его основе - либо второе, либо третье. Но не все вместе.

А можно не пытаться изобретать велосипед, а использовать готовое. Например - популярный фреймворк. Тогда желаемая функция будет выглядеть вот так:

public function viewUser($id)
{
    return User::model()->findByPk($id);
}

Это в самом предпочтительном случае - при использовании ORM.
На квери билдере это будет что-то вроде
public function viewUser($id)
{
    return DB::select('*')->from('users')->where("id", '=', $id);
}


При этом можно использовать и чистый SQL. Запрос прямо в классе юзера - это не так уж и страшно. Тем более, что есть такие запросы, которые по другому просто не выполнишь. Другое дело, что всю работу по исполнению запроса должен брать на себя хелпер. Пример можно посмотреть по ссылке выше - там хоть и SQL , но того ужаса, который здесь, нету:
public function viewUser($id)
{
    $sql = 'SELECT * FROM users WHERE id=?';
    return DB::prepare($sql)->execute([$id])->fetch();
}
Дальнейшую работы над классом можно производить только после того как ты определишься, какой именно класс ты хочешь написать.
Ответ написан
Fesor
@Fesor
Full-stack developer (Symfony, Angular)
Вам нужен класс Connection, в котором есть методы connect и exec (для запросов). И не делайте подключения прямо в конструкторе, лучше в exec сделать проверку и подключать при необходимости (ленивое подключение). Зачем нужно оборачивать соединение? Что бы можно было мокать в тестах.

Так же вам нужен класс Query умеющий строить запросы и который будет принимать метод exec класса Connection. Так же можно его мокать в тестах.

Так же можно замутить QueryBuilder что бы совсем круто было.... Вот его мокать в тестах не стоит, ибо надоест быстро.

Проектируйте интерфейсы классов сначала. Клиенский код (тот который будет использовать вашу библиотеку) не должен знать о реализации интерфейса ничего. Он так же ничего не должен знать о PDO. Так же было бы неплохо перед тем как выполнять запросы брать мета данные таблиц, с которыми вам нужно работать (их можно кешировать) и делать prepared statements внутри Query.

Ну и да, можете ориентироваться на популярные реализации DBAL. Например doctrine/dbal
Ответ написан
Ваш ответ на вопрос

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

Похожие вопросы