Немного в панике - я создаю форму Symfony для сложного поиска, то есть сопоставленные данные для сущности будут использоваться только для построения поискового запроса.
Я создаю простую форму, модель, некоторые расширенные типы из ChoiceType для выбора предопуляции по некоторой логике. Форма отправляется методом GET.
В модели вы найдете, например, поля maker
и model
. Последний заполнил на интерфейсе AJAX, после того как был выбран производитель. Когда я отправляю форму, и maker
и model
имеют значение не по умолчанию, handleRequest
заполняет только свойство maker
Модели, но model
остается пустым. Также флажки правильно заполнены, если установлен. В общем, $form->getData()
возвращает только Maker и флажки, остальные поля являются пустыми. $request->query
имеет все параметры.
Отображатели данных здесь бессмысленны. Кроме того, в данных нечего преобразовывать, модель в основном основана на скалярных значениях. Запрос содержит все, но он не обрабатывается правильно. Я пытался реализовать ChoiceLoaderInterface
, но у меня это не работает, потому что во время загрузки я должен иметь доступ к options
формы, которой у меня нет (я использовал эту статью https://speakerdeck.com/heahdude/symfony-forms-use-cases-and-optimization).
Я использую Symfony 4.2.4; PHP 7.2.
Метод контроллера
/**
* @Route("/search/car", name="car_search", methods={"GET"})
* @param Request $request
*/
public function carSearchAction(Request $request)
{
$carModel = new CarSimpleSearchModel();
$form = $this->createForm(CarSimpleSearchType::class, $carModel);
$form->handleRequest($request);
$form->getData();
.....
}
CarSimpleSearchModel
class CarSimpleSearchModel
{
public $maker;
public $model;
public $priceFrom;
public $priceTo;
public $yearFrom;
public $yearTo;
public $isCompanyOwner;
public $isPrivateOwners;
public $isRoublePrice;
}
CarSimpleSearchВведите форму
class CarSimpleSearchType extends AbstractType
{
protected $urlGenerator;
public function __construct(UrlGeneratorInterface $urlGenerator)
{
$this->urlGenerator = $urlGenerator;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('vehicle_type', HiddenType::class, [
'data' => VehicleTypeType::CAR,
'mapped' => false,
])
->add('maker', CarMakerSelectType::class)
->add('model', CarModelsSelectType::class)
->add(
'priceFrom',
VehiclePriceRangeType::class,
[
'vehicle_type' => VehicleTypeType::CAR,
]
)
->add(
'priceTo',
VehiclePriceRangeType::class,
[
'vehicle_type' => VehicleTypeType::CAR,
]
)
->add(
'yearFrom',
VehicleYearRangeType::class,
[
'vehicle_type' => VehicleTypeType::CAR,
]
)
->add(
'yearTo',
VehicleYearRangeType::class,
[
'vehicle_type' => VehicleTypeType::CAR,
]
)
->add('isCompanyOwner', CheckboxType::class)
->add('isPrivateOwners', CheckboxType::class)
->add('isRoublePrice', CheckboxType::class)
->add('submit', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
[
'data_class' => CarSimpleSearchModel::class,
'compound' => true,
'method' => 'GET',
'required' => false,
'action' => $this->urlGenerator->generate('car_search'),
]
);
}
public function getBlockPrefix()
{
return 'car_search_form';
}
}
Поле CarMakerSelectType
class CarMakerSelectType extends AbstractType
{
/**
* @var VehicleExtractorService
*/
private $extractor;
/**
* VehicleMakerSelectType constructor.
*
* @param VehicleExtractorService $extractor
*/
public function __construct(VehicleExtractorService $extractor)
{
$this->extractor = $extractor;
}
public function getParent()
{
return ChoiceType::class;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
[
'placeholder' => null,
'vehicle_type' => null,
'choices' => $this->getVariants(),
]
);
}
private function getVariants()
{
$makers = $this->extractor->getMakersByVehicleType(VehicleTypeType::CAR);
$choices = [];
foreach ($makers as $maker) {
$choices[$maker['name']] = $maker['id'];
}
return $choices;
}
}
Поле CarModelSelectType
class CarModelsSelectType extends AbstractType
{
private $extractor;
public function __construct(VehicleExtractorService $extractor)
{
$this->extractor = $extractor;
}
public function getParent()
{
return ChoiceType::class;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
[
'disabled' => true,
]
);
}
}
Поле VehiclePriceRangeType
class VehiclePriceRangeType extends AbstractType
{
private $extractor;
public function __construct(VehicleExtractorService $extractor)
{
$this->extractor = $extractor;
}
public function getParent()
{
return ChoiceType::class;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
[
'vehicle_type' => null,
]
);
}
public function buildView(FormView $view, FormInterface $form, array $options)
{
foreach ($this->getRange($options['vehicle_type']) as $value) {
$view->vars['choices'][] = new ChoiceView($value, $value, $value);
}
}
private function getRange(int $vehicleType)
{
return PriceRangeGenerator::generate($this->extractor->getMaxVehiclePrice($vehicleType));
}
}
Поле VehicleYearRangeType
class VehicleYearRangeType extends AbstractType
{
private $extractor;
public function __construct(VehicleExtractorService $extractorService)
{
$this->extractor = $extractorService;
}
public function getParent()
{
return ChoiceType::class;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
[
'vehicle_type' => null,
]
);
}
public function buildView(FormView $view, FormInterface $form, array $options)
{
foreach ($this->getRange($options['vehicle_type']) as $value) {
$view->vars['choices'][] = new ChoiceView($value, $value, $value);
}
}
protected function getRange(int $vehicleType): array
{
$yearRange = RangeGenerator::generate(
$this->extractor->getMinYear($vehicleType),
$this->extractor->getMaxYear($vehicleType),
1,
true,
true
);
return $yearRange;
}
}
Итак, я могу использовать необработанные данные из Request
и вручную проверять, заполнять модель и отправлять на дальнейшую обработку, но я полагаю, что это не правильный путь, и я хочу заполнить форму структурой. Как я могу? ..