Разделение DBAL на два класса, Connection и Statement

Фанат

oncle terrible
Команда форума
Спустя пару лет до меня, наконец, дошло, что единственный класс - это неправильно.
поскольку он обязан быть stateless, по очевидным причинам.
И в то же время, нужна некоторая сущность, которая может сохранять свое состояние, и отвечать за один конкретный запрос.
К примеру, Кузьма меня давно уже пинал за то, что не все сообщения об ошибке выдают исходник запроса. А они просто не могут: запрос выдается только в тех местах, где он явно присутствует. Чтобы выводить в других - надо сохранять в переменную класса. А в этом случае начнется ад с вложенными запросами и прочим. Так что лучше уж так.

Плюс можно будет реализовать поддержку нативных плейсхолдеров и множественного выполнения.

В общем, идея разделить либу на два класса, по примеру PDO и Mysqli, не оставляет меня.

Потихоньку пилю в этом направлении.
Хелпер-методы в основном классе хочу оставить, но только как обертку работы через стейтмент:
PHP:
    public function getOne()
    {
        $args  = func_get_args();
        $query = array_shift($args);
        return $this->prepare($query)->execute($args)->fetchOne();
    }
Но при этом возникает все больше и больше вопросов. Какие методы оставлять в основном классе, а какие передавать в стейтмент?

К примеру, форматирование входящих данных.
Кто его должен делать? Основной класс, или стейтмент?
Если делать по примеру старших товарищей, то основной класс (PDO::quote).
Но у меня таких методов много. И, самое главное - я АДСКИ не хочу делать методы форматирования публичными. Программист НЕ ДОЛЖЕН дергать их руками! Они должны использоваться только для обработки плейсхолдеров.
То есть, это должны быть методы стейт мента.
Но тогда им нужен коннекшен. Получается, в конструктор стейтмента надо передавать экземпляр основного класса, и делать переменную с коннектом (mysqli resource) публичным.
Так вообще делают? Как-то не хочется его наружу отдавать тоже. Или ничего?

Как вообще поступают в таких случаях?
 
Последнее редактирование:

Фанат

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

Хотя опять же, как писать статистику? Паблик методом? Как-то это некошерно.
С другой стороны - почему бы нет?
 
Последнее редактирование:

grigori

( ͡° ͜ʖ ͡°)
Команда форума
помню, лет 5 назад я доказывал @Sam Dark что слоев должно быть 3: маппер (например, active record), обработчик запроса, и объекты с данными, которые отдаются при обращении через lazy load :)

Можешь глянуть схему AR в yii 2 - они почти все сделали, это даст идеи для следующего шага. Хоть у тебя и не явный маппинг на классы, а неявный на массив, сути это не меняет.
 
Последнее редактирование:

fixxxer

К.О.
Партнер клуба
Фанат, ты, помнится, хотел сделать, чтобы всё (ну, точнее, почти всё - там, где это возможно) сводилось к prepared statements. В такой постановке вроде очевидно, кто что делает :)
 

Фанат

oncle terrible
Команда форума
Фанат, ты, помнится, хотел сделать, чтобы всё (ну, точнее, почти всё - там, где это возможно) сводилось к prepared statements. В такой постановке вроде очевидно, кто что делает :)
Честно говоря, не очень очевидно.

Ведь и сейчас все сводится. и при этом все лежит в классе коннекта. Со всеми перечисленными недостатками.
 

Фанат

oncle terrible
Команда форума
помню, лет 5 назад я доказывал @Sam Dark что слоев должно быть 3: маппер (active record), обработчик запроса, и объекты с данными, которые отдаются при обращении через lazy load :)

глянь схему AR в yii 2 - они почти все сделали, это будет твой следующий шаг
Понимаешь, там не только данные.
Там еще многократное выполнение, в случае с нативными плейсхолдерами. Хотелось их тоже запилить.
Но в этом случае куча логики утекает в стейтмент.
или сделать для такого случая совершенно отдельный класс и подключать его когда надо?
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
да, для запроса отдельный объект, для результата - отдельный, обработчик плейсхолдеров может быть один на все запросы.
 
Последнее редактирование:

AnrDaemon

Продвинутый новичок
Честно говоря, не очень очевидно.

Ведь и сейчас все сводится. и при этом все лежит в классе коннекта. Со всеми перечисленными недостатками.
Как раз очевидно. Вся обработка пользовательских данных должна быть в Statement.
Connection только отвечает за создание соединений и Statement из них.
Что-то типа
PHP:
class \DBAL\Connection {
  function create($query, $options = null) {
    if($options) {
      $options = array_merge($this->options, $options);
    }
    return new \DBAL\Statement(clone $this, $query, $options);
  }
}
И уже в конструкторе \DBAL\Statement делаешь prepare запроса. И там же - эскейпинг аргументов в соответствии с настройками запроса, переданными при его создании.
Если нужно создавать новое подключение к БД на каждый запрос, делаешь это в Connection::__clone().
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
И там же - эскейпинг аргументов в соответствии с настройками запроса, переданными при его создании.
Нет, там же кастомные плейсхолдеры, для которых нужен свой парсер со шлюхами.
Конкретно в конструкторе \DBAL\Statement не надо ничего, ну или, может, инициализация под конкретную БД, а прописать в объект запроса параметры запроса надо будет позднее извне через сеттеры.
Если нужно создавать новое подключение к БД на каждый запрос, делаешь это в Connection::__clone().
Для нового подключения лучше новый объект чтобы не чистить.
 
Последнее редактирование:

grigori

( ͡° ͜ʖ ͡°)
Команда форума
забей, он хочет каждому запросу "выдавать" свой личный объект подключения с общим handler-ом, но смысла по моему опыту мало

разве что для выставления запросам кастомных параметров вроде set names, но это как-то слишком редко нужно
 

Фанат

oncle terrible
Команда форума
В общем, получается, что у коннекшена должны быть открыты на публику, условно говоря,
1. функция prepare()
2. переменная с инстансом mysqli
3. функция для сбора статистики
и всё.
Так?
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
а зачем тебе prepare()-ить соединение? prepare() надо запрос, т.е. statement

>и всё.
как учил @Krishna, классы должны быть маленькие :)
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
аа, ну да
вообще, конечно, круто какой глубокий ресерч по теме ты делаешь уже который год
 

Фанат

oncle terrible
Команда форума
аа, ну да
вообще, конечно, круто какой глубокий ресерч по теме ты делаешь уже который год
Было бы круто, если бы выхлоп был...
У меня в голове уже давно вторая ерсия крутится, еще с год назад, кажется, постил основные фичи - именованные плейсхолдеры, поддержка нативных, и пр. А реализация не двигается. Вот сейчас недельку покопошусь, а потом опять на год встанет.
 

AnrDaemon

Продвинутый новичок
забей, он хочет каждому запросу "выдавать" свой личный объект подключения с общим handler-ом, но смысла по моему опыту мало

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

Фанат

oncle terrible
Команда форума
Если ты постоянно делаешь запросы на сотни тысяч записей, с которым работаешь параллельно, тебе нужно отдельное соединение на каждый, чтобы курсором шариться по результсету на стороне сервера..
Я одного не пойму. А соединение-то новое каждый раз зачем открывать?
 
Сверху