Skip to content

Commit

Permalink
Merge pull request #5 from etienne-monsieurbiz/feat/blog-ui-elements
Browse files Browse the repository at this point in the history
  • Loading branch information
maximehuran authored Oct 24, 2024
2 parents 97e76d4 + 3945a3e commit ed36080
Show file tree
Hide file tree
Showing 20 changed files with 792 additions and 15 deletions.
71 changes: 71 additions & 0 deletions src/Form/Type/ArticleSelectionElementType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

/*
* This file is part of Monsieur Biz's Blog plugin for Sylius.
* (c) Monsieur Biz <sylius@monsieurbiz.com>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace MonsieurBiz\SyliusBlogPlugin\Form\Type;

use MonsieurBiz\SyliusBlogPlugin\Entity\Article;
use MonsieurBiz\SyliusBlogPlugin\Entity\ArticleInterface;
use MonsieurBiz\SyliusBlogPlugin\Repository\ArticleRepositoryInterface;
use Sylius\Bundle\ResourceBundle\Form\DataTransformer\ResourceToIdentifierTransformer;
use Sylius\Component\Channel\Context\ChannelContextInterface;
use Sylius\Component\Locale\Context\LocaleContextInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\ReversedTransformer;
use Symfony\Component\Validator\Constraints as Assert;

final class ArticleSelectionElementType extends AbstractType
{
public function __construct(
private readonly ArticleRepositoryInterface $articleRepository,
private readonly ChannelContextInterface $channelContext,
private readonly LocaleContextInterface $localeContext,
) {
}

/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('article', EntityType::class, [
'class' => Article::class,
'label' => 'monsieurbiz_blog.ui_element.articles_selection_ui_element.fields.article',
'choice_label' => fn (Article $article) => $article->getTitle(),
'choice_value' => fn (?Article $article) => $article?->getId(),
'required' => true,
'query_builder' => function (ArticleRepositoryInterface $articleRepository) {
return $articleRepository->createShopListQueryBuilderByType(
$this->localeContext->getLocaleCode(),
ArticleInterface::BLOG_TYPE,
$this->channelContext->getChannel(),
null
)->orderBy('translation.title');
},
])
->add('position', IntegerType::class, [
'label' => 'monsieurbiz_blog.ui_element.articles_selection_ui_element.fields.position',
'required' => true,
'constraints' => [
new Assert\NotBlank(),
new Assert\GreaterThan(0),
],
])
;

$builder->get('article')->addModelTransformer(
new ReversedTransformer(new ResourceToIdentifierTransformer($this->articleRepository, 'id')),
);
}
}
43 changes: 43 additions & 0 deletions src/Form/Type/ArticlesDisplayType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

/*
* This file is part of Monsieur Biz's Blog plugin for Sylius.
* (c) Monsieur Biz <sylius@monsieurbiz.com>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace MonsieurBiz\SyliusBlogPlugin\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;

final class ArticlesDisplayType extends AbstractType
{
public const MULTIPLE_WITH_IMAGE = 'multiple_with_image';

public const MULTIPLE_WITHOUT_IMAGE = 'multiple_without_image';

public const SINGLE = 'single';

/**
* @SuppressWarnings(PHPMD.UnusedFormalParameters)
*/
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('display', ChoiceType::class, [
'label' => 'monsieurbiz_blog.articles.display.label',
'required' => true,
'choices' => [
'monsieurbiz_blog.articles.display.choices.multiple_with_image' => self::MULTIPLE_WITH_IMAGE,
'monsieurbiz_blog.articles.display.choices.multiple_without_image' => self::MULTIPLE_WITHOUT_IMAGE,
'monsieurbiz_blog.articles.display.choices.single' => self::SINGLE,
],
])
;
}
}
20 changes: 9 additions & 11 deletions src/Form/Type/CaseStudyElementType.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,21 @@ public function __construct(
*/
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$caseStudies = $this->articleRepository->createShopListQueryBuilderByType(
$this->localeContext->getLocaleCode(),
ArticleInterface::CASE_STUDY_TYPE,
$this->channelContext->getChannel(),
null
);

$caseStudies = $caseStudies->orderBy('translation.title')->getQuery()->getResult();

$builder
->add('case_study', EntityType::class, [
'class' => Article::class,
'label' => 'monsieurbiz_blog.ui_element.case_studies_ui_element.fields.case_study',
'choice_label' => fn (Article $caseStudy) => $caseStudy->getTitle(),
'choice_value' => fn (?Article $caseStudy) => $caseStudy ? $caseStudy->getId() : null,
'choice_value' => fn (?Article $caseStudy) => $caseStudy?->getId(),
'required' => true,
'choices' => $caseStudies,
'query_builder' => function (ArticleRepositoryInterface $articleRepository) {
return $articleRepository->createShopListQueryBuilderByType(
$this->localeContext->getLocaleCode(),
ArticleInterface::CASE_STUDY_TYPE,
$this->channelContext->getChannel(),
null
)->orderBy('translation.title');
},
])
->add('position', IntegerType::class, [
'label' => 'monsieurbiz_blog.ui_element.case_studies_ui_element.fields.position',
Expand Down
111 changes: 111 additions & 0 deletions src/Form/Type/UiElement/ArticlesByTagsUiElementType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?php

/*
* This file is part of Monsieur Biz's Blog plugin for Sylius.
* (c) Monsieur Biz <sylius@monsieurbiz.com>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace MonsieurBiz\SyliusBlogPlugin\Form\Type\UiElement;

use MonsieurBiz\SyliusBlogPlugin\Entity\Tag;
use MonsieurBiz\SyliusBlogPlugin\Form\Type\ArticlesDisplayType;
use MonsieurBiz\SyliusBlogPlugin\Repository\TagRepositoryInterface;
use MonsieurBiz\SyliusRichEditorPlugin\Attribute\AsUiElement;
use MonsieurBiz\SyliusRichEditorPlugin\Attribute\TemplatesUiElement;
use MonsieurBiz\SyliusRichEditorPlugin\Form\Type\LinkType;
use Sylius\Component\Locale\Context\LocaleContextInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints as Assert;

#[AsUiElement(
code: 'monsieurbiz_blog.articles_by_tags_ui_element',
icon: 'tags',
title: 'monsieurbiz_blog.ui_element.articles_by_tags_ui_element.title',
description: 'monsieurbiz_blog.ui_element.articles_by_tags_ui_element.description',
uiElement: 'MonsieurBiz\SyliusBlogPlugin\UiElement\ArticlesByTagsUiElement',
templates: new TemplatesUiElement(
adminRender: '@MonsieurBizSyliusBlogPlugin/Admin/UiElement/articles_by_tags.html.twig',
frontRender: '@MonsieurBizSyliusBlogPlugin/Shop/UiElement/articles_by_tags.html.twig',
),
wireframe: 'articles-by-tags',
tags: [],
)]
class ArticlesByTagsUiElementType extends AbstractType
{
public function __construct(
private readonly TagRepositoryInterface $tagRepository,
private readonly LocaleContextInterface $localeContext,
) {
}

/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('title', TextType::class, [
'label' => 'monsieurbiz_blog.ui_element.articles_by_tags_ui_element.fields.title',
'required' => false,
])
->add('display', ArticlesDisplayType::class, [
'label' => false, // already defined in the ArticlesDisplayType
])
->add('tags', EntityType::class, [
'label' => 'monsieurbiz_blog.ui_element.articles_by_tags_ui_element.fields.tags',
'required' => false,
'class' => Tag::class,
'choice_label' => fn (Tag $tag) => $tag->getName(),
'choice_value' => fn (?Tag $tag) => $tag?->getId(),
'query_builder' => function (TagRepositoryInterface $tagRepository) {
return $tagRepository->createListQueryBuilder(
$this->localeContext->getLocaleCode(),
)->orderBy('translation.name');
},
'multiple' => true,
])
->add('limit', IntegerType::class, [
'label' => 'monsieurbiz_blog.ui_element.articles_by_tags_ui_element.fields.limit',
'help' => 'monsieurbiz_blog.ui_element.articles_by_tags_ui_element.help.limit',
'required' => true,
'constraints' => [
new Assert\NotBlank(),
new Assert\GreaterThan(0),
],
])
->add('buttonLabel', TextType::class, [
'label' => 'monsieurbiz_blog.ui_element.articles_by_tags_ui_element.fields.button_label',
'required' => false,
])
->add('buttonUrl', LinkType::class, [
'label' => 'monsieurbiz_blog.ui_element.articles_by_tags_ui_element.fields.button_url',
'required' => false,
])
;

$builder->get('tags')->addModelTransformer(
new CallbackTransformer(
function ($tagsAsArray) {
return $this->tagRepository->findBy(['id' => $tagsAsArray ?? []]);
},
function ($tagsAsString) {
$tags = [];
foreach ($tagsAsString as $tag) {
$tags[] = $tag->getId();
}

return $tags;
}
),
);
}
}
94 changes: 94 additions & 0 deletions src/Form/Type/UiElement/ArticlesSelectionUiElementType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

/*
* This file is part of Monsieur Biz's Blog plugin for Sylius.
* (c) Monsieur Biz <sylius@monsieurbiz.com>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace MonsieurBiz\SyliusBlogPlugin\Form\Type\UiElement;

use MonsieurBiz\SyliusBlogPlugin\Form\Type\ArticlesDisplayType;
use MonsieurBiz\SyliusBlogPlugin\Form\Type\ArticleSelectionElementType;
use MonsieurBiz\SyliusRichEditorPlugin\Attribute\AsUiElement;
use MonsieurBiz\SyliusRichEditorPlugin\Attribute\TemplatesUiElement;
use MonsieurBiz\SyliusRichEditorPlugin\Form\Type\LinkType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Validator\Constraints as Assert;

#[AsUiElement(
code: 'monsieurbiz_blog.articles_selection_ui_element',
icon: 'newspaper',
title: 'monsieurbiz_blog.ui_element.articles_selection_ui_element.title',
description: 'monsieurbiz_blog.ui_element.articles_selection_ui_element.description',
uiElement: 'MonsieurBiz\SyliusBlogPlugin\UiElement\ArticlesSelectionUiElement',
templates: new TemplatesUiElement(
adminRender: '@MonsieurBizSyliusBlogPlugin/Admin/UiElement/articles_selection.html.twig',
frontRender: '@MonsieurBizSyliusBlogPlugin/Shop/UiElement/articles_selection.html.twig',
),
wireframe: 'articles-selection',
tags: [],
)]
class ArticlesSelectionUiElementType extends AbstractType
{
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('title', TextType::class, [
'label' => 'monsieurbiz_blog.ui_element.articles_selection_ui_element.fields.title',
'required' => false,
])
->add('display', ArticlesDisplayType::class, [
'label' => false, // already defined in the ArticlesDisplayType
])
->add('articles', CollectionType::class, [
'label' => 'monsieurbiz_blog.ui_element.articles_selection_ui_element.fields.articles',
'entry_type' => ArticleSelectionElementType::class,
'prototype_name' => '__article_selection__',
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'delete_empty' => true,
'attr' => [
'class' => 'ui segment secondary collection--flex',
],
'constraints' => [
new Assert\Count(['min' => 1]),
],
])
->add('buttonLabel', TextType::class, [
'label' => 'monsieurbiz_blog.ui_element.articles_selection_ui_element.fields.button_label',
'required' => false,
])
->add('buttonUrl', LinkType::class, [
'label' => 'monsieurbiz_blog.ui_element.articles_selection_ui_element.fields.button_url',
'required' => false,
])
;
}

/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function finishView(FormView $view, FormInterface $form, array $options): void
{
usort($view['articles']->children, function (FormView $articleA, FormView $articleB) {
return match (true) {
!$articleA->offsetExists('position') => -1,
!$articleB->offsetExists('position') => 1,
default => $articleA['position']->vars['data'] <=> $articleB['position']->vars['data']
};
});
}
}
4 changes: 2 additions & 2 deletions src/Form/Type/UiElement/CaseStudiesUiElementType.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@
#[AsUiElement(
code: 'monsieurbiz_blog.case_studies_ui_element',
icon: 'crosshairs',
uiElement: 'MonsieurBiz\SyliusBlogPlugin\UiElement\CaseStudiesUiElement',
title: 'monsieurbiz_blog.ui_element.case_studies_ui_element.title',
description: 'monsieurbiz_blog.ui_element.case_studies_ui_element.description',
uiElement: 'MonsieurBiz\SyliusBlogPlugin\UiElement\CaseStudiesUiElement',
templates: new TemplatesUiElement(
adminRender: '@MonsieurBizSyliusBlogPlugin/Admin/UiElement/case_studies.html.twig',
frontRender: '@MonsieurBizSyliusBlogPlugin/Shop/UiElement/case_studies.html.twig',
),
tags: [],
wireframe: 'case-studies',
tags: [],
)]
class CaseStudiesUiElementType extends AbstractType
{
Expand Down
Loading

0 comments on commit ed36080

Please sign in to comment.