Цель данной статьи — поделиться опытом по написанию простого ООП MVC PHP фреймворка. Так же хочу предоставить сообществу исходный код и попросить критики, одобрения, замечаний и поддержки.

Введение


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

Как показала дальше практика: читать, знать, слышать о чем-либо, и уметь самому это реализовать — совершенно разные вещи. Теоретизировать можно бесконечно, но только настоящее практическое задание позволяет понять, на каком уровне ты находишься. Всвязи с этим и было начато «написание собственного велосипеда». Каким он получился — судить вам.

Процесс разработки


Разработка фреймворка велась следующим образом: изначально было некое простейшее веб-приложение, которое дорабатывалось, переписывалось, меняло структуру, обрастало новыми классами и компонентами и т. д. Несколько месяцев назад у меня появилось свободное время, много энтузиазма и решительности таки доделать фреймворк «по-серьезному». На написание данной версии я потратил в совокупности 3-4 недели (работая в среднем 1-3 часа в день, 3-4 дня в неделю).

По мере разработки стандарты и требования я сознательно завышал для себя, искал оптимальные решения много раз переписывал код. Так, например, работу с конфигурацией я переделывал раз 5-6 (причем несколько раз кардинально), роутинг — 3-4 раза. В качестве примеров я брал код из статей, публикаций, руководств, фреймворков (Yii2, CodeIgniter, Zend, Phalcon, Bun) и т. п.

Анализ требований


Все начинается с анализа требований и пожеланий к итоговой системе.

Фреймворк должен:

  • позволять быстро создать сайт «с нуля»
  • иметь в себе ряд уже реализованных базовых технических решений и инструментов
  • содержать разделенный frontend и backend
  • отвечать современным требования по коду, технологиям, применяемым техническим решениям и т.п.
  • содержать уже в базовой комплектации демо-приложение, на основе которого можно вести свою разработку
  • быть модульным и расширяемым
  • иметь понятную документацию, техподдержку (в идеале — сообщество)

Применяемые технологии


Список таких решений напрямую зависит от того, о чём знаете и что применяете в своей реальной практике. Т.е. подходим к необходимости постоянного мониторинга новинок и изменений в сфере ИТ.

Практики и технологии:

  • Язык программирования: PHP >= 5.5.* или PHP >= 7.*
  • База данных: MySql >=5.4.*
  • Менеджер пакетов: Composer
  • Автозагрузка: PSR-4, кодирование: PSR-2, логирование: PSR-3
  • Используем функционал языка: неймспейсы, трейты, магические функции и т.п.
  • Применяем паттерны при построении структуры классов, реализации задач
  • Верстаем html-код с использованием Twitter Bootstrap
  • Пользуемся лучшими подходами в программировании: SOLID, DRY, KISS, YAGNI
  • Покрываем код PhpUnit-тестами (тестирование работы базовых классов приложения)
  • покрываем функционал Codeception-тестами (приемочное тестирование)

Структура папок


Приведу структуру файлов и папок в фреймворке (также можно посмотреть код на GitHub):

image

Код


В приложении есть одна единственная точка входа. Привожу код файла index.php из корневой публичной папки веб-сервера.

session_start();

$loader = require(__DIR__ . '/../../vendor/autoload.php');
$loader->addPsr4('framework\\', __DIR__ . '/../../system/');
$loader->addPsr4('frontend\\', __DIR__ . '/../');
$loader->addPsr4('common\\', __DIR__ . '/../../common/');

$config = array_merge(
        require(__DIR__ . '/../config/main.php'),
        require(__DIR__ . '/../../common/config/main.php')
        );

$appication = new \framework\core\Application();
$appication->run($config);

Код метода run($config) из класса \framework\core\Application(). Производится загрузка необходимых классов приложения и производится вызов соответствующего контроллера (в методе execute()).

    /**
     * 
     * @param array $config
     */
    public function run($config = []) 
    {           
        $this->benchmark = new Benchmark();  
        $this->environment = Environment::get();
        $this->config = new Registry($config);
        $this->response = new Response();
        $this->request = Request::getInstance();
        $this->assets = new Asset($this->config->assets); 
        $this->setParams();  
        $this->router = new Router($this->config->routes);
        $this->execute();
    }

Код метода execute() из класса \framework\core\Application(). Нужный контроллер на данном этапе уже выбран, производим инициализацию этого контроллера, обработку хеадеров, вывод контента. В случае ошибки — бросаем 404 Not Found.

    public function execute() 
    {
        $controllerName = $this->router->getControllerName();     
        try {
            $controllerClass = '\\' . $this->config->name . '\controllers\\' . $controllerName . 'Controller';
            if (class_exists($controllerClass)) {
                $controller = new $controllerClass;
                if ($controller instanceof Controller) {
                    $controller->setApplication($this)->run();
                }
            } else {
                throw new CoreException('Controller "' . $controllerName . '" not exists: ' . Request::getInstance()->server["REQUEST_URI"]);
            }
        } catch (CoreException $e) {
            $e->logError();
            $this->response->setHeader("HTTP/1.1 404 Not Found");
            $this->router->error404(); 
            $this->execute();
            exit();
        }        
        
        foreach ($this->response->getHeaders() as $header) {
            header($header);
        }
        
        echo $this->response->getContent();
    }

Улучшения и планы на будущее


В качестве адаптера для коннекта к БД я использовал PDO. В ходе работы PDO мне не очень понравился — сложно отлаживать запросы, хочется комфорта использования ORM. Можно установить Eloquent ORM — это современное и готовое решение (применяется в фреймворке Laravel), да и к тому же оно хорошо документировано и может быть установлено из composer за несколько минут.

Так же думал о расширении базового функционала фреймворка: хотел добавить поддержку модулей. Т.е. чтобы можно было написать например, блог как отдельный модуль (со своими контроллерами, вьюверами, моделями и т.п.). И потом подключать этот модуль в любом месте приложения.

Можно расширять и базовый «джентельменский» набор классов в ядре, усложнять систему логирования, обработки ошибок, конфигурирования, писать полноценный демо-сайт со всем функционалом и т.д.

Заключение


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

Так же хотелось бы реализовать какой-либо проект на базе данного фреймворка. Так сказать опробовать инструмент в работе. Понятно, что поиск заказчиков — это совсем не к данному разделу, но хотел бы услышать, можно ли на данном решении стартовать реальный проект? Популярные фреймворки «из коробки» дают функционал в 50-100 раз качественнее и масштабнее, конкурировать с ними сложно.

> Код на GitHub
Поделиться с друзьями
-->

Комментарии (71)


  1. eshimischi
    28.02.2017 13:54

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


    1. FedyaShlyapkin
      28.02.2017 16:41

      ну от чего же. начинающим будет интересно. PHP не по фреймворкам изучают.


      1. lair
        28.02.2017 16:45
        +3

        PHP не по фреймворкам изучают.

        И это написано в защиту статьи "простой современный MVC-фреймворк"?


        1. FedyaShlyapkin
          28.02.2017 17:59

          да. и в чем тут проблема? Неплохо понимать как писать фреймворк на PHP, а не как я крут зная фреймворк но не зная как все устроено.


          1. lair
            28.02.2017 18:05

            Неплохо понимать как писать фреймворк на PHP,

            Зачем? Есть, знаете ли, маленькая разница между "я понимаю, как устроено" и "я знаю, как написать".


    1. volmir
      28.02.2017 17:54

      Я писал весь код исключительно для повышения своей квалификации как программиста.
      Так как просто читать «о программировании» — это не то.
      Надо ручками, ручками — кодить, думать, анализировать.


      1. Fesor
        01.03.2017 09:06
        +3

        фреймворки — это не то на чем поднимается скил.


        Приведу пример:


        • ваш логгер — вы не рассмотрели случаи когда нужно делать блокировку на файл для предотвращения конкурентной записи
        • вы используете статику и суперглобальные переменные по всюду в коде.
        • нет тестов (то что есть — не то что нужно для "фреймворка")
        • в целом вы "повторили" то что уже знали, я подозреваю что знаете вы Yii. Никаких новых скилов.

        А вот если бы вместо этого вы бы просто на PHP написали логику какую сложную, поработали со структурами данных, написали бы e-commerce решение (только логика, ни баз данных ни прочего) с какими-нибудь сложными скидками и купонами, вот там есть где покачать скил. Особенно если при "качании скила" наложить кучу ограничений на свой код (аля никаких геттеров, ну или взять просто "объектную гимнастику"). Вот эти ограничения вас думать заставят. Ну или попробовали бы по TDD работать (phpspec желательно, ну или хотя бы phpunit, а не codeception который позволяет тестить любой код) отбросив все свои старые привычки. Это тоже будет "чем-то новым" для вас.


        А так… просто еще один фреймворк который не должен увидеть продакшена.


        1. volmir
          01.03.2017 11:42

          Спасибо за идеи куда «расти», как специалист. Я тоже подумывал о чем-то подобном.
          Хочется интегрировать программную составляющую (что писать: структуры, сложная логика, объекты и т.п.) и общественно-полезную составляющую (для кого для чего писать, суть самой разработки, её полезность для людей).


          1. Fesor
            01.03.2017 18:48
            -1

            и общественно-полезную составляющую

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


            Посмотрите чего не хватает инфраструктуре PHP. Я бы например предложил бы вам сделать солюшен вроде attache, и даже звездочку бы поставил.


            Еще как вариант — leaderboard для PHP, потом возможность истории впилить.


            Еще — можно попробовать попилить фичи для популярных библиотек/фреймворков.


  1. impwx
    28.02.2017 13:58
    +2

    По ходу эксплуатации различных современных фреймворков я понял, что недостаточно понимаю, как все устроено внутри, ..., я выбрал создание собственного проекта.
    Чукча не читатель, чукча писатель!

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


  1. oxidmod
    28.02.2017 14:00

    $loader->addPsr4('framework\\', __DIR__. '/../../system/');
    $loader->addPsr4('frontend\\', __DIR__. '/../');
    $loader->addPsr4('common\\', __DIR__. '/../../common/');


    это же можно вынести прямо в composer.json


    1. volmir
      28.02.2017 18:03

      А как?
      Я пробовал отключать этот код — но тогда ничего не работает.
      Добавлял такие конструкции:
      "psr-4": {
      "Frm\\": "/system/",
      "App\\": "/application/"
      }

      Но не дошел до работающей конструкции.
      :(


      1. Delphinum
        28.02.2017 18:08

        Добавлял такие конструкции

        А после добавления файл автозагрузки пересобирали?


        1. volmir
          28.02.2017 18:22

          Добавлял и пересобирал (пробовал разные способы).
          На досуге попробую ещё. Спасибо, что подсказали, что так можно сделать. Конечно, подобная конструкция не очень красива.


          1. kruslan
            28.02.2017 19:28
            +1

            "autoload": {
            		"psr-4": {
            			"Migration\\": "migration/"
            		}
            	}
            


            /system/ — это от корня, потому и не работает ;)


            1. Delphinum
              28.02.2017 19:44

              /system/ — это от корня, потому и не работает ;)

              как это связано?


              1. ellrion
                28.02.2017 19:49
                +1

                "Имя Ибрагим ведущий слеш вам о чём-нибудь говорит?")


                1. Delphinum
                  28.02.2017 19:52

                  "Migration\\": "migration/"
                  

                  Просто для контраста? Тогда понятно. А я пошел в репе автора искать, где же там у него миграции )


  1. GeMir
    28.02.2017 14:13
    +2

    «Хотел бы услышать, можно ли на данном решении стартовать реальный проект?» — я правильно понимаю, что вы что-то создали, но не понимая насколько оно жизнеспособно просите оценить со стороны?


    1. volmir
      28.02.2017 18:30
      -2

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

      Если поэксплуатировать этот фреймворк на продакшне в течении 6-9 месяцев (с постоянной доработкой и сопровождением реального проекта) — то после этого срока можно будет сделать програмный редизайн и рефакторинг кода ядра и всего приложения в целом.

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


      1. Delphinum
        28.02.2017 18:34
        +2

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


        1. volmir
          28.02.2017 19:52

          Да, согласен. Не спорю.


      1. Fesor
        01.03.2017 09:11
        -1

        Нет, не надо применять это "на реальных проектах", пожалуйста. Миру уже хватает проектов на no-name фреймворках которые нереально суппортить.


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


        1. volmir
          01.03.2017 11:36
          +1

          Я вот тоже к тому же прихожу: ОРМ хочется взять стороннюю, а так же и шаблонизатор, мейлер…


  1. vlreshet
    28.02.2017 14:38

    Можно немножко критики?

    — почему неймспейсы начинаются с маленьких букв?

    — вашей целью было использовать всю мощь языка — а везде конкатенации вместо двойных кавычек

    — что за прикол с двумя конфигами которым нужно ещё и array_merge делать? как минимум странно

    — что за прикол с путями в стиле "/../../system/"? почему нет константы корневого пути проекта вместо этого?

    — зачем у вас есть свой класс исключений типа CoreException, если вы строите строку-сообщение вручную, вместо передачи грубо говоря реквеста и всё? зачем новый класс если он судя по всему ничего не делает?

    — Не вопрос а просто критика — местами есть очень сомнительный код, типа вот такого:

    foreach ($this->response->getHeaders() as $header) {
        header($header);
    }
    


    P.S. Всё выше — чисто моё ИМХО, на истину в последней инстанции не претендую


    1. goodnickoff
      28.02.2017 18:48

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


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


      1. ellrion
        28.02.2017 18:59

        в yii то понятно это так сложилось, это их стиль и всё такое, но все пакеты нынче используют UpperCamelCase. Так зачем следовать за таким редким стандартом. Да и смотрится потом при использовании сторонних пакетов это стремно.


      1. vlreshet
        28.02.2017 21:25

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


        1. BoShurik
          28.02.2017 21:50

          Скажите это PSR
          https://github.com/php-fig/log/blob/master/Psr/Log/InvalidArgumentException.php


          Очень удобно ловить исключения, относящиеся к конкретному модулю/библиотеке/etc, а не все подряд


          1. volmir
            01.03.2017 11:29

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


        1. goodnickoff
          28.02.2017 22:08

          Этим вы подтвердили мои догадки о том, что понятия не имеете как работать с исключениями.


          1. vlreshet
            28.02.2017 23:26

            Как скажете.


    1. volmir
      01.03.2017 12:06

      что за прикол с двумя конфигами которым нужно ещё и array_merge делать?

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

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


      1. oxidmod
        01.03.2017 12:13

        а если я с cli хочу конектится к другой базе?


        1. volmir
          01.03.2017 12:53

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


          1. oxidmod
            01.03.2017 13:04

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


            1. volmir
              01.03.2017 13:10

              Чтобы разрешить такие коллизии нужно:
              1) Иметь в фреймвопке возможность задавать неогр. количество конфигов к БД.
              2) Иметь возможность передавать в скрипт коннекта к БД один из выбранных конфигов.
              Тогда будет универсальность и многовариантность применения.


              1. oxidmod
                01.03.2017 13:17

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


  1. fuzzy
    28.02.2017 15:24
    +3

    Ого, Вы изобрели урезанный Yii 2 :) Даже структура и названия директорий почти один в один.


    1. volmir
      28.02.2017 18:09

      Вы правы :)
      С него структуру и копировал (но не только из этого фреймворка, источников кода было много).


  1. ellrion
    28.02.2017 15:31
    +4

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


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


    Еще очень просится разделить код самого ядра с его тестами и непосредственно приложения на его основе. Вынесите его в отдельный пакет.


    Управление ассетами в контролере и конфигах бекенда как то неуклюже, не их это дело.


    Php как шаблонизатор если используете то имхо лучше использовать <?= ... ?> вместо <?php echo ...; ?> и для управляющих конструкций вариант синтаксиса с :. Т. е.


    <?php foreach(): ?>
    ...
    <?php endforeach ?>


    1. vlreshet
      28.02.2017 16:39
      -1

      Я не автор, но с вами немножко поспорю :) Шорт-теги (<?=) — зло, и использовать их нежелательно. А учитывая то что надо бы экранировать вывод в шаблонах (во избежание XSS) — так лучше ещё и нагородить свою функцию вместо echo). А синтаксис с : — оно то вроде как для вёрстки и задумано, но есть большой минус — редакторы кода не находят такие логические блоки, в отличие от блоков с классическими фигурными скобками.


      1. Delphinum
        28.02.2017 16:45

        Шорт-теги (<?=) — зло, и использовать их нежелательно

        Потому что потому?

        так лучше ещё и нагородить свою функцию вместо echo

        <?= escape($data) ?>
        

        Не вариант?


      1. ellrion
        28.02.2017 17:03
        +1

        Функцию экранирования это само собой. Т. е. в идеале нужен нормальный шаблонизатор. Но если мы используем просто php шаблон то делаем функцию


        /**
         * @param mixed $value
         * @return string
         */
        function e($value)
        {
            return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8', false);
        }

        и используем её: <?= e($some) ?>


        Шорт-теги (<?=) — зло

        У вас устаревшая информация. Во первых шорттеги считались злом потому что могут быть не включены. Но с PHP 5.4.0 именно <?= не считается шорттегом и директива конфигурации на него не влияет.


        редакторы кода не находят такие логические блоки

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


        1. vlreshet
          28.02.2017 17:39

          Хмм. По поводу шорт-тегов — признаю, как-то пропустил этот момент когда они перестали быть злом (хотя уже давно на PHP7 мигрировал).

          А вот по поводу шторма — только что пробовал, и мой шторм не воспринимает endif и endforeach как логические блоки, и следовательно не работают хоткеи перехода к логической скобке. Мы у себя на проекте пробовали использовать : синтаксис — но в итоге вернулись на классику, не понравилось. Но тут уже вкусовщина, спорить не буду.


          1. Xu4
            28.02.2017 17:56

            По поводу шорт-тегов — признаю, как-то пропустил этот момент когда они перестали быть злом

            Мне кажется вы не до конца правильно понимаете ситуацию. Шорт-теги остались злом. Но конкретно тег <?= к этому злу не имеет отношения, так как конфигурационная опция, связанная с шорт-тегами, на его работоспособность не влияет после версии 5.4.


          1. ellrion
            28.02.2017 18:16

            У вас "неправильный" шторм


            Работает же

            image


            1. vlreshet
              28.02.2017 21:28

              Тюю. Пошёл тогда копать настройки шторма…


    1. volmir
      28.02.2017 23:45

      Еще очень просится разделить код самого ядра с его тестами и непосредственно приложения на его основе. Вынесите его в отдельный пакет.


      Ага, пришел так же к этому (вообще узнал о такой возможности недавно).

      лучше использовать <?=… ?> вместо <?php echo ...; ?>

      Да, буду переучиваться себя на такой стиль.
      Одно время были споры по поводу коротких тегов.
      Рад что сообщество вернулось к <?=… ?>


  1. Alex_T666
    28.02.2017 17:49

    А сессию вы используете всегда при любом запросе?


    1. volmir
      28.02.2017 18:16

      Получается, что всегда.
      «Узких» мест много.
      На своем опыте я понял, что чем больше пишешь — тем больше разрастается код, тем больше надо думать об архитектуре и общей схеме вызова компонентов.
      Список «К исправлению» увеличивается и увеличивается, а для каждого исправления — надо много читать и думать об оптимальных решениях.
      И тесты писать и прогонять их после каждого изменения в ядре.
      Положительным моментом всей этой затеи является то, что квалификация и опыт программирования растет так же с исправлением каждой ошибки. Но цена этого опыта — перебор горы пустой породы в поисках того алмаза.
      Хотя другого пути нет, никто за человека это не сделает, кроме него самого.


  1. bioroot
    28.02.2017 19:11

    Говорил Чехов нехорошее, про людей, которые не умеют скрывать свои плохие повести… Велосипедить всегда хорошо, но для себя.

    По коду. А что будет, если нам подсунули не инстанс контроллера?

                    if ($controller instanceof Controller) {
                        $controller->setApplication($this)->run();
                    }
    

    Молча проглотим? Хотя если класс не существует, то кидаем исключение. Не аккуратненько…


  1. volmir
    01.03.2017 13:15

    Спасибо всем за критику, замечания и предложения.
    Со «своей колокольни» не все видно (или видно недалеко), а так все накидали комментариев по чуть-чуть — понятно что с этим всем делать и что есть в сухом остатке на данный момент.


  1. sspat
    01.03.2017 13:33

    Похвально желание учиться, поэтому вместо критики лучше добавлю свои пару копеек к процессу обучения.

    — Хранить пароли в md5 не стоит.
    http://php.net/manual/ru/function.password-hash.php
    https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet
    — Модели уж совсем примитивные, нужно как минимум абстрагироваться от работы с PDO напрямую. Вообще я бы не рекомендовал учиться на примере Active Record, ничему хорошему не научит. Лучше возьмите готовый слой абстракции построения запросов, например Doctrine DBAL (там особо нечему учиться, но много рутины) и стройте поверх него свою ORM.
    — Сила фреймворков в расширяемости и модульности, у вас же все компоненты очень тесно связаны, компонент авторизации например может работать только с вашей моделью User. На весь фреймворк у вас один интерфейс.
    — Как дальнейшее направление для обучения — написать свой DI контейнер и диспетчер событий.


  1. Skull
    01.03.2017 23:21

    База данных: MySql >=5.4.*

    Это что за зверь такой? После версии 5.1 сразу же идет 5.5


  1. nepster-web
    06.03.2017 15:10

    Почему почти все пытаются влепить AR в свой велосипед?


    1. Fesor
      06.03.2017 18:39

      потому что это самый простой способ сделать взаимодействие с БД. Даже DAO сложнее потому что думать больше надо. А тут — есть табличка в базе и она 1:1 мэпится на объектик. Ну и еще — многие кроме AR ничего и не юзали.


      1. nepster-web
        06.03.2017 18:43

        Я тоже практически ничего не использовал кроме AR, но он практически всегда приносит жесткий дискомфорт, как только приложения выходит за рамки блога или сайта визитки.

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


        1. ellrion
          06.03.2017 18:52

          Т. е. вы "практически ничего не использовал кроме AR" и при этом уверены что где то там солнце ярче?


          1. nepster-web
            06.03.2017 19:02

            Ключевое слово практически. Мое объяснение будет заключатся скорее всего в минусах AR связанных с магией и большой связанность. По мимо этого AR внес весьма солидную путаницу в разработку, а именно теперь большинство разработчиков, а в частности это новички, понимают примерно так:
            модель == один класс == active record (я тоже так думал еще 2 года назад).

            На примере Eloquent ORM: так есть такие понятия как аксессоры, мутаторы, скопы и тп. Работая со всеми этими терминами в контексте одного класса «модель» становится не много неповоротливой. Дальше более продвинутые новички, которые почитали про «MVC», и узнали примерно такую цитату "… Контроллеры должны быть тонкими, а логика должна быть в модели...", придерживаясь вышеупомянутого сравнения начинают логику писать в моделях (а в 99% это 1 класс).

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

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

            А так все получилось, потому, что очень сложно объяснить новичку, который 10 лет пишет на AR, почему нужно создать еще 5 классов, а не один AR.


        1. Fesor
          06.03.2017 18:56

          но он практически всегда приносит жесткий дискомфорт

          Ну можете перейти на уровень выше — row data gateway. Это когда у вас над каждой "моделькой" или графом "моделек" представляющих именно модель данных, есть объекта модель которая представляет корень агрегата например.


          Можно по разному готовить работу с базой данных. И вы редко ограничены каким-то одним подходом. Причем даже в одном проекте могут быть ситуации при которых надо комбинировать подходы.


        1. Fesor
          06.03.2017 18:58
          -1

          Поэтому нужно как-то людей толкать в сторону сущностей, репозиториев и дата-маперов.

          или DAO.


          1. nepster-web
            06.03.2017 19:08

            я сейчас сделал не много по другому.
            — сущность
            — репозиторий
            — для некоторых вещей View Model

            А вот реализация уже использует AR. И то очень аккуратно, так как строить запросы в нем очень легко. А с помощью View Model иногда можно возвращать кастомные штуки, чтобы не плодить частичные объекты (http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/partial-objects.html).

            В теории я тестировал, реализацию можно подменить без проблем, так как на выходе единый формат сущности или view model


            1. Fesor
              06.03.2017 20:02

              Кто у вас отвечает за сохранение сущности (кто дергает save)? Как разруливаете связи? Что у вас является сущностью? Выделяете ли корень агрегата сущностей?


              чтобы не плодить частичные объекты

              partial objects из доктрины слегка бесполезная штука. Мне больше импонируют выборки с select new SomeDTO(u.id, u.name)...;


              Тут проблема в другом. Вам действительно все это надо? Особенно например репозитории (я так понимаю что у вас нет UoW). Ну и в целом посмотрите в сторону row data gateway, я подозреваю что вы что-то подобное и сделали только сложнее.


              1. nepster-web
                07.03.2017 12:02

                Кто у вас отвечает за сохранение сущности (кто дергает save)?
                За сохранение отвечает тот-же репозитроий. Хоть и во круг этого ходят холивары, однако репозиторий это абстракция над хранилищем, поэтому думаю, что методу «сохранить в хранилище» место в репозитории, ну и пока на практике не узнал обратного.

                Как разруливаете связи?
                AR. Тоесть репозитории и сущности это такая себе абстракция над AR в моем случае.

                Что у вас является сущностью?
                Взял идею из Symfony. Тоесть у меня есть несколько понятий DTO — класс только с геттерами, который я гоняю между модулями и Сущности, которые в основном находятся в главном модуле Application, и являются практически идентичными как в Symfony.

                «я так понимаю что у вас нет UoW»
                Да, UoW пока нет, мы только начали пилить проект и подобные штуки я взял на себя смелость внести на этап оптимизации.

                Тут проблема в другом. Вам действительно все это надо?
                Как показывает практика, если Вы за рамками «бложика» или «сайта-визитки» и требуется постоянно допиливать и обновлять функционал, то подобные вещи весьма кстати.


                1. Fesor
                  07.03.2017 12:16

                  Хоть и во круг этого ходят холивары

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


                  Взял идею из Symfony.
                  являются практически идентичными как в Symfony.

                  Тут подробнее. У вас там геттеры/сеттеры или полноценная объектая модель, с логикой в сущностях и тд.


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


                  смелость внести на этап оптимизации.

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


                  Если вы это уже разруливаете руками, я не вижу смысла уже делать UoW. Но тогда мне было бы интересно узнать как вы это разруливаете.


                  то подобные вещи весьма кстати.

                  Ну для того что бы объективно оценить надо все же видеть код, просто как по мне у вас слишком уж резкий переход от простого active record к DDD-ной структуре. Да и AR не мешает делать DDD, просто будет больше похоже на row data gateway нежели обычный ar.


                  1. nepster-web
                    07.03.2017 12:36

                    у вас не очень то и репозиторий, больше походит на table gateway.
                    Ну тут спорный вопрос, так как данные одной сущности могут быть раскинуты на несколько таблиц. К примеру есть сущность Product, у него есть поля name, description и тп. и данные этих полей могут быть в таблице скажем product_translations (да я пока не решился отойти от этого подхода в пользу json).

                    Тут подробнее. У вас там геттеры/сеттеры или полноценная объектая модель, с логикой в сущностях и тд.

                    Видимо не натыкался я на примеры с логикой, но в моем понятии сущность — это класс с геттерами и сеттерами, который ничего не делает, кроме как позволяет предоставить контейнер для данных сущности. Как-то я уже натыкался на весьма серьезную проблему (https://habrahabr.ru/post/316836/) и сущности рассматриваю как обертку над данными и единый формат, который можно гонять по проекту, ну скажем если подменить хранилище, то оперируя сущностями приложение не должно заменить, что что-то пошло там поменялось.

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

                    UoW нужен не для оптимизации
                    Походу его основная задача, это (с) "… Реализация паттерна Unit of Work следит за всеми действиями приложения, которые могут изменить БД в рамках одного бизнес-действия. Когда бизнес-действие завершается, Unit of Work выявляет все изменения и вносит их в БД. ..."

                    у вас слишком уж резкий переход от простого active record к DDD-ной структуре
                    Много обжигался. Однако не совсем DDD у меня, скорее просто модульная архитектура и стараюсь следовать SOLID. Сейчас мы пилим API, и возможно версия 2 будет разбита на микросервисы и некоторые даже начнут жить по DDD. Однако сейчас просто нет финансовой возможности позволить «жирных» специалистов. Клиент скорее наймет 5 человек за $5000, чем одного. Поэтому как минимум я весь опыт черпаю из неудачных решений проходя через собственные ошибки.


                    1. Fesor
                      07.03.2017 14:10
                      -1

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

                      А это уже спрятано за уровень абстракции, так что не столь важно.


                      но в моем понятии сущность — это класс с геттерами и сеттерами

                      Рекомендую почитать про такой термин как "анемичная модель". Если коротко — это такая модель которая налагает ту же сложность что и обычная, но не приносит профита поскольку ничего не делает.


                      А с примерами туго, согласен.


                      просто модульная архитектура и стараюсь следовать SOLID

                      Если с тестами и именно юнит тестами, тогда все хорошо)


          1. nepster-web
            06.03.2017 19:10

            Что касается темы, то мне точно не хватает статей на PHP про:
            — DDD
            — Entity / VO / DTO
            — Repository

            и тп.

            Ну и Вы про всякие гексагональные архитектуры весьма редко пишите.


            1. Fesor
              06.03.2017 19:59
              -1

              точно не хватает статей на PHP про:

              Начните с синей книги Эрика Эванса. Там это все тесть. И вы забыли про агрегаты сущностей, bounded context и т.д.


              Ну и Вы про всякие гексагональные архитектуры весьма редко пишите.

              Я вообще редко пишу… Да и гексагональная архитектура — это один из вариантов построения приложений. Нужно понимать какие характеристики дает та или иная архитектура и думать надо ли вам это или нет.


    1. http3
      06.03.2017 18:52
      -1

      Ну вот я не леплю :)