diff --git a/src/Application/Cms/PageComponentForm/Assembler/GetPageComponentFormV1ResultAssembler.php b/src/Application/Cms/PageComponentForm/Assembler/GetPageComponentFormV1ResultAssembler.php new file mode 100644 index 0000000..e70bb31 --- /dev/null +++ b/src/Application/Cms/PageComponentForm/Assembler/GetPageComponentFormV1ResultAssembler.php @@ -0,0 +1,33 @@ +formBuilder = $formBuilder; + } + + public function assemble(PageComponent $component): GetPageComponentFormResultDto + { + $form = $this->formBuilder->create(PageComponentType::class, $component); + + $resultDto = new GetPageComponentFormResultDto(); + $resultDto->result = $form; + + return $resultDto; + } +} diff --git a/src/Application/Cms/PageComponentForm/Assembler/GetPageComponentFormV1ResultAssemblerInterface.php b/src/Application/Cms/PageComponentForm/Assembler/GetPageComponentFormV1ResultAssemblerInterface.php new file mode 100644 index 0000000..f9068ec --- /dev/null +++ b/src/Application/Cms/PageComponentForm/Assembler/GetPageComponentFormV1ResultAssemblerInterface.php @@ -0,0 +1,13 @@ +assembler = $assembler; + } + + public function getPageComponentForm(GetPageComponentFormRequestDto $dto): GetPageComponentFormResultDto + { + $pageComponent = new PageComponent( + new Id('1'), + null, + new PageComponentName($dto->name), + [], + 1 + ); + + return $this->assembler->assemble($pageComponent); + } +} diff --git a/src/Domain/Entity/ValueObject/PageComponentName.php b/src/Domain/Entity/ValueObject/PageComponentName.php index 7d0b4b7..e12f013 100644 --- a/src/Domain/Entity/ValueObject/PageComponentName.php +++ b/src/Domain/Entity/ValueObject/PageComponentName.php @@ -10,9 +10,11 @@ class PageComponentName { public const HTML_COMPONENT = 'html-component'; + public const SIMPLE_FORM_COMPONENT = 'simple-form-component'; public const AVAILABLE_COMPONENTS = [ self::HTML_COMPONENT => self::HTML_COMPONENT, + self::SIMPLE_FORM_COMPONENT => self::SIMPLE_FORM_COMPONENT, ]; use ValueObjectTrait; @@ -28,4 +30,9 @@ public function isHtml(): bool { return $this->value === self::HTML_COMPONENT; } + + public function isSimpleForm(): bool + { + return $this->value === self::SIMPLE_FORM_COMPONENT; + } } diff --git a/src/Infrastructure/Symfony/Form/ComponentTypes/HTMLComponentType.php b/src/Infrastructure/Symfony/Form/ComponentTypes/HTMLComponentType.php index c61effd..2a24974 100644 --- a/src/Infrastructure/Symfony/Form/ComponentTypes/HTMLComponentType.php +++ b/src/Infrastructure/Symfony/Form/ComponentTypes/HTMLComponentType.php @@ -11,7 +11,7 @@ class HTMLComponentType extends AbstractType implements DataTransformerInterface { - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { $builder->add('html', TextareaType::class, [ 'attr' => [ diff --git a/src/Infrastructure/Symfony/Form/ComponentTypes/SimpleFormComponentType.php b/src/Infrastructure/Symfony/Form/ComponentTypes/SimpleFormComponentType.php new file mode 100644 index 0000000..74a20a7 --- /dev/null +++ b/src/Infrastructure/Symfony/Form/ComponentTypes/SimpleFormComponentType.php @@ -0,0 +1,40 @@ +add('field', TextType::class) + ->add('stk', IntegerType::class); + + $builder->resetViewTransformers(); + $builder->addViewTransformer($this); + } + + public function transform($value): array + { + $field = $value['field'] ?? null; + $stk = $value['stk'] ?? null; + + return [ + 'field' => (string)$field, + 'stk' => (int)$stk, + ]; + } + + public function reverseTransform($value): array + { + return $value; + } +} diff --git a/src/Infrastructure/Symfony/Form/ContentTypeType.php b/src/Infrastructure/Symfony/Form/ContentTypeType.php index fe5f984..3dc9228 100644 --- a/src/Infrastructure/Symfony/Form/ContentTypeType.php +++ b/src/Infrastructure/Symfony/Form/ContentTypeType.php @@ -19,12 +19,12 @@ public function buildForm(FormBuilderInterface $builder, array $options): void $builder->addViewTransformer($this); } - public function transform($choice) + public function transform($choice): string { return (string)$choice; } - public function reverseTransform($value) + public function reverseTransform($value): ContentType { if (!is_string($value)) { throw new TransformationFailedException('Expected a string.'); diff --git a/src/Infrastructure/Symfony/Form/PageComponentType.php b/src/Infrastructure/Symfony/Form/PageComponentType.php index 144059b..5cb668a 100644 --- a/src/Infrastructure/Symfony/Form/PageComponentType.php +++ b/src/Infrastructure/Symfony/Form/PageComponentType.php @@ -8,16 +8,29 @@ use Skyeng\MarketingCmsBundle\Domain\Entity\ValueObject\PageComponentName; use Skyeng\MarketingCmsBundle\Domain\Repository\PageComponentRepository\PageComponentRepositoryInterface; use Skyeng\MarketingCmsBundle\Infrastructure\Symfony\Form\ComponentTypes\HTMLComponentType; +use Skyeng\MarketingCmsBundle\Infrastructure\Symfony\Form\ComponentTypes\SimpleFormComponentType; use Skyeng\MarketingCmsBundle\Infrastructure\Symfony\Form\PageComponentNameType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\IntegerType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormInterface; use Symfony\Component\OptionsResolver\OptionsResolver; class PageComponentType extends AbstractType { + private const COMPONENT_CLASS = [ + PageComponentName::HTML_COMPONENT => HTMLComponentType::class, + PageComponentName::SIMPLE_FORM_COMPONENT => SimpleFormComponentType::class, + ]; + + private const COMPONENTS_LIST = [ + 'HTML' => PageComponentName::HTML_COMPONENT, + 'Simple form' => PageComponentName::SIMPLE_FORM_COMPONENT, + ]; + /** * @var PageComponentRepositoryInterface */ @@ -32,11 +45,11 @@ public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('name', PageComponentNameType::class, [ - 'choices' => ['HTML' => PageComponentName::HTML_COMPONENT], + 'choices' => self::COMPONENTS_LIST, 'required' => true, 'label' => 'Компонент', 'attr' => [ - 'class' => 'field-select', + 'class' => 'field-select page-component-name-select', 'data-widget' => 'select2', ] ]) @@ -53,6 +66,35 @@ public function buildForm(FormBuilderInterface $builder, array $options) ->add('data', HTMLComponentType::class, [ 'label' => 'Данные' ]); + + $builder->addEventListener(FormEvents::PRE_SET_DATA, static function (FormEvent $event) { + $data = $event->getData(); + $form = $event->getForm(); + + if ($data instanceof PageComponent) { + $form->add('data', self::COMPONENT_CLASS[$data->getName()->getValue()], [ + 'label' => 'Данные' + ]); + } + }); + + $builder->addEventListener(FormEvents::PRE_SUBMIT, static function (FormEvent $event) { + $data = $event->getData(); + $form = $event->getForm(); + + if (!array_key_exists('name', $data)) { + return; + } + + if (!in_array($data['name'], self::COMPONENTS_LIST, true)) { + return; + } + + $pageComponentName = new PageComponentName($data['name']); + $form->add('data', self::COMPONENT_CLASS[$pageComponentName->getValue()], [ + 'label' => 'Данные' + ]); + }); } public function configureOptions(OptionsResolver $resolver): void diff --git a/src/Resources/public/dynamic-page-components.js b/src/Resources/public/dynamic-page-components.js new file mode 100644 index 0000000..9938db2 --- /dev/null +++ b/src/Resources/public/dynamic-page-components.js @@ -0,0 +1,21 @@ +$('body').on('change', '.page-component-name-select', function (event) { + var data = {'name': $(this).val()}; + + var idPrefix = $(this).attr('id'); + idPrefix = idPrefix.substr(0, idPrefix.length - 5); //name + var namePrefix = $(this).attr('name'); + namePrefix = namePrefix.substr(0, namePrefix.length - 6); //[name] + + $.ajax({ + url : '/admin/page-component/form', + type: 'GET', + data : data, + success: function(html) { + html = html.replaceAll('name="page_component', 'name="' + namePrefix); + html = html.replaceAll('id="page_component_', 'id="' + idPrefix + '_'); + + $dataObj = $('#' + idPrefix).find('#' + idPrefix + '_data'); + $dataObj.closest('.form-widget-compound').replaceWith(html); + } + }); +}); diff --git a/src/Resources/views/page_component/page_component_data_form.html.twig b/src/Resources/views/page_component/page_component_data_form.html.twig new file mode 100644 index 0000000..14c94af --- /dev/null +++ b/src/Resources/views/page_component/page_component_data_form.html.twig @@ -0,0 +1,5 @@ +{% form_theme form with themes only %} + +{{ form_widget(form.data) }} + + diff --git a/src/UI/Controller/Admin/PageComponentForm/GetPageComponentFormController.php b/src/UI/Controller/Admin/PageComponentForm/GetPageComponentFormController.php new file mode 100644 index 0000000..0f608b3 --- /dev/null +++ b/src/UI/Controller/Admin/PageComponentForm/GetPageComponentFormController.php @@ -0,0 +1,75 @@ +validator = $validator; + $this->pageComponentFormService = $pageComponentFormService; + $this->responseFactory = $responseFactory; + } + + /** + * Получить виджет формы для компонента + * + * @Route("/admin/page-component/form", methods={"GET"}) + * + * @param Request $request + * @throws ValidationException + */ + public function __invoke(Request $request): Response + { + $dto = new GetPageComponentFormRequestDto(); + + try { + $this->validator->validate(GetPageComponentFormV1Form::class, $request->query->all(), $dto); + } catch (ValidationException $exception) { + return ResponseFactory::createErrorResponse($request, $exception->getMessage(), $exception->getErrors()); + } + + $result = $this->pageComponentFormService->getPageComponentForm($dto); + + return $this->responseFactory->createViewResponse( + '@MarketingCms/page_component/page_component_data_form.html.twig', + [ + 'form' => $result->result->createView(), + 'themes' => [ + '@EasyAdmin/crud/form_theme.html.twig', + ], + ] + ); + } +} diff --git a/src/UI/Controller/Admin/PageComponentForm/Validation/GetPageComponentFormV1Form.php b/src/UI/Controller/Admin/PageComponentForm/Validation/GetPageComponentFormV1Form.php new file mode 100644 index 0000000..a657d37 --- /dev/null +++ b/src/UI/Controller/Admin/PageComponentForm/Validation/GetPageComponentFormV1Form.php @@ -0,0 +1,31 @@ +setDefaults([ + 'csrf_protection' => false, + ]); + } + + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder->add('name', TextType::class, [ + 'constraints' => [new NotBlank()], + ]); + } +} diff --git a/src/UI/Controller/Admin/PageCrudController.php b/src/UI/Controller/Admin/PageCrudController.php index 38fec4f..09856ef 100644 --- a/src/UI/Controller/Admin/PageCrudController.php +++ b/src/UI/Controller/Admin/PageCrudController.php @@ -127,7 +127,8 @@ public function configureFields(string $pageName): iterable ->addCssClass('expanded-collection') ->addCssClass('positionable-collection') ->addCssFiles('bundles/marketingcms/expanded-collection-style.css') - ->addJsFiles('bundles/marketingcms/positionable-collection.js'); + ->addJsFiles('bundles/marketingcms/positionable-collection.js') + ->addJsFiles('bundles/marketingcms/dynamic-page-components.js'); if (in_array($pageName, [Crud::PAGE_INDEX, Crud::PAGE_DETAIL], true)) { return [$resource, $title, $isPublished, $publishedAt]; diff --git a/src/UI/Service/Response/ResponseFactory.php b/src/UI/Service/Response/ResponseFactory.php index 6c963a1..b011d98 100644 --- a/src/UI/Service/Response/ResponseFactory.php +++ b/src/UI/Service/Response/ResponseFactory.php @@ -11,9 +11,20 @@ use Symfony\Component\HttpFoundation\ResponseHeaderBag; use Symfony\Component\HttpKernel\EventListener\AbstractSessionListener; use Throwable; +use Twig\Environment; class ResponseFactory { + /** + * @var Environment + */ + private $twigEnvironment; + + public function __construct(Environment $twigEnvironment) + { + $this->twigEnvironment = $twigEnvironment; + } + /** * @param Request $request * @param mixed $data @@ -131,4 +142,16 @@ protected static function createJsonResponse( ): JsonResponse { return new JsonResponse($data, $code); } + + public function createViewResponse( + string $template, + array $params + ): Response { + $content = $this->twigEnvironment->render($template, $params); + + $response = new Response(); + $response->setContent($content); + + return $response; + } }