From 4bee2c9ae9339bc87f8e8e678a94e971b378cafc Mon Sep 17 00:00:00 2001 From: jkindly Date: Wed, 11 Sep 2024 11:31:14 +0200 Subject: [PATCH 1/2] OP-544: Page teaser translatable --- .../TranslationFormReduceRuntimeSpec.php | 125 ++++++++++++++++++ src/Entity/Page.php | 47 ++++++- src/Entity/PageTranslation.php | 3 + src/Entity/PageTranslationInterface.php | 8 +- src/Fixture/Factory/PageFixtureFactory.php | 17 ++- src/Fixture/PageFixture.php | 6 +- src/Form/Type/PageType.php | 12 -- .../Type/Translation/PageTranslationType.php | 14 ++ src/Migrations/Version20240910113936.php | 36 +++++ src/Resources/config/doctrine/Page.orm.xml | 8 -- .../config/doctrine/PageTranslation.orm.xml | 8 ++ src/Resources/config/services/twig.xml | 9 ++ src/Resources/views/Page/Crud/_form.html.twig | 22 ++- .../TranslationFormReduceExtension.php | 27 ++++ .../Runtime/TranslationFormReduceRuntime.php | 33 +++++ .../TranslationFormReduceRuntimeInterface.php | 12 ++ .../config/packages/sylius_cms_plugin.yaml | 18 +-- 17 files changed, 349 insertions(+), 56 deletions(-) create mode 100644 spec/Twig/Runtime/TranslationFormReduceRuntimeSpec.php create mode 100644 src/Migrations/Version20240910113936.php create mode 100644 src/Twig/Extension/TranslationFormReduceExtension.php create mode 100644 src/Twig/Runtime/TranslationFormReduceRuntime.php create mode 100644 src/Twig/Runtime/TranslationFormReduceRuntimeInterface.php diff --git a/spec/Twig/Runtime/TranslationFormReduceRuntimeSpec.php b/spec/Twig/Runtime/TranslationFormReduceRuntimeSpec.php new file mode 100644 index 00000000..0e8e718f --- /dev/null +++ b/spec/Twig/Runtime/TranslationFormReduceRuntimeSpec.php @@ -0,0 +1,125 @@ +shouldHaveType(TranslationFormReduceRuntime::class); + } + + public function it_reduces_form_to_specified_fields( + FormView $form, + FormView $localeForm, + FormView $slugForm, + FormView $titleForm + ): void { + $form->children = [ + 'en_US' => $localeForm + ]; + + $localeForm->children = [ + 'slug' => $slugForm, + 'title' => $titleForm, + 'metaDescription' => new FormView(), + ]; + + $result = $this->reduceTranslationForm($form, ['slug', 'title']); + $result->shouldHaveKey('en_US'); + $result['en_US']->shouldHaveKey('slug'); + $result['en_US']->shouldHaveKey('title'); + $result['en_US']->shouldNotHaveKey('metaDescription'); + } + + public function it_throws_exception_if_field_is_not_found(FormView $form, FormView $localeForm): void + { + $form->children = [ + 'en_US' => $localeForm, + ]; + + $localeForm->children = [ + 'metaDescription' => new FormView(), + ]; + + $this->shouldThrow(\InvalidArgumentException::class)->during('reduceTranslationForm', [$form, ['slug', 'title']]); + } + + public function it_handles_multiple_locales( + FormView $form, + FormView $enLocale, + FormView $deLocale, + FormView $slugForm, + FormView $titleForm + ): void { + $form->children = [ + 'en_US' => $enLocale, + 'de_DE' => $deLocale, + ]; + + $enLocale->children = [ + 'slug' => $slugForm, + 'title' => $titleForm, + ]; + + $deLocale->children = [ + 'slug' => $slugForm, + 'title' => $titleForm, + ]; + + $result = $this->reduceTranslationForm($form, ['slug', 'title']); + $result->shouldHaveCount(2); + $result['en_US']->shouldHaveKey('slug'); + $result['en_US']->shouldHaveKey('title'); + $result['de_DE']->shouldHaveKey('slug'); + $result['de_DE']->shouldHaveKey('title'); + } + + public function it_throws_exception_if_field_is_not_present_in_multiple_locales( + FormView $form, + FormView $enLocale, + FormView $deLocale, + FormView $slugForm + ): void { + $form->children = [ + 'en_US' => $enLocale, + 'de_DE' => $deLocale, + ]; + + $enLocale->children = [ + 'slug' => $slugForm, + ]; + + $deLocale->children = [ + 'slug' => $slugForm, + // 'title' is missing in de_DE + ]; + + $this->shouldThrow(\InvalidArgumentException::class)->during('reduceTranslationForm', [$form, ['slug', 'title']]); + } + + public function it_handles_empty_field_array( + FormView $form, + FormView $localeForm, + FormView $slugForm, + FormView $titleForm + ): void { + $form->children = [ + 'en_US' => $localeForm, + ]; + + $localeForm->children = [ + 'slug' => $slugForm, + 'title' => $titleForm, + ]; + + $result = $this->reduceTranslationForm($form, []); + $result->shouldHaveCount(0); + } +} diff --git a/src/Entity/Page.php b/src/Entity/Page.php index 9a2470a0..03f0e27f 100755 --- a/src/Entity/Page.php +++ b/src/Entity/Page.php @@ -7,7 +7,6 @@ use Sylius\CmsPlugin\Entity\Trait\ChannelsAwareTrait; use Sylius\CmsPlugin\Entity\Trait\CollectibleTrait; use Sylius\CmsPlugin\Entity\Trait\ContentElementsAwareTrait; -use Sylius\CmsPlugin\Entity\Trait\TeaserTrait; use Sylius\Component\Resource\Model\TimestampableTrait; use Sylius\Component\Resource\Model\ToggleableTrait; use Sylius\Component\Resource\Model\TranslatableTrait; @@ -20,7 +19,6 @@ class Page implements PageInterface use TimestampableTrait; use ChannelsAwareTrait; use ContentElementsAwareTrait; - use TeaserTrait; use TranslatableTrait { __construct as protected initializeTranslationsCollection; } @@ -108,6 +106,51 @@ public function setMetaDescription(?string $metaDescription): void $pageTranslationInterface->setMetaDescription($metaDescription); } + public function getTeaserTitle(): ?string + { + /** @var PageTranslationInterface $pageTranslationInterface */ + $pageTranslationInterface = $this->getPageTranslation(); + + return $pageTranslationInterface->getTeaserTitle(); + } + + public function setTeaserTitle(?string $teaserTitle): void + { + /** @var PageTranslationInterface $pageTranslationInterface */ + $pageTranslationInterface = $this->getPageTranslation(); + $pageTranslationInterface->setTeaserTitle($teaserTitle); + } + + public function getTeaserContent(): ?string + { + /** @var PageTranslationInterface $pageTranslationInterface */ + $pageTranslationInterface = $this->getPageTranslation(); + + return $pageTranslationInterface->getTeaserContent(); + } + + public function setTeaserContent(?string $teaserContent): void + { + /** @var PageTranslationInterface $pageTranslationInterface */ + $pageTranslationInterface = $this->getPageTranslation(); + $pageTranslationInterface->setTeaserContent($teaserContent); + } + + public function getTeaserImage(): ?MediaInterface + { + /** @var PageTranslationInterface $pageTranslationInterface */ + $pageTranslationInterface = $this->getPageTranslation(); + + return $pageTranslationInterface->getTeaserImage(); + } + + public function setTeaserImage(?MediaInterface $teaserImage): void + { + /** @var PageTranslationInterface $pageTranslationInterface */ + $pageTranslationInterface = $this->getPageTranslation(); + $pageTranslationInterface->setTeaserImage($teaserImage); + } + public function getName(): ?string { return $this->name; diff --git a/src/Entity/PageTranslation.php b/src/Entity/PageTranslation.php index fe1f9503..62402333 100755 --- a/src/Entity/PageTranslation.php +++ b/src/Entity/PageTranslation.php @@ -4,10 +4,13 @@ namespace Sylius\CmsPlugin\Entity; +use Sylius\CmsPlugin\Entity\Trait\TeaserTrait; use Sylius\Component\Resource\Model\AbstractTranslation; class PageTranslation extends AbstractTranslation implements PageTranslationInterface { + use TeaserTrait; + protected ?int $id; protected ?string $slug = null; diff --git a/src/Entity/PageTranslationInterface.php b/src/Entity/PageTranslationInterface.php index 4940a5fb..0ab27d96 100755 --- a/src/Entity/PageTranslationInterface.php +++ b/src/Entity/PageTranslationInterface.php @@ -7,7 +7,7 @@ use Sylius\Component\Resource\Model\ResourceInterface; use Sylius\Component\Resource\Model\TranslationInterface; -interface PageTranslationInterface extends ResourceInterface, TranslationInterface +interface PageTranslationInterface extends ResourceInterface, TranslationInterface, TeaserInterface { public function getSlug(): ?string; @@ -24,4 +24,10 @@ public function setMetaDescription(?string $metaDescription): void; public function getTitle(): ?string; public function setTitle(?string $title): void; + + public function getTeaserTitle(): ?string; + + public function getTeaserContent(): ?string; + + public function getTeaserImage(): ?MediaInterface; } diff --git a/src/Fixture/Factory/PageFixtureFactory.php b/src/Fixture/Factory/PageFixtureFactory.php index 24ebb75b..ac96822b 100755 --- a/src/Fixture/Factory/PageFixtureFactory.php +++ b/src/Fixture/Factory/PageFixtureFactory.php @@ -56,15 +56,6 @@ private function createPage( $page->setName($pageData['name']); $page->setEnabled($pageData['enabled']); - /** @var MediaInterface|null $mediaImage */ - $mediaImage = $this->mediaRepository->findOneBy(['code' => $pageData['teaser_image']]); - if ($mediaImage) { - $page->setTeaserImage($mediaImage); - } - - $page->setTeaserTitle($pageData['teaser_title']); - $page->setTeaserContent($pageData['teaser_content']); - foreach ($pageData['translations'] as $localeCode => $translation) { /** @var PageTranslationInterface $pageTranslation */ $pageTranslation = $this->pageTranslationFactory->createNew(); @@ -75,6 +66,14 @@ private function createPage( $pageTranslation->setTitle($translation['meta_title']); $pageTranslation->setMetaKeywords($translation['meta_keywords']); $pageTranslation->setMetaDescription($translation['meta_description']); + $pageTranslation->setTeaserTitle($translation['teaser_title']); + $pageTranslation->setTeaserContent($translation['teaser_content']); + + /** @var MediaInterface|null $mediaImage */ + $mediaImage = $this->mediaRepository->findOneBy(['code' => $translation['teaser_image']]); + if ($mediaImage) { + $pageTranslation->setTeaserImage($mediaImage); + } $page->addTranslation($pageTranslation); } diff --git a/src/Fixture/PageFixture.php b/src/Fixture/PageFixture.php index 655f5bad..c015ec89 100755 --- a/src/Fixture/PageFixture.php +++ b/src/Fixture/PageFixture.php @@ -36,9 +36,6 @@ protected function configureOptionsNode(ArrayNodeDefinition $optionsNode): void ->scalarNode('name')->end() ->arrayNode('collections')->scalarPrototype()->end()->end() ->arrayNode('channels')->scalarPrototype()->end()->end() - ->scalarNode('teaser_title')->defaultNull()->end() - ->scalarNode('teaser_content')->defaultNull()->end() - ->scalarNode('teaser_image')->defaultNull()->end() ->arrayNode('translations') ->prototype('array') ->children() @@ -46,6 +43,9 @@ protected function configureOptionsNode(ArrayNodeDefinition $optionsNode): void ->scalarNode('meta_title')->defaultNull()->end() ->scalarNode('meta_keywords')->defaultNull()->end() ->scalarNode('meta_description')->defaultNull()->end() + ->scalarNode('teaser_title')->defaultNull()->end() + ->scalarNode('teaser_content')->defaultNull()->end() + ->scalarNode('teaser_image')->defaultNull()->end() ->end() ->end() ->end() diff --git a/src/Form/Type/PageType.php b/src/Form/Type/PageType.php index 100b702f..528a78cb 100755 --- a/src/Form/Type/PageType.php +++ b/src/Form/Type/PageType.php @@ -62,18 +62,6 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'label' => false, 'mapped' => false, ]) - ->add('teaserTitle', TextType::class, [ - 'label' => 'sylius_cms.ui.teaser.title', - 'required' => false, - ]) - ->add('teaserContent', WysiwygType::class, [ - 'label' => 'sylius_cms.ui.teaser.content', - 'required' => false, - ]) - ->add('teaserImage', MediaImageAutocompleteChoiceType::class, [ - 'label' => 'sylius_cms.ui.teaser.image', - 'required' => false, - ]) ; } diff --git a/src/Form/Type/Translation/PageTranslationType.php b/src/Form/Type/Translation/PageTranslationType.php index 3960690a..fbd26456 100755 --- a/src/Form/Type/Translation/PageTranslationType.php +++ b/src/Form/Type/Translation/PageTranslationType.php @@ -5,6 +5,8 @@ namespace Sylius\CmsPlugin\Form\Type\Translation; use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType; +use Sylius\CmsPlugin\Form\Type\MediaImageAutocompleteChoiceType; +use Sylius\CmsPlugin\Form\Type\WysiwygType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; @@ -29,6 +31,18 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'label' => 'sylius_cms.ui.meta_description', 'required' => false, ]) + ->add('teaserTitle', TextType::class, [ + 'label' => 'sylius_cms.ui.teaser.title', + 'required' => false, + ]) + ->add('teaserContent', WysiwygType::class, [ + 'label' => 'sylius_cms.ui.teaser.content', + 'required' => false, + ]) + ->add('teaserImage', MediaImageAutocompleteChoiceType::class, [ + 'label' => 'sylius_cms.ui.teaser.image', + 'required' => false, + ]) ; } diff --git a/src/Migrations/Version20240910113936.php b/src/Migrations/Version20240910113936.php new file mode 100644 index 00000000..f65ad059 --- /dev/null +++ b/src/Migrations/Version20240910113936.php @@ -0,0 +1,36 @@ +addSql('ALTER TABLE sylius_cms_page DROP FOREIGN KEY FK_2C2740B2F56F16CF'); + $this->addSql('DROP INDEX IDX_2C2740B2F56F16CF ON sylius_cms_page'); + $this->addSql('ALTER TABLE sylius_cms_page DROP teaser_image_id, DROP teaser_title, DROP teaser_content'); + $this->addSql('ALTER TABLE sylius_cms_page_translation ADD teaser_image_id INT DEFAULT NULL, ADD teaser_title VARCHAR(255) DEFAULT NULL, ADD teaser_content LONGTEXT DEFAULT NULL'); + $this->addSql('ALTER TABLE sylius_cms_page_translation ADD CONSTRAINT FK_6D0D401BF56F16CF FOREIGN KEY (teaser_image_id) REFERENCES sylius_cms_media (id) ON DELETE SET NULL'); + $this->addSql('CREATE INDEX IDX_6D0D401BF56F16CF ON sylius_cms_page_translation (teaser_image_id)'); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE sylius_cms_page ADD teaser_image_id INT DEFAULT NULL, ADD teaser_title VARCHAR(255) DEFAULT NULL, ADD teaser_content LONGTEXT DEFAULT NULL'); + $this->addSql('ALTER TABLE sylius_cms_page ADD CONSTRAINT FK_2C2740B2F56F16CF FOREIGN KEY (teaser_image_id) REFERENCES sylius_cms_media (id) ON DELETE SET NULL'); + $this->addSql('CREATE INDEX IDX_2C2740B2F56F16CF ON sylius_cms_page (teaser_image_id)'); + $this->addSql('ALTER TABLE sylius_cms_page_translation DROP FOREIGN KEY FK_6D0D401BF56F16CF'); + $this->addSql('DROP INDEX IDX_6D0D401BF56F16CF ON sylius_cms_page_translation'); + $this->addSql('ALTER TABLE sylius_cms_page_translation DROP teaser_image_id, DROP teaser_title, DROP teaser_content'); + } +} diff --git a/src/Resources/config/doctrine/Page.orm.xml b/src/Resources/config/doctrine/Page.orm.xml index b0ccdcdc..4f4a081a 100644 --- a/src/Resources/config/doctrine/Page.orm.xml +++ b/src/Resources/config/doctrine/Page.orm.xml @@ -49,13 +49,5 @@ - - - - - - - - diff --git a/src/Resources/config/doctrine/PageTranslation.orm.xml b/src/Resources/config/doctrine/PageTranslation.orm.xml index 19e22954..2968a0cd 100644 --- a/src/Resources/config/doctrine/PageTranslation.orm.xml +++ b/src/Resources/config/doctrine/PageTranslation.orm.xml @@ -17,5 +17,13 @@ + + + + + + + + diff --git a/src/Resources/config/services/twig.xml b/src/Resources/config/services/twig.xml index 7a36e5cc..ff0c2e71 100644 --- a/src/Resources/config/services/twig.xml +++ b/src/Resources/config/services/twig.xml @@ -80,5 +80,14 @@ %sylius_cms.twig.link_template% + + + + + + + + + diff --git a/src/Resources/views/Page/Crud/_form.html.twig b/src/Resources/views/Page/Crud/_form.html.twig index adfd1e44..d46eb10f 100755 --- a/src/Resources/views/Page/Crud/_form.html.twig +++ b/src/Resources/views/Page/Crud/_form.html.twig @@ -1,4 +1,4 @@ -{% from '@SyliusCmsPlugin/Macro/translationForm.html.twig' import translationFormWithSlug %} +{% from '@SyliusCmsPlugin/Macro/translationForm.html.twig' import translationFormWithSlug, translationForm %} {% form_theme form '@SyliusCmsPlugin/Form/theme.html.twig' %} {% include '@SyliusCmsPlugin/Modal/_resourcePreview.html.twig' %} @@ -25,20 +25,18 @@ {{ 'sylius_cms.ui.teaser.header'|trans }} - {{ 'sylius_cms.ui.teaser.help'|trans }} - {{ form_row(form.teaserTitle) }} - {{ form_row(form.teaserContent) }} - {{ form_row(form.teaserImage) }} - {% if page.teaserImage %} -
- - - -
- {% endif %} + {{ translationForm( + translation_form_reduce(form.translations, ['teaserTitle', 'teaserContent', 'teaserImage']), + page + ) }}

{{ 'sylius_cms.ui.seo'|trans }}

- {{ translationFormWithSlug(form.translations, '@SyliusCmsPlugin/Page/Crud/_slugField.html.twig', page) }} + {{ translationFormWithSlug( + translation_form_reduce(form.translations, ['title', 'slug', 'metaKeywords', 'metaDescription']), + '@SyliusCmsPlugin/Page/Crud/_slugField.html.twig', + page + ) }}
diff --git a/src/Twig/Extension/TranslationFormReduceExtension.php b/src/Twig/Extension/TranslationFormReduceExtension.php new file mode 100644 index 00000000..e9f36d7c --- /dev/null +++ b/src/Twig/Extension/TranslationFormReduceExtension.php @@ -0,0 +1,27 @@ +translationFormReduce, 'reduceTranslationForm'], + ['is_safe' => ['html']], + ), + ]; + } +} diff --git a/src/Twig/Runtime/TranslationFormReduceRuntime.php b/src/Twig/Runtime/TranslationFormReduceRuntime.php new file mode 100644 index 00000000..0d978a3e --- /dev/null +++ b/src/Twig/Runtime/TranslationFormReduceRuntime.php @@ -0,0 +1,33 @@ +children as $localeKey => $localeForm) { + $localeReducedForm = []; + + foreach ($fields as $field) { + if (!isset($localeForm->children[$field])) { + throw new \InvalidArgumentException(sprintf('Field "%s" does not exist in the form.', $field)); + } + + $localeReducedForm[$field] = $localeForm->children[$field]; + } + + if (!empty($localeReducedForm)) { + $reducedForm[$localeKey] = $localeReducedForm; + } + } + + return $reducedForm; + } +} diff --git a/src/Twig/Runtime/TranslationFormReduceRuntimeInterface.php b/src/Twig/Runtime/TranslationFormReduceRuntimeInterface.php new file mode 100644 index 00000000..80532708 --- /dev/null +++ b/src/Twig/Runtime/TranslationFormReduceRuntimeInterface.php @@ -0,0 +1,12 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean molestie nulla ac tempus volutpat. Aenean aliquet viverra sem a feugiat. Pellentesque a sollicitudin lacus. Mauris vel dolor quis justo vestibulum posuere. Sed sagittis, ipsum a cursus porttitor, justo felis tincidunt neque, eget scelerisque lacus sapien tempor felis.
" - teaser_image: "sale" translations: en_US: slug: "blog-post-1" meta_title: "Blog post 1" + teaser_title: "Blog post 1" + teaser_content: "
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean molestie nulla ac tempus volutpat. Aenean aliquet viverra sem a feugiat. Pellentesque a sollicitudin lacus. Mauris vel dolor quis justo vestibulum posuere. Sed sagittis, ipsum a cursus porttitor, justo felis tincidunt neque, eget scelerisque lacus sapien tempor felis.
" + teaser_image: "sale" content_elements: single_media: type: "single_media" @@ -433,13 +433,13 @@ sylius_fixtures: - "FASHION_WEB" collections: - "blog" - teaser_title: "Blog post 2" - teaser_content: "
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean molestie nulla ac tempus volutpat. Aenean aliquet viverra sem a feugiat. Pellentesque a sollicitudin lacus. Mauris vel dolor quis justo vestibulum posuere. Sed sagittis, ipsum a cursus porttitor, justo felis tincidunt neque, eget scelerisque lacus sapien tempor felis.
" - teaser_image: "sale" translations: en_US: slug: "blog-post-2" meta_title: "Blog post 2" + teaser_title: "Blog post 2" + teaser_content: "
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean molestie nulla ac tempus volutpat. Aenean aliquet viverra sem a feugiat. Pellentesque a sollicitudin lacus. Mauris vel dolor quis justo vestibulum posuere. Sed sagittis, ipsum a cursus porttitor, justo felis tincidunt neque, eget scelerisque lacus sapien tempor felis.
" + teaser_image: "sale" content_elements: single_media: type: "single_media" @@ -492,13 +492,13 @@ sylius_fixtures: - "FASHION_WEB" collections: - "blog" - teaser_title: "Blog post 3" - teaser_content: "
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean molestie nulla ac tempus volutpat. Aenean aliquet viverra sem a feugiat. Pellentesque a sollicitudin lacus. Mauris vel dolor quis justo vestibulum posuere. Sed sagittis, ipsum a cursus porttitor, justo felis tincidunt neque, eget scelerisque lacus sapien tempor felis.
" - teaser_image: "sale" translations: en_US: slug: "blog-post-3" meta_title: "Blog post 3" + teaser_title: "Blog post 3" + teaser_content: "
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean molestie nulla ac tempus volutpat. Aenean aliquet viverra sem a feugiat. Pellentesque a sollicitudin lacus. Mauris vel dolor quis justo vestibulum posuere. Sed sagittis, ipsum a cursus porttitor, justo felis tincidunt neque, eget scelerisque lacus sapien tempor felis.
" + teaser_image: "sale" content_elements: single_media: type: "single_media" From 5917fc5edec16f0fbbefe37f2c8e8ffe6c930286 Mon Sep 17 00:00:00 2001 From: jkindly Date: Wed, 11 Sep 2024 11:38:21 +0200 Subject: [PATCH 2/2] OP-544: Mapping fix --- src/Resources/config/doctrine/Media.orm.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Resources/config/doctrine/Media.orm.xml b/src/Resources/config/doctrine/Media.orm.xml index ab603784..7ce6df21 100644 --- a/src/Resources/config/doctrine/Media.orm.xml +++ b/src/Resources/config/doctrine/Media.orm.xml @@ -39,7 +39,7 @@ - +