Entity “фреймворк” для PHP из одного класса

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

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

    Перейдем к конкретной «ходовой» задаче — объектная прослойка для работы с базами данных в PHP. Решений великое множество, начиная от PDO и заканчивая многоуровневыми (и, на мой взгляд, не совсем уместными в PHP) ORM движками.

    Большинство этих решений перекочевали в PHP из других платформ. Но зачастую авторы не учитывают особенности PHP, которые позволили бы резко упростить как написание, так и использование портируемых конструкций.
    Одной из распространенных архитектур для данного класса задач является паттерн Active Record. В частности, по этому шаблону строятся так называемые Entity (сущности), в том или ином виде использующиеся в ряде платформ, начиная от персистентных бинов в EJB3 заканчивая EF в .NET.

    Итак, построим подобную конструкцию для PHP. Соединим между собой две клёвые штуки — готовую библиотеку ADODB и слаботипизированность и динамические свойства объектов языка PHP.
    Одной из многочисленных фич ADODB является так называемая автогенерация SQL запросов для вставки (INSERT) и обновления (UPDATE) записей на основе ассоциативных массивов с данными.
    Собственно нет ничего военного взять массив, где ключи — имена полей а значения — соответственно, данные и сгенерировать строку SQL запроса. Но ADODB делает это более интеллектуально. Запрос строится на основе структуры таблицы, которая предварительно считывается с схемы БД. В результате во-первых, в sql попадают только существующие поля а не всё подряд, во-вторых, учитывается тип поля — для строк добавляются кавычки, форматы дат могут формироваться на основе timestamp если ADODB видит оный вместо строки в передаваемом значении и т.д.

    Теперь зайдем со стороны PHP.
    Представим такой класс (упрощенно).

    class Entity{
       protected $fields = array();
       public final function __set($name, $value) {
            $this->fields[$name] = $value;
       }
       public final function __get($name) {
            return $this->fields[$name];
       }
    
    }
    


    Передавая внутренний массив библиотеке ADODB мы можем автоматически генерировать SQL запросы для обновления записи в БД данным объектом, При этом, не нужны громоздкие конструкции маппинга полей таблиц БД на поля объекта сущности на основе XML и тому подобного. Нужно только чтобы имя поля совпадало со свойством объекта. Поскольку то, как именуются поля в БД и поля объекта, для компьютера значения не имеет, то нет никаких причин чтобы они не совпадали.

    Покажем, как это работает в конечном варианте.
    Код законченного класса расположен на Gist. Это абстрактный класс, содержащий минимум необходимого для работы с БД. Отмечу, что данный класс — упрощенная версия решения, отработанного на нескольких десятках проектов.

    Представим, что у нас такая табличка:

    CREATE TABLE   `users` (
      `username` varchar(255) ,
      `created` date  ,
      `user_id` int(11) NOT NULL AUTO_INCREMENT,
      PRIMARY KEY (`user_id`)
    )
    

    Тип БД не имеет значения — ADODB обеспечивает переносимость для всех распространенных серверов БД.

    Создадим класс сущности Пользователь, на основе класса Entity

    /**
     * @table=users
     * @keyfield=user_id
     */
    class User extends Entity{
    
    }
    


    Собственно и все.
    Используется просто:

    
    $user = new User();
    $user->username='Вася Пупкин';
    $user->created=time();
    $user->save(); //сохраняем в  хранилище
    
    // загрузим  опять
    $thesameuser = User::load($user->user_id);
    echo $thesameuser ->username;
    
    


    Таблицу и ключевое поле указываем в псевдоаннотациях.
    Также можем указать представление (к примеру, view=usersview) если, как это часто бывает, сущность выбирается на основе своей таблицы с приджойнеными или вычисляемыми полями. В этом случае выбираться данные будут из представления а обновляться будет таблица. Кому не нравятся такие аннотации могут переопределить метод getMetatada() и указать параметры таблицы в возвращаемом массиве.

    Что еще полезного представляет класс Entity в данной реализации?

    Например, мы можем переопределить метод init(), который вызывается после создания экземпляра Entity, чтобы инициализировать дату создания по умолчанию.
    Или перегрузить метод afterLoad(), который автоматически вызывается после загрузки сущности из БД, чтобы преобразовать дату в timestamp для дальнейшего более удобного использования.
    В результате получим не намного более сложную конструкцию.

    /**
     * @table=users
     * @view=usersview
     * @keyfield=user_id
     */
    class User extends Entity{
        protected function init() {
            $this->created = time();
        }
        protected function afterLoad() {
            $this->created = strtotime($this->created);
        }
    }
    


    Также можно перегрузить методы beforeSave и beforeDelete и другие события жизненного цикла где можно, например, выполнить валидацию перед сохранением или какие нибудь другие действия — например, убрать за собой картинки из аплоада при удалении пользователя.

    Загружаем список сущностей по критерию (по сути условия для WHERE ).
    $users = User::load("username like 'Пупкин' ");
    

    Также класс Entity позволяет выполнить произвольный, «нативный» так сказать SQL запрос. Например, мы хотим вернуть список пользователей с какими нибудь группировками по статистике. Какие конкретно поля вернутся, не имеет значения (главное чтобы там было user_id, если есть необходимость дальнейшей манипуляции сущностью), нужно только знать их наименования чтобы обратится к выбранным полям. При сохранении сущности, как очевидно из вышеприведенного, тоже не надо заполнять все поля, какие будут присутствовать в объекте сущности, те и пойдут в БД. То есть нам не нужно создавать дополнительные классы для произвольных выборок. Примерно как анонимные структуры при выборке в EF только здесь это тот же класс сущности со всеми методами бизнес-логики.

    Строго говоря, вышеприведенные методы получения списков несколько выходят за пределы паттерна AR. По сути — это фабричные методы. Но как завещал старик Оккама, не будем плодить сущности сверх необходимого и городить отдельный Entity Manager или типа того.

    Поскольку загрузка и сохранение сущностей — это базовые операции, которые занимают две трети обращений к БД, мы можем избежать сотен строк рутинного кодирования.

    Заметим что вышеприведенное — просто классы PHP и их можно как угодно расширять и модифицировать, дописывать в сущности (или базовый класс Entity) свойства и методы бизнес-логики. То есть мы получаем не просто копию строки таблицы БД, а именно бизнес-сущность как часть объектной архитектуры приложения.

    Кому это может быть полезно? Разумеется, не прокачанным разрабам, которые считают что использование чего то проще доктрины — не солидно, и не перфекционистам, уверенным, что если решение не вытягивает миллиард обращений к Бд в секунду то это не решение. Судя по форумам, перед многими обычными разработчиками, работающими над обычными (коих 99.9%) проектами рано или поздно возникает проблема найти простой и удобный объектный способ для доступа к БД. Но сталкиваются с тем, что большинство решений либо неоправданно наворочены, либо являются частью какого-либо фреймворка.

    P.S. Вынес решение из фреймворка отдельным проектом GitHub
    Поделиться публикацией
    Похожие публикации
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 239
    • 0
      Бизнес-логика в doc-комментариях, оригинально :)
      Хорошая работа, спасибо вам, а то по заголовку я уж было боялся, что снова увижу mysql_connect и иже с ним.
      • 0
        ну. это скорее декларативная разметка а не бизнес логика. Такое кстати применяется в Symfony везде. Единственная проблема — коменты может покурочить какой нибудь акселератор или шифровальщик кода, потому и оставлена возможность задавать маппинг на таблицу в коде.
        Но конечно нормальные аннотации в PHP было бы круто.
      • +3
        Поскольку то, как именуются поля в БД и поля объекта, для компьютера значения не имеет, то нет никаких причин чтобы они не совпадали.

        Зато для компиляторов имеет значение, т.к. есть такая штука, как зарезервированные слова. Что, если уже есть готовая таблица, в которой колонка названа class или public?


        клёвые штуки…
        слаботипизированность

        уж слабая типизация ничего, кроме головной боли, обычно не несёт.

        • 0
          ну, человек должен думать когда задает имена полям как в коде так и в структуре БД.
          Насчет слаботипизации — это отдельная долгая дискусия которой уверен на хабре было полно. Опять же в PHP с каждой версией ввожить все больше контроля типов если это нужно. Но главное — отается и нетипизированые возможности. Кстати в той же яве появилось куча скриптовых языков типа Groovy именно для того чтобы сочетать мощь типизировного языка с более простом кодированием.
          • +2
            ну, человек должен думать когда задает имена полям как в коде так и в структуре БД.

            А если БД уже есть? И нужно пристроится к готовой БД. Колонку переименовать тоже не всегда можно. Не во всех же случаях ты и код пишешь, и БД проектируешь.


            На счёт типизации — вы явно путаете слабую типизацию с динамической. Ну да ладно.

            • 0
              я и не обещал лекарство от всех проблем. Если Бд уже есть то там скорее всего и программный код уже есть.

              Впрочем так и не понял зачем переименовывать поля в БД — суть в том что для данного решения пофиг какое там поле. Если кто то влепил имя поля как то противоречащее ключевым словам PHP — то напишете на это конкретное свойство гетер и сеттер в классе сущности и все дела.
        • 0
          я и не обещал лекарство от всех проблем. Если Бд уже есть то там скорее всего и программный код уже есть.

          Впрочем так и не понял зачем переименовывать поля в БД — суть в том что для данного решения пофиг какое там поле. Если кто то влепил имя поля как то противоречащее ключевым словам PHP — то напишете на это конкретное свойство гетер и сеттер в классе сущности и все дела.

          • 0
            Простейший AR на одну сущность действительно пишется в экран кода.
            Вот только сущности обычно связаны.
            Попробуйте добавить туда хотя бы hasOne и hasMany. Боюсь, сразу окажется проще взять готовое решение типа Eloquent :-)
            • –1
              Не буду и пробовать потому как это не ORM.
              Но я не зря написал что решение отработано и все шишки уже набиты и грабли уже наступлены.
              Нет никаких проблем со связями.
              Прочитайте внимательно абзац с нативным SQL.
              Кроме того в отличие от например явы, контекст PHP разрушается после отработки страницы — посему нет никакого практического смысла вытягивать гроздья взаимосвязанных сущностей.

              • +1
                > Кроме того в отличие от например явы, контекст PHP разрушается после отработки страницы

                Работать с базой данных может понадобиться не только в веб-приложениях, и даже если в веб-приложениях, то необязательно в таких, которые реализуют принцип «PHP is meant to die». Конечно, иных нынче мало, но ведь в будущем ситуация может измениться — вдруг разработчики фреймворков внезапно массово заинтересуются чем-то типа PHP-SGI или хотя бы просто задействуют какой-нибудь ReactPHP.
                • 0
                  WST опередили меня :)
                  недавно столкнулся с проблемой выбора слоя для работы с БД для приложения построенного на основе ReactPHP
                  • 0
                    Как говорится нельзя объять необъятное. Как я отметил ориентируюсь на большиство обычных задач которые нужны повседневно.

              • +4
                зачем использовать ADODB, если есть PDO?
                Из минусов:
                • Нельзя конфигурировать конекшен к базе: для разных Entity — разные конекшены (если переопределить getConnect, то конекшен переопределиться для всех классов)
                • Использование магических __get, __set (что если я присвою значение несуществующему в БД полю и вызову save() ?)
                • Быстродействие (парсить мтаданные для каждой сущности одного класса не кажется быстрым)
                • По самому коду очень много вопросов, например https://gist.github.com/leon-mbs/7fb0a0881253a583017dadcdd8179325#file-entity-php-L274 а потом вы возвращаете false


                И пару вопросов:
                • Как вы решаете проблему валидации?
                • Где вы держите бизнес-логику?
                • Как тестируете сущности?
                • –3
                  зачем использовать ADODB, если есть PDO

                  вообще то я написал почему именно ADODB.
                  Нельзя конфигурировать конекшен к базе: для разных Entity — разные конекшены

                  а нафига? проект с больше чем одной БД? Давайте оставим экзотику в стороне.
                  Использование магических __get, __set (что если я присвою значение несуществующему в БД полю и вызову save() ?)

                  как я писал выше — абсолютно ничего. Потому и ADODB.

                  https://gist.github.com/leon-mbs/7fb0a0881253a583017dadcdd8179325#file-entity-php-L274

                  косяк несущественный но спасибо.

                  Как вы решаете проблему валидации?

                  в методе befireSave() — по сути это как бы событие жизненного цикла — срабатывает перед записью в БД. Если нужна валидация — переопределяете метод в сущности.

                  Где вы держите бизнес-логику?

                  смотря какую. Если бизнес-логика относится к бизнес-сущности (класс User к примеру) то в этом классе, разумеется.

                  Как тестируете сущности?

                  тестирование зависит от проекта в котором данное решение используется. не вижу какой то специфики.

                  • 0
                    вообще то я написал почему именно ADODB.

                    как я писал выше — абсолютно ничего. Потому и ADODB.


                    Так дело в том, что это наоборот не очень. Я глядя на код где устанавливается поле и потом сущность сохраняет ожидаю, что оно будет сохранено. Потому что не указано какие поля виртуальные, а какие реальные в БД.
                    Да и тянуть целую библиотеку ради возможности из массива сделать UPDATE/INSERT запрос тоже не вижу смысла, это пишется очень просто.

                    а нафига? проект с больше чем одной БД? Давайте оставим экзотику в стороне.

                    не такая уж и экзотика.
                    как вы тогда справляетсь с разными конфигурациями БД на живом, тестовм и локальном сервере?

                    тестирование зависит от проекта в котором данное решение используется. не вижу какой то специфики.

                    валидация сущности в beforeSave, он protected. Я хочу протестировать валидацию. Как мне это сделать без БД?
                    Я к тому, что класс жёстко завязан на БД и нельзя мокнуть её, а мокать БД в тестах — первое дело

                    косяк несущественный но спасибо.


                    https://gist.github.com/leon-mbs/7fb0a0881253a583017dadcdd8179325#file-entity-php-L96
                    а ещё вместо таких конструкций проще вызвать метод сразу со static::
                    • –2
                      как вы тогда справляетсь с разными конфигурациями БД на живом, тестовм и локальном сервере?

                      поменял адрес в конфиге и все дела.

                      Я к тому, что класс жёстко завязан на БД и нельзя мокнуть её, а мокать БД в тестах — первое дело

                      не хочу усложнять что можно не усложнять. Пока нормально обхожусь комплексным тестированием на уровне приложения.
                      а ещё вместо таких конструкций проще вызвать метод сразу со static::

                      не совсем понял но мне нужно имя как раз вызывающего класса-наследника.

                      я и не претендую на идеальное качество кода — это я писал не просто так а для фреймворка который тоже писал не для красоты. В этом случае хочется сразу писать конечные задачи а не возится с моками. Собственно на этих задачах я все и отлаживал. Это не по классике но это опенсорс, я за деньги не продаю.
                      Загляните для интереса в некоторые очень популярные проекты — там не то что моки и ORM там дикая смесь кода разметки и запросов к БД.
                      Но если людям нужно ехать а не шашечки то как то справляются.

                • +2
                  Одной из распространенных архитектур для данного класса задач является паттерн Active Record. В частности, по этому шаблону строятся так называемые Entity (сущности), в том или ином виде использующиеся в ряде платформ, начиная от персистентных бинов в EJB3 заканчивая EF в .NET.

                  А ничего, что в EF никакого ActiveRecord нет, там честный ORM?


                  $thesameuser = User::load($user->user_id);


                  Статический метод? А тестировать как?


                  (про повторное использование подобной статики тоже вопрос открытый)

                  • –9
                    Статический метод? А тестировать как?

                    это к разработчикам PHP — зачем они ввели в язык статические методы которые оказывается невозможно тестировать.

                    вы как обычно придираетесь не по существу. Будем считать что вы уже отметились коментом и в этой статье тоже. Поставьте галочку что не пропустили и все.

                    • +2
                      это к разработчикам PHP — зачем они ввели в язык статические методы которые оказывается невозможно тестировать.

                      Нет, это конкретно к вам — потому что вы используете эти методы. Каждому инструменту своя область применения.


                      (Я так понимаю, по поводу EF ответа не будет)

                      • –8
                        Я так понимаю, по поводу EF ответа не будет

                        более бесполезного занятия чем дискутировать что такое EF в статье про PHP не нашли?
                        • +7

                          (а теперь вы с той же элегантностью ушли от вопроса про тестирование)


                          более бесполезного занятия чем дискутировать что такое EF в статье про PHP не нашли?

                          Ну, вы же зачем-то помянули EF в статье про PHP? Ложные утверждения — они везде ложные утверждения.

                      • +1
                        это к разработчикам PHP — зачем они ввели в язык статические методы которые оказывается невозможно тестировать.

                        Статические методы придуманы просто для других вещей, в них удобно складывать stateless-логику, которая не должна принодлижать одному конкретному инстансу (или если инстанса еще нет). Чаще всего использование статики сводится к фабричным методам, реже — какие-нибудь проверки которые нужно производить над несколькими сущностями: $owner = User::whoInCharge(...$users);, в этом случае статический метод whoInCharge просто пробежится по списку сущностей, заберет например значение приватных полей и проголосует кто из них главнее. Пример синтетический, но изредко такое бывате нужно.


                        Их проще воспринимать как обычные функции, которым положено знать о внутренней реализации объектов. И вот такие штуки тестировать проблем нет. А теперь посмотрим на ваш случай:


                            public static function getConnect()
                            {
                                if (self::$conn instanceof \ADOConnection) {
                                    return self::$conn;
                                }
                                self::$conn = \ADONewConnection(self::$driver);
                                self::$conn->Connect("localhost", "root", "root", "test");
                                self::$conn->Execute("SET NAMES 'utf8'");
                                return self::$conn;
                            }

                        Тут мы просто забили на управление зависимостями, они не передаются внутрь а стало быть "замокать" это дело мы не сможем. Да и не станем (никто в здравом уме не будет мокать коннекшен или квери билдер, слишком большая завязка на текущую реализацию выходит).


                        В целом проблема решается извне. То есть у нас должен быть отдельный компонент — Finder, который уже занимается выборками объектов и т.д. Словом нужно всего-лишь "добавить классов" и система станет уже намного более поддерживаемой.


                        Сейчас ваше решение тянет на одноразовое а стало быть использовать его я бы никому не рекомендовал, с учетом того какое огромное количество качественных решений доступно в packagist.

                        • 0
                          я уже писал — статические методы — по сути фабричные непосредственно к AR не относятся и с точки зрения академического подхода — должны быть вынесены в отдельный класс. Но с практической точки зрения не вижу смысла.
                          Но не все. Например метод find никак не вынести потому что он по имени класса определяет с какой сущностью надо работать. ПО мне так удобнее чем каждый раз указывать параметр.

                          Что касается моков — да, с академической точки зрения должны быть моки, покрытие тестами и т.д. Но здесь бОльшую часть работы выполняет ADODB. Если убрать слой работы с БД то «мокать» останется только ассоциативный массив и магичские методы что имеет мало смысла.
                          Попросту говоря там нечего тестить будет вообще.
                          То есть тут лучше тестировать комплексно вместе с реальными сущностями и БД.

                          Сейчас ваше решение тянет на одноразовое

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

                          это как правило копии ORM с ява и.нет платформ.
                          Покажите решение где не нужно например мапить поля таблицы БД на классы, следить за типами и выполнять кучу другой работы которую мое решение попросту автоматизирует. Я не переизобретаю то что уже есть.

                          теперь посмотрим на ваш случай:

                          На этот случай как раз смотреть на надо. В реальном фреймворке есть отдельный класс синглтон https://github.com/leon-mbs/zippy/blob/master/zcl/db/db.php
                          Здесь я просто сложил в кучу на скорую руку чтобы не распылятся.
                          Разумеется можно вынести коннект, вынести статику и т.д.
                          Ну выносите кому надо — я просто предложил концепцию которой уже давно пользуюсь.
                          Почему вместо сути все концентрируются на том сответствует ли решение академическому подходу, читал ли я Фаулера и что использование представлений — отстой?


                          • 0
                            с академической точки зрения

                            Ох уж эта академическая точка зрения. Я не ее пытаюсь объяснить, а юзкейсы для статики. То что для каждого правила есть исключения, вызванные желанием сэкономить на качестве — это нормально. Но только делая все это нужно делать акцент на том, что подобные решения увеличивают технический долг. Если проект простой — то не страшно, а если он будет расти и развиваться, может наступить момент когда все это сделает разработку болью. Сложность именно понять что когда применять, потому новичкам лучше сразу давать пожеще, с большим количеством правил и ограничений, и по мере постижения смысла этих правил и ограничений расслаблять хватку.


                            Попросту говоря там нечего тестить будет вообще.

                            Именно для этого AR и придумывали. Этот паттерн идеален для проектов, где логика идеально ложится на реляционную базу, и сущности тонкие. По сути это комбинация Domain Object + Row Data Gateway, правда люди об этом забыли и именно domain object частенько выкидывают, юзая AR-модельки как анемичные модели, тупые структурки данных.


                            Вообще то могу показать ссылки на сайты в инете сделанные с таким решением.

                            А говнокода, знаете ли, более чем хватает. Так что на любую даже самую стремную задумку примерчик с продакшена найти не так уж и сложно будет.


                            В конце концов задача статьи, как принято на Хабре, описать идею а не продать код.

                            Вас могут читать впечатлительные PHP-ники, которые наберутся этого и потом их будет долго переучивать. Любая точка зрения достойна существования, и ваш подход имеет место применяться в реальных проектах, но только если знаешь что делаешь. Большинство PHP-ников обитающих на хабре — не знает что хорошо а что плохо, а стало быть нужно четко прописывать допущения для решения. что мол "логики мало, хорошо на реляционку ложится, для прототипов".


                            Покажите решение где не нужно например мапить поля таблицы БД на классы, следить за типами и выполнять кучу другой работы которую мое решение попросту автоматизирует. Я не переизобретаю то что уже есть.

                            Легко. Вот первое что выдал поиск: https://github.com/j4mie/paris Так же если память не изменяет, реализация AR в Yii и Laravel делает ровно то же самое.

                            • 0
                              юзая AR-модельки как анемичные модели, тупые структурки данных.

                              если уж на то пошло Entity еще более тупые структуруки… Что не мешает их юзать в EJB3 например.
                              может наступить момент когда все это сделает разработку болью

                              во первых, подавляюзее число проектов именно простые. Это и есть решение которое экономит время на относительно простых проектах.
                              Таких проектах которых рядовому разрабу приходится клепать десятками.
                              А уж если что создаст проблеммы на больших проектах то \то как раз модные нынче ORM потому что во-первых создается неоптимальная структуру хранилища, во вторых еще более неоптимальные SQL запросы. В некоторых БД созданных через code first со временем сделать сложную выборку по большим данным даже нативный SQL не помогает без денормализации.
                              Опять же речь о PHP. В яве не так критично вытащить список ненужных ща Entity через связи- они останутся некоторое время в памяти и могут быть подхвачены вместо обращения к БД (во всяком случае в контейнерах EJB3). В PHP подобные решения мягко говоря не оправданы.
                              Вот первое что выдал поиск.

                              части фреймворков, не в счет. Пикси — надстройка над громоздким ORM со всеми вытекающими последствиями.

                              • +2
                                если уж на то пошло Entity еще более тупые структуруки…

                                Ну уж нет. Entity — это термин из DDD, и предполагает он вполне себе конкретную вещь, а именно — объект, наделенный идентичностью (отличной от его значимых атрибутов) и жизненным циклом. DTO — не entity.

                                • +2
                                  Это и есть решение которое экономит время на относительно простых проектах.

                                  Я могу предложить вам решение еще проще — тупо юзать mongodb + rest интерфейс к ней. И вообще никаких проблем и необходимости писать бэкэнд. Ну и опять же можно ваять мидлвэры с бизнес ограничениями если проект будет усложняться. Главное что бы все объекты бизнес-транзакции в один документ укладывались.


                                  А уж если что создаст проблеммы на больших проектах то \то как раз модные нынче ORM потому что во-первых создается неоптимальная структуру хранилища, во вторых еще более неоптимальные SQL запросы.

                                  Вы просто не умеете их готовить.


                                  1) ORM нужны только для задач, в рамках которых у нас есть простая бизнес транзакция с небольшим количеством объектов. Скажем если вам репорт нужно сагрегировать — SQL. А еще можно просто дублировать информацию в том виде, который будет удобен для запросов. Есть миллион различных вариантов как работать с данными, не нужно зацикливаться на одном и говорить что это плохо во всех случаях просто потому что большинство оверюзят подход.


                                  2) ORM ответственны только за мэппинг релейшенов между объектами. Все остальное — это другие паттерны. Причем "генериться" хранилище только в случае data mapper и вы строго можете контролировать этот процесс. В остальных случаях хранилище проектируете вы.


                                  3) ORM это абстракция. Любая абстракция имеет свой оверхэд. Потому, в документации к таким решениям как Hibernate например четко прописано что "ребята, это текущая абстракция". То есть если мы хотим скорость разработки и выразительность языка — мы можем юзать абстракцию на полную. А там где нужна производительность — мы можем смешать все с нативным SQL.


                                  Например если у нас в проекте есть сложное взаимодействие между бизнес сущностями — мне намного удобнее и проще описать это все в виде взаимодействия сущностей, протестить, и потом прицепить дата мэппер что бы подключить базу. Но если мне нужно будет делать какие-то сложные выборки этих объектов на чтение — то я просто сделаю SQL запрос и замэплю результат не на сущность а сразу на DTO.


                                  В PHP подобные решения мягко говоря не оправданы.

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


                                  части фреймворков, не в счет. Пикси — надстройка над громоздким ORM со всеми вытекающими последствиями.

                                  То что я скинул — это самодостаточная библиотека. Причем ут пикси если библиотека называется "париж"?

                              • +2

                                ~~Вам уже 100500 раз сказали, что если отбросить всю конкретику вашего решения, то "суть" капитанская и несколько устарела.
                                "Идея — очевидная, спасибо Кеп" — это одна мысль. Повторять ее смысла нет.
                                "Ваша реализация отстой, особенно бац1, бац2, бац" — может быть бесконечно разнообразным, потому что бац1, бац2, и бац3 может быть одним из множества замечаний. Вот большинство и идет по второму пути.~~
                                Нет, конечно всё не так. Просто все тупые, и только один вы знаете сакральные истины и по достоинству оцениваете гениальную сущность подхода и самого кода.

                                • 0
                                  если нет возражений по существу то аргументы типа «устарела» и «отстой» оставьте при себе.
                                  Решение работает — экономит нехилое количество времени кодирования и строк кода. Для того оно и сделано а не для того чтобы мерятся пиписками кто современнее и навороченее.

                                  • 0
                                    Решение работает — экономит нехилое количество времени кодирования и строк кода.

                                    Это так же не по существу, вы же не предоставляете никаких подтверждений этим словам.


                                    В рамках маленьких проектов оно будет хорошо показывать себя. В рамках проектов чуть сложнее бложика — уже могут быть проблемы. С тем же успехом можно было бы просто отказаться от подобного и использовать старый добрый table data gateway, который еще проще и намного более гибкий.

                        • +8
                          Эм… лживый заголовок, о чем понимаешь только к середине статьи. В середине статьи понимаешь что статья на самом деле называется «мой HelloWorld для библиотеки ADODB (слой абстракции построителя запросов)».
                          После прочтения статьи понимаешь что про ADODB тоже ничего толком нет и даже чтобы понять функции ADODB нужно идти на сайт и вычитывать документацию. Содержание статьи «Это круто что есть такая библиотека».
                          Эм…

                          Обертка над PDO — один класс на пару методов.
                          Обертка над ответом PDO для абстракции реализующая итератор и АrrayAccess и инстансцирование сущностей в классы (опционально). Еще один класс. На два экрана.
                          Построитель запросов для основных операций. Еще один класс (ну два, если операций много).
                          Базовый класс типа вашего Entity только с Итератором и АrrayAccess, ну и еще по строчке добавить чтобы фильтровать по списку разрешенных полей, и еще пять строк чтобы у нас уже методы getИмяПоля() и setИмяПоля() работали.

                          Собственно всё. Пишется быстро, работает как часы. Дальше уже ORM идет с валидациями и связями. Которые кстати тоже не экзотика а суровая необходимость. О чем статья?
                          • –6
                            о чем понимаешь только к середине статьи.

                            по моему вы не поняли ни к середине ник концу ни с обратной стороны.

                            чтобы понять функции ADODB нужно идти на сайт и вычитывать документацию

                            для применения данного решения вообще не надо понимать функции ADODB.
                            это как пример вашего «понимания»

                            Дальше уже ORM идет с валидациями и связями. Которые кстати тоже не экзотика а суровая необходимость.

                            Нету такой необходимости в ПОДАВЛЯЮЩЕМ большинстве проектов.

                            О чем статья?

                            Жаль что вы не поняли о чем. Возможно из-за заангажированости.

                            • 0
                              Жаль что вы не поняли о чем. Возможно из-за заангажированости.
                              ==
                              Ржу. Если я не понял смысла вашей статьи, то или его там нет, или я плохо понимаю, или вы плохо объясняете. Но поскольку я заангажирован, то вариант что смысла нет и что вы не умеете объяснять конечно не рассматриваются.

                              Перечитал статью. Или я слепой, или кроме упоминания ADODB и примитивного класса наподобии моего базового класса (см ниже) я в статье не нашел.

                              <?php
                              /**
                               * Класс позволяющий работать с массивом как с объектом.
                               * Ну или с объектом как с массивом, кому как нравится....
                               * Строго говоря предназначен для наследования, но не абстрактный, ибо
                               * в принципе может быть использован самостоятельно.
                               */
                              class arrayComponent extends component implements ArrayAccess, Iterator, Countable {
                                  protected $fields = [];
                                  protected $_position;
                                  //
                                  public function __construct($config = NULL) {
                                      parent::__construct($config);
                                      $this->rewind();
                                  }
                                  //
                                  public function offsetSet($offset, $value) {
                                      if (is_null($offset)) {
                                          $this->add($value);
                                      }
                                      else {
                                          $this->__set($offset, $value);
                                      }
                                  }
                                  public function offsetExists($offset) {
                                      return isset($this->fields[$offset]);
                                  }
                                  public function offsetUnset($offset) {
                                      unset($this->fields[$offset]);
                                  }
                                  public function offsetGet($offset) {
                                      $exist = (isset($this->fields[$offset]) AND $this->allowed($offset));
                                      return $exist ? $this->__get($offset) : null;
                                  }
                                  //
                                  public function rewind() {
                                      reset($this->fields);
                                      $this->_position = key($this->fields);
                                  }
                                  public function current() {
                                      return $this->fields[$this->_position];
                                  }
                                  public function key() {
                                      return $this->_position;
                                  }
                                  public function next() {
                                      next($this->fields);
                                      $this->_position = key($this->fields);
                                  }
                                  public function valid() {
                                      return array_key_exists($this->_position,$this->fields);
                                  }
                                  //
                                  public function count() {
                                      return count($this->fields);
                                  }
                                  
                                  /**
                                   * Проверяет разрешено ли такое поле
                                   * Всегда возвращает true. Нужно чтобы потомки могли его переопределять.
                                   * @param type $name
                                   * @return boolean
                                   */
                                  protected function allowed($name) {
                                      return true;
                                  }
                                  
                                  /**
                                  * Магический метод проверяющий что устанавливаемое поле является разрешенным
                                  * и сохраняющий его значение в соответствующее поле
                                  * в противном случае вызывает родительский метод который подключает
                                  * сеттер или выдает ошибку
                                  * @param string $name имя устанавливаемого поля
                                  * @param mixed $value значение
                                  */
                                  public function __set($name,$value) {
                                      if($this->allowed($name)) {
                                          $this->fields[$name] = $value;
                                      }
                                      else parent::__set($name,$value);
                                  }
                              
                                  /**
                                  * Магический метод проверяющий наличие имени свойства в списке разрешенных,
                                  * в случае отсутствия вызываем родителя который ищет геттер
                                  * Если поле разрешено, то возвращаем его из массива разрешенных полей
                                  * @param string $name имя читаемого поля
                                  * @return mixed
                                  */
                                  public function __get($name){
                                      if($this->allowed($name)) {
                                      return $this->fields[$name];
                                      }
                                      else return parent::__get($name);
                                  }
                                  
                                  //Добавим в наш массив новый элемент. Если без индекса нельзя, то переопределим метод на запрет
                                  public function add($data) {
                                      $this->fields[] = $data;
                                  }
                              }
                              
                              • 0
                                вы не поняли что суть моего класса не работа с массивом а работа с записями БД.
                                А забота ADODB корректно запихнуть данные массива в БД.

                                • 0
                                  И?
                                  Работа с базой:
                                  <?php
                                  /**
                                   * Инициирует ПДО, выполняет запрос с параметрами.
                                   * Результат возвращает в виде dbResult.
                                   * dbResult - итератор
                                   */
                                  class db extends component {
                                    protected $pdo;
                                    protected $dsn = 'mysql:host=localhost;dbname=test';
                                    protected $user = 'root';
                                    protected $password = '';
                                    protected $options = [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''];
                                    protected $queryClass = 'mysqlQuery';
                                  
                                    public function __construct($config = NULL) {
                                      parent::__construct($config);
                                      $this->pdo = new PDO($this->dsn, $this->user, $this->password, $this->options);
                                      // бросаем исключения при проблемах
                                      $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
                                    }
                                  
                                    public function insId() {
                                      return $this->pdo->lastInsertId();
                                    }
                                  
                                    protected function prepareParam($param) {
                                      $result = [];
                                      foreach($param as $key=>$value) {
                                        $key = ':' . (string) $key;
                                        $value = (string) $value;
                                        $result[$key] = $value;
                                      }
                                      return $result;
                                    }
                                  
                                    public function go($sql, $param=NULL) {
                                      $statement = $this->pdo->prepare($sql, [\PDO::ATTR_CURSOR => \PDO::CURSOR_SCROLL]);
                                      $statement->setFetchMode(\PDO::FETCH_ASSOC);
                                      if($param) {
                                        $param = $this->prepareParam($param);
                                        $executeResult = $statement->execute($param);
                                      }
                                      else {
                                        $executeResult = $statement->execute();
                                      }
                                      return new dbResult([
                                        'statement'=>$statement,
                                        'ok'=>$executeResult
                                      ]);
                                    }
                                  
                                    // Вернем построитель запросов характерный для данной базы
                                    public function query($config) {
                                        $class = $this->queryClass;
                                        return new $class($config);
                                    }
                                  }
                                  

                                  «Запихивание» данных в наш массив/класс:
                                  <?php
                                  /**
                                   * Итератор по результатам запроса.
                                   * публичное свойство ОК содержит булево значение удачности запроса.
                                   * Метод one - сахар для запросов когда ожидается только один резульат.
                                   */
                                  class dbResult extends component implements Iterator
                                  {
                                      protected $key;
                                      protected $current;
                                  
                                      protected $statement;
                                      public $ok;
                                      public $modelName;
                                  
                                      public function current() {
                                          return $this->current;
                                      }
                                  
                                      public function next() {
                                          $this->key++;
                                      }
                                  
                                      public function key() {
                                          return $this->key;
                                      }
                                  
                                      public function valid() {
                                          $this->current = $this->getRecord($this->key);
                                          if (false === $this->current) { // (!array()) == True
                                              return false;
                                          } else return true;
                                      }
                                  
                                      // Получаем отдельную запись, и если это уместно - выполняем манипуляции после загрузки
                                      protected function getRecord($key) {
                                          $record = $this->statement->fetch(
                                              \PDO::FETCH_ASSOC,
                                              \PDO::FETCH_ORI_ABS, 
                                              $key
                                          );
                                          if($record AND $this->modelName) {
                                              $model = model($this->modelName);
                                              $model->load($record);
                                              return $model;
                                          } else return $record;
                                      }
                                  
                                      public function rewind() {
                                          $this->key = 0;
                                      }
                                  
                                      public function one() {
                                          $this->rewind();
                                          $this->valid();
                                          $obj = $this->current();
                                          $this->statement->closeCursor();    
                                          return $obj;
                                      }
                                  }
                                  

                                  Чтобы дальше не играть в подкидного «генерация запросов»:
                                  <?php
                                  class mysqlQuery extends mysqlQueryCore {
                                    public function insert($data) {
                                      $data = $this->filterField($data);
                                      $this->param = $data;
                                      $sql = 'INSERT '.$this->table().' SET '.$this->fields2set($data);
                                      $this->sql = $sql;
                                      return $this;
                                    }
                                  
                                    public function update($data, $where, $whereParam=NULL) {
                                      $data = $this->filterField($data);
                                      $this->param = $data;
                                      $where = $this->where($where, $whereParam);
                                      $sql = 'UPDATE '.$this->table().' SET '.$this->fields2set($data).' WHERE '.$where;
                                      $this->sql = $sql;
                                      return $this;
                                    }
                                  
                                    public function delete($where, $whereParam=NULL) {
                                      $this->param = []; // reset old request
                                      $where = $this->where($where, $whereParam);
                                      $sql = 'DELETE FROM '.$this->table().' WHERE '.$where;
                                      $this->sql = $sql;
                                      return $this;
                                    }
                                  
                                    public function select($data, $where, $whereParam=NULL) {
                                      $this->param = []; // reset old request
                                      if(is_array($data)) {
                                        $data = $this->quoteFieldList($data);
                                      }
                                      $where = $this->where($where, $whereParam);
                                      $sql = 'SELECT '.$data.' FROM '.$this->table().' WHERE '.$where;
                                      $this->sql = $sql;
                                      return $this;
                                    }
                                  
                                  
                                    public function count($where, $whereParam=NULL) {
                                      $data = 'COUNT(1) as cnt';
                                      return $this->select($data, $where, $whereParam);
                                    }
                                  
                                    public function limit($limit, $offset=NULL) {
                                      $this->sql = $this->sql . $this->limitSql($limit, $offset);
                                      return $this;
                                    }
                                  
                                    public function order($order) {
                                      $this->sql = $this->sql . $this->orderSql($order);
                                      return $this;
                                    }
                                  }
                                  

                                  Ну и вспомогательный код для последнего класса:
                                  <?php
                                  class mysqlQueryCore extends component {
                                    protected $table;
                                    protected $allowed=[];
                                  
                                  
                                    public $param=[];
                                    public $sql='';
                                  
                                    public function reset() {
                                      $this->sql = '';
                                      $this->param = [];
                                    }
                                  
                                    public function where($data, $param = NULL) {
                                      if($param) {
                                        foreach($param as $key=>$value) {
                                          $this->param['where_'.$key] = $value;
                                        }
                                        return $data;
                                      }
                                      if(true === $data) $data = '1=1';
                                      if(is_int($data)) {
                                        $this->param['where_id'] = $data;
                                        $where = $this->quoteId('id') .'=:where_id';
                                        return $where;
                                      }
                                      if(is_array($data)) {
                                        $where = $this->array2where($data);
                                        return $where;
                                      }
                                      if(is_string($data)) {
                                        return $data;
                                      }
                                      return FALSE;
                                    }
                                  
                                    protected function array2where($data) {
                                      $data = $this->filterField($data);
                                      $mylist = [];
                                      foreach($data as $key=>$value) {
                                        $mylist[] = $this->quoteId($key) .'=:where_'.$key;
                                        $this->param['where_'.$key] = $value;
                                      }
                                      return implode(' AND ', $mylist);  
                                    }
                                  
                                  
                                    public function orderSql($order) {
                                      if($order{0}=='!') {
                                        $field = substr($order,1);
                                        $field = $this->quoteId($field);
                                        $result = ' ORDER BY '.$field.' DESC';
                                      }
                                      else {
                                        $field = $order;
                                        $field = $this->quoteId($field);
                                        $result = ' ORDER BY '.$field.' ASC';
                                      }
                                      return $result;
                                    }
                                  
                                    public function limitSql($limit,$offset=NULL) {
                                      $result = ' LIMIT ';
                                      if($offset) $result .= intval($offset).','.intval($limit);
                                      else $result .= intval($limit);
                                      return $result;
                                    }
                                  
                                  
                                    public function quoteFieldList($fields) {
                                  //    $allowed = array_flip($this->allowed);
                                      $allowed = $this->allowed;
                                      $mylist = [];
                                      foreach($fields as $value) {
                                        if(isset($allowed[$value])) $mylist[] = $this->quoteId($value);
                                      }
                                      return implode(' , ', $mylist);
                                    }
                                  
                                    // $fields = ['name'=>'namedata','value'=>'valuedata']
                                    // return: `name` = :name, `value` = :value
                                    public function fields2set($fields) {
                                      $mylist = [];
                                      foreach($fields as $key=>$value) {
                                        $mylist[] = $this->quoteId($key) .'=:'.$key;
                                      }
                                      return implode(' , ', $mylist);
                                    }
                                  
                                  
                                    public function quoteId($id) {
                                      return "`".$id."`";
                                    }
                                  
                                    public function filterField($data) {
                                  //    $allowed = array_flip($this->allowed);
                                      $allowed = $this->allowed;
                                      if (is_object($data) OR is_array($data)) {
                                        $result = [];
                                        foreach ($data as $key => $value) {
                                          if(isset($allowed[$key])) $result[$key] = $value;
                                        }
                                        return $result;
                                      }
                                      else {
                                        return FALSE;
                                      }	
                                    }
                                  
                                    public function table() {
                                      return $this->quoteId($this->table);
                                    }
                                  }
                                  
                                  • 0
                                    Кода здесь примерно как слов в вашей статье, зато смысла немного побольше.
                                    ПС: За ляпы и стиль сильно не бить, до ревью и рефакторнга еще не добрался, сам знаю что кривенько :)
                                    • 0
                                      половину того что вы написали решает ADODB и решает их гораздо лучше. В первую очередь проверяет структуру таблицы и формирует запрос только с теми полями что там есть что исключает ошибки при неправильном свойстве или свойстве не предназначеном для записи в БД. Плюс ADODB сама расставляет кавычки, формирует правильные форматы дат (причем в зависимости от типа БД и диалекта SQL то есть обеспечивает переносимость) и так далее.
                                      Я не понимаю зачем бы я повторял эту функциональность в своем решении.
                                      • 0
                                        А зачем расставлять кавычки? PDO уже отменили?
                                        Зачем проверять структуру? Тяжелая наркомания и не более.
                                        Если я неправильно написал имя свойства, то я хочу увидеть исключение, которое схоронится в лог и выдаст красивую 500-ошибку юзеру, а не проигнорирует какой-то пункт в моем запросе и повалит или базу или логику прав доступа.
                                        Формирование запросов под разные БД — да, не дописал пока. Но то такое. Работа измеряется в часах.
                                        Форматирование даты? Мелочь и вкусовщина. У меня этим занимается ORM, ибо это именно его уровень ответственности, и ему знать какие форматы даты предпочтительнее в разных случаях. Я лишь указываю структуру базы, полей связей и т.п.
                                        И кстати фильтрация по доступным полям таки есть, см. метод protected function allowed($name).

                                        НО всё это фигня. Вы так и не ответили на вопрос — так о чем статья то? О том что при наличии библиотек которые за тебя уже написали (кривых библиотек, да, ибо раз оно без PDO то deprecated по определению), уже написанные функции можно не писать? Или о том что «решает ADODB и решает их гораздо лучше»? Ну решает. Ну есть такая библиотека. Ну Есть в пхп методы __гет() и __сет(). Дальше что? О чем статья?
                                        • 0
                                          А зачем расставлять кавычки? PDO уже отменили?

                                          mysqli_connect тоже не отменили.
                                          При прямой работе с PDO нужно вставлять значения полей параметрами. То есть писать кучу кода как запрос так и параметры.
                                          В моем решении НИЧЕГО писать не надо. Умножьте ничего (и соответственно ноль ошибок) на сотни мест в проекте где надо обращатся к БД и получите не одну человеко-неделю.

                                          О том что при наличии библиотек которые за тебя уже написали (кривых библиотек, да, ибо раз оно без PDO то deprecated по определению),

                                          Вообще то ADODB может работать и через PDO — какой вариант укажешь так и работает.

                                          Если я неправильно написал имя свойства, то я хочу увидеть исключение

                                          а я хочу писать в моей сущности свойства которые не хранятся в БД (мало ли какая бизнес логика в бизнес сущности а не просто копии строки БД) и чтобы не было изза этого никаких исключений.
                                          Разница просто в «я хочу» не более того.

                                          НО всё это фигня. Вы так и не ответили на вопрос — так о чем статья то?

                                          И не должен — но то есть текст статьи.

                                          • 0
                                            Не хотите сохранять — не сохраняйте. Есть геттеры/Сеттеры, сценарии и много чего еще.
                                            Отказ от параметров в запросах с написанием оных руками (иначе зачем «множество раз»? У меня вон и CRUD и массовый и единичный, и условия по массиву и по единичной записи — уже готовые генерятся) — это путь либо в wet либо в дырявое решето.
                                            • 0
                                              Есть геттеры/Сеттеры, сценарии и много чего еще.

                                              конечно. Мир велик и разнобразен. Суть том что в моем решении -«этого много чего» просто не нужно.
                                              Отказ от параметров в запросах с написанием оных руками

                                              пример того чего не нужно — ни параметров ни написания их руками.

                                    • +2
                                      Может вы уберёте простыню под спойлер?!
                                      • 0
                                        Простите. Упустил. А редактировать поздно.
                                    • +1
                                      И кстати забавно не то, что вы не понимаете какие-то простые вещи которые вам повторяют. Все писали велосипеды, и все капитанили. У меня у самого схожих по тупости статей было навалом. Смешит ваш подход — все тупые, и не могут понять мою гениальную мысль :)
                                      • –5
                                        Пока что не поняли вы один и то потому что не собираетесь ничего понимать.
                                        • +1
                                          А вы хотите чтобы вам 10 человек одно и то же написали? Ну я, например, тоже не понял. У вас нет ничего нового ни в идее, ни в реализации. Генерировать SQL из ассоциативного массива это первая мысль, которая появляется при изучении работы с БД в PHP, а маппинг таблиц на сущности есть практически в любом фреймворке.
                                  • +4
                                    А над какими проектами Вы работали, где, в подавляющем большинстве, нет необходимости в валидации и связях? Можно посмотреть или школьники уже сломали?
                                    • –1
                                      Я не говорил что валидация не нужна. Я не вижу какие тут проблемы с валидацией.

                                      Что касается связей — я имел ввиду не то что существуют логические связи в бизнес-логике а то что необходимо каждый раз вытаскивать все эти связи в виде гроздьев объектов на страницу контекст которой будет разрушен через долю миллисекунды и в следующую милисекунду пять посылать в Бд запрос с кучей джойнов (причем неоптимальный запрос — ORM не такие умные ) и создавать в памяти десятки если не сотни не нужных в данный момент объектов.

                                      Это имеет смысл в яве где объекты сущностей могут находится в памяти ява-машины и быть повторно использованными. Но речь конкретно о PHP а не о теоретизировании насчет общих принципов работы с БД.

                                      • 0
                                        необходимо каждый раз вытаскивать все эти связи в виде гроздьев объектов на страницу контекст которой будет разрушен через долю миллисекунды

                                        А никто и не говорит про "каждый раз". Но это с завидной регулярностью оказывается нужно в бизнес-логике, и вот тогда адекватные агрегаты оказываются намного проще для использования, чем несвязанные классы.

                                        • –1
                                          А никто и не говорит про «каждый раз». Но это с завидной регулярностью оказывается нужно в бизнес-логике,

                                          Это смотря как писать бизнес-логику. У меня есть проект учетной системы с кучей сущностей и ни разу не понадобилось вытаскивать какие то связи. Просто такова особенность PHP — каждый раз страница формируется по новой посему обьективно нужно вытаскивать только то что отображается на данным момент. Поэтому нет никакой «завидной регулярности» и быть не может. По крайней мере до тех пор пока в PHP не появится возможность персистентного хранения объектов в памяти.

                                          • 0
                                            Это смотря как писать бизнес-логику.

                                            Ну да, тут вам никто, включая Эванса, не указ.


                                            У меня есть проект учетной системы с кучей сущностей и ни разу не понадобилось вытаскивать какие то связи.

                                            Завидую вам, что тут еще скажешь.


                                            Просто такова особенность PHP — каждый раз страница формируется по новой посему обьективно нужно вытаскивать только то что отображается на данным момент.

                                            Ну например, надо вытащить список кастомеров с количеством и общей суммой заказов для каждого кастомера. Как это сделать, сохранив объектную модель, и не имея связей?

                                        • 0
                                          Вот. Вот теперь сразу стало всё понятно. Вот этого комментария и не хватало чтобы понять статью. Без комментария «я не знаю как работают ORM, зачем там связи и что такое жадная отложенная и прочие виды загрузок» было не понятно что и зачем тут написано :)
                                          • 0
                                            чтобы понять статью достаточно было последнего абзаца асчет перфекционистов.
                                            Для 99.9% проектов которые пишет каждый день обычный трудяга -програмист нафиг не нужны никакие ни «жадные» ни «щедрые» виды связей.
                                            Это просто не актуально. Глупо усложнять решение тем что будет использоватся в одном случае из тысячи. Для этого есть другие готовые решения.

                                            • +2
                                              классно вы статистику по проектам то выдаёте :) что-то у меня обратная совсем статистика
                                              • 0
                                                серьезно? 99.9% не могут функционировать без ORM типа доктрины, загрузки связей, ленивой загрузки, кеширования и прочего?
                                                Ну возмите статистику по посещаемости — у какого процента сайтов средняя загрузка хотя бы сотня обращений к Бд в секунду. Или по другому — сотни тысяч посещений в сутки.

                                                • 0
                                                  Могут, но если проект долгий, вы почувствуете разницу в сложности управления связей руками и на уровне ORM
                                                  А касательно кеширования и ленивой загрузки — во всех проектах для быстродействия мы делали это. Неважно с или без ORM
                                                  • 0
                                                    не почувствую потому что нет необходимости ни в каком управлении. Это проблема тех кто создает связи ради применения ORM
                                                    А если проект длительный и постоянно переделывается да так чтобы не свалился продакшен то там неизбежно возникает такой бардак и столько костылей что зачастую разруливается только нативными SQL. И не надо мне про книги типа совершенный код, паттерны проектирования и прочую теорию которая НА ПРАКТИКЕ работает не далее написания ТЗ.


                                                    • +1
                                                      классическая связь почти для любого проекта.
                                                      User, который связан с UserProfile, UserPreferences, UserACL. Всем этим так или иначе надо управлять. и это идёт из требований.

                                                      И не надо мне про книги типа совершенный код, паттерны проектирования и прочую теорию которая НА ПРАКТИКЕ работает не далее написания ТЗ.


                                                      мы с вами наверное совершенно на разных проектах работаем, потому что у нас теория вписывается в практику. понятно, что есть неидеальные моменты, но все эти книги как раз таки написаны, основываясь на практике.
                                                      • –1
                                                        User, который связан с UserProfile, UserPreferences, UserACL. Всем этим так или иначе надо управлять.

                                                        Не вижу зачем тут нужен именно ORM. Да и не во всех проектах есть ACL и тому подобное.
                                                        мы с вами наверное совершенно на разных проектах работаем

                                                        на каких проектах мы работаем не имеет значения и не относится к сути дела.
                                                        Я не предлагаю замену doctrine или еще чего.
                                                        Речь о том чтобы упростить разработку обычных сайтов — не порталов и не соцсетей. Сайтов где не критична скорость и нет сотен таблиц в БД.
                                                        И таких сайтов подавляющее большинство.
                                                        Ок. Не 99.9%. Возмем принцип Парето — 80% — это тоже до фига.

                                                        • 0
                                                          покажите где я написал про ORM или доктрину, на них свет клином не сошёлся. я говорю в целом про решение связанное с работой с БД.

                                                          Речь о том чтобы упростить разработку обычных сайтов — не порталов и не соцсетей. Сайтов где не критична скорость и нет сотен таблиц в БД.


                                                          ну в таком случае ваше решение тоже не самое простое, зачем beforeSave, afterSave и тд?
                                                          • 0
                                                            не надо не используйте. Но события жизненного цикла должны быть.
                                                            Да и нету там ничего сложного — строчка кода в методе Save()
                                                            Но суть не в том сложное решение или нет. Суть в том чтобы упростить разработку.
                                                            Проблема аналогичных решений не в том что они сложные (в ADODB тоже кода до фига)
                                                            а в пороге вхождения и количестве танцев с бубном при использовании — маппинге, генерации всяких прокси и т.д. вместо просто сесть и писать прикладную бизнес логику. Которую потом и читать проще будет тем кто сопровождает.

                                                            • 0
                                                              вы сравниваете только своё решение и доктрину. это два разных полюса, чёрное и белое. а есть ещё серое.

                                                              https://github.com/analogueorm/analogue
                                                              https://github.com/j4mie/idiorm
                                                              https://github.com/jpfuentes2/php-activerecord

                                                              да и ещё много
                                                        • 0
                                                          Могу еще проще пример привести:
                                                          После нормализации базы производители отделены от марок автомобилей.
                                                          Ну и хочу вывести во вью описание автомобиля. Хочу сделать во вью простое <?=$mark->brend()->name?> но не могу ибо связей нет, и приходится это городить уже в контроллере.
                                                          • 0
                                                            а что мешает во вью машин выводить имя производителя?

                                                            Нужно програмировать от бизнес-логики а не от того могу ли я написать выражение с двумя стрелочками.

                                                            • 0

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

                                                              • 0
                                                                Во вью в терминах MVC мешает здравый смысл.

                                                                ИМХО использование MVC в вебе вообще лишено здравого смысла (и проблемы работать с представлениями если они есть только это подтыверждают). Лично я предпочитаю компонентные решения.

                                                                Вообще не рекомендуется ими злоупотреблять без особой необходимости.

                                                                Вообще ничем в не рекомендуется употреблять без особой необходимости.

                                                              • 0

                                                                А где это имя хранить в вашей архитектуре?
                                                                На уровне БД машины и производители лежат в разных таблицах. Т.е. нужен будет джойн. В коде это, соответственно, 2 класса-сущности.
                                                                Как с помощью вашей библиотеки получить список список авто и связанных с ними производителей?
                                                                Как поддерживать консистентность данных при этом?


                                                                Вообще, не знаю, что за проекты у вас, но в большинстве проектов, использующих реляционные БД так или иначе возникают потребности в связях (не хранить же всё денормализированным).

                                                                • 0
                                                                  Как с помощью вашей библиотеки получить список список авто и связанных с ними производителей

                                                                  не вижу зачем одновременно нужен список машин и список производителей со всеми полями.
                                                                  Поле имени производителя выберется из представления и будет доступно в сущности авто (это же PHP с динамическими свойствами).

                                                                  если вам понадобится просмотреть детальную инфу производителя это будет уже другая страница с другим контекстом к в котором можно загрузить целиком производителя как отдельную сущность.

                                                                  использующих реляционные БД так или иначе возникают потребности в связях

                                                                  Вы понимаете что логическая связь в БД и то что вы пытаетесь выбирать это не одно и тоже. Даже если связь в БД прописана констрейтами это ничего не меняет.
                                                                  Попробуйте мыслить не категориями ORM а бизнес-логикой приложения — ORM вторичен его наличие или отсутсвтвие никак не влияет на архитектуру данных в БД.


                                                                • 0
                                                                  а что мешает во вью машин выводить имя производителя?

                                                                  То, что теперь представление в БД (вы же его имеете в виду) напрямую зависит от того, какие данные нужны в UI. И каждое изменение в UI будет влиять на БД.


                                                                  Нужно програмировать от бизнес-логики

                                                                  Так то, что показано — и есть пример бизнес-логики (как ее понимает, скажем, Эванс).

                                                                  • 0

                                                                    Нее. Это не пример бизнеслогики. В бизнес-логике были изначально аккумуляторы у которых были модели у которых уже марки. Но я решил упростить.)

                                                                    • –1
                                                                      это степень детализации. Рассматриваем машину как старик Хоттабыч телефонный апарат.
                                                            • +3
                                                              не надо мне про книги типа совершенный код, паттерны проектирования и прочую теорию которая НА ПРАКТИКЕ работает не далее написания ТЗ.

                                                              То есть подождите, я правильно вас понял, вы считаете GoF, PoEAA, Code Complete и так далее — не работающей на практике теорией?

                                                              • 0
                                                                я считаю что есть практическое програмирование а есть люди которые любят кидать умняки и употреблять модные термины.
                                                                В свое время работал в почтовом ящике где проектировали системы управления для космоса ( а пока я ходил в детсад для SS-18 Satan).
                                                                И как-то то работало (и кое где до сих пор) без книг фаулера, макконела и банды четырех.
                                                                Ничего не имею против теории но она не заменит здравого смысла, прагматического подхода и прямых рук.

                                                                • 0

                                                                  Я, вроде бы, задал конкретный вопрос — перечисленные книги работают на практие или нет?


                                                                  И как-то то работало (и кое где до сих пор) без книг фаулера, макконела и банды четырех.

                                                                  Ключевое слово — "как-то". Речь идет не о том, что без них не работает, речь идет об увеличении качества/уменьшении расходов.

                                                        • 0
                                                          В том то и дело. Даже в простеньких порталах на полтора десятка моделей уже без связей ад адовый, толстые контроллеры, и прочие антипаттерны. Вот прямо сейчас на скорую руку накостылили небольшой проект без связей, типа «потом добьем». Ад. Но дедлайн есть дедлайн. И слушать о том что «связи редко кому нужны» мне смешно :)
                                                          Черт, у меня контроллеры до 30 строк доходят потому что связей не хватает!!! рас
                                                          • 0
                                                            Грамотно написанные модели не требуют чтобы их связывали по полтора десятка в одной выборке.
                                                            Посему их количество не имеет значения.
                                                            Делайте модели не от фонаря а чтобы они соответствовали бизнес-сущностям и не будет вопроса.

                                                            • +1
                                                              Например, есть сущность «Заказ», у него есть поле user_id. Как вы выводите имя пользователя в списке заказов?
                                                              • 0
                                                                ну это же очевидно — пишется представление куда приджойнивается имя юзера — стандартный подход который и так должен применятся.

                                                                Таким образом это имя появится в объекте сущности
                                                                Заказ. Только нужно указать имя представления в анотации класса чтобы вибирать с него а не с таблицы.

                                                                Или по вашему вытаскивать юзеров целиком на каждую строку записи о заказе это правильней? А если к заказу еще есть манагер который отвечает за заказ? и их всех тащить вместо приджойнить имя манагера в представление?

                                                                • 0
                                                                  А что кроме * в select больше ничего нет?)
                                                                  И что для каждого случая писать свой вью?))))
                                                                  Мне кажется я понимаю откуда берутся люди пишушие такие вещи как опенкарт.
                                                                  • 0
                                                                    В моем решении можно выбрать и произвольным запросом. И опять же ничего не надо програмировать.

                                                                    Кроме того если вам нужны сложные выборки, например заказчик захотел витиеватый отчет) вам никакой ORM не поможет.

                                                                    • –1
                                                                      В моем решении можно выбрать и произвольным запросом. И опять же ничего не надо програмировать.

                                                                      То есть SQL-запрос писать не надо?

                                                                      • 0
                                                                        не надо писать геттеры, сеттеры, маппинги.вареники из объектов со стрелочками тыкающими в другие объекты и прочее.
                                                                        • 0
                                                                          не надо писать геттеры, сеттеры, маппинги

                                                                          А их и так писать не надо.


                                                                          А SQL-запрос, по-вашему, не программирование?

                                                                  • +1
                                                                    А для страницы просмотра заказа делать другое представление со своими джойнами? И при добавлении/удалении полей делать ALTER VIEW для каждого?

                                                                    Делается какой-нибудь метод with($relationName), который после основного запроса делает второй к таблице пользователей с условием WHERE id IN ($userIDs), и заполняет связь user у всех $orders. Без лишних джойнов.
                                                                    • –1
                                                                      во первых представления в любом случае бэст практикс тем более что большинство сущностей которые надо приджойнить известны уже на стадии проектирования. Очевидно же что если есть заказ то там есть заказчики так или иначе понадобится посмотреть кто он.

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

                                                                      • 0
                                                                        откуда у вас информация, что использование БД представлений — бэст практис?
                                                                        • –1
                                                                          вы это серьезно? Попробуйте сказать такое на sql.ru.
                                                                          • 0

                                                                            Совершенно серьезно. У вас информация с sql.ru?

                                                                            • 0
                                                                              Мне авторитетнее в этом вопросе разработчик MySQL https://www.percona.com/blog/2007/08/12/mysql-view-as-performance-troublemaker/
                                                                              • 0
                                                                                То что в Mysql проблемы с представлениями — это проблемы Mysql. В промышленных серверах БД такой проблемы нет. Просто выбирайте подходящий инструмент.
                                                                                Хотя и в Mysql это не проблема если речь не идет о высоконагруженном проекте о коих мы не говорим.

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

                                                                                  Всё, что я могу сказать по представлениям — это вынос логики из кода в БД, так же как хранимые пооцедуры (если не ошибаюсь представления и реализовпны как хранимки), поэтому он должен быть обоснован
                                                                                  • 0
                                                                                    ок, что такое промышленные сервера БД

                                                                                    MSSQL, Oracle и иже с ними. Сервера для решений энтерпрайз уровня.

                                                                                    Сервера в разработку алгоритмов оптимизатора запросов вкладывают милионы долларов. Чего очевидно никто не вкладывает в mysql.

                                                                                    если не ошибаюсь представления и реализовпны как хранимки

                                                                                    Представления реализованы как SQL запросы.
                                                                                    это вынос логики из кода в БД, так же как хранимые пооцедуры

                                                                                    насчет ХП согласен. Представления никак не влият на бизнес-логику вам никто не мешает обращатся прямо к таблицам.
                                                                                    представления позволяют избежать написания сложных SQL запросов.
                                                                                    А запросы бывают не на один десяток строк — особенно в разного рода репортах.
                                                                                    Если даже удастся такое собрать в ORM то либо ляжет приложение либо ляжет сервер БД.
                                                                                    кроме того вьюхи это логический слой — вам может понадобится скоректировать структуру БД Обычное дело — проект растет набирает данных и сервер дохнет.
                                                                                    Если у вас вьюха вы меняете структуру таблиц и ваше приложение продолжает работать.
                                                                                    это реальные ситуации в каждом втором реальном проекте.

                                                                                    • 0
                                                                                      Сервера в разработку алгоритмов оптимизатора запросов вкладывают милионы долларов.

                                                                                      … и эти оптимизаторы давно уже не делают различий между представлением и просто запросом.


                                                                                      Если даже удастся такое собрать в ORM то либо ляжет приложение либо ляжет сервер БД.

                                                                                      Почему это?


                                                                                      кроме того вьюхи это логический слой — вам может понадобится скоректировать структуру БД

                                                                                      Для этого уже давно придумали слой прикладного сервера.

                                                                          • 0
                                                                            Очевидно же что если есть заказ то там есть заказчики так или иначе понадобится посмотреть кто он.

                                                                            Нюансы скрываются в "так или иначе", которые очень быстро могут меняться с каждой версией требований.

                                                                            • 0
                                                                              Представления иногда оправданы для десктопных приложений. В веб-приложениях они используются очень редко, потому что — а зачем?
                                                                              • 0
                                                                                затем же что и в десктопных.
                                                                                • 0

                                                                                  Так зачем же?

                                                                                • 0
                                                                                  (* не дописал)
                                                                                  Пользователь, подключающийся к БД, один, программный код один, запрос с джойнами генерируется ORM либо пишется один раз, права пользователя приложения удобнее проверять кодом на серверном языке, а не в SQL.
                                                                                  • 0
                                                                                    запрос с джойнами генерируется ORM либо пишется один раз

                                                                                    Только генерит SQL за который хочется руки повыдергивать. Я тоже напишу его один раз если он не вписывается в существующие представления.

                                                                                    права пользователя приложения удобнее проверять кодом на серверном языке, а не в SQL

                                                                                    А кто против? С этой точки зрения и констрейнты не нужны.
                                                                                    Но всякое бывает — мало ли какое еще приложение пользует ту же БД. А еще могут и руками напрямую полезть.
                                                                                    Никто не проектирует БД в зависимости от того какой ORM в приложении. Сначала проектируется грамотная структура хранилища а потом хоть ORM хоть чертом.

                                                                                    • +1
                                                                                      Я тоже напишу его один раз если он не вписывается в существующие представления.

                                                                                      Только вы еще и запросы для представлений напишете, и поддерживать их будете.

                                                                                      Никто не проектирует БД в зависимости от того какой ORM в приложении

                                                                                      Никто не проектирует с точки зрения «а еще могут и руками напрямую полезть». Вернее, может кто-то и проектирует, но делать так не надо.

                                                                                      Проектирование БД и навешивание представлений на спроектированную БД — это не одно и то же. Наличие/отсутствие ORM на структуру таблиц не влияет. Вопрос в том, зачем использовать представления в веб-приложении.
                                                                                      • 0
                                                                                        Никто не проектирует с точки зрения «а еще могут и руками напрямую полезть».

                                                                                        вобще то именно такие вещи и учитываются при проектировании — потому и ставятся констрейты.
                                                                                        Пусть это не прямо руки -пусть это просто другое приложение, десктопное, мобильное и т.д.
                                                                                        А в более менее сложных проектах без исправлений руками в БД вообще не обходится. Гладко бывает только в книгах по реляционной алгебре.
                                                                                        Вопрос в том, зачем использовать представления в веб-приложении.

                                                                                        Они используются БД а не в веб приложении.
                                                                                        Когда я проектирую БД я не думаю о там что там за приложеие. Я не знаю какое будет приложение завтра, будет оно веб или не веб. Сегодня веб завтра мобильное после завтра REST сервис. Мне потом БД переделывать? А если разные приложения одновременно?
                                                                                        Нет, я ее проектирую как положено проектировать хранилища данных.
                                                                                        Даже не понимаю смысла данной дискусии.

                                                                                        • +2
                                                                                          Зачем переделывать БД? Таблицы одинаковые для любого приложения. Единственное это если вы вдруг решили из мобильного клиента коннектиться напрямую к базе, и хотите через представления ограничить права пользователю. По-моему, это слишком редкий случай, а вы там выше писали «ориентируюсь на большинство обычных задач которые нужны повседневно». А вообще в таких случаях для мобильного приложения делается API.

                                                                                          Кстати, не подскажете ссылки на материалы о том, что положено проектировать хранилища данных через представления?)
                                                                                          • 0
                                                                                            Зачем переделывать БД? Таблицы одинаковые для любого приложения.

                                                                                            потому что это приходится делать НА ПРАКТИКЕ по объективным причинам.
                                                                                            И не потому что это кому то хочется.


                                                                                            подскажете ссылки на материалы о том, что положено проектировать хранилища данных через представления?)

                                                                                            как только покажете литературу где написано что положено использовать ORM.

                                                                                            • 0
                                                                                              потому что это приходится делать НА ПРАКТИКЕ по объективным причинам.

                                                                                              Какие объективные причины требуют от вас переделывать таблицы с появлением нового приложения?


                                                                                              как только покажете литературу где написано что положено использовать ORM.

                                                                                              Ну так PoEAA же.

                                                                                              • 0
                                                                                                Я нигде не писал, что положено использовать ORM. Даже наоборот: «Наличие/отсутствие ORM на структуру таблиц не влияет». Это верно для 99.9% случаев (ок, 80%). User — он в любом приложении User.
                                                                                                Ладно, литературу вы почему-то привести не желаете, может какой-нибудь пример из практики приведете?
                                                                                                • –1
                                                                                                  нет никакой литературы. Есть выработаные практикой принципы грамотной разработки хранилищ БД.
                                                                                                  Так же как есть неписаные законы по написанию програмного кода, верстке и т.д.
                                                                                                  И есть многолетная практика разработки (в том числе и мой личный опыт)
                                                                                                  Литература — это для начинающих.
                                                                                                  Опытный разработчик БД — сразу как создал таблицу, создает и вьюху на нее. Одни к одному, без джойнов. просто затем чтобы разработчик приложения сразу юзал вьюху а не влепил таблицу а через две недели перебивал код на представление.
                                                                                                  Потому что все равно придется.
                                                                                                  Ну а не придется для какой то таблицы наличие вьюхи никак не отразится на работе — ничего не теряем.
                                                                                                  Но практика показывает обратное приходится менять структуру, (или как минимум типы полей). Причины — начиная от изменения требований к проекту заканчивая необходимостью
                                                                                                  денормализации после того как Бд заполнена данными и долгих танцев с бубном с профайлером и изучением планов запросов чтобы выяснить узкое место.

                                                                                                  Представте что вы пишете приложение на PHP. Вы можете написать код PHP (лии верстку HTML и CSS) так чтобы в процессе проекта его ни разу не пришлось коректировать? Сомневаюсь.
                                                                                                  Точно так же и с БД.
                                                                                                  Объяснять вам бесполезно -сами наберетесь опыта столкнувшишь с более менее серьезным проектом.


                                                                                                  • +1
                                                                                                    вот честно, приходилось работь и общаться с архитекторами БД и представления они использовали в случаях:
                                                                                                    — спрятать какие-то данные(колонки) из запрсов
                                                                                                    — спрятать тяжелые запросы, например, при генерации отчета
                                                                                                    — для некоторых таблиц заведомо было известно, что их структура будет меняться, причем часто. Там тоже добавляли вью

                                                                                                    Лично я вас просил указать статьи/книги и тд, где рассказано, почему обильное использование представлений хорошо, потому что мой опыт и опыт коллег это не доказывает. На мой взгляд, добавление вьюх на каждую таблицу добавляет лишней сложности, потому что теперь сопровождать надо не только таблицы, но и вью
                                                                                                    • 0

                                                                                                      Андрей, Вы что еще не поняли?
                                                                                                      Человек ОПЫТНЫЙ, неопытных не слушает.
                                                                                                      К сожалению у Леонида не указан возраст, но такая проблема часто встречается у разработчиков с возрастом. На нее Леонид уже неоднократно намекал, в частности "как-то же ракеты запускали без всяких этих ваших умных подходов".
                                                                                                      В частности здесь мы видим подход который имел место лет пять назад, когда вьювы уже были более менее популярны, а про миграции еще мало кто знал. Вот человек познакомился с подходом и на нем и сидит. Аналогично и с остальными вопросами. И со связями и со всем остальным.


                                                                                                      У меня был похожий период лет десять назад, с восьмибиток, бейсиков ассемблера и т.п. слазил тяжело.Нет, вроде Си и Паскаль знал, вроде другие языки были понятны, но подходы оставались те же.
                                                                                                      Не, ну а чё? годами работало, чего менять то рабочий подход? :)


                                                                                                      Потом вот точно так-же лет пять назад спорил тут на хабре и на серче на примерно похожие темы. Только я отрицал пользу таких паттернов как АктивФорм и им подобных. Я тогда навелосипедил свой "шаблонизатор" на конечных автоматах с тремя управляющими конструкциями и упорствовал что 640килобайт хватит каждому...


                                                                                                      Тут или перерастет, или нет. Нам не повлиять :)

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

                                                                                                        Скорее — десять-пятнадцать. Пять лет назад мы уже всеми силами от представлений в БД избавлялись (наевшись, в частности, проблем с их поддержкой).

                                                                                                        • 0
                                                                                                          я сейчас а не пять лет назад сижу на проекте для англичан по логистике складов. Причем именно веб приложение. Даже в страшном сне не представляю как бы я работал тут с одними таблицами. Даже с учетом вьюх приходится на некоторые репорты наворачивать запросы по двадцать тридцать строк кода.
                                                                                                          Сервер БД не в курсе какие вы там теперь продвинутые и шо там за приложения — пока рулят реляционные БД пока и останутся неизменными принципы разработки реляционных хранилищ.
                                                                                                          Возможно если речь о Mysql то таки да, он оптимизирован под быстрые линейные выборки с одной таблицы и на джойнах работает не очень.


                                                                                                          • 0
                                                                                                            Сервер БД не в курсе какие вы там теперь продвинутые и шо там за приложения — пока рулят реляционные БД пока и останутся неизменными принципы разработки реляционных хранилищ.

                                                                                                            Внезапно, представления не входят в "неизменные принципы разработки реляционных хранилищ".

                                                                                                        • 0
                                                                                                          а мой опыт и опыт моих коллег доказывает.
                                                                                                          возьмите хранилище какого нибудь серьезного проекта типа Oracle Apex и посмотрите.
                                                                                                          Или как я уже рекоменовал зайдите на sql.ru где тусуются спецы по БД и заявите что представления отстой.
                                                                                                          Я про хранимки такое сказал и то выгребал эпитеты в коментах целый день.
                                                                                                          Еще раз — если в проекте больше пары десятков таблиц без представлений не обойтись. Либо выгребать каждый раз сотни ненужных объектов в приложение связывая по десятку таблиц в ORM что очевидно глупо.

                                                                                                          • 0
                                                                                                            Либо выгребать каждый раз сотни ненужных объектов в приложение связывая по десятку таблиц в ORM что очевидно глупо.

                                                                                                            Вообще-то, строго наоборот. Если я использую ad-hoc-запросы вместо представлений, я получу ровно те данные, которые мне нужны, а не те, которые в представление заложил автор.

                                                                                                            • +1

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


                                                                                                              Трезвые, современные разработчики пишут примерно так:
                                                                                                              Бутстрапим структуру базы, бутстрапим CRUD и прочие базовые вещи кодогенерацией и ORM, правим структуру, допиливаем неучтенные моменты ТЗ, оптимизируем узкие места структуры или кода.
                                                                                                              Подобная дорожная карта сокращает время разработки, сложность поддержки и количество ошибок — на порядок.
                                                                                                              При этом 90% задач (не проектов, а грубо говоря "сервисов") обходятся вообще без оптимизации, и 90% из тех где оптимизация таки нужна — решаются средствами ORM вроде добавления инструкции для жадной загрузки или указания того какие поля тянуть а какие нет. Для остальных случаев уже идут прямые запросы (и то через построитель), вьювы и хранимки.


                                                                                                              Я понимаю что вы из мира велосипедных ERP, где чуть другая специфика. Но частое изменение структуры которое лучше делать вьюа не миграцией да еще и запросы по два экрана это
                                                                                                              как правило признак изначально плохой архитектуры.
                                                                                                              Уверен что если вы почитаете литературу, посмотрите классику, например как денормализацию делает тот же 1С (да и вообще почитаете откуда взялось название 1С :) ), то 90% ваших сложностей исчезнет.

                                                                                                              • 0
                                                                                                                Бутстрапим структуру базы, бутстрапим CRUD и прочие базовые вещи кодогенерацией и ORM

                                                                                                                Или наоборот. Я тут недавно писал некий мелкий проектик, так там БД появилась сильно после того, как половина прикладного слоя была написана и протестирована. Заодно ее структура к этому моменту была полностью понятна и очевидна.

                                                                                                                • –1
                                                                                                                  1С до 8 версии работала на DBF что не помешало ей стать успешным проектом. посему пример неудачный.
                                                                                                                  Кстати именно вьюхи позволяют прозрачно денормализовать БД не создавая разрабам приложений лишних сущностей.
                                                                                                                  Не вижу смысла дискусии о представлениях в контексте данной статьи…
                                                                                                                  Уверен на хабре если не было то рано или поздно будет соответствущая публикация и соответствующий холивар.
                                                                                                                  Для проектов которые архитектурю я больше подходят представления.
                                                                                                                  Можете считать меня ретроградом но я также считаю что MVC на вебе идиотизм. Ангуляры и прочие способы вытащить бизнес логику на клиента — еще больший идиотизм.
                                                                                                                  Про ORM в PHP, которые тупо портируют с явовского Hibernate или нетовского LINQ я уже говорил. Посему ваши аргументы что там как надо бустрапить не ко мне.
                                                                                                                  В своей карьере я уже повидал не один модный тренд в ИТ переживу и нынешние.

                                                                                                                  • 0
                                                                                                                    1С до 8 версии работала на DBF что не помешало ей стать успешным проектом. посему пример неудачный.

                                                                                                                    Да хоть на CSV. К чему это вообще? 1С во многом занял свою позицию в том числе и благодаря грамотной денормализации. В то время это еще не было типовым и очевидным. Посмотрите на их базовые сущности вроде "регистров" различных видов. Не утверждаю что это лучший подход, но один из удачных моментов позволяющих избегать тридцатиэтажных тяжелых запросов существующих в любой ERP. Мне пофиг что за движок у них на уровне СУБД. Да, в чем-то их абстракция в свой язык запросов поверх ORM который поверх нативного SQL — напоминает ваше злоупотребление вьювами, но я не о том…
                                                                                                                    А впрочем ладно. Всё сказано вашей последней фразой:
                                                                                                                    В своей карьере я уже повидал не один модный тренд в ИТ переживу и нынешние.
                                                                                                                    • +1
                                                                                                                      Можете считать меня ретроградом

                                                                                                                      Оно и видно по http://zippy.com.ua/

                                                                                                              • 0
                                                                                                                Есть выработаные практикой принципы грамотной разработки хранилищ БД.

                                                                                                                Которые не записаны ни в какой литературе? Не смешите меня.


                                                                                                                Опытный разработчик БД — сразу как создал таблицу, создает и вьюху на нее.

                                                                                                                Я видел много опытных разработчиков БД, и никто из них так не делает сейчас.


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

                                                                                                                Вставки, обновления и удаления — тоже через представления?


                                                                                                                Потому что все равно придется.

                                                                                                                Странно, мне вот не приходилось последние годы. А главное, вы так говорите "перебивать код на представления", как будто это сложнее, чем исправить ровно одну строчку в приложении.


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

                                                                                                                … кроме необходимости поддерживать два объекта БД вместо одного. Очень милое дело, ага.

                                                                                                        • 0
                                                                                                          Они [представления] используются БД

                                                                                                          Для чего?


                                                                                                          Когда я проектирую БД я не думаю о там что там за приложеие.

                                                                                                          Ну и зря.


                                                                                                          Я не знаю какое будет приложение завтра, будет оно веб или не веб. Сегодня веб завтра мобильное после завтра REST сервис. Мне потом БД переделывать? А если разные приложения одновременно?

                                                                                                          Вы правда никогда не слышали про трехзвенную или сервисную архитектуру?

                                                                                                      • 0
                                                                                                        Только генерит SQL за который хочется руки повыдергивать.

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


                                                                                                        Но всякое бывает — мало ли какое еще приложение пользует ту же БД.

                                                                                                        Не "мало ли". Каждый слой приложения должен быть контролируем, иначе вы просто не сможете вносить предсказуемые изменения.


                                                                                                        Никто не проектирует БД в зависимости от того какой ORM в приложении.

                                                                                                        Правда? Никто-никто, никогда?

                                                                                                  • +1

                                                                                                    Вопрос не в том что лучше, вопрос в том, что проще.
                                                                                                    Если я могу передать модельку и не думать о том что для нее надо еще что-то тащить, потому что оно отлично достается по связям, то я так и сделаю.
                                                                                                    Про вьювы как "бест практик" это вы отожгли да. На каждый чих по вьюву. Больше вьювов, красивых и разных… Вьювы нарушают структуру кода утягивая часть бизнеслогики в базу, что усложняет анализ кода другими разработчиками. Да и самим спустя время. Вьювы уместны тогда и только тогда когда выигрыш от них заметен. Или сокрытие чего-то, или производительность или еще что-то. Один из тех инструментов которыми как и статикой любят злоупотреблять новички.

                                                                                                    • 0
                                                                                                      Вопрос не в том что лучше, вопрос в том, что проще.

                                                                                                      ORM никаким каком не может относится к «проще».
                                                                                                      не думать о том что для нее надо еще что-то тащить, потому что оно отлично достается по связям,

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

                                                                                                      Это в случае использования ORM при котором нужно понимать какие где таблицы.
                                                                                                      Но ставить телегу впереди лошади, то есть ORM впереди проектирования архитектуры хранилища — это точно не бэст практикс.

                                                                                                      • 0
                                                                                                        Это в случае использования ORM при котором нужно понимать какие где таблицы.

                                                                                                        Это у вас кривой ORM. В нормальном нужно понимать, где какие сущности.

                                                                                            • +1
                                                                                              как раз-таки модель/сущность, соответствующая какому-то домену, особенно сложному и имеет наличие множества связей.

                                                                                              опережая ваш ответ, что в большинстве сайтов такое не надо — так и ведь сущность (Entity) как таковая там не нужная, достаточно просто реализовать Table Data Gateway
                                                                                              http://martinfowler.com/eaaCatalog/tableDataGateway.html
                                                                                              • 0
                                                                                                во первых бизнес сущности обьективны. Если юзер есть то он есть как его не назови.
                                                                                                Во вторых — суть идеи в том что тому кто работает с сущностью юзер в простом проекте — вообще ничего не надо реализовывать.
                                                                                                кроме отнаследоватся пустым классом и указат ему имя таблицы.

                                                                              • +4
                                                                                1. Возьмём готовый DBAL из сотни файлов и прикрутим к нему ArrayAccess + Iterator, которые обзовём Entity. Ура, у нас получился ORM (ну, допустим) из одного класса.
                                                                                2. Не забудем, что чтение из docblock — это долгая процедура и прикрутим хранилище метаданных с кешированием. Ура, у нас уже больше одного файла.
                                                                                • 0
                                                                                  Возьмём готовый DBAL из сотни файлов и прикрутим к нему ArrayAccess + Iterator, которые обзовём Entity. Ура, у нас получился ORM (ну, допустим) из одного класса.

                                                                                  да, представте себе получим. Но тут не совсем ORM, впрочем какая разница как его назвать. Назовите хоть нинзя-черепашкой.

                                                                                  Это решение которое позволяет резко упростить написание кода для работы с БД. Это для тех кому надо ехать а не шашечки.
                                                                                  Не забудем, что чтение из docblock — это долгая процедура

                                                                                  сколько часов занимает чтение? А то разрабы Symfony не в курсе.
                                                                                  прикрутим хранилище метаданных с кешированием

                                                                                  это забота ADODB посему просто прикручиваем одну строку в composer.json

                                                                                  • +1
                                                                                    сколько часов занимает чтение? А то разрабы Symfony не в курсе.

                                                                                    У doctrine/annotations как минимум есть кэш. Это к слову о чтении аннотаций.
                                                                                    А doctrine/orm генерирует proxy-файлы, в которых помимо нативного php-кода больше ничего нет.

                                                                                    • –1
                                                                                      а у меня ничего никто не генерирует не плоит кучу прокси и прочего барахла. И в этом суть решения.
                                                                                      И пофиг сколько будет парсится комент — десять микросекунд или двадцать — не актуально для 99.9% реальных проектов.

                                                                                      • 0
                                                                                        Такое ощущение что 99.9 проектов на PHP это домашние pet-проекты…
                                                                                        • 0
                                                                                          это большинство сайтов в интернете. Домашние они или уличные не меняет сути дела.
                                                                                        • 0
                                                                                          с микросекундами вы слишком ошибаетесь, особенно если вернуть сотню Entity и для каждой распарсить аннотации
                                                                                          • 0
                                                                                            так не парсится для каждой это метаописание — парсится один раз когда формируется запрос к БД.

                                                                                            • 0
                                                                                              Вы видимо сами не разобрались как у вас класс работает, у вас на каждый вызов конструктора вызывается init() и в нем getMetadata(), которая не кешируется нигде.
                                                                                              Поправьте если не прав.
                                                                                              • –2
                                                                                                да. Там инициализируется нулем ключевое поле. Но как показалла практика в реальных проектах обычно метод init переружается — датами там проинициализировать или типа того.

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

                                                                                      • +1
                                                                                        > Это решение которое позволяет резко упростить написание кода для работы с БД. Это для тех кому надо ехать а не шашечки.
                                                                                        Для тех кому надо ехать уже давно придумали хотя бы и Doctrine ORM

                                                                                        > сколько часов занимает чтение? А то разрабы Symfony не в курсе.
                                                                                        Да почему же, в курсе, они же используют AnnotationReader от Doctrine
                                                                                        http://doctrine-orm.readthedocs.io/projects/doctrine-common/en/latest/reference/annotations.html
                                                                                        Ниже уже написали, как это работает.

                                                                                        > это забота ADODB посему просто прикручиваем одну строку в composer.json
                                                                                        Ну так прикрутите Doctrine ORM и не нужно будет ни одного файла.
                                                                                    • +2

                                                                                      Чтобы взять и использовать, не хватает поддержки сеттеров-геттеров для полей, как и в большинстве подобных библиотек. Допилить их не большая проблема, но видеть их из коробки — уже большое дело. Поддержка сеттеров и геттеров помимо типизации полей дает возможность легко допилить эмуляцию реляционных связей.


                                                                                      Это если закрыть глаза на:


                                                                                      • статические вызовы и переменные ($class:: в эту же копилку)
                                                                                      • отсутствие тестов (и не очень-то большую подготовленность к тестированию, честно говоря)
                                                                                      • малую распространенность ADODB

                                                                                      Ну а если бы вы положили все это в репозиторий, добавили README.md и composer.json, остальные участники помогли бы вам сделать 90% рутинной работы.


                                                                                      Было бы интересно посмотреть на сравнение (плюсы/минусы) вашей библиотеки и других существующих, вроде:



                                                                                      P.S. в чем преимущества ADODB перед, скажем, Doctrine DBAL?

                                                                                      • –1
                                                                                        Чтобы взять и использовать, не хватает поддержки сеттеров-геттеров для полей

                                                                                        Никто не запрещает писать гетеры сеттеры но суть решения в том чтобы ВООБЩЕ ничего не писать.

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

                                                                                        противоречит идее решения.

                                                                                        Было бы интересно посмотреть на сравнение

                                                                                        ну сравните. Я не знаю что делают это решения -пусть опишут их на Хабре. Но судя по коду мое проще как в реализации так и в использовании. Да и не ORM у меня и не построитель SQL.

                                                                                        в чем преимущества ADODB перед, скажем, Doctrine DBAL

                                                                                        в том что там есть нужный мне функционал, причем не требующих никаких дополнительных настроек.

                                                                                        • 0
                                                                                          Никто не запрещает писать гетеры сеттеры но суть решения в том чтобы ВООБЩЕ ничего не писать.

                                                                                          Не о том речь. Этого было бы достаточно, с условием поддержки остальных вызовов внутри вашего кода (из-за final сделать обертку без внедрения в ваш код невозможно):


                                                                                          public final function __set($name, $value)
                                                                                          {
                                                                                              if ($setter = $this->getSetter($name)) {
                                                                                                  $setter($name, $value);
                                                                                              } else {
                                                                                                  $this->fields[$name] = $value;
                                                                                              }
                                                                                          }

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


                                                                                          противоречит идее решения.

                                                                                          Не стоит противостояние гибкости реализации записывать в ключевые принципы.


                                                                                          Идея-то интересная. Реализуйте вы её не "как хочу", а, почитав вдумчиво вполне здравые комментарии, получился бы не просто какой-то набросок на gist'е, а полноценная библиотека, которая бы нашла своего пользователя. А пока только минусы ловите :(

                                                                                          • –1
                                                                                            Не о том речь. Этого было бы достаточно, с условием поддержки остальных вызовов внутри вашего кода (из-за final сделать обертку без внедрения в ваш код невозможно)

                                                                                            ну уберите финал — это же опенсорс
                                                                                            Но нужно принимать во внимание этот подход, когда придумываете названия другим методам. Сейчас приличную часть кода в связи с этим было бы правильным переписать.

                                                                                            любой програмист глядя на код другого програмиста скажет — я бы его переписал

                                                                                            Реализуйте вы её не «как хочу», а, почитав вдумчиво вполне здравые комментарии, получился бы не просто какой-то набросок на gist'е,


                                                                                            Это не набросок на гисте а немного упрощенный вариант неоднократно используемого решения. Код не идеален но это решение упрощает мне работу и меня это устраивает.

                                                                                            И таки да — речь об идее. Разве хабр не для этого? Готовые решения это в раздел Я пиарюсь.

                                                                                            А пока только минусы ловите

                                                                                            Ну понятно — есть люди которым не нравится если не по ихнему.
                                                                                            Я например не ставлю минусы за то что человек фанат ORM.

                                                                                            • 0
                                                                                              ну уберите финал — это же опенсорс
                                                                                              Challenge Accepted
                                                                                              image
                                                                                              • –1
                                                                                                не знаю что это значит но проблема с финалом у вас а не у меня
                                                                                                Я описал идею. Для того и Хабр — для обмена идеями и опытом.
                                                                                                Мог описать на «пальцах» и никто бы не придирался к коду. Но это другая крайность.
                                                                                                Просто считайте что код это драфт который я набросал на салфетке в кафе для демонстрации идеи. И забудьте про финалы, моки и прочие несуществующие в контексте статьи проблемы.



                                                                                        • –1
                                                                                          Ну а если бы вы положили все это в репозиторий, добавили README.md и composer.json, остальные участники помогли бы вам сделать 90% рутинной работы.

                                                                                          не помогли бы — у каждого свой самый клевый велосипед.
                                                                                          • 0
                                                                                            P.S. До недавнего момента я упорно думал (не прочитав документацию по ссылкам, разумеется), что речь о MS ADO

                                                                                            Вчитавшись в документацию и коды, я не нашел никаких преимуществ ADODB(php) перед Doctrine/DBAL, кроме большего количества поддерживаемых БД (7 vs 25 с натяжкой).
                                                                                            Напротив: комбайн все-в-одном (это может быть и плюсом, и минусом), древняя архитектура с множеством legacy-кода, отсутствие современных стандартов и тестов, глобальные функции, отсутствие неймспейсов и т.д.
                                                                                          • +4

                                                                                            По-моему, там не совсем entity по ссылке. Ну и именовать методы надо получше, а то видим findCnt, понимаем, что описочка вышла, букву пропустили. Только вот сходу не ясно u пропустили или o...

                                                                                            • –1
                                                                                              По описанию понятно суть, кто хотел понять. Терминология не принципиальна… Редко какое решение 100% соответствует терминам.
                                                                                              Ну и именовать методы надо получше, а то видим findCnt, понимаем, что описочка вышла, букву пропустили. Только вот сходу не ясно u пропустили или o...

                                                                                              Решение используется в куче моих проектов как то стремно что то переименовывать.
                                                                                              Кому надо возмет и переименует как захочет.

                                                                                              • +1

                                                                                                Если уж хочется назвать как-то по своему, не вижу препятствий. Но, я бы всё-таки не переиспользовал уже существующие устоявшиеся термины, которые означают несколько иные решения. Путает.


                                                                                                Я уже делал эту ошибку и могу сказать, что порождаемые ей непонятки и дискуссии кушают кучу времени. Лучше превентивно ошибки избежать...

                                                                                                • 0
                                                                                                  ну я не знаю какой тут термин. Скорее Entity чем что то другое.
                                                                                                  Имеется класс соответсвующий бизнес-сущности, сущности представленой строкой в БД.
                                                                                                  Есть какие-то тонкости? А в каких решениях именуемых Entity их нет?

                                                                                                  • 0
                                                                                                    во многих фреймворках это называется Active Record. Такое же название использует и Фаулер в PoEAA
                                                                                                    • 0
                                                                                                      да
                                                                                                      с точки зрения архитектуры Active Record.
                                                                                                      С точки зрения бизнес-логики — Entity. Кто сказал что AR не может быть Entity?
                                                                                                      И с каких пор Фаулер стал истиной в последней инстанции.

                                                                                                      • +1
                                                                                                        просто довольно авторитетный человек
                                                                                                        Просто то, что у вас — это классическая Active Record, сущность, которая знает как достать себя из БД и как себя сохранить
                                                                                                        Не знаю зачем вы не стали использовать довольно устоявшийся термин
                                                                                                        • 0
                                                                                                          Я не говорю что это не AR.
                                                                                                          Я не понимаю почему это не Entity.