Выношу на суд классы работы с БД. Причем разного уровня абстракции. 1. DBAL: Оболочка над mysqli/PDO для удобной работы с параметрами. Ничего фантастического, просто чуть более удобный API, настраиваемый префикс для имен таблиц и параметр-массив, — то чего мне не хватает в нативных драйверах БД. Код (PHP): $rows = $db->query("SELECT * FROM `:p_users` WHERE `id` IN(:ids)", ['ids' => $ids]) ->fetchAssocAll(); 2. ORM: Очень простая реализация шаблона Data Mapper. Делал под влиянием Doctrine, но конкурировать не планирую. Хотелось чего-то более легковесного, но при этом чтобы покрывало 90% задач. Паттерн Data Mapper, в отличие от Active Record, не заставляет вас наследовать все объекты от абстрактной записи. Идея такова: пусть класс предметной области заботится о предметной области, а отображеним его в базу пусть занимается отдельный класс. Для большинства простых случаев подойдет универсальный репозиторий, а для сложных можно отнаследовать новый класс-репозиторий и перекрыть пару методов. Пока всё даже не Beta, а очень предварительное. Так что ваши замечания могут реально повлиять на развитие фреймворка. Из коробки в DM есть поддержка автоинкремента и отображение имен $object->fieldName на `objects`.`field_name`. Код (PHP): namespace MySpace; class User { public $id = 0; public $userName = ''; public $groupId = 1; } Код (PHP): CREATE TABLE `users` ( `id` INT NOT NULL AUTO_INC, `user_name` varchar(200) NOT NULL DEFAULT '', `group_id` INT NOT NULL DEFAULT 1, PRIMARY KEY (`id`) ); Код (PHP): // INSERT $user = new \MySpace\User(); $user->userName = 'xxx'; $entityManager->persist($user); echo "New id: {$user->id}\n"; // UPDATE $user = $entityManager->find('MySpace\\User', 1); $user->userName = 'ololoich'; $entityManager->persist($user); Код (PHP): echo 'Count: '.$entityManager->count('MySpace\\User', ['groupId' => 1]),"\n"; $userIterator = $entityManager->findBy('MySpace\\User', ['groupId' => 1]); foreach ($userIterator as $u) { echo $->userName."\n"; } https://github.com/scrap-bin/unicycle Есть зачатки документации и тесты. Повторять всё, что там написано не буду. Кому захочется запустить на пробу, начните с прочтения app/doc/install.ru.md, там требования и шаги по развертыванию расписаны. Само тестовое приложение просто выводит "Привет Мир!". А вот тесты могут рассказать многое. Спрашивайте — я поясню чо как. Буду благодарен за замечания и предложения.
Можно определить другую карту отображения имён через конфигурацию. Но нельзя переопределить одно поле, придется описывать всю таблицу. Я использую такие умолчания, к которым привык.
$entityManager->persist($user); встретив такую строку, я незнаю ЧТО ИМЕННО щас происходит. приходится изучать тонну кода выше, чтоб понять, инсерт там или апдейт... совершенно неинтуитивно $user = $entityManager->find('MySpace\\User', 1); постоянно указывать НС класса - тудаже. многословно както. ушел от наследования классов от AR(в одном месте) - к чему пришел?: - жесткая зависимость осталась, только в прикладном коде теперь - зависимость описывается не в одном месте(компактно) - а 'размазана' по всему приложению. антипрофит - вместо работы с экземпляром класса, работаем с классами. антипаттерн какойто в общем плюсов подхода ненаблюдаю, минусы(ИМХО) очевидны. я щас не про производительность, а именно удобство написания, сопровождения кода. интуитивность, компактность
На это можно ответить, что нам необязательно знать что происходит внутри: мы просто сохраняем объект. Может там вообще non-SQL, почему мы должны об этом думать на прикладном уровне?
это ответ мне? тогда невижу связи. естественно на прикладном уровне мне нужна простая абстракция, с которой я работаю. сохраняю данные. выбираю данные. т.е. я её родил. и работаю с ней дальше. для реализации этого поведения достаточно классического подхода - инстанцирования объекта от класса который умеет и знает все что нужно. и назвать его я могу хоть $obj. и обращаться к нему. тупо - меньше кодить. и любая IDE подскажет мне все что он умеет. а тут. один god -манагер который умеет всё и для любых классов. но эту связь нужно постоянно прописывать в прикладном коде. раз за разом. неудобно.
Вот именно — абстракция, а не буквальное повторение. Важно, что мы сохраняем состояние объекта, а не то, какой оператор SQL будет этому соответствовать и вообще есть ли там SQL. Типа того. Для вещей, о которых ты говоришь, когда весь SQL на виду, есть уровень DBAL. Там абстракций минимум. Код (Text): $id = $db->query("INSERT blablabla", [params])->insertId(); Немного расскажу про иерархию классов. EntityManager это "точка входа", через которую мы вытягиваем остальные объекты. Реально отображением занимается EntityRepository. Теоретически можно для каждой сущности держать по отдельному классу-репозиторию, в котором будет вся специфика расписана. Можно сказать, что репозиторий — это Модель в иерархии MVC. А менеджер это сервис-локатор для поиска репозитория. Если специальный класс репозитория не описан, то используется репозиторий-по-умолчанию. Чтобы получить экземпляр репозитория, для сущности, надо вызвать метод EntityManager->getRepository($entity). Но для краткости EntityManager предоставляет "шорткаты", чтобы этот шаг можно было пропустить. Это так сказать, синтаксический сахарок. Полный вариант получения объекта без магии выглядел бы так: Код (PHP): $user = $entityManager->getRepository('MySpace\\User')->find(1); // а можно сократить до $entityManager->find('MySpace\\User', 1) Т.е. найти репозиторий для класса User, затем уже у него запросить объект, соответствующий значению первичного ключа == 1. Все методы find* ищите в классе EntityRepository ! Мне тоже не нравится такой способ упоминания класса — как строка, но нет в PHP типа ClassRef, как в некоторых языках. Поэтому надо либо строку с именем класса передать или готовый объект нужного класса. Для наборов записей есть класс EntityIterator — его можно получить, например, по $repository->findBy([condition], [order], [limit]), Итератор можно подставить в foreach() Добавлено спустя 11 минут 44 секунды: ёпрст! столько опечаток совершаю, когда тороплюсь ответить, что потом очень долго надо вычитывать и исправлять )))) лучше не торопиться. Добавлено спустя 12 минут 30 секунд: я подумал над тем, что igordata сказал и, пожалуй, сделаю маппинг FooBar -> foo_bar опциональным. будет специальная настройка для этого.
Тестов производительности нет и не будет в ближайшее время. DBAL это такая тонкая прослойка, что скорость очевидно не сильно страдает. Нефиг там мерять. ORM на таком раннем этапе меня больше интересует удобство использования и вообще ORM не ради скорости выполнения делается. ORM по интерфесу он напоминает доктрину, но это вещи разных весовых категорий, я сразу это оговорил. Доктрина с ее прокси классами и своим собственным языком DQL будет в разы медленнее, но я ей не судья ))) Опять не ради скорости она делается! Тема меня волнует, moar фидбека!