Skip to content

Commit

Permalink
WIP - Adding sample variant config to the admin UI
Browse files Browse the repository at this point in the history
  • Loading branch information
mbabker committed Feb 21, 2022
1 parent ad0bca5 commit 8073913
Show file tree
Hide file tree
Showing 25 changed files with 547 additions and 17 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ phpstan.neon export-ignore
phpstan-baseline.neon export-ignore
phpunit.xml.dist export-ignore
psalm.xml export-ignore
psalm-baseline.xml export-ignore
README.md export-ignore
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,11 @@ jobs:

-
name: Check coding standards
run: vendor/bin/ecs check src
run: vendor/bin/ecs check

-
name: Run PHPStan
run: vendor/bin/phpstan analyse -c phpstan.neon -l max src/
run: vendor/bin/phpstan analyse

-
name: Run Psalm
Expand Down
5 changes: 3 additions & 2 deletions ecs.php
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
<?php

use PhpCsFixer\Fixer\ClassNotation\VisibilityRequiredFixer;
use SlevomatCodingStandard\Sniffs\Commenting\InlineDocCommentDeclarationSniff;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symplify\EasyCodingStandard\ValueObject\Option;

return static function (ContainerConfigurator $containerConfigurator): void {
$containerConfigurator->import('vendor/sylius-labs/coding-standard/ecs.php');

$containerConfigurator->parameters()->set(Option::SKIP, [
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::PATHS, [__DIR__ . '/src']);
$parameters->set(Option::SKIP, [
VisibilityRequiredFixer::class => ['*Spec.php'],
]);
};
31 changes: 30 additions & 1 deletion phpstan-baseline.neon
Original file line number Diff line number Diff line change
@@ -1,3 +1,32 @@
parameters:
ignoreErrors: []
ignoreErrors:
-
message: "#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#"
count: 1
path: src/BabDevSyliusProductSamplesPlugin.php

-
message: "#^Cannot call method getData\\(\\) on Symfony\\\\Component\\\\Form\\\\FormInterface\\|null\\.$#"
count: 2
path: src/Form/Type/SampleProductVariantType.php

-
message: "#^Cannot call method getParent\\(\\) on Symfony\\\\Component\\\\Form\\\\FormInterface\\|null\\.$#"
count: 1
path: src/Form/Type/SampleProductVariantType.php

-
message: "#^Cannot call method getSamplesActive\\(\\) on Sylius\\\\Component\\\\Product\\\\Model\\\\ProductInterface\\|null\\.$#"
count: 1
path: src/Form/Type/SampleProductVariantType.php

-
message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#"
count: 1
path: src/Form/Type/SampleProductVariantType.php

-
message: "#^Parameter \\#1 \\$sampleOf of method BabDev\\\\SyliusProductSamplesPlugin\\\\Model\\\\ProductVariantInterface\\:\\:setSampleOf\\(\\) expects Sylius\\\\Component\\\\Product\\\\Model\\\\ProductVariantInterface\\|null, mixed given\\.$#"
count: 1
path: src/Form/Type/SampleProductVariantType.php

5 changes: 5 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ parameters:
reportUnmatchedIgnoredErrors: false
checkMissingIterableValueType: false

level: max

paths:
- 'src'

excludePaths:
# Makes PHPStan crash
# - 'src/DependencyInjection/Configuration.php'
Expand Down
26 changes: 26 additions & 0 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="4.20.0@f82a70e7edfc6cf2705e9374c8a0b6a974a779ed">
<file src="src/BabDevSyliusProductSamplesPlugin.php">
<MixedInferredReturnType occurrences="1">
<code>?ExtensionInterface</code>
</MixedInferredReturnType>
<MixedReturnStatement occurrences="1">
<code>$this-&gt;extension ?: null</code>
</MixedReturnStatement>
</file>
<file src="src/Form/Type/SampleProductVariantType.php">
<MixedArgument occurrences="1">
<code>$variantForm-&gt;getData()</code>
</MixedArgument>
<MixedAssignment occurrences="1">
<code>$productVariant</code>
</MixedAssignment>
<PossiblyNullReference occurrences="2">
<code>getParent</code>
<code>getSamplesActive</code>
</PossiblyNullReference>
<UndefinedInterfaceMethod occurrences="1">
<code>getSamplesActive</code>
</UndefinedInterfaceMethod>
</file>
</files>
1 change: 1 addition & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
errorBaseline="psalm-baseline.xml"
>
<projectFiles>
<directory name="src" />
Expand Down
2 changes: 2 additions & 0 deletions spec/Menu/ProductFormMenuBuilderSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@ public function it_adds_the_menu_item_to_the_product_form_menu(ProductMenuBuilde
$menu->addChild('product_samples')->willReturn($menu);
$menu->setAttribute('template', '@BabDevSyliusProductSamplesPlugin/Admin/Product/Tab/_product_samples.html.twig')->willReturn($menu);
$menu->setLabel('babdev_sylius_product_samples.ui.product_samples')->willReturn($menu);

$this->addProductSamplesMenu($event);
}
}
22 changes: 22 additions & 0 deletions spec/Menu/ProductVariantFormMenuBuilderSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace spec\BabDev\SyliusProductSamplesPlugin\Menu;

use Knp\Menu\ItemInterface;
use PhpSpec\ObjectBehavior;
use Sylius\Bundle\AdminBundle\Event\ProductVariantMenuBuilderEvent;

final class ProductVariantFormMenuBuilderSpec extends ObjectBehavior
{
public function it_adds_the_menu_item_to_the_product_form_menu(ProductVariantMenuBuilderEvent $event, ItemInterface $menu): void
{
$event->getMenu()->willReturn($menu);
$menu->addChild('product_samples')->willReturn($menu);
$menu->setAttribute('template', '@BabDevSyliusProductSamplesPlugin/Admin/ProductVariant/Tab/_product_samples.html.twig')->willReturn($menu);
$menu->setLabel('babdev_sylius_product_samples.ui.product_samples')->willReturn($menu);

$this->addProductSamplesMenu($event);
}
}
14 changes: 3 additions & 11 deletions src/BabDevSyliusProductSamplesPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,12 @@ protected function getBundlePrefix(): string
return 'babdev_sylius_product_samples';
}

/**
* @psalm-suppress DocblockTypeContradiction
* @psalm-suppress InvalidReturnStatement
* @psalm-suppress InvalidReturnType
*/
public function getContainerExtension(): ?ExtensionInterface
{
if (null === $this->containerExtension) {
$this->containerExtension = new BabDevSyliusProductSamplesExtension();
if (null === $this->extension) {
$this->extension = new BabDevSyliusProductSamplesExtension();
}

/**
* @phpstan-ignore-next-line
*/
return $this->containerExtension === false ? null : $this->containerExtension;
return $this->extension ?: null;
}
}
32 changes: 32 additions & 0 deletions src/EventListener/ProductListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace BabDev\SyliusProductSamplesPlugin\EventListener;

use BabDev\SyliusProductSamplesPlugin\Model\ProductInterface;
use BabDev\SyliusProductSamplesPlugin\Model\ProductVariantInterface;
use Sylius\Bundle\ResourceBundle\Event\ResourceControllerEvent;

final class ProductListener
{
public function ensureSampleVariantsHaveCodes(ResourceControllerEvent $event): void
{
/** @var ProductInterface $product */
$product = $event->getSubject();

foreach ($product->getVariants() as $variant) {
if (!$variant instanceof ProductVariantInterface) {
continue;
}

if (null === $sample = $variant->getSample()) {
continue;
}

if (null === $sample->getCode()) {
$sample->setCode(sprintf('SAMPLE-%s', $variant->getCode() ?? ''));
}
}
}
}
28 changes: 28 additions & 0 deletions src/Form/Extension/ProductTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,23 @@

namespace BabDev\SyliusProductSamplesPlugin\Form\Extension;

use BabDev\SyliusProductSamplesPlugin\Model\ProductInterface;
use BabDev\SyliusProductSamplesPlugin\Model\ProductVariantInterface;
use Sylius\Bundle\ProductBundle\Form\Type\ProductType;
use Sylius\Component\Product\Factory\ProductVariantFactoryInterface;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Webmozart\Assert\Assert;

final class ProductTypeExtension extends AbstractTypeExtension
{
public function __construct(private ProductVariantFactoryInterface $productVariantFactory)
{
}

public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
Expand All @@ -19,6 +29,24 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
'label' => 'babdev_sylius_product_samples.form.product.samples_available',
])
;

$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event): void {
$product = $event->getData();

Assert::isInstanceOf($product, ProductInterface::class);

if (!$product->isSimple()) {
return;
}

$variant = $product->getVariants()->first();

Assert::isInstanceOf($variant, ProductVariantInterface::class);

if (null === $variant->getSample()) {
$variant->setSample($this->productVariantFactory->createForProduct($product));
}
});
}

public static function getExtendedTypes(): iterable
Expand Down
25 changes: 25 additions & 0 deletions src/Form/Extension/ProductVariantTypeExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace BabDev\SyliusProductSamplesPlugin\Form\Extension;

use BabDev\SyliusProductSamplesPlugin\Form\Type\SampleProductVariantType;
use Sylius\Bundle\ProductBundle\Form\Type\ProductVariantType;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormBuilderInterface;

final class ProductVariantTypeExtension extends AbstractTypeExtension
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('sample', SampleProductVariantType::class)
;
}

public static function getExtendedTypes(): iterable
{
return [ProductVariantType::class];
}
}
101 changes: 101 additions & 0 deletions src/Form/Type/SampleProductVariantType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php

declare(strict_types=1);

namespace BabDev\SyliusProductSamplesPlugin\Form\Type;

use BabDev\SyliusProductSamplesPlugin\Model\ProductVariantInterface;
use Sylius\Bundle\CoreBundle\Form\Type\ChannelCollectionType;
use Sylius\Bundle\CoreBundle\Form\Type\Product\ChannelPricingType;
use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType;
use Sylius\Bundle\ShippingBundle\Form\Type\ShippingCategoryChoiceType;
use Sylius\Bundle\TaxationBundle\Form\Type\TaxCategoryChoiceType;
use Sylius\Component\Core\Model\ChannelInterface;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;

final class SampleProductVariantType extends AbstractResourceType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('shippingCategory', ShippingCategoryChoiceType::class, [
'required' => false,
'placeholder' => 'sylius.ui.no_requirement',
'label' => 'sylius.form.product_variant.shipping_category',
])
->add('width', NumberType::class, [
'required' => false,
'label' => 'sylius.form.variant.width',
'invalid_message' => 'sylius.product_variant.width.invalid',
])
->add('height', NumberType::class, [
'required' => false,
'label' => 'sylius.form.variant.height',
'invalid_message' => 'sylius.product_variant.height.invalid',
])
->add('depth', NumberType::class, [
'required' => false,
'label' => 'sylius.form.variant.depth',
'invalid_message' => 'sylius.product_variant.depth.invalid',
])
->add('weight', NumberType::class, [
'required' => false,
'label' => 'sylius.form.variant.weight',
'invalid_message' => 'sylius.product_variant.weight.invalid',
])
->add('taxCategory', TaxCategoryChoiceType::class, [
'required' => false,
'placeholder' => '---',
'label' => 'sylius.form.product_variant.tax_category',
])
;

$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event): void {
$productVariant = $event->getData();

$event->getForm()->add('channelPricings', ChannelCollectionType::class, [
'entry_type' => ChannelPricingType::class,
'entry_options' => static fn (ChannelInterface $channel): array => [
'channel' => $channel,
'product_variant' => $productVariant,
'required' => false,
],
'label' => 'sylius.form.variant.price',
]);
});

$builder->addEventListener(FormEvents::SUBMIT, function (FormEvent $event): void {
/** @var ProductVariantInterface $sampleVariant */
$sampleVariant = $event->getData();

$variantForm = $event->getForm()->getParent();

if (null !== $productForm = $variantForm->getParent()) {
if (!$productForm->get('samplesActive')->getData() && null === $sampleVariant->getId()) {
$event->setData(null);

return;
}
} else {
if (!$sampleVariant->getProduct()->getSamplesActive() && null === $sampleVariant->getId()) {
$event->setData(null);

return;
}
}

if (null !== $sampleVariant->getId()) {
return;
}

/** @var ProductVariantInterface $actualVariant */
$actualVariant = $variantForm->getData();
$actualVariant->setSample($sampleVariant);

$sampleVariant->setSampleOf($variantForm->getData());
});
}
}
Loading

0 comments on commit 8073913

Please sign in to comment.