Обновить
30
Пётр Грибанов@ghost404

Symfony professional developer

19
Подписчики
Отправить сообщение

method у форм по умолчанию POST и поэтому его явно указывать не нужно.


Ну и обработку формы можно оптимизировать


$form = $this
    ->createForm(EnquiryType::class, $enquiry)
    ->handleRequest($request);

if ($form->isValid()) {
    // отправка письма
}

Нет необходимости проверять запрос на POST, это сделает автоматически компонент форм

Я ожидал этот комментарий. Есть одно но. Мы используем регклярку для проверки email введенного пользователем, а он не будет переводить свой интернациональный email в punycode только чтоб угодить вам
Добавлю свои 5 копеек
  1. Самая первая регулярка из статьи посчитает валидным email: #@*%ab
  2. В локальной части email могут быть русские буквы. Встречал компании в которых все корпоративные email были такими.
  3. Домен верхнего уровня может содержать цифры (.i2p) и русские буквы (.рф)
  4. Домен верхнего уровня может быть длиннее 5 символов. Пример: .example, .localhost (RFC2606) это конечно не рабочие домены, но все равно домены.

Желающие могут полистать RFC4185
А я и не говорил что валидация должна выполнятся в entity. Я говорю что валидация и entity это связанные вещи. Бизнес сущность, которой является entity, определяет правила собственной валидации. И я не считаю правельным описывать правила валидации сущности вне её контекста. Об этом же говорит best practice от Symfony
Некоторое дублирование имеет место между «парными» операциями типа «создать/изменить»

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

Для какого ни будь Yii это так, но в случае Doctrine нет. Как говорили выше, Doctrine предлагает работать с сущностями не зацикливаясь на структуре БД. Почему вы считаете что ORM не может быть объектом предметной области?
Да, ORM только описывает сами сущность и не позволяет выполнять какие-то действия напрямую из сущность как например в случае Active Record. Но у нас есть еще один уровень Repository который позволяет выполнять бизнес процессы над конкретной сущностью.
Можно даже пойти дальше и создать сервис который помимо действий заложенных в Repository сможет выполнять дополнительные действия обращаясь к своим зависимостям. Например логировать события добавления новой записи.
А валидация это неотъемлемая часть бизнес логики и должна быть как можно ближе к сущности, то есть в аннотациях ORM, и не как не в командах которые ничего не знают о бизнес сущностях.
PS: Можно конечно валидацию и в конфиги вынести, но это не best practice.
Чем плоха анонимная функция в данном контексте? «Можно и без нее» — слабый аргумент. :)

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

в общем это мое субъективное мнение
идея конечно интересная, но вот с реализацией я не согласен
  1. Валидирование размазано по командам.
    Правила валидирования должны описываться в сущности, то есть в Project. А так вы дублируете правила валидации от команды к команде и велика вероятность что где-то что-то потеряете. А бизнес логика должна быть рядом с сущностью
  2. Не нужно создавать анонимную функцию $empty2null.
    1. Ни что не мешает создать приватную функцию
    2. Правильней использовать трансформеры

  3. Для преобразования запроса в сущность лучше подходит механизм форм

Такой вариант проще и эластичней
$project = new Project();

$form = $this->createForm(ProjectForm::class, $project);
$form->handleRequest($request);

чем такой
$data = $request->request->get('project');

$entity = (new Project())
    ->setName($data['name'])
    ->setDescription($data['description']);

это у вас только 2 простых поля, а что если полей 15 и некоторые из них являются связями
Ну и напоследок.
На мой взгляд будет проще и удобней если команда на вход будет принимать http запрос и внутри его преобразовывать в сущность.
То есть команда инкапсулирует преобразование запроса в сущность, а обработчик команды уже сохраняет сущность из команды, обрабатывает ошибки, логирует что надо и бросает евенты какие надо.
Разработчики Symfony: https://github.com/orgs/symfony/people
Главный у них: Fabien Potencier
На тему скорости разработки:
  • Когда я начинал изучать Symfony я написал сайт с нуля за одну неделю
  • Недавно писал CRM с нуля. Через неделю у меня был уже рабочий прототип, а еще через неделю я сдал проект

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

картинка в тему
image
Если количество зависимостей в объекте достаточно высоко, а реализуемый им функционал довольно сложен

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

Если логика слишком сложная то её нужно разносить на отдельные сервисы. Где-то можно сгруппировать действия, где-то бросить событие, где-то ввести уровень абстракции. Хорошую планку задает SensioLabsInsight — метод должен быть не длиннее 50 строк.
+1 это ещё и короче с namespase-ами. И зависимости лучше видно
Уязвимость через регистрацию будет если форма регистрации состоит предположим из полей email и password.
Есть проверки которые выполняются в указанном порядке:

  1. указан email
  2. формат email-а
  3. наличие email в бд
  4. указан пароль
  5. длинна пароля
  6. сложность пароля

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

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

`
POST /user/isValidEmail

POST /user/isValidUsername
`

И да. Наличие такого метода это серьёзная дыра в безопастности позволяющая собрать базу email ваших пользователей. На месте бизнеса я бы ещё подумал что хуже — уменьшение конверсии или утечка персональных данных пользователей.
Валедировать чернз API не входит в принцыпы REST. Я считаю что без этого можно обойтись. Это как в Web. Есть валидация на стороне клиента и на стороне сервера. На клиенте мы проверяем заполненность полей и формат данных, а наличие совпадений в бд уже после отпрвки формы на сервере.

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

POST /insurance/{code}/user

code — соответственно номер страховки
В случае ошибки переходим к шагу 2 и отправляем запрос:

POST /user/

Принципы REST не нарушены. Интерфейс вполне логичен
1. Возможность централизованно мигрировать различные инстансы

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

2. Уметь определять ошибки миграции и править их в полуавтоматическом режиме

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

3. Иметь систему прав доступа и аппрува изменений

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

4. Желательно уметь связывать миграции с версией кода

миграции нужно хранить в репозитории с кодом

5. Желательно уметь автоматически генерировать миграции на основании уже внесённых изменений

Многие системы миграций умеют генерить код на основе БД и миграции на основе кода. Doctrine Migrations по крайней мере точно умеет

6. Комментировать и привязывать к задачам каждую миграцию

добавляя к коммиту с миграцией номер задачи мы автоматически связываем их. Так работает GitHub и GitLab

7. Сравнивать итоговый DDL произвольных моментов в жизненном цикле.

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

видел реализацию в Yii

Я лично считаю не правильным использовать PHP конструкции для описания миграции. Во всех проектах которые я видел миграции описывались как SQL. Где-то это был *.sql файл, где-то PHP класс в котором описывались изменения как SQL. Для Yii можно вызвать execute(), для Doctrine Migrations addSql()

Получать у этих людей SQL миграции и отдавать разработчикам

Если миграции пишет разработчик БД, а не разработчик приложения, он все равно должен сохранить миграцию в проекте. Для упрощения можно описывать миграции, как говорил AlexLeonov, в отдельный *.sql файлах. Любой нормальный редактор будет поддерживать подсветку SQL синтаксиса в таких файлах.
Doctrine поддерживает Oracle и многие другие
+1 автор ищет проблемы на своё мягкое место. За изменение БД должен отвечать программист который делал это изменение и миграции должны хранится вместе с кодом проекта.
Я в PHP использую DoctrineMigrations
Ну я же не знаю, чего не знаете вы :)

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

Подведем итоги:

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

2. Кроме вашего DataObject есть и другие методы решения описанной проблемы, но в целом оно вполне имеет право на жизнь.

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

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

Я вижу 2 пути решения проблемы. Вариант с хранением в БД JSON я не рассматриваю потому что это… извращение. В таком случает лучше сразу хранить все данные в документоориентированных СУБД.

1. Создание отдельной таблицы с дополнительными полями необходимыми для плагина и сделать связь с базовой таблицей OneToOne.

Преимущества:
Ни базовый класс, ни базовая таблица никак не затрагиваются. Новая сущность существует параллельно с базовой. Доступ осуществляется так:

$customer_ref->getCustomer()->getId();
$customer_ref->getRef();

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

$customer->getCustomerRef()->getRef();

Нужно создавать еще одну таблицу и делать JOIN для выбора дополнительных данных

2. Расширить функционал базового класса через extends и новые поля из класса CustomerRef должны просто игнорироваться движком.

Преимущества:
Просто в реализации. Код будет выглядеть так:

$customer->getId();
// $customer->getRef(); // no work
$customer_ref->getId();
$customer_ref->getRef();

Недостатки:
Опять же нельзя получить поля плагина из базового класса.
Усложняется разработка движка который будет отвечать за загрузку/сохранение данных. При неправильной реализации могут потеряться значения дополнительных полей CustomerRef при сохранении объекта как Customer.

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

Ваше решение с DataObject позволяет получить дополнительные поля из базового класса, хотя автодополнение в IDE работать не будет.

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

Информация

В рейтинге
Не участвует
Откуда
Россия
Зарегистрирован
Активность