Работа с БД, паттерн Фабрика или другой?

Статус
В этой теме нельзя размещать новые ответы.

Bambino

Новичок
Работа с БД, паттерн Фабрика или другой?

Хочу создать классы для работы с различными БД на основе PDO. Есть два варианта реализации, но не могу выбрать. Поможете?

вариант 1:
PHP:
class DbDriver extends PDO {
  function __construct () {
    $args = func_get_args ();
    // здесь вызываем конструктор PDO с нужными параметрами
    switch ( count ( $args ) ) {
    case 2:
      parent::__construct ( $args[0] . ':' . $args[1] );
    case 4:
      parent::__construct ( $args[0] . ':' . $args[1], $args[2], $args[3] );
    }
  }

  function query () {
    // выполняем запросы
  }

  static function CreateMySQLConnection ( $db, $username, $password ) {
    return new self ( 'mysql', $db, $username, $password );
  }

  static function CreateSQLiteConnection ( $db ) {
    return new self ( 'sqlite', $db );
  }
}

$sqlite = DbDriver::CreateSQLiteConnection ( 'db' );
$sqlite->query ( 'SELECT...' );
вариант 2:
PHP:
abstract class DbDriver extends PDO {
  function __construct () {
    $args = func_get_args ();
    // здесь вызываем конструктор PDO с нужными параметрами
    switch ( count ( $args ) ) {
    case 2:
      parent::__construct ( $args[0] . ':' . $args[1] );
    case 4:
      parent::__construct ( $args[0] . ':' . $args[1], $args[2], $args[3] );
    }
  }
}

class DbSQLite extends DbDriver {
  function __construct ( $db ) {
    parent::__construct ( 'sqlite:' . $db );
  }
  static function Create ( $db ) {
    return new self ( $db );
  }
}

class DbMySQL extends DbDriver {
  function __construct ( $db, $username, $password ) {
    parent::__construct ( 'mysql:' . $db, $username, $password );
  }
  static function Create ( $db, $username, $password ) {
    return new self ( $db, $username, $password );
  }
}

$sqlite = DbSQLite::Create ( $db );
$sqlite->query ( 'SELECT...' );
-~{}~ 30.03.10 13:39:

Что правильнее и лучше, создать новый метод для нового драйвера БД или создать новый класс?
 

crocodile2u

http://vbolshov.org.ru
А почему бы не обойтись самим PDO ? Какую полезную нагрузку несут твои классы?
 

Bambino

Новичок
ну скажем так у меня более "продвинутый" метод query:

PHP:
    public function query ( $sql )
    {
        try {
            $statement = parent::query ( $sql );
            if ( !$statement ) {
                $errorInfo = $this->errorInfo ();
                throw new ASM_Exception ( $this->driver . _( ' Query Error ' ), $errorInfo[2] );
            }
        } catch ( PDOException $e ) {
            throw new ASM_Exception ( $this->driver . _( ' Error ' ), $e->getMessage () );
        }
        return $statement;
    }
Ну даже пусть это будет new PDO () вместо new self (). Интересует способ, когда я безболезненно могу добавить новый драйвер для работы с БД, не ломая структуры.
 

kirill538

Новичок
А почему бы не обойтись самим PDO ?
А потому, что хочется иметь собственные хелперы типа
$my_pdo->sevectValue('SELECT COUNT(*) FROM users WHERE is_admin = ?', true); которые скрывают многочисленные bindParam. Или добавить разбор макроподстановок в тексте запроса (см. на dklab.ru).

когда я безболезненно могу добавить новый драйвер для работы с БД
Боюсь, драйвер вам добавить не удасться :) Если имеется в виду некая рефлексия типа используемого драйвера PDO - то лучше делать разбор DSN типа mysql://user@pass/host/base (pgsql://, sqlite:// etc). А инстанс создавать фабрикой, которой DSN передавать параметром. Бонус - фабрика может кешировать инстансы по DSN (чтобы гарантировать один инстанс к одному DSN). Рекомендую посмотреть на DbSimple у Котерова на dklab.ru, там много полезного.
 

Bambino

Новичок
Автор оригинала: dimagolov
это все равно практической пользы не имеет.
А можно привести пример кода того, что Вы имеете в виду? Просто я начитался информации о слабой связанности, об абстракциях... теперь пытаюсь как это применить, но, похоже, не очень успешно.

-~{}~ 30.03.10 17:52:

Автор оригинала: kirill538
А потому, что хочется иметь собственные хелперы типа
Если честно, меня интересует, какой вариант лучше выбрать... из тех, что я привел. Или в данном случае все равно?
Например, в будущем мне понадобится использовать PostgreSQL...
 

crocodile2u

http://vbolshov.org.ru
kirill538
Это, простите, вам нужны "хелперы" и прочее. А я спрашивал товарища Bambino. Или вы его мысли читаете?

А у товарища Bambino всего лишь "более "продвинутый" метод query" - который, правда, непонятно чем продвинутее PDO-шного.

"безболезненно могу добавить новый драйвер для работы с БД, не ломая структуры" - не хватает имеющихся в PDO драйверов? С какой СУБД ты планируешь работать?

Впечатление такое, что код пишется исключительно ради того, чтобы его писать. А в таком разе, можно писать как угодно. Хоть с фабрикой, хоть еще с чем.
 

kirill538

Новичок
crocodile2u, а что вам товарищ Bambino в примере кода показал ? Не хелпер, нет ? Ну он свое исключение кидает. Принцип тот же. Мысли я не читаю, просто обертка PDO имеeт смысл только в случае с хелперами :)
меня интересует, какой вариант лучше выбрать... из тех, что я привел
Второй вариант с типа драйверами имеет смысл тогда, когда наследник ПДО занимается несвойственными ему функциями, реализация которых зависит от тиба базы. Например постраничная выборка с использованием SQL_CALC_FOUND_ROWS. Тогда в безусловном порядке - вариант 2.
Но если делать более верно (постраничными выборками не ПДО нагружать, а класс для пейдж-скроллера) - тогда вариант 1. За исключением странных статических методов. Их заменить на статическую фабрику (параметризуемую DSN). Поскольку классы клиенты о типе используемой базы могут ничего не знать, а DSN jпределяется в конфигах.
 

crocodile2u

http://vbolshov.org.ru
kirill538 Оборачивать PDO или делать класс-наследник - имеет смысл. Точнее, может иметь смысл.

А вот делать то, что Bambino делает - не имеет смысла. У PDO уже есть хороший, единый интерфейс конструктора на все поддерживаемые СУБД. А Bambino хочет написать свои конструкторы, на каждую СУБД свои, да еще и менее гибкие, чем конструктор PDO.

Bambino, если ты действительно жаждешь написать что-то вроде этого, советую посмотреть на архитектуру Zend_Db из Zend Framework. Там как раз что-то похожее на твои потуги, только сделано грамотно. В классах Zend_Db_Adapter_* (например, Zend_Db_Adapter_Mysql) - реализована исключительно специфика работы с конкретным диалектом SQL и конкретной СУБД.
 

Bambino

Новичок
Автор оригинала: crocodile2u
"безболезненно могу добавить новый драйвер для работы с БД, не ломая структуры" - не хватает имеющихся в PDO драйверов? С какой СУБД ты планируешь работать?

Впечатление такое, что код пишется исключительно ради того, чтобы его писать. А в таком разе, можно писать как угодно. Хоть с фабрикой, хоть еще с чем.
crocodile2u, ну я же за помощью пришел, ты уж не сильно нажимай :)
Изначально предполагается использовать SQLite. PDO как раз хватает. Может быть я просто бью из пушки по воробьям?.. Как я уже писал, прочел много информации об уровнях абстракции, паттернах. Хочется применить... вероятнее всего не к месту?

-~{}~ 30.03.10 18:27:

А у товарища Bambino всего лишь "более "продвинутый" метод query" - который, правда, непонятно чем продвинутее PDO-шного.
"Продвинутость" в том, что создаются мои объекты-исключения и ошибки, отформатированны моим классом Exception.
Плюс есть еще метод execute:

PHP:
    public function execute ( $sql )
    {
        try {
            $statement = $this->prepare ( $sql );
            if ( !$statement ) {
                $errorInfo = $this->errorInfo ();
                throw new ASM_Exception ( $this->driver . _( ' Prepare Error ' ), $errorInfo[2] );
            }
            if ( !$statement->execute () ) {
                $errorInfo = $statement->errorInfo ();
                throw new ASM_Exception ( $this->driver . _( ' Execute Error ' ), $errorInfo[2] );
            }
        } catch ( PDOException $e ) {
            throw new ASM_Exception ( $this->driver . _( ' Error ' ), $e->getMessage () );
        }
        return $statement;
    }
Вот почему наследник. Я хочу просто вызвать $db->execute (), который уже содержит проверку ошибок, а не заботиться о проверке ошибок после каждого вызова PDO::execute.
 

kirill538

Новичок
У PDO уже есть хороший, единый интерфейс конструктора
Не совсем. Он требует нескольких параметров, которые зависимы от типа базы. Передавать (и прописывать в конфиге) удобнее 1 строку.
 

crocodile2u

http://vbolshov.org.ru
Bambino

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

http://ru.php.net/manual/en/pdo.setattribute.php

На этом поле PDO твой класс явно делает, потому что PDO может по-разному: хошь, ошибку выдаст, хошь эксепшн... Ну а форматирование твое тоже в общем бесполезное.

Послушай совета: займись другими делами, наверняка есть что-то важное, нужное, что нужно немедленно запрограммировать.
 

Fortop

Новичок
Bambino
Может быть я просто бью из пушки по воробьям?
Вообще-то да.
Поясню в чем фокус.

Абсолютно платформонезависимым синтаксис SQL увы не является. Т.е. можно рассчитывать на некое соблюдение ANSI SQL-92 (кто-то даже хвастался вроде как 100%), но многие вещи в MySQL, MSSQL, Oracle, PgSQL решаются специфичными для БД средствами.

Поэтому смысла в смене драйвера крайне мало.
В сложных приложениях переписывать придется весьма много кода (хотя и далеко не весь - так что выгода есть).
А в простых приложениях подобная универсальность ничего не дает, кроме лишних трудозатрат.

Тем не менее crocodile2u. правильно написал о том, куда смотреть.

P.S. Если уж углубляться в паттерны, то по такой же схеме(адаптеров/мапперов) стоит организовать и собственные модели (если уж хотите полной гибкости).

Т.е. , например, будут
Model_MySQL_User
Model_Oracle_User
и т.д.

И работать не с прямыми запросами, а с
PHP:
$model->getUser($id);
 

crocodile2u

http://vbolshov.org.ru
kirill538:

PDO::__construct ( string $dsn [, string $username [, string $password [, array $driver_options ]]] )

$dsn, $username, $password, $driver_options прописываешь в конфиге - вуаля.
 

kirill538

Новичок
Хочется применить... вероятнее всего не к месту?
Чтобы научиться плавать - надо плавать :) И Фабрика вполне к месту. Просто вы пока смысл фабрики не догоняете. Она должна снижать связанность. Если под каждый тип базы у вас свой статический метод - вы создаете зависимость, а не снижаете (относительно PDO). Классы, использующие вашу обертку, должны знать, какая база используется, чтобы этот статический метод вызвать. Если фабрика сделана как $pdo = PDO_extends::factory($dsn); - класс-клиент ничего не знает о типе инстанса (и ему глубоко это безразлично). В этом случае конкретную реализацию (специальные классы для разных баз или просто инстанс PDO там создается) можно изменять независимо (при условии, что интерфейс сохраняется, конечно).

-~{}~ 30.03.10 18:51:

crocodile2u:
Я в курсе, как выглядит конструктор PDO.
Лично мне удобнее в конфиге прописывать не 4 параметра, один из которых массив, а строку mysql://user@pass/host/db?param-param. По этой строке еще и кеш инстансов отлично организуется.
 

Духовность™

Продвинутый новичок
Поэтому смысла в смене драйвера крайне мало.
В сложных приложениях переписывать придется весьма много кода (хотя и далеко не весь - так что выгода есть).
А в простых приложениях подобная универсальность ничего не дает, кроме лишних трудозатрат.
+1

Я никогда не понимал, зачем пишут кучу разных драйверов под разные якобы БД. Кто-нибудь реально менял БД на реальном проекте? Или это всё из области вакуумной теории, потипу заботы о мифическом верстальщике?
 

Fortop

Новичок
Кто-нибудь реально менял БД на реальном проекте?
Для промежуточных решений a-la CMS, CMF, Framework - в этом есть смысл. Поскольку предоставляется выбор для реализации конечного решения.

Для конечного решения, конечно же, смысла почти нет.
 

kirill538

Новичок
зачем пишут кучу разных драйверов под разные якобы БД
Пока PDO не было, видимо, была потребность. Как минимум чтобы конструкторы стандартизировать :)
Имхо, если есть нужда в драйверах базы - значит, что-то в архитектуре сильно не так. Типа запах кода :)
 

akd

dive now, work later
Команда форума
triumvirat, я менял. 2 раза. за 10 лет.

в обоих случаях 99% времени заняло переписывание запросов и процедур а не собсна вызовов :)
 
Статус
В этой теме нельзя размещать новые ответы.
Сверху