SQL vs ORM

    Друзья, вновь пришло время авторской колонки корпоративного блога PG Day’17. Предлагаем вашему вниманию сравнительный анализ работы с PostgreSQL из популярных ORM от varanio.


    ORM (Object-Relational Mapping), по идее, должен избавить нас от написания SQL запросов и, в идеале, вообще абстрагировать от базы данных (от способа хранения данных), чтобы мы могли работать с классами, в той или иной степени выражающими объекты бизнес-логики, не задаваясь вопросом, в каких таблицах всё это по факту лежит.


    Посмотрим, насколько это удается современным библиотекам на PHP. Давайте рассмотрим несколько типичных кейсов и сравним ORM с голым SQL, написанным вручную.


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


    Вот схема
    CREATE TABLE authors (
       id bigserial,
       name varchar(1000) not null,
       primary key(id)
    );
    
    CREATE TABLE books (
       id bigserial,
       name VARCHAR (1000) not null,
       text text not null,
       PRIMARY KEY (id)
    );
    
    CREATE TABLE author_book (
       author_id bigint REFERENCES authors(id),
       book_id bigint REFERENCES books(id),
       PRIMARY key(author_id, book_id)
    );
    

    Рассмотрим несколько кейсов использования.


    Кейс 1. Создание записей


    Добавим авторов и книг.


    Голый SQL


    Ну, тут всё просто и прямолинейно:


    Голый SQL
        $stmt = $pdo->prepare(
            "INSERT INTO books (name, text) VALUES (:name, :text) RETURNING id"
        );
        $stmt->execute(
            [':name' => 'Книга', ':text' => 'Текст']
        );
        $bookId = $stmt->fetchColumn();
    
        $stmt = $pdo->prepare(
            "INSERT INTO authors (name) VALUES (:name) RETURNING id"
        );
        $stmt->execute(
            [':name' => 'Автор']
        );
        $authorId = $stmt->fetchColumn();
    
        $pdo->prepare(
            "INSERT INTO author_book (book_id, author_id) VALUES (:book_id, :author_id)"
        )->execute(
            [':book_id' => $bookId, ':author_id' => $authorId]
        );

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


    Laravel (Eloquent SQL)


    В Laravel используется ORM под названием Eloquent. Eloquent — это, по сути, ActiveRecord, т.е. отображение таблиц на некие соответствующие им классы ("модели"), причем модель сама умеет себя сохранять.


    Итак, делаем две модели. По умолчанию даже имена таблиц нигде указывать не надо, если они называются как классы. Надо указать $timestamps = false, чтобы не сохраняло автоматически время обновления модели.


    Классы моделей Eloquent
     namespace App;
    
     use Illuminate\Database\Eloquent\Model;
    
     class Book extends Model
     {
         public $timestamps = false;
    
         public function authors()
         {
             return $this->belongsToMany(Author::class);
         }
     }

    namespace App;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Author extends Model
    {
        public $timestamps = false;
    
        public function books()
        {
            return $this->belongsToMany(Books::class);
        }
    }

    Как видно, мы запросто описали отношение many-to-many буквально парой строк кода. Создание записей в базе и связь между ними делается достаточно просто:


    $book = new \App\Book;
    $book->name = 'Книга';
    $book->text = 'Текст';
    $book->save();
    
    $author = new \App\Author;
    $author->name = 'Автор';
    $author->save();
    
    // делаем связь
    $book->authors()->save($author);

    Или списком:


    $book = \App\Book::create(['name' => 'Книга', 'text' => 'Текст']);
    $author = \App\Author::create(['name' => 'Автор']);
    $book->authors()->save($author);

    Так, конечно, поприятнее, чем возиться с SQL, и даже запись в связочную таблицу делается очень легко.


    Symfony (Doctrine ORM)


    В доктрине используется подход DataMapper. По уверениям документации, объекты бизнес-логики отделены от способа сохранения. Здесь объекты получаются из Репозитория (Repository), т.е. сущность не знает как себя получить, это знает только Repository, а для сохранения потребуется EntityManager.


    Сгенерировать классы из существующих таблиц можно одним движением:


    bin/console doctrine:mapping:import --force AppBundle yml
    bin/console doctrine:generate:entities AppBundle

    Первая команда создаст yml-файлы для сущностей, описывающие типы полей в базе, взаимосвязь объектов (например, many-to-many) и т.д. Вторая команда создаст классы сущностей.


    Прямо скажем, yml получились немаленькие, и они набиты подробностями о структуре таблиц и их связях. Вообще, можно обойтись и без yml, всё делая в аннотациях классов. Но когда классы сущностей совершенно отделены от базы, это больше соответствует концепции DDD.


    Зато сами классы-сущности у нас получились совершенно простые, т.е. POJO (plain old php object):


    Классы-сущности
    namespace AppBundle\Entity;
    
    /**
     * Authors
     */
    class Authors
    {
        /**
         * @var integer
         */
        private $id;
    
        /**
         * @var string
         */
        private $name;
    
        /**
         * @var
     \Doctrine\Common\Collections\Collection
         */
        private $book;
    
        /**
         * Constructor
         */
        public function __construct()
        {
            $this->book = new \Doctrine\Common\Collections\ArrayCollection();
        }
    
        /**
         * Get id
         *
         * @return integer
         */
        public function getId()
        {
            return $this->id;
        }
    
        /**
         * Set name
         *
         * @param string $name
         *
         * @return Authors
         */
        public function setName($name)
        {
            $this->name = $name;
    
            return $this;
        }
    
        /**
         * Get name
         *
         * @return string
         */
        public function getName()
        {
            return $this->name;
        }
    
        /**
         * Add book
         *
         * @param \AppBundle\Entity\Books $book
         *
         * @return Authors
         */
        public function addBook(\AppBundle\Entity\Books $book)
        {
            $this->book[] = $book;
    
            return $this;
        }
    
        /**
         * Remove book
         *
         * @param \AppBundle\Entity\Books $book
         */
        public function removeBook(\AppBundle\Entity\Books $book)
        {
            $this->book->removeElement($book);
        }
    
        /**
         * Get book
         *
         * @return \Doctrine\Common\Collections\Collection
         */
        public function getBook()
        {
            return $this->book;
        }
    }
    

    namespace AppBundle\Entity;
    
    /**
     * Books
     */
    class Books
    {
        /**
         * @var integer
         */
        private $id;
    
        /**
         * @var string
         */
        private $name;
    
        /**
         * @var string
         */
        private $text;
    
        /**
         * @var \Doctrine\Common\Collections\Collection
         */
        private $author;
    
        /**
         * Constructor
         */
        public function __construct()
        {
            $this->author = new \Doctrine\Common\Collections\ArrayCollection();
        }
    
        /**
         * Get id
         *
         * @return integer
         */
        public function getId()
        {
            return $this->id;
        }
    
        /**
         * Set name
         *
         * @param string $name
         *
         * @return Books
         */
        public function setName($name)
        {
            $this->name = $name;
    
            return $this;
        }
    
        /**
         * Get name
         *
         * @return string
         */
        public function getName()
        {
            return $this->name;
        }
    
        /**
         * Set text
         *
         * @param string $text
         *
         * @return Books
         */
        public function setText($text)
        {
            $this->text = $text;
    
            return $this;
        }
    
        /**
         * Get text
         *
         * @return string
         */
        public function getText()
        {
            return $this->text;
        }
    
        /**
         * Add author
         *
         * @param \AppBundle\Entity\Authors $author
         *
         * @return Books
         */
        public function addAuthor(\AppBundle\Entity\Authors $author)
        {
            $this->author[] = $author;
    
            return $this;
        }
    
        /**
         * Remove author
         *
         * @param \AppBundle\Entity\Authors $author
         */
        public function removeAuthor(\AppBundle\Entity\Authors $author)
        {
            $this->author->removeElement($author);
        }
    
        /**
         * Get author
         *
         * @return \Doctrine\Common\Collections\Collection
         */
        public function getAuthor()
        {
            return $this->author;
        }
    }

    Создаем объекты и сохраняем. Примерно так:


    $em = $this->getDoctrine()->getManager();
    
    $author = new Authors();
    $author->setName("Автор");
    
    $book =  new Books();
    $book->setName("Книга");
    $book->setText("Текст");
    
    $book->addAuthor($author);
    $author->addBook($book);
    
    $em->persist($book);
    $em->persist($author);
    $em->flush();

    Вывод


    В целом, использование ORM для простых случаев создания записей в таблицах является более предпочтительным способом, чем чистый SQL. Методы setName и т.д. в коде читаются лучше, чем SQL-запрос. Нет жесткой зависимости от БД.


    Кейс 2. Обновление названия книги


    Голый SQL


    $stmt = $pdo->prepare('UPDATE books SET name=:name WHERE id=:id');
    $stmt->execute([
        ':name' => 'Книга 2', ':id' => 1
    ]);

    Laravel (Eloquent)


    $book = \App\Book::find(1);
    $book->name = 'Книга 2';
    $book->save();

    Symfony


    $em = $this->getDoctrine()->getManager();
    $repository = $em->getRepository(Books::class);
    $book = $repository->find(1);
    $book->setName("Книга 2");
    $em->persist($book);

    Вывод


    Обновление какого-то поля в целом тоже вполне можно делать через ORM, не вдаваясь в детали SQL.


    Кейс 3. Получить список названий книг с авторами


    Для тестов создадим такие записи в таблице:


    Данные
    delete from author_book;
    delete from books;
    delete from authors;
    
    insert into authors 
    (id, name) 
    values 
    (1, 'Автор 1'),
    (2, 'Автор 2'),
    (3, 'Автор 3');
    
    insert into books 
    (id, name, text) 
    values 
    (1, 'Книга 1', 'Много текста 1'),
    (2, 'Книга 2', 'Много текста 2'),
    (3, 'Книга 3', 'Много текста 3');
    
    insert into author_book 
    (author_id , book_id) 
    values 
      (1,1),
      (1,2),
      (2,2),  
      (3,3);

    Голый SQL


    Если брать голый SQL для вывода списка книг с авторами, то это будет примерно так (допустим, авторов хотим получить в виде json):


    select 
      b.id as book_id, 
      b.name as book_name, 
      json_agg(a.name) as authors 
    from books b 
       inner join author_book ab
          on b.id = ab.book_id
       INNER join authors a 
          on ab.author_id = a.id
    GROUP BY 
       b.id

    Результат:


     book_id | book_name |        authors         
    ---------+-----------+------------------------
           1 | Книга 1   | ["Автор 1"]
           3 | Книга 3   | ["Автор 3"]
           2 | Книга 2   | ["Автор 1", "Автор 2"]
    (3 rows)

    Laravel


    Сделаем сначала втупую из мануалов а-ля "Getting Started":


        $books = \App\Book::all();
    
        /** @var $author \App\Author */
        foreach ($books as $book) {
            print $book->name . "\n";
            foreach ($book->authors as $author) {
                print $author->name . ";";
            }
        }

    Код получился гораздо проще, чем голый SQL. Все просто и понятно, works like magic. Только при детальном рассмотрении магия там достаточно фиговая. Eloquent делает аж 4 запроса:


    select * from "books";
    -- и еще по запросу на каждую книгу: 
    select 
       "authors".*, 
       "author_book"."book_id" as "pivot_book_id", 
       "author_book"."author_id" as "pivot_author_id" 
    from "authors" 
       inner join "author_book" 
           on "authors"."id" = "author_book"."author_id" 
    where "author_book"."book_id" = ?

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


    Во-первых, конструкции select * и select authors.*. За такое сразу партбилет на стол. Если книги будут "жирными" ("Война и Мир" или "Британская энциклопедия"), то ни к чему тянуть сразу их текст, когда нужен только список названий. К тому же, со временем в таблицах количество полей обычно все нарастает и нарастает, т.е. такое приложение будет работать всё медленнее и медленнее, жрать всё больше и больше памяти. Я уж не говорю о том, что количество запросов authors.* равно количеству книг.


    Что тут можно предпринять? Во-первых, можно указать, какие поля берем из книги, т.е (['id', 'name']). Ну, и использовать with() для т.н. "eager loading". Итого:


    $books = \App\Book::with('authors')->get(['id', 'name']);

    Стало немного получше, но всё равно далеко от идеала:


    select "id", "name" from "books";
    select 
      "authors".*, 
      "author_book"."book_id" as "pivot_book_id", 
      "author_book"."author_id" as "pivot_author_id" 
    from "authors" 
      inner join "author_book" 
        on "authors"."id" = "author_book"."author_id" 
    where 
      "author_book"."book_id" in (?, ?, ?);

    Тут две проблемы: authors идут всё равно со звездочкой. Кроме того, появился оператор in() с перечислением всех id, который нормально работает при маленьком количестве книг, но для большого списка это будет работать очень медленно, по крайней мере в PostgreSQL. Хотя, конечно, быстрее, чем по запросу на каждый. И с этим уже, похоже, ничего не сделать, по крайней мере я ничего не нашел.


    Точнее, помимо ORM есть еще Query Builder:


         $result = DB::table('books')
             ->join('author_book', 'books.id', '=', 'author_book.book_id')
             ->join('authors', 'author_book.author_id', '=', 'authors.id')
             ->select('books.id', 'books.name', 'authors.name')
             ->get();

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


    Symfony


    Для начала тоже попробуем по-простому:


    $doctrine = $this->getDoctrine();
    $books = $doctrine
        ->getRepository(Books::class)
        ->findAll();
    
    foreach ($books as $book) {
        print $book->getName() . "\n";
        foreach ($book->getAuthor() as $author) {
            print $author->getName() . ";";
        }
    }

    Код первой попытки почти такой же как в Laravel. SQL-запросы, в общем, тоже:


    SELECT 
        t0.id AS id_1, 
        t0.name AS name_2, 
        t0.text AS text_3 
    FROM books t0;
    
    -- и еще 3 запроса таких:
    SELECT 
        t0.id AS id_1, 
        t0.name AS name_2 
    FROM authors t0 
        INNER JOIN author_book 
             ON t0.id = author_book.author_id 
    WHERE 
         author_book.book_id = ?

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


    У стандартных методов типа findAll и т.д., похоже, нет способа указать, что мне надо только такие-то поля и сразу приджойнить такие-то таблицы. Но, зато в Доктрине есть SQL-подобный синтаксис DQL, абстрагированный от конкретной СУБД, которым можно воспользоваться. Он оперирует не таблицами, а сущностями.


     $query = $this->getDoctrine()->getManager()->createQuery('
        SELECT 
             partial b.{id, name}, partial a.{id, name} 
        FROM AppBundle\Entity\Books b 
           JOIN b.author a'
     );
     $books = $query->getResult();
    

    Ну да, получилось типа того, что надо, один запрос, с одним полем:


    SELECT 
       b0_.id AS id_0, 
       b0_.name AS name_1, 
       a1_.id AS id_2, 
       a1_.name AS name_3 
    FROM 
       books b0_ 
    INNER JOIN author_book a2_ 
       ON b0_.id = a2_.book_id 
    INNER JOIN authors a1_ 
       ON a1_.id = a2_.author_id

    Выводы


    На мой взгляд, простой SQL выглядит проще и стандартнее. Кроме того, в ORM-подходах мы не смогли полностью сферически абстрагироваться от базы данных, нам пришлось подстроиться под реальный мир.


    DQL в принципе сойдет на замену SQL, и он не особо привязан к СУБД, но это еще один странноватый синтаксис, который надо учить отдельно.


    Кейс 4. Чуть более сложный UPDATE


    Допустим, стоит задача обновить двум последним авторам имя на "Жорж".


    голый SQL


    Тут всё просто, запрос с подзапросом.


    UPDATE authors
    SET name = 'Жорж'
    WHERE id in (
        SELECT id
        FROM authors
        ORDER BY id DESC
        LIMIT 2
    );

    Laravel


    Сначала я попробовал сделать так:


     \App\Author::orderBy('id', 'desc')->take(2)->update(["name" => "Жорж"]);

    Это было бы здорово и красиво, однако не сработало. Точнее сработало, но заменило записи всем авторам, а не только двум.


    Тогда, покурив мануал и SO, удалось родить такую конструкцию:


    \App\Author::whereIn(
            'id',
            function($query) {
                $query->select('id')
                    ->from((new \App\Author())->getTable())
                    ->orderBy('id', 'desc')
                    ->limit(2);
            }
        )->update(['name' => 'Жорж']);

    Это работает хорошо, хоть и не особо читабельно. Да и query builder опять какой-то подъехал.


    Symfony


    Сразу скажу, что выразить через DQL мне этот запрос вообще не удалось, с вложенными подзапросами там всё плохо.


    Есть, конечно, query builder, но получалось что-то совсем зубодробительное, и я бросил эту затею. ORM должен помогать экономить время, а не наоборот. Надеюсь, опытные симфонисты в коментах подскажут какой-нибудь легкий и изящный способ сделать update с подзапросом.


    Вывод


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


    Как всегда обычно и бывает, истина где-то посередине. Для простых CRUD-операций ORM вполне может сэкономить время разработки и улучшить читабельность кода. Однако шаг вправо, шаг влево — и гораздо удобнее пользоваться нативным SQL. Например, сложные выборки/обновления (особенно, аналитические отчеты с оконными функциями и рекурсивными CTE). Компромиссным вариантом является маппинг результатов нативных запросов на объекты, Доктрина это позволяет.


    В споре ORM vs SQL не победил никто.


    Тем временем, всех кто намучался с ORM, тормозящими запросами и плохой производительностью в рабочих ситуациях, приглашаем на PG Day'17. У нас подготовлено для вас множество различных докладов и мастер-классов для самых разных баз данных!

    PG Day'17 Russia
    0,00
    Компания
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 192

      +3
      Как обычно, «Истина где-то рядом»…
      Идеальным мне лично представляется ORM с возможностью писать чистые запросы, и далее в коде —
      «select * from table where id = ?» — через ORM,
      «select a.field, b.field as field2 from… left join on… where a.id in(....) or b.name like ....» — всё же писать на чистом SQL
        0
        Стоит добавить, что доктрина кроме DQL/QueryBuilder поддерживает и нативные запросы
          +1

          Как и Eloquent, как впрочем и большинство ORM

          0
          Такое есть в X++ (Microsoft Dynamics AX)
            +3

            Я пару лет назад понял и принял одно очень простое правило — списков объектов нет, есть только отчёты и там проще и быстрее в поддержке м развитии sql. Для выборки одного объекта на карточку этого объекта скорее всего orm может прокатить до определенного момента, в некоторых случаях, все равно на sql и там перейдешь. Для добавления или редактирования одного объекта тоже orm прокатит. А вот для тех, кто статистику с 100к записями в модели сериализует вместо hashmap или пр. есть отдельный котёл. )

              0

              А что не так со списком объектов? Вот прямо сейчас пишу вывод отчёта в виде шаблонизации и простенькой агрегации (суммирования итогов) в приложении списка объектов CashflowMonthlyReportEntry. Можно было бы и агрегацию в БД делать, но это два тяжелых запроса, отличающихся только наличием одного поля и GROUP BY по нему — не уверен, мягко говоря, в способности СУБД понять это и использовать результат предыдущего запроса их кэша в качестве основы для второго. Можно было бы и на массивах это сделать, но с объектами как-то удобнее в плане автодополнения, навигации, рефакторинга и т. п. в IDE.

                0

                А вам не надо два тяжёлых запроса. Положите всё во времянку, и дальше делайте что угодно, выборка, выборка с group by по одному полю, по второму, агрегация вообще всего. Хотя это опять же надо в хранимой процедуре делать, а вы их, насколько помню, не любите)

                  0

                  Это уже не только логику приложения, но и логику представления в базу переносить :) Я не хранимые процедуры или что ещё не люблю, а размазывание логики одного уровня по разным слоям приложения. Если и переносить логику в базу, то практически всю, кроме самой примитивной шаблонизации и т. п.

                    0

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


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

                    но вы же от SQL всеравно не уйдете. А значит у вас уже все размазано между слоями. Нет? А если у нас часть агрегации делается в базе (потому что это проще) и часть в приложений — это вроде как и есть размазывание, нет?

                      +1

                      В 90+% случаев об SQL даже не "подозреваю" :) И в 99+% случаев логики в СУБД не хранится, только данные их схема (если не считать логикой автоматически вставляемые ограничения по ключам). Формирование SQL запроса динамически я не отношу к размазыванию логики. А вот уже вьюшки хранить в базе — размазывание по сути.

                        +1

                        Ну в целом я с вами согласен.

                  0
                  Можно было бы и на массивах это сделать, но с объектами как-то удобнее в плане автодополнения, навигации, рефакторинга и т. п. в IDE.

                  нормальная IDE это все умеет и для SQL.

                    0

                    Я не про SQL, а про оперирование результатами запроса и(или) формирование его параметров.

                      0

                      А просто мэппер не подходит? без обратной конвертации (для этого ORM есть).

                        0

                        Просто мэппер — это половина ORM (или ORM в режиме read only)?

                          0

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

              +2
              А вот интересно, какие вообще плюсы от использования ORM?
              Абстрагирование от конкретной БД в них так себе, да и кому оно надо? Т.е. я понимаю, что это мифическая возможность без головной боли менять СУБД как перчатки. Но это работает только когда приложение не использует специфику СУБД.
              Маппинг строк таблицы на объекты? Ну тоже не оч. Зачастую бизнес-сущность хранится сразу в нескольких таблицах, и вместо того чтобы оперировать одним объектом, который мы вручную написали как нам надо, мы жуём кактус, состоящий из типовых объектов-строк.
              Возможность не знать SQL? да ладно? Его надо знать в любом случае.
                0

                Возможность удобно динамически формировать запрос

                  +4
                  Для формирования запроса можно использовать QueryBuilder.
                    0

                    Редкий QueryBuilder позволяет формировать вложенные запросы...

                      +1

                      Ну те что мне доводилось использовать это умели.


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

                  0

                  Что такое "типовые объекты-строки"?
                  Если у нас данные о сущности хранятся в разных таблицах, то есть связи.

                    0
                    Абстрагирование оно получается из-за архитектуры — потому что orm должна поддерживать много баз и она строит запрос сама — в результате получается что все равно делается драйвера с одним интерфейсом.

                    Вот именно маппинг на объекты, а потом обновление всего после того как поменяли объекты.
                    Как-бы orm могут разбивать объекты и таблицы и автоматически подтягивать вложенные по связям.
                    В 90% легких запросов у нас уже есть готовый код, а в случае тяжелых — ну выявятся и оптимизируются.
                      +2
                      ORM позволяет абстрагироваться от конкретной базы, но не от хранилища данных в целом. Вот только совместимые различия баз, вероятнее всего можно сгладить просто используя QueryBuilder, который будет переводить унифицированные запросы в специфичные.
                      По поводу маппинга на объекты, а точнее управления их состоянием, может быть, вот только польза от этого видна лишь в stateful приложениях. На PHP, где 90% эндпоинтов либо только читают из базы, либо только пишут в неё, держать пул объектов и следить за их идентичностью (IdentityMap) бесполезно.
                      Конечно, бывают случаи повторного чтения/записи, сам с таким сталкивался. Но бывает это редко, и наверное оно не стоит того чтобы постоянно иметь немалый оверхед от ORM.
                      Да, ORM предлагают красивое решение для простых случаев. Но как только начинается что-то посложнее, начинается борьба с ORM, когда ты понимаешь как можно написать запрос на sql, но ORM заставляет тебя совать друг в друга лямбды, которые строят куски запросов с помощью того-же QueryBuilder'a.
                        0
                        ORM позволяет абстрагироваться от конкретной базы, но не от хранилища данных в целом.

                        Позволяет и от хранилища в целом, если приложение сильно не завязано на конкретную ORM. Вот давеча вынес модуль из монолитного приложенияс активным использованием Doctrine в REST-like HTTP-сервис почти без изменения остального кода. Основные изменения (кроме собственно выделение модуля в отдельное приложение т. п.):


                        • замена class DoctrineCashflowRepository implements CashflowRepositoryInterface на class HttpCashflowRepository implements CashflowRepositoryInterface
                        • удаление из контроллеров $om->flush();
                        • замена Doctrine relations на ручное заполнение и, самое сложное, сохранение с помощью нового сервиса (есть идея написать свою имплементацию доктриновского ObjectManagerInterface с использованием её же UnotOfWork, IdentityMap и т. д., но надо разобраться стоит ли овчинка выделки и может имеет смысл собирать информацию из нескольких источников исключительно на этапе отдачи ответа клиенту )
                          0
                          Это заслуга не столько ORM, сколько того, что вы заранее ввели абстракцию в виде интерфейса.
                          Того же эффекта можно было добиться и без ORM, главное тут — чтобы клиентский код обращался строго к интерфейсу и не знал подробности того как этот интерфейс работает внутри.
                            0

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

                    • НЛО прилетело и опубликовало эту надпись здесь
                        +1
                        Миграции к ORM отношения не имеют. Механизм миграций может быть реализован в рамках ORM, как и построитель запросов (для сахара, да и для защиты от дурака), но это не значит, что они не могут жить отдельно от него.
                          0

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

                            0
                            ORM, а точнее популярные ORM-библиотеки всё же делают упор на работу с графами объектов данных и соответственно на генерацию DML запросов.
                            Генерация же DDL запросов, как и их применение к базе это задача механизма миграций.
                              0

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

                                0
                                Я не говорю что через ORM нельзя выполнять миграции. Кроме того я не говорю что надо писать механизм миграций с нуля. Но возможность выполнять миграции это не то что нам даёт ORM.
                                Это не тот плюс ORM ради которого стоит выбрать ORM вместо библиотеки, которая заточена только под миграции.
                                  0

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

                                    0
                                    Согласен. Но тогда получается, что люди выбирают не столько ORM, сколько хорошо притёртый набор библиотек в составе ORM. Что же тогда отличает ORM от набора библиотек?
                                      0

                                      Именно. Многие люди основной функцией ORM (по крайней мере ActiveRecord или DataMapper) пользуются как небольшим бесплатным бонусом, используя объекты, контролируемые ORM, просто как структуры данных.


                                      А отличает ORM-библиотеки с функциями типа миграции и абстракции от СУБД от набора библиотек с этими функциями именно наличие собственно ORM — универсального механизма отображения объектной модели на реляционную и наоборот. Остальное в ORM (миграции, генерации схемы по объектам и объектов по базе, абстракция от СУБД и прочая, и прочая, и прочая :) по сути бесплатные бонусы

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

                        ORM как раз пзволяет представлять «несколько таблиц» как один объект. По крайней мере (не упомянутый в статье) AR из Yii2. И работать с результатом как с одним объектом, а не городить огород из вложенных циклов.
                        В целом же, как верно заметили выше, ORM очень экономит время на простых (и, как показывает практика, самых частых в написании) запросах типа CRUD. Мне не надо думать как там называется таблица, в какой схеме/базе она лежит, какие поля там PR… я просто вызываю метод delete/save у объекта…
                          0
                          Вложенные циклы устраняются не использованием ORM, а использованием объектов для выстраивания структуры обрабатываемых данных.
                          Не думать как там называется таблица можно введя дополнительный слой абстракции над слоем, который выполняет запросы к базе. Более того, выделение слоя, который выполняет запросы к базе, позволяет таки… выполнять запросы к базе. Т.е. ты пишешь sql запрос, и тебя совесть не мучает за то что ты делаешь хак в обход ORM.
                          Мне кажется, лучше руками писать простые запросы и иметь возможность так же быстро написать сложный, чем экономить время на простых запросах и безбожно тратить его на сложных.
                            0
                            Мне, возможно, не хватает какого-то опыта с ORM, но что мешает писать sql запрос через ORM и чтоб при этом «не мучала совесть»? В том же упомянутом Yii2 — ничто не мешает подсунуть голый SQL, если требуется, при этом пользоваться фишками ORM, если требуется (indexBy, подключение к базе, параметры запроса, из того что первое пришло в голову). Не понимаю что мешает и «экономить время на простых запросах» и при этом не париться со сложными.

                            Вводить дополнительные сущности и изобретать заного то что уже много лет используется в проде — это по меньшей мере странно, и как минимум — контрпродуктивно. Введя «дополнительный слой абстракции над слоем», нужно понимать что этот код может в итоге поддерживать не разработчик который напридумывал «выделение слоя, который выполняет запросы к базе», то есть нужно писать документацию и комментировать каждый шаг. и всё это вместо того чтобы НЕ использовать ORM потому что… почему?
                              –1
                              Когда вы используете ORM и пишете SQL код в сложных случаях, вы теряете независимость от конкретной СУБД, т.е. один из «плюсов» использования ORM.
                              В случаях, когда вам необходимо абстрагироваться от СУБД, вам придётся вводить слой абстркации. Да и без такой необходимости, выделение работы с базой делает код чище.
                              «Фишки ORM» совсем не фишкки ORM:
                              • indexBy — работа с коллекциями
                              • подключение к базе — фишка драйвера, такого как PDO, ну или библиотеки, облегчающей работу с базой
                              • параметры запроса — аналогично

                              то есть нужно писать документацию

                              Да ладно? Вы считаете что это оверхед? А ничего что документация должна быть в любом случае?

                                +1
                                Я не приводил аргументов за «независимость от конкретной СУБД», не считаю вообще это плюсом.
                                indexBy — работа с коллекциями
                                Эту работу за меня сделал ORM, я этого не писал, мне не нужно этот код поддерживать, например.
                                подключение к базе — фишка драйвера, такого как PDO, ну или библиотеки, облегчающей работу с базой
                                Этой библиотекой и является ORM. Просто библиотека-надстройка над PDO.
                                Да ладно? Вы считаете что это оверхед? А ничего что документация должна быть в любом случае?
                                Кол-во документации различается, нет? Таки да, я считаю оверхэдом писать документацию в своём проекте для стандартных модулей моего фреймворка. Или вы мне сейчас ещё скажете что и фреймворки — зло?

                                Как там на счёт ответов про «что мешает писать sql запрос через ORM»? Это, пожалуй, единственная интересная часть этой переписки.
                                  +1
                                  Вы путаете набор библиотек для работы с БД и коллекциями, и ORM, основной задачей которого является поддержание консистентного состояния объектов в памяти приложения в соответствии с данными в БД.
                                  Этим я хочу сказать, что ни работа с коллекциями, ни удобное подключение к базе, ни сахар при работе с БД не являются киллер-фичами ORM. Да, они есть в ORM, но также их можно заполучить просто используя отдельные библиотеки.
                                  Так что же полезного в самом ORM?

                                  По поводу документации. Так или иначе, сущности и логику работы приложения как-то описать надо, не важно, используете ли вы ORM или пишете запросы руками. Слой абстракции не подразумевает создание собственного сложного фреймворка для доступа к данным, достаточно писать запросы не в самом объекте предметной области, а в отдельном классе, названия методов которого вполне самодокументируемы по названию. Что может быть непонятного в методе findNewsByTitle(title) внутри которого одной-двумя строчками делается запрос и отдаётся массив объектов? Для этого не требуется обширной документации.

                                  Ну и напоследок про sql через ORM.
                                  Всё-таки ORM сам по себе является слоем абстракции. Работа с базой должна быть скрыта в этом слое. А когда мы пишем sql через ORM — мы вытаскиваем работу с базой наружу.
                                    0
                                    Что может быть непонятного в методе findNewsByTitle(title) внутри которого одной-двумя строчками делается запрос и отдаётся массив объектов?

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


                                    А когда мы пишем sql через ORM — мы вытаскиваем работу с базой наружу.

                                    Мы пишем sql не через ORM, а внутри ORM. Клиенту того же репозитория Doctrine без разницы используется внутри репозитория полностью автоматическая генерация запросов средствами Doctrine, ручками написанный DQL-запрос, или вылизанный SQL-запрос, если результат один и тот же.

                          0

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

                            +1

                            Будет более чем уместно, если правильно сделан маппинг (M) между базой данных и объектами.

                              0
                              Если у вас маппинг 1:1 то все хорошо и ORM уместен. Иначе приходится делегировать маппинг SQL запросам или делать это на уровне приложения получая риск проблем с производительностью.
                              +1

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

                                0
                                Не совсем понимаю, что вы подразумеваете под маппингом 1:N. Можете привести пример? Могу лишь предположить, что вы получаете от ORM «N» промежуточных объектов и агрегируете их в один объект бизнес модели
                                  0

                                  Маппинг 1:1 — это когда строка таблицы (или даже шире — результата запроса) маппится в объект с однозначным соответствием столбцов таблицы и свойств объекта.

                                0
                                Однако, если данные хранятся не так как обрабатываются, то использование ORM, скорее всего, будет неуместно.

                                А как же тогда подход EventSourcing, который вполне себе нормально ложится на ORM?
                                  0
                                  который вполне себе нормально ложится на ORM?

                                  Это как простите?


                                  При event sourcing у нас нет ORM. Ну то есть она может быть где-то на стороне read model если вам так удобно, но это уже никакого отношения к event sourcing не имеет.


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


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

                                    0
                                    Ну лично мне удобно было вбросить doctrine для:
                                    а) DataMapping
                                    b) Абстракции от СУБД(ибо было большое желание поиграть с разными)

                                    Но никакого мэппинга не происходит

                                    Мне кажется вы заблуждаетесь. Далеко не всегда нам нужны конечные состояния объектов. Часто бывают ситуации, когда я хочу анализировать массивы эвентов. И часто бывает ситуация, когда хочется анализировать их в PHP/Python-коде, работая как с объектами. Они иммутабельны, но это вовсе не означает, что я после сохранения не захочу мапить их на объекты.
                                    Ну и плюс построение read-model из событий все же является частью подхода под названием event-sourcing, а там ORM — очень удобно.
                                      0
                                      Далеко не всегда нам нужны конечные состояния объектов.

                                      А я где-то говорил про конечное состояние? В этом же и прелесть ES, проигрывайте события, генерьте ветки событий что бы посмотреть "а что было бы"… но это все равно не мэппинг а вычисление. Вопрос зоны ответственности.


                                      И часто бывает ситуация, когда хочется анализировать их в PHP/Python-коде, работая как с объектами.

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


                                      Ну и плюс построение read-model из событий все же является частью подхода под названием event-sourcing, а там ORM — очень удобно.

                                      вы можете и без event sourcing строить read model. Как никак ES не обязательная штука для CQS/CQRS. Потому если уж обобщать случаи где ORM удобно — то это OLTP. Когда нам нужно поработать с небольшим графом объектов а потом сохранить этот граф в базу.

                                      0
                                      ORM же это когда у нас есть реляции (таблички) и мы их мэпим на объекты

                                      Я думаю, что про реляции тоже не совсем правда. Буква R в аббревиатуре скорее характеризует тип СУБД нежели обязательное требование к наличию реляций. Иначе как же быть с ODM, который по сути является тем же паттерном. А ведь в документо-ориентированных БД реляции не такое уж и частое явление.
                                        0
                                        Буква R в аббревиатуре скорее характеризует тип СУБД нежели обязательное требование к наличию реляций.

                                        Вопрос терминологии. Я не случайно добавил пометку рядом со словом "реляции" — таблички.


                                        В целом все так, R про тип СУБД. Что мол мы берем нормализованное представление данных и мэпим их на наши объекты.


                                        Иначе как же быть с ODM, который по сути является тем же паттерном.

                                        но вы же заметили что буковки R там нет. В чем разница между ODM и ORM так это в том что в первом нам не нужно (как правило) менять структуру. Мы храним документы как отображение состояния наших агрегатов и все. В ORM же мы получаем в результате SQL запроса денормализованные данные и должны их замэпить, что уже задачка посложнее и сам мэппинг происходит сильно по другому. Потому я бы все же разделял эти два паттерна, хоть они и очень похожи по назначению.

                                        +1

                                        ORM вполне может быть и при ES, как минимум, для хранения собственно событий, плюс снэпшотов агрегатов и прочих сущностей, в том числе и для write model. Число релейшенов наоборот растёт, добавляется как минимум SomeAggregate one-to-many SomeAggregateEvent. Да и в целом, ES не подразумевает хранение всех событий в одной табличке. Вполне может быть множество типов (читай — классов) событий, как-то маппящихся на базу.

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

                                            От задач зависит. Поиск по данным события вполне распространенный кейс.

                                              0

                                              Да, но опять же для этого удобнее одна коллекция ивентов. Нет?

                                            0
                                            Вполне может быть множество типов (читай — классов) событий, как-то маппящихся на базу.

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


                                            для хранения собственно событий

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


                                            плюс снэпшотов агрегатов и прочих сущностей

                                            ну вот тут вполне можно, но я бы тут использовал все же ODM и превращал бы весь процесс в сериализацию.

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

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


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

                                              Хранилищ, заточенных на ES, раз-два и обчёлся (по крайней мере FOSS), да и те сыроваты похоже. По любому нужно брать какую-то существующую модель и приспосабливать её к ES. В пользу реляционной — огромная и отлаженная экосистема. Особенно это важно, если происходит плавный перевод существующего продукта на ES.

                                                0
                                                Для того отчасти и нужна ORM, чтобы организовать хранение разных типов событий в едином потоке: все эти маппинги наследования на таблицы.

                                                Простите, а откуда там "наследования на таблицы" и прочие мэппинги? Мы храним события. По сути что оно будет — айдишка события, айдишка агрегата возможно, тэймстэмп, тип, и жирный json с пэйлоадом. Далее по вкусу. Никаких связей с другими табличками. Никаких наследований и прочее.


                                                Хранилищ, заточенных на ES, раз-два и обчёлся да и те сыроваты похоже.

                                                Тут вопрос что вы подразумеваете под "заточенных на ES". Я правильно понимаю что это некая штука которая помимо хранения ивентов возьмет на себя построение проекций, снэпшеты и т.д.? знаю одно неплохое — https://geteventstore.com/ но как по мне это не очень удобно.


                                                По факту нам нужна база данных заточенная под хранение и организацию быстрого чтения большой коллекции событий. Из проверенных — cassandra. Снэпшеты вы можете уже грузить куда хотите, хоть в jsonb в каком-нибудь postgres.

                                      +2

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


                                      И маппинг может быть (в теории) сколь угодно сложным — сводить несколько записей разных таблиц в объект, разбивать одну строку таблицы на несколько объектов и т. п.


                                      И не надо путать ORM как архитектурный паттерн с универсальными ORM-библиотеками. Если вы в приложении по результатам SELECT-запросов формируете граф объектов, а по результатам изменения этого графа формируете INSERT/UPDATE/DELETE-запросы, то вы уже используете паттерн ORM в той или иной разновидности. Реализация сторонняя или своя, универсальная или только то, что нужно, с абстракцией от СУБД или без — это нюансы.

                                      0
                                      А вот интересно, какие вообще плюсы от использования ORM?

                                      Смотря какой.


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

                                      Если вы про приемы вроде наследования таблиц или еще чего такое — то это все как бы умеют ORM. А если вы про случаи когда одну сущность мы по каким-то причинам поделили на две таблицы — я не знаю зачем так делать. Этим мы только себе в ногу выстрелили.


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


                                      Для меня профит от ORM в операциях на запись!, когда у нас есть небольшой граф объектов и мы что-то с ним делаем. В этом случае на уровне приложения у меня кучка объектов, которые обмениваются сообщениями. Вся логика спокойно покрывается юнит тестами и тд. Ну и за счет механизмов вроде unit-of-work я могу "закоммитить" изменения всего графа в базу. И это реально удобно и реально круто!


                                      НО на выборки для операций чтения ORM не нужны. То есть я согласен с комментарием выше — любой список это вид репорта. Там ORM не нужны.

                                        0

                                        Прошу прощения за офтоп


                                        А если вы про случаи когда одну сущность мы по каким-то причинам поделили на две таблицы — я не знаю зачем так делать. Этим мы только себе в ногу выстрелили.
                                        Полностью согласен про выстрел в ногу, но как бы вы поступили в следующей ситуации:
                                        у нас есть сущность "Экскурсия" со своим списком полей и реляций и мы её продавали как товар. Грубо говоря одна экскурсия — одна строчка из таблицы "excursion" + по одной/несколько строк из связанных таблиц.

                                        Потом кто-то захотел ввести дополнительно сущность "Экскурсия с фиксированной датой" и эта сущность от базовой отличается только наличием полей "дата проведения" и "квота". Теперь у нас товар не просто строчка из таблицы "excursion", а строчка из таблицы "excursion_date" плюс привязанная к ней строчка из таблицы "excursion".


                                        Т.е. просто для какого-нибудь процесса вывода информации а-ля "вывести в какие даты доступна экскурсия N" можно использовать простую реляцию вида $excursionWithDate->getExcursionDates(). Но если нужно к примеру добавить экскурсию в корзину — то мне уже нужен объект собранный из двух таблиц.


                                        Как бы вы поступили в этой ситуации?
                                        P.S. знаю что бизнес-объекты рассматривать как строчки из таблицы нельзя, это было сделано для наглядности.

                                          0

                                          пардон, с разметкой немного ошибся

                                            0

                                            По описанию "Экскурсия с фиксированной датой" должна быть наследником "Экскурсия", что легко решается нормальными ORM несколькими способами, в том числе созданием таблицы excursion_date с тремя полями, но по тому же описанию объекты класса "Экскурсия" — это не товар, а наименование товара, а "Экскурсия с фиксированной датой" — ограничения на покупку товаров данного наименования: есть запись с датой и квотой — есть ограничения, нет — нет. То есть не "Экскурсия с фиксированной датой", а "Квоты на Экскурсию по датам", ссылающаяся на "Экскурсия" и в логике добавления экскурсию в корзину добавляется проверка на ограничения, а так всё остаётся тем же самым.


                                            Структура таблиц одна и та же, но вот маппинг их на объекты кардинально разный.

                                              0
                                              По описанию "Экскурсия с фиксированной датой" должна быть наследником "Экскурсия"

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


                                              p.s. не люблю наследование.

                                                0

                                                Исхожу исключительно из описания:


                                                Потом кто-то захотел ввести дополнительно сущность "Экскурсия с фиксированной датой" и эта сущность от базовой отличается только наличием полей "дата проведения" и "квота".

                                                Бизнес считает "Экскурсия с фиксированной датой" отдельной сущностью, для которой базовой является "Экскурсия". Технически это наследование, которое я тоже не люблю. И тут бы я с аналитиком поспорил, по задаче не похоже что тут должно быть наследование.

                                                  0

                                                  Возможно я не совсем понятно объяснил разницу между этими двумя сущностями. На живом примере:
                                                  Обычная экскурсия "Прогулочный маршрут по центру столицы" можно купить билет и использовать его 1 раз в любой день в течении полугода с момента покупки.


                                                  Экскурсия с фиксированной датой "Прогулочный маршрут по историческим местам". Экскурсия проводится к примеру 16.05.2017, 23.05.2017 и 30.05.2017. Купить билет возможно на любую из этих трех дат при условии что дата еще не наступила и в желаемую дату есть свободные места. Использовать билет можно только в выбранную дату.


                                                  Контролем использования билетов занимается отдельная система, так что эта часть процесса в рамках задачи не важна

                                                    +1

                                                    вот, чуть конкретнее, ок.


                                                    То есть наши "экскурсии" на самом деле ничем не отличаются. Просто для некоторых видов экскурсий у нас есть еще отдельно билеты с разными датами.


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

                                                      0
                                                      надо логику понимать лучше.

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

                                                        0
                                                        В книжках по DDD очень много внимания этому уделено) Разговаривайте со своими заказчиками больше. То что они сами, как правило, не знают свою предметную область — факт. И есть методы, благодаря которым и Вы и заказчик сформируете ее понимание. При том общее. И придете к единому языку и будет вам счастье
                                                          0

                                                          Понимать они понимают, обычно. По крайней мере если брать заказчика как организацию в целом :)


                                                          Проблема в основном в том, что либо представитель заказчика, который формирует задание не знает каких-то нюансов, особенно не формализованных типа "перед выставлением счёта Галя из бухгалтерии звонит в СБ и интересуется не будет ли проблем с финмониторингом", либо очень многое считает очевидным, не требующего отдельного пояснения, хотя бы различные правила округления при различных операциях типа "а мы покупаем или продаем?", либо и то, и другое :(

                                                0
                                                Экскурсия с фиксированной датой

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


                                                Предположим что это не отдельная сущность а лишь опциональная характеристика экскурсии. Типа дэйт рэйндж за который оно действует. У остальных по умолчанию будет null-object с null-вым рэйнджем. Эту характеристику в силу ограничений СУБД я запихну в отдельную таблицу excursion_date. И в объектной модели будет соответствующая пропертя которую я буду использовать на запись.


                                                вывода информации а-ля "вывести в какие даты доступна экскурсия N"

                                                А тут я сделаю SQL запрос и замэплю данные сразу на DTO которое плюну во view. Если мне не надо сохранять изменения стэйта мне не нужен ORM. Хотя если под ORM мы подразумеваем именно паттерн а не какую-то реализацию — то этим мэппингом SQL -> DTO и будет заниматься мой ORM.

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

                                                  Жизненный цикл фактически один и тот же. На практике разница выливается только в два момента:


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

                                                  Предположим что это не отдельная сущность а лишь опциональная характеристика экскурсии. Типа дэйт рэйндж за который оно действует. У остальных по умолчанию будет null-object с null-вым рэйнджем. Эту характеристику в силу ограничений СУБД я запихну в отдельную таблицу excursion_date. И в объектной модели будет соответствующая пропертя которую я буду использовать на запись.

                                                  Т.е. вы предлагаете сделать все экскурсии "как бы" с фиксированной датой, но у обычных экскурсий вместо даты/квоты будет null-object?

                                                    0
                                                    Т.е. вы предлагаете сделать все экскурсии "как бы" с фиксированной датой, но у обычных экскурсий вместо даты/квоты будет null-object?

                                                    да, иначе мы нарушим LSP (если вдруг решили наследоваться). Да и с точки зрения отображения это будет проще и логичнее. А те экскурсии для которых нет фиксированных дат — ну они всегда будут возвращать true при вызове isAvailableAt какого-нибудь.


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


                                                    В любом случае тупое наследование многие ORM умеют. Другое дело что я предпочитаю подумать как избежать наследования.

                                                      0

                                                      понял. спасибо за разъяснения.

                                              0

                                              Как на PHP не знаю, но на C# крайне полезными являются язык выражений, который ближе к реляционной алгебре чем SQL, и проверка корректности запросов компилятором.


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

                                                +2
                                                Абстрагирование от конкретной БД в них так себе, да и кому оно надо?

                                                Я бы сказал, что это абстрагирование нужно не для смены СУБД как таковой, а для более правильного разделения ответственности\дизайна объектов бизнес-логики. Когда ты дизайнишь исходя из специфики своей СУБД — одно дело, когда ты делаешь это так, как того требует в первую очередь бизнес — совершенно другое. Это как с написанием кода по TDD в результате которого сами тесты — только верхушка айсберга.
                                                0

                                                Когда вы говорите про просадку перфоманса от WHERE IN или от *. То забываете упомянуть что обычно в приложении оперировать приходится не всеми хранимыми сущностями. Пагинация или бесконечная прокрутка или обработка чанками идет практически всегда. И эти факторы превращаются в экономию на спичках. А удобство и простота кода гораздо более приоритетные.

                                                  +1

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

                                                    0
                                                    Я никогда не говорил, что голые запросы надо делать прямо в контроллере. Нужно выносить, конечно. Просто сложные запросы легче конструировать на SQL
                                                • НЛО прилетело и опубликовало эту надпись здесь
                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                      0

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


                                                      А уж если выбрали объектную модель и реляционное хранилище, то остаётся, если хочется минимальной поддерживаемости кода, только выбирать между разными реализациями ORM, включая написания своей собственной, возможно универсальной, а может и тупо захрадкаженным вимператином стиле маппингом типа $contract->number = $sqlQueryResult['contract_number']

                                                      • НЛО прилетело и опубликовало эту надпись здесь
                                                          0

                                                          Вместо объектов — массивы, списки, хэш-таблицы и т. п.

                                                    0
                                                    UPDATE authors
                                                    SET name = 'Жорж'
                                                    WHERE id in (
                                                        SELECT id
                                                        FROM authors
                                                        ORDER BY id DESC
                                                        LIMIT 2
                                                    );
                                                    

                                                    и
                                                    UPDATE authors
                                                    SET name = 'Жорж'
                                                    ORDER BY id DESC
                                                    LIMIT 2;
                                                    

                                                    зачем подзапрос?
                                                      0
                                                      UPDATE… LIMIT не работает в посгресе, например
                                                        +1
                                                        с посгрес не работал, не знал, моя ошибка значит.
                                                      0
                                                      Было бы интересно сравнить не с классическими ORMмами, а с (относительно) новыми решениями типа jOOQ, когда и гибкость SQL остается, и модели есть, и типобезопасность присутствует.
                                                        0
                                                        Насколько я вижу, JOOQ — это query builder. Т.е. по сути тот же SQL, только вместо пробелов скобочки и точки. Нет никакого абстрагирования от базы
                                                        • НЛО прилетело и опубликовало эту надпись здесь
                                                            0
                                                            Ну я ж и написал «типа jOOQ». Или под PHP такого не делают?
                                                              0

                                                              не надо путать DBAL и ORM. Если что-то не умеет мэпить результат SQL на объекты и обратно — значит это не ORM.

                                                              • НЛО прилетело и опубликовало эту надпись здесь
                                                              +1
                                                              Я не могу согласится с тем, что jOOQ это query builder. Он в нем есть, но кроме него есть еще и модели, и кодогенерация и т.д. Это реализация object-per-table патерна. А такой код не похож на квери билдер, согласитесь:
                                                              BookRecord book1 = create.newRecord(BOOK);
                                                              book1.setTitle("1984");
                                                              book1.store();
                                                              


                                                              Как такового абстрагирования от базы нет, да. Но с другой стороны, в крупном проекте в большинстве случаев случится завязка на какую-то конкретную базу и ее фичи, так что абстрагирование будет очень условным.
                                                            +1
                                                            У стандартных методов типа findAll и т.д., похоже, нет способа указать, что мне надо только такие-то поля и сразу приджойнить такие-то таблицы.


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

                                                            Если нужно получить получить информацию одним запросом, то используем EntityRepository.

                                                            class BooksRepository extends EntityRepository
                                                            {
                                                            
                                                                public function getBooksWithAuthors ()
                                                                {
                                                                	 $result = $this->createQueryBuilder('u')
                                                                        ->select('u, a')
                                                                        ->leftJoin('u.authors', 'a')
                                                                        ->getQuery()
                                                                        ->getResult();
                                                            
                                                                        return $result;
                                                                        
                                                            	}
                                                            }
                                                            


                                                            Уточню, что authors — ManyToMany связь для Books и entity класс для книг создан правильно, а не так как в примере.

                                                            class BooksController
                                                            {
                                                            	$doctrine = $this->getDoctrine();
                                                            	$books = $doctrine->getRepository(Books::class)->getBooksWithAuthors();
                                                            }
                                                            


                                                            И получаем все в одном запросе. Книги с их авторами.

                                                            dump($books)
                                                            
                                                              0
                                                              > ManyToMany связь для Books и entity класс для книг создан правильно, а не так как в примере
                                                              а что именно неправильно?
                                                                0
                                                                Как по мне, SQL vs ORM некорректное сравнение у своего основания.

                                                                sql — это язык, ORM — это оверхэд над sql, как большинство языков это оверхэд над процессором.

                                                                Задача ORM — абстрагироваться от базы данных. Каждый объект ОRM служит определенной цели в бизнес-логике. Мы перестаем думать о базе данных в принципе, а начинаем мыслить объектом и целью для которой он создан.

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

                                                                Так что бы получить всех пользователей которые привязаны к одному банку мы сделаем, что-то вроде: $banks = $user->getBanks()->getUsers();

                                                                Получить адреса банка пользователя: $banksAddr = $user->getBanks()->getAddrs();

                                                                Теперь мы хотим, чтобы пользователь мог, что-то заказать с сайта, если у него указан хотя бы один банк:
                                                                if($user->hasBanks()){}
                                                                


                                                                Так же, в каждый момент времени мы должны быть уверены, что кто-то не допишет sql, который уберет нам половину данных или наоборот запишет в базу потеряв в половину.
                                                                Или кто-то добавил обязательное поле в таблицу, чтобы все запросы работали корректно, нам нужно поправить 1 entity class и все запросы продолжат работать.

                                                                Всю ответственность за целостность данных берет на себя берет ORM.

                                                                Так в пример с книгами это было бы

                                                                class Book
                                                                {
                                                                
                                                                  /**
                                                                     * @ORM\ManyToMany(targetEntity="Author", inversedBy="books", fetch="EXTRA_LAZY")
                                                                     * @ORM\JoinTable(name="books_author")
                                                                     */
                                                                    private $authors;
                                                                }
                                                                
                                                                class Author
                                                                {
                                                                 /**
                                                                     * @ORM\ManyToMany(targetEntity="Book", mappedBy="authors")
                                                                     */
                                                                    private $books;
                                                                }
                                                                


                                                                Теперь мы можем получить для книги всех ее авторов, а для автора — книги

                                                                $idBook = 10;
                                                                $authors = $em->getRepository('Books::class')->find($idBook)->getAuthors();
                                                                


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

                                                                class createBook
                                                                {
                                                                function createAction(){
                                                                  $idAuthor = 10;
                                                                  $author = $em->getRepository('Books::Author')->find($idAuthor);
                                                                  $book = new Book();
                                                                  $book->setAuthor($author);
                                                                  $em->persist($book);
                                                                  $em->flush($book);
                                                                
                                                                }
                                                                }
                                                                

                                                                  0
                                                                  В моем примере все то же самое примерно, только в yaml, а не в аннотациях. Через консольную команды я создал yaml на основе foreign keys базы
                                                                    0
                                                                    Как по мне, SQL vs ORM некорректное сравнение у своего основания.

                                                                    Согласен. Но только в этом.


                                                                    ORM — это оверхэд над sql

                                                                    ORM — это механизм прозрачной трансформации сущностей ООП-языка в язык sql и обратно. Да, универсальные механизмы вносят некоторый оверхед по сравнению с захардкоженными преобразованиями, но если для работы с данными в приложении мы выбираем объекты (особенно полноценные, а не подобные структурам C), а для их хранения SQL-базы, то нельзя говорить об ORM как об оверхеде — это необходимый для этого выбора механизм. Можно говорить о том, сколько приносят (и приносят ли) оверхеда конкретные реализации ORM по сравнению с идеальными, но не более.


                                                                    Задача ORM — абстрагироваться от базы данных.

                                                                    Всё же, абстрагироваться от реляционной сущности хранилища, а не от его наличия вообще.


                                                                    Мы перестаем думать о базе данных в принципе

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

                                                                      +1
                                                                      Задача ORM — абстрагироваться от базы данных.

                                                                      тут могут быть недопонимания. Не от базы данных а в принципе от способа хранения данных. Разделение ответственности банальное. Это не означает что мы можем взять одну СУБД и заменить другой на раз два. Это не является целью.

                                                                  +1
                                                                  Допустим, стоит задача обновить двум последним авторам имя на «Жорж».

                                                                  Сразу скажу, что выразить через DQL мне этот запрос вообще не удалось, с вложенными подзапросами там всё плохо.


                                                                  Вот так, например

                                                                  public function get2LastAuthors ()
                                                                      {
                                                                      	 $result = $this->createQueryBuilder('u')
                                                                              ->select('a')
                                                                              ->addOrderBy('a.id', 'DESC')
                                                                              ->setMaxResults( 2 );
                                                                              ->getQuery()
                                                                              ->getResult();
                                                                              return $result;
                                                                              
                                                                  	}
                                                                  


                                                                  
                                                                  class AuthorsController
                                                                  {
                                                                  	$doctrine = $this->getDoctrine();
                                                                  	$em = $this->getDoctrine()->getManager();
                                                                  	//используем QueryBuilder
                                                                  	$authors = $em->getRepository(Books::class)->get2LastAuthors();
                                                                  	//или так
                                                                  	$authors = $em->getRepository('Books::class')->findBy([], ['id' => 'ASC'], 2);
                                                                  
                                                                  	$name = "Жорж";
                                                                  	foreach($authors as $val){
                                                                              $val->setName($name);
                                                                  	}
                                                                  	$em->flush();
                                                                  }
                                                                  


                                                                    0
                                                                    спасибо
                                                                    0
                                                                    Довольно негативная получилась заметка в отношении ORM) Давно работаю с ORM на Perl и в защиту этого подхода могу привести пример решения последних двух задач на местном ORM — DBIX::Class. Решения должны выглядеть примерно так:

                                                                    * Книги с авторами (специфичный json_agg я заменил на GROUP_CONCAT)
                                                                    $schema->resultset('Book')->search( undef, 
                                                                      { select => [
                                                                        'me.id', 'me.title', 
                                                                        { 'GROUP_CONCAT' => 'author.name', -as => 'authors_list' }
                                                                       ],
                                                                       join => 'authors',
                                                                       group_by => 'me.id'
                                                                      } );
                                                                    


                                                                    * Правка имен авторов
                                                                    $schema->resultset('Author')->search( undef,
                                                                      { order_by => { -desc => 'id' }, rows => 2 }
                                                                     )->update({ name => 'Жорж' });
                                                                    
                                                                      0

                                                                      Вот я не понял что предполагалось во втором примере сделать. Последним двум строкам поменять автора? И что сделает на практике ORM — два запроса или один? Когда это будет понятно? Не придётся ли включать вывод каждого сгенерированного запроса, чтобы понять что выполняется и не надо ли выполнить чистый SQL, не уйдёт ли на это всё сэкономленное время?


                                                                      Была интересная статья — сравнение перловых ORM, в частности Rose::DB::Object и DBIC. По производительности разница была до 20-ти-кратной. То есть SQL(Rose к нему очень близок) в 20 раз быстрее некоторые операции выполняет. Это цена ОРМ, не считая времени на поиск проблем при разработке.

                                                                      0
                                                                      Мне интересно посмотреть на ORM если нужно сделать временную таблицу в БД, а потом с ней дальше то то делать)
                                                                        0
                                                                        :) Да много чего еще можно на SQL.
                                                                        Можно функций понаписать и в запросе к полям использовать эти функции прямо в запросе.
                                                                        ORM такого не может да, но он не для этого.
                                                                          0
                                                                          ORM is for CRUD
                                                                            0

                                                                            Как раз для CRUD ORM нафиг не нужен, особенно в PHP.

                                                                            0

                                                                            А какие PHP-объекты этой таблице будут соответствовать?

                                                                            –1
                                                                            SQL vs ORM — из одной крайности в другую :)
                                                                              +2

                                                                              Я на ORM и генераторы запросов смотрю как на хорошую подсказку IDE и компилятору, что у нас из базы возвращается не какой-то сферический ResultSet, а некоторый объект с вполне определёнными полями и методами.
                                                                              Это отсекает такой класс ошибок, как написать запрос, отладить его в IDE для БД (она как правило отдельная), перенести его в код и потом с удивлением ловить ошибки в рантайме, из-за того, что поле в результате или называется не как надо, или просто забыли добавить поле в выборку.
                                                                              Хорошие IDE/ORM ещё могут слазить в базу за схемой, проверить имена маппингов, сгенерить миграции итд.


                                                                              Ещё веселее становится, когда надо динамически собирать where и делать join у запроса в зависимости от входных параметров — собирать запросы конкатенацией или шаблонами довольно утомительное и багообразующее занятие.

                                                                                0
                                                                                Кейс 1 и 2 в Yii2 такой же как в ларавеле, а вот 3 и 4:

                                                                                Кейс 3 Yii2
                                                                                $books = Book::find()
                                                                                    ->with('authors')
                                                                                    ->all();
                                                                                    foreach ($books as $book) {
                                                                                        print $book->name . "\n";
                                                                                        foreach ($book->authors as $author) {
                                                                                            print $author->name . ";";
                                                                                        }
                                                                                    }
                                                                                

                                                                                Тут главное что будет всего два запроса, вне зависимости от числа книг. Но тут конечно ещё должен быть прописан relation через `viaTable()`.

                                                                                Кейс 4 Yii2
                                                                                Author::updateAll(
                                                                                    ['name' => 'Жорж'],
                                                                                    ['id' => Author::find()->select('id')->orderBy(['id' => SORT_DESC])->limit(2)->column()]
                                                                                );
                                                                                

                                                                                  0
                                                                                  Забавно смотреть, как, для того, что бы не использовать SQL образуются некие псевдоязыки даже для простейших, с точки зрения SQL задач, для понимания конструкций которых нужно мозг напрягать больше, чем при разборе SQL.
                                                                                  При этом производительность остается вообще за скобками, хотя, конечно, есть туча проектов, где не Big Data, а Micro Data и где главное это быстро быстро выпустить первый прототип.
                                                                                    0

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

                                                                                      0
                                                                                      Уверен, что ваше определение, это не то, что имеет ввиду подавляющее большинство программистов использующих, то, что они называют ORM.
                                                                                      Они не проектируют и не пишут слои абстракции, имея ввиду, ваше определение, они используют готовые ORM решения.
                                                                                      Посмотрите даже на примеры в статье.
                                                                                      А то
                                                                                      главное, чтобы он наружу не протекал, чтобы его фасад оперировал только объектами приложения и скалярами, без всяких таблиц, столбцов, строк, джойнов и т. д
                                                                                      :)
                                                                                        0

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


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

                                                                                      0

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

                                                                                        +2
                                                                                        Я понимаю, понимаю, что люди разные и образ мышления разный.
                                                                                        Да, реляционные БД, это мой профиль.
                                                                                        Кроме того я был архитектором в нескольких, не самых маленьких проектах по созданию ИТ систем.
                                                                                        Системы здравствуют и живут дальше по сию пору.
                                                                                        Когда нибудь, я встречу проект, где без ORM никуда и осознаю весь дзен ORM.
                                                                                        Пока что, по результатам статей и обсуждений на хабре, я вижу только примеры элементарных каких то случаев, где записали строку, считали строку из таблички. Иногда, о боже, даже из 2-3 таблиц. И делается вывод ORM это круто.
                                                                                        Есть еще другая категория. Это Гуру.
                                                                                        Они говорят о «протекающих слоях абстракций, фасадов, скаляров, синтаксическом сахаре и гидрации данных».
                                                                                        Куда уж тут простым реляционным таблицам.
                                                                                        Я, признаюсь, плохо отношусь к технологиям, которые нельзя объяснить простым языком.
                                                                                        Как правило это трудно поддерживать.
                                                                                        Особенно, если технологией владеет некий супер гуру, который может уйти в силу разных причин.
                                                                                        Да, есть задачи, когда стоит требование использовать любую из СУБД, с возможностью быстрого переключения. Мой опыт говорит, что в таком случае неизбежны проблемы с производительностью.
                                                                                        Т.е. вот так просто сделать такую фичу для сложного и объемного (по данным) проекта, совсем не просто.
                                                                                        Допускаю, что это у меня такие проекты, когда даже владея знаниями по какой то одной СУБД, думаешь, как извернуться, что бы это вообще работало в приемлемые сроки, а еще если навесить сверху «слой абстракции», то будет вообще труба.
                                                                                        Поэтому, мне кажется, что ORM это игрушка для студентов (не знающих SQL) и для любителей сложностей и «слоев абстракции».
                                                                                        Но, время движется вперед.
                                                                                        Дорогу новым технологиям и молодежи :)
                                                                                        Благо работы пока хватает и «старикам».
                                                                                          0
                                                                                          где без ORM никуда и осознаю весь дзен ORM.

                                                                                          Мне кажется что весь конфликт в разном трактовании этих трех букв. Что вы подразумеваете под ORM? Ибо из того что вы пишите выглядит так как будто бы это некий монстр вроде Hybrenate который должен использоваться всегда и везде без компромиссно и без учета того что он как бы позволяет мэпить результаты SQL на объекты.


                                                                                          Я, признаюсь, плохо отношусь к технологиям, которые нельзя объяснить простым языком.

                                                                                          Есть хороший доклад Грэга Янга под названием "8 lines of code". Вам должно понравиться.

                                                                                            0
                                                                                            Посмотрю на досуге, спасибо.
                                                                                            +1

                                                                                            Механизм ORM объясняется легко: объектная модель и реляционная не соответствуют друг другу и нужен механизм их отображения друг на друга, коль скоро принято решение использовать в приложение объектную модель и реляционное хранилище для неё. Неужели не встречали таких приложений в мире где ООП мэйнстрим для прикладной разработки, а SQL мэйнстрим для хранения структурированных данных?

                                                                                              0

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

                                                                                          +1

                                                                                          При использовании ORM напрягает тот факт, что нужно постоянно следить, какие реально SQL-запросы выполняются, т.к. если этого не делать, можно нарваться на тормоза, баги и т.п. А это лишняя работа, проще самому сразу написать на SQL "как надо", чем танцевать с бубном вокруг ORM, пытаясь добиться от нее чего-то похожего на то, что надо, но всё равно в итоге не совсем того.

                                                                                            +1

                                                                                            Почему вы отбрасываете вариант "самому сразу написать на ORM как надо"? :-)

                                                                                              0

                                                                                              Если только для себя и только для конкретного проекта/задачи и эффект от этого понятен (упростится написание кода и т.п.), то можно. Но как только эту ORM кто-то другой попробует применить для своих целей, он столкнется с этими же проблемами. Т.к. "как надо" — это не какая-то математическая правильность, а лишь факт соответствия конкретной ситуации.

                                                                                                +3

                                                                                                То же самое я могу сказать и вам.


                                                                                                А это лишняя работа, проще самому сразу написать на SQL "как надо"

                                                                                                Если только для себя и только для конкретного проекта/задачи и эффект от этого понятен (упростится написание кода и т.п.), то можно. Но как только этот запрос кто-то другой попробует применить для своих целей, он столкнется с этими же проблемами. Т.к. "как надо" — это не какая-то математическая правильность, а лишь факт соответствия конкретной ситуации.

                                                                                                  +1

                                                                                                  Наверное все популярные универсальные ORM-библиотеки появились из "для себя" и у них есть вполне определенная область применения для реализации как функциональных, так и нефункциональных (прежде всего ресурсных) требований по взаимодействию приложения с СУБД. Если кто-то упорно пихает ORM туда, где она даже себя не заявляет как решение, то кто в этом виноват?

                                                                                              +1

                                                                                              С ORM всё кажется красиво, пока есть четыре таблички. Как только их 400, сразу возникает проблема автоматизации генерации описания для ORM, потом оказывается что генератор делает не всё так как надо и после генерации приходится что-то поправлять, потом оказывается что всё-таки надо указывать 10 нужных столбцов из сотни в таблице, потом вдруг появляется какая-то непонятная ошибка, потом вдруг обнаруживаешь что ORM работает в 20 раз медленнее чем SQL, потом через пару лет оказывается что современныый обновлённый ORM совсем не так совместим со старым… и появляются мысли что зачем я вложился в этот ОРМ? Он требует знать всё об ОРМ, всё об SQL, а в качестве плюшек — только синтаксический сахар, так как обычный SQL на самом деле выдаёт точно такой же результат — как видно из примеров выше — и требует примерно столько же кода. Причём диалектов ОРМ много, а SQL один.


                                                                                              Это я пишу глядя на свой код давности несколько лет, который пришло время корректировать, и на код напарника, у которого почему-то пять запросов Hibernate выполняет пол секунды, а казалось бы должно быть на пару порядков быстрее. И код которого я не могу проверить, так как не разбираюсь в достаточной мере в Hibernate. Код другого — который пишет на SQL — элементарно.

                                                                                                0
                                                                                                а в качестве плюшек — только синтаксический сахар, так как обычный SQL на самом деле выдаёт точно такой же результат

                                                                                                В качестве основной плюшки — маппинг объектов на базу и наоборот. И если на объекты с базы ещё можно как-то маппить 1:1 малой кровью чем-то вроде PDOStatement::fetchObject, то для сохранения объектов вам понадобится ORM, чтоб приложение было мало-мальски поддерживаемым.

                                                                                                  0

                                                                                                  Вы в вышеприведённых примерах можете показать, в каком месте объекты мапятся, тем более двунаправленно? В каком из приведённых ОRM изменение объекта в базе тут же изменится в смапленном объекте? Можете представить, что в какой-то более-менее большой базе удастся сделать этот маппинг и приложение будет продолжать работать?

                                                                                                    +1

                                                                                                    В вызовах типа find/get (с базы на объекты) и save/persist/flush (с объектов на базу).


                                                                                                    О "тут же изменится" в целом речи нет, с одной стороны, с другой — в рамках ORM и сопутствующих технологий типа UoF, IdM и т. п. предполагается по умолчанию, что в базе не может быть изменений неинициированных ORM и каждая сессия работы СУБД выполняется в отдельной транзакции.


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

                                                                                                  0

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


                                                                                                  Мой опыт говорит как раз обратное — когда таблиц в базе 400, только ORM и позволяет не сойти с ума.

                                                                                                    0
                                                                                                    только ORM и позволяет не сойти с ума.

                                                                                                    тут скорее разделение ответственности, но ORM как раз об этом.

                                                                                                  0
                                                                                                  Как-то написал что-то типа ORM для себя, потому что надоело писать сотни одинаковых запросов. Про то как оно назывется даже и не знал тогда, что вылилось в велосипед. Просто сделал класс, представляющий таблицу в базе, где экзепляр класса — это запись в таблице, со стандартными функциями получения списка/удаления/сохранения, а так же функции построения кусочков sql-запроса (типа where, order by), которые можно переопределить. Наследуясь от этого класса можно переопределить таблицу, особенности полей и добавить методы с какими-то нетипичными/оптимизированными запросами. А сам класс-предок использует позднее связывание и строит sql с учетом особенностей потомка. Для больших результатов делал ленивую подгрузку (доп. класс с интерфейсом массива). В итоге удобно получилось и намного меньше писанины. С голым SQL теперь сталкиваюсь только для необходимой оптимизации и сильно нестандартных запросов. В общем быстрее получается написать свой кустарный ORM, чем писать сотни однообразных SQL-запросов и методов.
                                                                                                    0
                                                                                                    > сотни однообразных SQL-запросов и методов.
                                                                                                    Я посмотрел запросы в одном большом старом проекте, и знаете, там нет однообразных SQL-запросов. Т.е. если мы берем список юзеров, то обязательно с какой-то статистикой и т.д. На 90% запросы кастомные и не очень простые
                                                                                                      0
                                                                                                      В общем конечно же зависит от проекта. У меня это были в основном CRUD с фильтрами. А для отчетов конечно же отдельные классы с SQL. Тут ORM лишнее, согласен.
                                                                                                        0

                                                                                                        Результаты SELECT-запросов представлялись в виде объектов предметной области? UPDATE/INSERT-запросы зависели от таких объектов? Если да, то просто у вас была своя ORM, которая маппила объекты на SQL и обратно.

                                                                                                        0
                                                                                                        сотни однообразных SQL-запросов и методов

                                                                                                        Что же за предметная область такая, в которой сотни! типов разных объектов описываются, да еще однообразных?
                                                                                                          0
                                                                                                          Cистема — что-то между CRM и 1С. Cущностей то около пол сотни. А вот запросов на каждую по несколько. Чтобы не писать генерацию SQL на каждый случай, зависящий от аргументов, я сделал один генератор SQL, который бы подходил в большинстве простых случаев.
                                                                                                        0

                                                                                                        ORM-кам — нет, QueryBuilder-ам — да

                                                                                                          0
                                                                                                          По мне, так это как взять крутую и полезную штуку, которая имеет свои проблемы, выкинуть и оставить только проблемы.
                                                                                                          0
                                                                                                          Удобство написания кода и его читаемость безусловно важны, но как быть с производительностью7 Честно говоря прежде всего анализа производительности ожидал, когдда переходил сюда по ссылке.
                                                                                                            0
                                                                                                            О подобном дискутировали с michael_vostrikov в моей статье «Реализация бизнес-логики в MySQL»
                                                                                                            https://habrahabr.ru/post/312134/#comment_9850218
                                                                                                            с примерами кода и оценкой производительности

                                                                                                            Проблема ОРМ что он пытается натянуть ООП парадигму на декларативный язык SQL. Естественно вся декларативность SQL теряется, отсюда и просадки производительности
                                                                                                              0

                                                                                                              Скорее наоборот для большинства приложений: натянуть императивные команды SQL на декларативно описанный класс.

                                                                                                                0

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

                                                                                                                  0

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

                                                                                                                    0

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

                                                                                                                      0

                                                                                                                      Перефразирую: построение сложного графа может занимать значительно больше времени чем выполнение простого запроса :)

                                                                                                                        0

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

                                                                                                                  0
                                                                                                                  Проблема ОРМ что он пытается натянуть ООП парадигму на декларативный язык SQL

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


                                                                                                                  Как пример — например у нас есть данные и все хорошо. Но вот нам надо составить по этим данным граф связей кто с чем. Что проще:


                                                                                                                  • извращаться с процедурами и SQL что бы добиться желаемого
                                                                                                                  • добавить доменные ивенты в наши объекты по которым мы будем собирать проекцию данных в neo4j какой?
                                                                                                                    +1
                                                                                                                    Сила СУБД как раз не просто в хранении данных, а именно в их обработке. И на мой взгляд, язык SQL идеально для этого подходит. Это как раз язык манипулирования и обработки данных, специально для этого созданный.
                                                                                                                    Перекладывая функции СУБД в приложение посредством ОРМ, мы потенциально роем себе могилу.

                                                                                                                    Про граф связей и в чём извращение с SQL не понял, но в БД для этого есть внешние ключи.
                                                                                                                      0
                                                                                                                      Сила СУБД как раз не просто в хранении данных, а именно в их обработке.

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


                                                                                                                      Перекладывая функции СУБД в приложение посредством ОРМ, мы потенциально роем себе могилу.

                                                                                                                      Мы роем себе могилу когда доводим все до абсолюта. А так — все прекрасно. Юзаем ORM когда задача подходит под OLTP, и не юзаем когда не подходит.


                                                                                                                      Про граф связей и в чём извращение с SQL не понял, но в БД для этого есть внешние ключи.

                                                                                                                      У нас есть таблица с пользователями. Есть внешний ключ на introducer_id. Задача — вам нужно отобразить для анализа полное дерево рефералов. Количество уровней не ограничено.


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

                                                                                                                  0
                                                                                                                  Прям представил себе проект, где несколько десятков разработчиков, сотни сущностей и т.д. и т.п.
                                                                                                                  Интересно будет искать SQL код, писать его и еще что бы все разработчики отлично знали SQL.
                                                                                                                  Как по мне, ORM для больших проектов просто необходим. Собственно как мы и делаем.
                                                                                                                  Программисту проще оперировать объектами чем запросами. А маппинг поможет в сложных ситуациях.
                                                                                                                    0
                                                                                                                    Интересно будет искать SQL код

                                                                                                                    Если эти десятки разработчиков нормально умеют в декомпозицию то проблем не будет.


                                                                                                                    Программисту проще оперировать объектами чем запросами.

                                                                                                                    В каких-то случаях — да. А в каких-то декларативный SQL намного удобнее. Репорты — хороший пример.


                                                                                                                    А маппинг поможет в сложных ситуациях.

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

                                                                                                                      0
                                                                                                                      Да, про палки согласен.
                                                                                                                      При реализации DDD/CQRS/CommanBus… etc. очень много времени ушло на доработку архитектуры по работе с маппингом/доктриной и всем слоем персистенций. Коммиты отправили им.
                                                                                                                      В конечном счете оно того стоило.
                                                                                                                        0
                                                                                                                        очень много времени ушло на доработку архитектуры по работе с маппингом/доктриной и всем слоем персистенций.

                                                                                                                        Либо вы делали просто command bus (без CQRS) то скорее всего у вас могли быть проблемы со слоем персистентности. А с CQRS же мы бы имели чисто объектную модель которая вычисляет свой стэйт по ивентам (делаем мы event sourcing или нет — это детали, я сейчас в общем про eventual consistency). То есть в модели на запись у нас ORM по сути нет. Есть только ивенты.


                                                                                                                        Далее эти ивенты должны ловиться отдельными штуками и писаться с тем представлением данных которое вам нужно для конкретной задачи. Иначе толку от CQRS нет.


                                                                                                                        Но все же интересно что именно вы дорабатывали.

                                                                                                                      0
                                                                                                                      Вот именно с такими проектами я уже который год и сталкиваюсь, где десятки разработчиков, сотни сущностей, причем все разработчики хорошо знают SQL и пишут почти исключительно на нем. Вакансий программистов на T-SQL или PL/SQL сильно меньше со временем не становиться.
                                                                                                                        +1

                                                                                                                        А вакансий на программистов со знанием популярных ORM-библиотек становится все больше. Более того, эти знания подразумеваются, даже если ORM не упоминаются: пишут Symfony — имеют в виду Doctrine, пишут Laravel — имеют в виду Eloquent, пишут Yii — имеют в виду ActiveRecord, пишут Rails — имеют в виду ActiveRecord (но другой), пишут .Net — имеют в виду EntityFramework, пишут Spring — имеют в виду Hibernate и т. д. Да и просто, если упоминается какой-то ООП-язык типа PHP, Ruby, Java, C#, C++ и какая-то SQL-база в одной вакансии, то чаще всего имеется в виду, что соискатель должен уметь отображать объектную модель на реляционную и обратно, а не редко и без популярных универсальных ORM-библиотек, то есть должен уметь написать свой ORM.

                                                                                                                      0
                                                                                                                      ORM — зло, т.к. пытается «впихнуть не впихуемое».
                                                                                                                      Т.е. создать «универсальный преобразователь» из РМД в ООМД.
                                                                                                                      Пока модели простые, все замечательно, по мере усложнения модели начинают вылазить не стыковки.
                                                                                                                      Т.к. бизнес сущность обычно не может быть выражена один в один в виде таблицы РМД.

                                                                                                                        0

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

                                                                                                                          0

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

                                                                                                                            0

                                                                                                                            Согласен.

                                                                                                                          +1
                                                                                                                          Т.к. бизнес сущность обычно не может быть выражена один в один в виде таблицы РМД.

                                                                                                                          Для этого и нужны, прежде всего, ORM, чтобы выражать не один в один. Использовать универсальный преобразователь as is, расширять его для частных случаев или писать свой неуниверсальный — это вопрос выбора преобразователя, а не вопрос его необходимости. Необходим он стал, когда вы выбрали использовать РМД и ООМД не просто одновременно, а отображая друг в друга.

                                                                                                                          0
                                                                                                                          ORM и SQL это разные инструменты для работы с данными. Соответственно, применять их надо по назначению. Строить отчёты и делать выборки, это задача SQL, для этого он разработан. ORM нужен для записи и DDD.
                                                                                                                          Read model — SQL, write model — ORM.
                                                                                                                            –1
                                                                                                                            Мне кажется автор, не много напутал в понятиях. Тут же не используется голый sql а все примеры на pdo и специфичных ORM для каждого фреймворка. Он не делает escape_string, и прочее он все те же самые функции которые делает orm для каждой специфичной базы пусть то Oracle, Postgreql переложил на PDO. ГДЕ ТУТ ГОЛЫЕ ЗАПРОСЫ SQL. От куда беруться такие умники.
                                                                                                                              +1

                                                                                                                              PDO работает с голыми SQL-запросами. Он не модифицирует запросы под базу, только предоставляет единый API для разных баз.

                                                                                                                              0
                                                                                                                              Вся беда возникает, когда инструмент используют не только когда он что-то облегчает, а для всего подряд, т.е. пытаются сделать из конкретной тулзы инструмент на все случаи жизни. А потом идут длинные споры про то что инструмент не идеален, потому что он может не всё на свете. Так не бывает. Автоматизация всегда подразумевает какие-то ограничения. Инструмент применяют там, где он удобен. Остальное — уже какое-то извращение.
                                                                                                                                +1

                                                                                                                                Примеры какие-то немного надуманные. "Допустим, стоит задача обновить двум последним авторам имя на "Жорж"". Часто вы так делаете в рабочих приложениях?


                                                                                                                                Можно другие примеры рассмотреть.


                                                                                                                                // ------
                                                                                                                                
                                                                                                                                select id, name, field1, field2, field3, field4 ...
                                                                                                                                from books
                                                                                                                                inner join author_book ab on b.id = ab.book_id
                                                                                                                                inner join authors a on ab.author_id = a.id
                                                                                                                                where <фильтр по authors>
                                                                                                                                
                                                                                                                                Book::find()->joinWith('authorBook')->joinWith('authorBook.author')->where(<фильтр по authors>)
                                                                                                                                
                                                                                                                                // ------
                                                                                                                                
                                                                                                                                select id from books where id = X
                                                                                                                                // показать 404 если не найдено
                                                                                                                                // $queryString = <куча конкатенаций>;
                                                                                                                                update books set id = ..., name = ..., field1 = ..., field2 = ..., field3 = ..., field4 = ... where id = X
                                                                                                                                
                                                                                                                                $book = Book::find()
                                                                                                                                // показать 404 если не найдено
                                                                                                                                $book->load($data);
                                                                                                                                $book->save();
                                                                                                                                
                                                                                                                                // ------

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

                                                                                                                                  0

                                                                                                                                  Есть ещё одно решение по мимо ORM, не использовать ОО языки или просто ОО возможности — тогда оно (ORM) не нужно. Просто работаете со списками, отображениями, векторами.
                                                                                                                                  Собственно ими(списками, отображениями, векторами) и представлены различные структуры в большинстве БД(как в реляционных так и nosql) и протоколы передачи данных(например JSON).

                                                                                                                                    0

                                                                                                                                    то есть полностью отказаться от преимуществ actor model и начать обмазываться монадами?

                                                                                                                                      0

                                                                                                                                      Не понял причем тут модель акторов. Она хоть и имеет общие с ООП корни но различий очень много, хотя бы начиная с того что акторы обмениваются сообщениями(асинхронно) а объекты вызывают методы друг у друга(синхронно). И за счет асинхронности вы вряд ли сможете эффективно объединить несколько сообщений в одну SQL транзакцию в отличии от методов модифицирующих объекты, да и это в принципе не имеет смысла потому что акторы это принципиально более высокий уровень абстракции. А тема о SQL vs ORM.

                                                                                                                                        +1
                                                                                                                                        Она хоть и имеет общие с ООП корни но различий очень много

                                                                                                                                        Ну как вам сказать, если мы пороемся и вспомним что подразумевалось под термином ООП (message passing, late binding) то как бы мы будем иметь просто определение actor model. Так что "общие корни" это мягко сказано.


                                                                                                                                        хотя бы начиная с того что акторы обмениваются сообщениями(асинхронно) а объекты вызывают методы друг у друга(синхронно).

                                                                                                                                        Да, сами акторы по хорошему существуют независимо друг от друга, но это совершенно не значит что они не ожидают ответа на свои сообщения. А вот блокировать им свое выполнение или нет — решать только им. Как никак event loop и все такое это не такая уж и редкость.


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


                                                                                                                                        акторы это принципиально более высокий уровень абстракции

                                                                                                                                        Примерно тот же уровень абстракции что и "все есть объект". То есть по сути никакой конкретики.


                                                                                                                                        А тема о SQL vs ORM.

                                                                                                                                        Нам удобно представлять систему как объекты и процессы (их можно функциями делать), а SQL это лишь декларативный способ работы с данными. То есть вот этот вот vs надо заменить на with и все счастливы.

                                                                                                                                      0

                                                                                                                                      Тащить низкоуровневую реализацию в приложение? Не, спасибо)

                                                                                                                                        +1

                                                                                                                                        Тогда нужны другие *RM, потому что в СУБД данные представляются в виде отношений и кортежей.

                                                                                                                                          –1

                                                                                                                                          Вижу в вашем возражении проблему в том что вы все же думаете в рамках ORM.
                                                                                                                                          SQL запрос возвращает вам именно коттедж или их список а не граф. Что собственно имеет нативную поддержку во многих не ООП языках — другими словами тот же список кортежей.
                                                                                                                                          Если вы хотите автоматического сохранения и другой обработки связанного графа сущностей как вам предлагаю некоторые ORM тогда да вам что-то близкое к просто SQL не подойдет. На моей практике данные встроенные возможности либо не эффективно работали и подходили для ограниченного набора задач или требовали написание обвязок на SQL-схожем языке для конкретной ORM, что сложнее обычного SQL запроса, который к тому же элементарно при написании проверить, минуточку тем же запросом в БД и не нужно писать сложные интеграционные тесты с моками (учтите ещё что под каждую ORM нужно изучать особенности её работы и ещё языка запросов).
                                                                                                                                          А можно просто написать ещё ещё один sql запрос или объединить несколько уже существующих в транзакцию.

                                                                                                                                            0

                                                                                                                                            Я думаю в разных рамках. При фразе о "не ООП языках" я думаю, прежде всего, о языках типа C. И писал (да и пишу) достаточно сложную логикe на голом SQL как раз из-за неэффективности ORM на больших массивах данных.

                                                                                                                                              +2

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


                                                                                                                                              Вообще, одна из проблем SQL на мой взгляд в том, что оно хоть и относится к реляционным базам данным, но нормально работать с этими реляциями не позволяет. Только при создании таблицы можно указать, что у нас есть связь с вон той. А данные по этой связи в запросе достать не получится (строка из другой таблицы как значение поля), и даже join по foreign key сделать нельзя. ORM восполняет эту часть, задавая связи между объектами.

                                                                                                                                                +1

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

                                                                                                                                                  0

                                                                                                                                                  Хм, да, многозначное слово. Я имел в виду выражения вида "one-to-many relationship".

                                                                                                                                                    +1

                                                                                                                                                    А реляционные СУБД от relation в смысле relational algebra, которая определяет такие операции как джойн над реляциями, в "простонародье" таблицами. One-to-many relationship к реляционным СУБД напрямую не относится, всякие явно и неявно заданные relationship — это семантика, которой мы описываем схему базы, но в самой СУБД её нет. ORM, кстати, один из инструментов такого наполнения, с помощью которого мы описываем, что таблица такая-то содержит данные объектов такого-то класса, вторая таблица — второго класса, а эти классы (а значит и таблицы) связаны один-ко-многим через такие-то поля. foreign key — это лишь инструмент обеспечения целостности, защита от кривых рук разработчика или пользователя, а не инструмент задания связей.

                                                                                                                                                      0

                                                                                                                                                      Ну насчет ORM я примерно о том же. Только foreign key это именно что связь между данными. Первичный ключ это ссылка, обозначающая объект. Целостность это значит, что мы не можем ссылаться на объект, которого нет в другой таблице. Зачем бы нам нужна была целостность, если нет связи.


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

                                                                                                                                                        0

                                                                                                                                                        "Ссылка, обозначающая объект" — это уже вкладываемая нами семантика, для базы это значение, идентифицирующее строку в таблице. То же с внешними ключами — ограничение foreign key лишь указывает базе данных, что значение в одном столбце одной таблицы должно быть из множества значений другого столбца другой (иногда этой же) таблицы и ничего более, что-то вроде динамического enum без всякой семантики связей, связь у нас в голове или в файле с маппингом ORM. Связь описывается в терминах базы данных, но сама база о наличии связи "не подозревает", она просто не оперирует подобными терминами.


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

                                                                                                                                                          0

                                                                                                                                                          Выделение сущностей и определение их атрибутов, это уже семантика. Назначение набора атрибутов первичным ключом это тоже семантика. Это движок оперирует байтами без семантики, ему и названия столбцов не нужны, как и сам текстовый SQL.


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

                                                                                                                                          0
                                                                                                                                          Вот было же уже сказано, что для сколько-нибудь нетривиальных ситуаций <a href=«http://blogs.tedneward.com/post/the-vietnam-of-computer-science/>ORM не годится, исходя из общий соображений. Для бесхитростного CRUD'а — вполне. Остальное — перемалывание воды в ступе. Несвежей воды…
                                                                                                                                            +1

                                                                                                                                            Скорее для CRUD как раз ORM вещь избыточная. А вот если надо прочитать объект из базы со всеми связями (желательно лениво в общем случае), дернуть его метод, изменяющий состояние его и некоторых его связей, а потом сохранить всё изменённое одной транзакцией, то тут без ORM (универсальной библиотеки) плохо. В лучшем случае много аккуратной работы.

                                                                                                                                            +1
                                                                                                                                            $books = \App\Book::with('authors')->get(['id', 'name']);
                                                                                                                                            

                                                                                                                                            И с этим уже, похоже, ничего не сделать, по крайней мере я ничего не нашел.

                                                                                                                                            with(['authors' => function($query) { 
                                                                                                                                                $query->select('field1', 'field2'); 
                                                                                                                                            }]) ...
                                                                                                                                            

                                                                                                                                            Ощущение, что автор просто не использует ORM в реальной жизни, отсюда и претензии к читабельности с неуклюжими примерами с заменой жоржей. Тот же eloquent, например, лично меня выручает тем, что берет на себя преобразование дат и массивов из бд, плюс, из коробки кидает события при сохранении и создании объектов (понятно, что это пара строк кода и это уже не область ОРМ, но не мог не упомянуть). чистый sql использую только в случае сложных запросов.
                                                                                                                                              0

                                                                                                                                              @Tantacula


                                                                                                                                              Ощущение, что автор просто не использует ORM в реальной жизни

                                                                                                                                              вы вообще query builder обсуждаете, это не ORM. И Eloquent не особо то и ORM.

                                                                                                                                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                                                                                              Самое читаемое