sm-vasya писал(а): ↑2018.03.08, 00:04
как в доктрине реализовать форинкей по 2-3-4-6-99 полей ?
Doctrine придумана для высокоуровневого программирования объектов вместо низкоуровневой возни с БД. Здесь рулит ООП, а не SQL.
Сначала нужно определиться, из каких соображений появилась необходимость составного ключа. Обычно только для того, чтобы ссылаться напрямую на вложенную сущность или VO другого агрегата, а не на его корень. А это уже с точки зрения агрегатов нелогично и лишь повышает внешнюю связанность. Поэтому, как вариант, оставляем связь на корень агрегата, а второе поле делаем простым числовым $xxxId. А в миграции прописываем нормальный составной FK вручную. В итоге получаем логически понятный код:
Код: Выделить всё
class OrderRow {
public function getProduct() {
return $this->product;
}
public function getModification() {
return $this->product->getModification($this->modificationId);
}
}
вместо одной связи по составному FK:
Код: Выделить всё
class OrderRow {
public function getModification() {
return $this->modification;
}
}
и с получением обратного паровоза $orderRow->getModification()->getProduct() для костыльного извлечения продукта из связи на его модификацию.
sm-vasya писал(а): ↑2018.03.08, 00:04
как в доктрине подключить поведение к модели ?
Если программировать в Symfony как в Yii, то получаем затык "как же мне тут запилить поведения". Если же как в Symfony, то всё логично и удобно.
Поведение в Yii - это миксин для магического добавления методов и подписчик для отлова событий. Миксины легко реализуются просто трейтами, содержащими чистые методы с параметрами. Подписчики - через EventSubscriber. Если корректно разбивать код по ответственностям, то поведения оказываются или трейты не нужны. Их методы переходят в сервисы, а поля сущности заполняются уже готовыми данными. То есть вместо смеси с поведением внутри AR:
Код: Выделить всё
$post->image = $file;
$post->save(); // с сохранением файла в beforeSave()
echo $post->getThumbUrl('medium');
выносим все операции в сторонний сервис и получаем разделённый по ответственностям тестируемый код:
Код: Выделить всё
$image = $uploader->uploadImage($file, Post::class);
$post->setImage($image);
...
echo $uploader->getThumbUrl($post-getImage(), 'medium');
где можно подменить LocalUploader на FtpUploader в конфигурации, не переписывая ни одну сущность, контроллер или тест.
sm-vasya писал(а): ↑2018.03.08, 00:04
в симфони к контроллеру и тд ? или там такого в принципе нет ?
Для отлова событий выполнения контроллера пишем тот же Event Subscriber.
sm-vasya писал(а): ↑2018.03.08, 00:04
как в симфони сделать экшен классом ? чтоб я его мог тиражировать с нужными мне параметрами а не писать каждый раз ?
Например, в админке нам нужно к компаниям и к товарам сделать подгрузку городов. Выносим код получения городов в отдельный класс, потом дёргаем его из всех экшенов с индивидуальными параметрами:
Код: Выделить всё
function actionAjaxRegion1($request) {
return new JsonResponse($this->regions->getAutocompleteResults($request->get('query'), self::LIMIT1));
}
function actionAjaxRegion2($request) {
return new JsonResponse($this->regions->getAutocompleteResults($request->get('query'), self::LIMIT2));
}
Весь код уйдёт в компонент и проблема "копипастить огромный экшен" исчезнет. Самый явный и вариант.
Если же у сервиса куча меняющихся настроек, то в DI определяем несколько сервисов от одного класса с разными настройками и именами и контекстуально прокидываем в нужные контроллеры. Это вариант уже с магией.
Либо, если выносить в сервис не хочется, то всё-таки записываем сам экшен отдельным классом и в DI прописываем его два экземпляра под именами action.regions1 и action.regions2. Потом в маршрутах прописываем controller: action.regions1 и action.regions2.