mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-14 09:02:12 +00:00
[Admin] Added autocompletes to product images variant collection
This commit is contained in:
parent
0d75d45651
commit
ead270d7bd
25 changed files with 416 additions and 47 deletions
|
|
@ -0,0 +1,37 @@
|
|||
@managing_product_variants
|
||||
Feature: Product variants autocomplete
|
||||
In order to get hints when looking for named product variants
|
||||
As an Administrator
|
||||
I want to get product variants according to my specified phrase
|
||||
|
||||
Background:
|
||||
Given the store operates on a single channel in "United States"
|
||||
And the store has a "Snake" configurable product
|
||||
And this product has "Ouroboros", "Boomslang" and "Bimini" variants
|
||||
And I am logged in as an administrator
|
||||
|
||||
@api
|
||||
Scenario: Getting a hint when looking for product variants
|
||||
When I look for a variant with "i" in descriptor within the "Snake" product
|
||||
Then I should see 1 product variant on the list
|
||||
And I should see the product variant named "Bimini" on the list
|
||||
|
||||
@api
|
||||
Scenario: Getting a hint when looking for product variants
|
||||
When I look for a variant with "bo" in descriptor within the "Snake" product
|
||||
Then I should see 2 product variants on the list
|
||||
And I should see the product variants named "Ouroboros" and "Boomslang" on the list
|
||||
|
||||
@api
|
||||
Scenario: Getting a hint when looking for product variants
|
||||
Given the product "Snake" also has a "Python" variant with code "TIMOR"
|
||||
When I look for a variant with "ti" in descriptor within the "Snake" product
|
||||
Then I should see 1 product variant on the list
|
||||
And I should see the product variant named "Python" on the list
|
||||
|
||||
@api
|
||||
Scenario: Getting a hint when looking for product variants
|
||||
Given the product "Snake" also has a nameless variant with code "CERBERUS"
|
||||
When I look for a variant with "cer" in descriptor within the "Snake" product
|
||||
Then I should see 1 product variant on the list
|
||||
And I should see the product variant labeled "Snake (CERBERUS)" on the list
|
||||
|
|
@ -1,33 +1,33 @@
|
|||
@managing_taxons
|
||||
Feature: Taxons autocomplete
|
||||
In order to getting hint when looking for taxons
|
||||
In order to get hints when looking for taxons
|
||||
As an Administrator
|
||||
I want to get taxon
|
||||
I want to get taxons according to my specified phrase
|
||||
|
||||
Background:
|
||||
Given the store classifies its products as "T-Shirts", "Watches", "Belts" and "Wallets"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@api
|
||||
Scenario: Getting hint when looking for taxons
|
||||
Scenario: Getting a hint when looking for taxons
|
||||
When I look for a taxon with "b" in name
|
||||
Then I should see 1 taxons on the list
|
||||
And I should see the taxon named "Belts" in the list
|
||||
|
||||
@api
|
||||
Scenario: Getting hint when looking for taxons
|
||||
Scenario: Getting a hint when looking for taxons
|
||||
When I look for a taxon with "shi" in name
|
||||
Then I should see 1 taxons on the list
|
||||
And I should see the taxon named "T-Shirts" in the list
|
||||
|
||||
@api
|
||||
Scenario: Getting hint when looking for taxons
|
||||
Scenario: Getting a hint when looking for taxons
|
||||
When I look for a taxon with "e" in name
|
||||
Then I should see 3 taxons on the list
|
||||
And I should see the taxon named "Belts", "Wallets" and "Watches" in the list
|
||||
|
||||
@api
|
||||
Scenario: Getting taxon from its code
|
||||
Scenario: Getting a taxon from its code
|
||||
When I want to get taxon with "belts" code
|
||||
Then I should see 1 taxons on the list
|
||||
And I should see the taxon named "Belts" in the list
|
||||
|
|
|
|||
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Paweł Jędrzejewski
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Sylius\Behat\Context\Api\Admin;
|
||||
|
||||
use Behat\Behat\Context\Context;
|
||||
use Sylius\Component\Core\Model\ProductInterface;
|
||||
use Symfony\Component\BrowserKit\Client;
|
||||
use Symfony\Component\BrowserKit\Cookie;
|
||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
/**
|
||||
* @author Jan Góralski <jan.goralski@lakion.com>
|
||||
*/
|
||||
final class ManagingProductVariantsContext implements Context
|
||||
{
|
||||
/**
|
||||
* @var Client
|
||||
*/
|
||||
private $client;
|
||||
|
||||
/**
|
||||
* @var SessionInterface
|
||||
*/
|
||||
private $session;
|
||||
|
||||
/**
|
||||
* @param Client $client
|
||||
* @param SessionInterface $session
|
||||
*/
|
||||
public function __construct(Client $client, SessionInterface $session)
|
||||
{
|
||||
$this->client = $client;
|
||||
$this->session = $session;
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I look for a variant with :phrase in descriptor within the :product product
|
||||
*/
|
||||
public function iLookForVariantWithDescriptorWithinProduct($phrase, ProductInterface $product)
|
||||
{
|
||||
$this->client->getCookieJar()->set(new Cookie($this->session->getName(), $this->session->getId()));
|
||||
$this->client->request(
|
||||
'GET',
|
||||
'/admin/ajax/product-variants/search',
|
||||
['phrase' => $phrase, 'productCode' => $product->getCode()],
|
||||
[],
|
||||
['ACCEPT' => 'application/json']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^I should see (\d+) product variants? on the list$/
|
||||
*/
|
||||
public function iShouldSeeProductVariantsInTheList($number)
|
||||
{
|
||||
Assert::eq(count($this->getJSONResponse()), $number);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should see the product variant named :firstName on the list
|
||||
* @Then I should see the product variants named :firstName and :secondName on the list
|
||||
* @Then I should see the product variants named :firstName, :secondName and :thirdName on the list
|
||||
* @Then I should see the product variants named :firstName, :secondName, :thirdName and :fourthName on the list
|
||||
*/
|
||||
public function iShouldSeeTheProductVariantNamedAnd(...$names)
|
||||
{
|
||||
$itemsNames = array_map(function ($item) {
|
||||
return strstr($item['descriptor'], ' ', true);
|
||||
}, $this->getJSONResponse());
|
||||
|
||||
Assert::allOneOf($itemsNames, $names);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should see the product variant labeled :label on the list
|
||||
*/
|
||||
public function iShouldSeeTheProductVariantLabeledAs($label)
|
||||
{
|
||||
$itemsLabels = array_map(function ($item) {
|
||||
return $item['descriptor'];
|
||||
}, $this->getJSONResponse());
|
||||
|
||||
Assert::oneOf($label, $itemsLabels, 'Expected "%s" to be on the list, found: %s.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
private function getJSONResponse()
|
||||
{
|
||||
return json_decode($this->client->getResponse()->getContent(), true);
|
||||
}
|
||||
}
|
||||
|
|
@ -86,7 +86,7 @@ final class ManagingTaxonsContext implements Context
|
|||
{
|
||||
$response = json_decode($this->client->getResponse()->getContent(), true);
|
||||
|
||||
Assert::eq($number, count($response));
|
||||
Assert::eq(count($response), $number);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -334,6 +334,50 @@ final class ProductContext implements Context
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^the (product "[^"]+") has(?:| a| an) "([^"]+)" variant$/
|
||||
* @Given /^(this product) has(?:| a| an) "([^"]+)" variant$/
|
||||
* @Given /^(this product) has "([^"]+)", "([^"]+)" and "([^"]+)" variants$/
|
||||
*/
|
||||
public function theProductHasVariants(ProductInterface $product, ...$variantNames)
|
||||
{
|
||||
$channel = $this->sharedStorage->get('channel');
|
||||
|
||||
foreach ($variantNames as $name) {
|
||||
$this->createProductVariant(
|
||||
$product,
|
||||
$name,
|
||||
0,
|
||||
StringInflector::nameToUppercaseCode($name),
|
||||
$channel
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^the (product "[^"]+")(?:| also) has a nameless variant with code "([^"]+)"$/
|
||||
* @Given /^(this product)(?:| also) has a nameless variant with code "([^"]+)"$/
|
||||
* @Given /^(it)(?:| also) has a nameless variant with code "([^"]+)"$/
|
||||
*/
|
||||
public function theProductHasNamelessVariantWithCode(ProductInterface $product, $variantCode)
|
||||
{
|
||||
$channel = $this->sharedStorage->get('channel');
|
||||
|
||||
$this->createProductVariant($product, null, 0, $variantCode, $channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^the (product "[^"]+")(?:| also) has(?:| a| an) "([^"]+)" variant with code "([^"]+)"$/
|
||||
* @Given /^(this product)(?:| also) has(?:| a| an) "([^"]+)" variant with code "([^"]+)"$/
|
||||
* @Given /^(it)(?:| also) has(?:| a| an) "([^"]+)" variant with code "([^"]+)"$/
|
||||
*/
|
||||
public function theProductHasVariantWithCode(ProductInterface $product, $variantName, $variantCode)
|
||||
{
|
||||
$channel = $this->sharedStorage->get('channel');
|
||||
|
||||
$this->createProductVariant($product, $variantName, 0, $variantCode, $channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+") which does not require shipping$/
|
||||
*/
|
||||
|
|
@ -447,6 +491,8 @@ final class ProductContext implements Context
|
|||
*/
|
||||
public function itComesInTheFollowingVariations(ProductInterface $product, TableNode $table)
|
||||
{
|
||||
$channel = $this->sharedStorage->get('channel');
|
||||
|
||||
foreach ($table->getHash() as $variantHash) {
|
||||
/** @var ProductVariantInterface $variant */
|
||||
$variant = $this->productVariantFactory->createNew();
|
||||
|
|
@ -455,8 +501,9 @@ final class ProductContext implements Context
|
|||
$variant->setCode(StringInflector::nameToUppercaseCode($variantHash['name']));
|
||||
$variant->addChannelPricing($this->createChannelPricingForChannel(
|
||||
$this->getPriceFromString(str_replace(['$', '€', '£'], '', $variantHash['price'])),
|
||||
$this->sharedStorage->get('channel')
|
||||
$channel
|
||||
));
|
||||
|
||||
$variant->setProduct($product);
|
||||
$product->addVariant($variant);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,15 +11,17 @@
|
|||
|
||||
-->
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
<services>
|
||||
<service id="sylius.behat.context.api.admin.managing_taxons" class="Sylius\Behat\Context\Api\Admin\ManagingTaxonsContext">
|
||||
<argument type="service" id="__symfony__.test.client" />
|
||||
<argument type="service" id="__symfony__.session" />
|
||||
<tag name="fob.context_service" />
|
||||
</service>
|
||||
<service id="sylius.behat.context.api.admin.managing_product_variants" class="Sylius\Behat\Context\Api\Admin\ManagingProductVariantsContext">
|
||||
<argument type="service" id="__symfony__.test.client" />
|
||||
<argument type="service" id="__symfony__.session" />
|
||||
<tag name="fob.context_service" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
# (c) Paweł Jędrzejewski
|
||||
|
||||
imports:
|
||||
- suites/api/taxon/managing_taxon.yml
|
||||
- suites/api/taxon/managing_taxons.yml
|
||||
- suites/api/product/managing_product_variants.yml
|
||||
|
||||
- suites/cli/installer.yml
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
# This file is part of the Sylius package.
|
||||
# (c) Paweł Jędrzejewski
|
||||
|
||||
default:
|
||||
suites:
|
||||
api_managing_product_variants:
|
||||
contexts_services:
|
||||
- sylius.behat.context.hook.doctrine_orm
|
||||
|
||||
- sylius.behat.context.transform.product
|
||||
- sylius.behat.context.transform.shared_storage
|
||||
|
||||
- sylius.behat.context.setup.channel
|
||||
- sylius.behat.context.setup.product
|
||||
- sylius.behat.context.setup.admin_security
|
||||
|
||||
- sylius.behat.context.api.admin.managing_product_variants
|
||||
filters:
|
||||
tags: "@managing_product_variants && @api"
|
||||
|
|
@ -3,3 +3,30 @@ sylius_admin_ajax_product_variants_update_position:
|
|||
methods: [PUT]
|
||||
defaults:
|
||||
_controller: sylius.controller.product_variant:updatePositionsAction
|
||||
|
||||
sylius_admin_ajax_product_variants_by_phrase:
|
||||
path: /search
|
||||
methods: [GET]
|
||||
defaults:
|
||||
_controller: sylius.controller.product_variant:indexAction
|
||||
_sylius:
|
||||
serialization_groups: [Autocomplete]
|
||||
repository:
|
||||
method: findByPhraseAndProductCode
|
||||
arguments:
|
||||
phrase: $phrase
|
||||
locale: expr:service('sylius.context.locale').getLocaleCode()
|
||||
productCode: $productCode
|
||||
|
||||
sylius_admin_ajax_product_variants_by_code:
|
||||
path: /
|
||||
methods: [GET]
|
||||
defaults:
|
||||
_controller: sylius.controller.product_variant:indexAction
|
||||
_sylius:
|
||||
serialization_groups: [Autocomplete]
|
||||
repository:
|
||||
method: findByCodeAndProductCode
|
||||
arguments:
|
||||
code: $code
|
||||
productCode: $productCode
|
||||
|
|
|
|||
|
|
@ -48,6 +48,13 @@
|
|||
}, 50);
|
||||
});
|
||||
|
||||
$(document).on('collection-form-add', function () {
|
||||
$.each($('.sylius-autocomplete'), function (index, element) {
|
||||
if ($._data($(element).get(0), 'events') == undefined) {
|
||||
$(element).autoComplete();
|
||||
}
|
||||
});
|
||||
});
|
||||
$(document).on('collection-form-update', function () {
|
||||
$.each($('.sylius-autocomplete'), function (index, element) {
|
||||
if ($._data($(element).get(0), 'events') == undefined) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
{% extends '@SyliusUi/Form/imagesTheme.html.twig' %}
|
||||
|
||||
{% block sylius_product_image_widget %}
|
||||
{% spaceless %}
|
||||
<div class="ui upload box segment">
|
||||
{{ form_row(form.type) }}
|
||||
<label for="{{ form.file.vars.id }}" class="ui icon labeled button"><i class="cloud upload icon"></i> {{ 'sylius.ui.choose_file'|trans }}</label>
|
||||
{% if form.vars.value.path|default(null) is not null %}
|
||||
<img class="ui small bordered image" src="{{ form.vars.value.path|imagine_filter('sylius_small') }}" alt="{{ form.vars.value.type }}" />
|
||||
{% endif %}
|
||||
<div class="ui hidden element">
|
||||
{{ form_widget(form.file) }}
|
||||
</div>
|
||||
<div class="ui element">
|
||||
{{- form_errors(form.file) -}}
|
||||
</div>
|
||||
{% if product.id is not null and 0 != product.variants|length and not product.simple %}
|
||||
<br/>
|
||||
{{ form_row(form.productVariants, {'remote_url': path('sylius_admin_ajax_product_variants_by_phrase', {'productCode': product.code}), 'remote_criteria_type': 'contains', 'remote_criteria_name': 'phrase', 'load_edit_url': path('sylius_admin_ajax_product_variants_by_code', {'productCode': product.code})}) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endspaceless %}
|
||||
{% endblock %}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
{% form_theme form 'SyliusUiBundle:Form:imagesTheme.html.twig' %}
|
||||
{% form_theme form '@SyliusAdmin/Form/imagesTheme.html.twig' %}
|
||||
|
||||
<div class="ui tab" data-tab="media">
|
||||
<h3 class="ui dividing header">{{ 'sylius.ui.media'|trans }}</h3>
|
||||
|
|
|
|||
|
|
@ -65,13 +65,8 @@ final class TaxonsToCodesTransformer implements DataTransformerInterface
|
|||
return [];
|
||||
}
|
||||
|
||||
$taxonCodes = [];
|
||||
|
||||
/** @var TaxonInterface $taxon */
|
||||
foreach ($taxons as $taxon) {
|
||||
$taxonCodes[] = $taxon->getCode();
|
||||
}
|
||||
|
||||
return $taxonCodes;
|
||||
return array_map(function (TaxonInterface $taxon) {
|
||||
return $taxon->getCode();
|
||||
}, $taxons->toArray());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ final class ProductTypeExtension extends AbstractTypeExtension
|
|||
'allow_delete' => true,
|
||||
'by_reference' => false,
|
||||
'label' => 'sylius.form.product.images',
|
||||
'block_name' => 'entry',
|
||||
])
|
||||
;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
namespace Sylius\Bundle\CoreBundle\Form\Type\Product;
|
||||
|
||||
use Sylius\Bundle\CoreBundle\Form\Type\ImageType;
|
||||
use Sylius\Bundle\ProductBundle\Form\Type\ProductVariantChoiceType;
|
||||
use Sylius\Bundle\ResourceBundle\Form\Type\ResourceAutocompleteChoiceType;
|
||||
use Sylius\Component\Core\Model\ProductInterface;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
|
|
@ -33,12 +33,13 @@ final class ProductImageType extends ImageType
|
|||
|
||||
if (isset($options['product']) && $options['product'] instanceof ProductInterface) {
|
||||
$builder
|
||||
->add('productVariants', ProductVariantChoiceType::class, [
|
||||
->add('productVariants', ResourceAutocompleteChoiceType::class, [
|
||||
'label' => 'sylius.ui.product_variants',
|
||||
'multiple' => true,
|
||||
'expanded' => false,
|
||||
'required' => false,
|
||||
'product' => $options['product'],
|
||||
'choice_name' => 'descriptor',
|
||||
'choice_value' => 'code',
|
||||
'resource' => 'sylius.product_variant',
|
||||
])
|
||||
;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,9 @@
|
|||
<parameter key="sylius.form.type.channel_pricing.validation_groups" type="collection">
|
||||
<parameter>sylius</parameter>
|
||||
</parameter>
|
||||
<parameter key="sylius.form.type.product_image.validation_groups" type="collection">
|
||||
<parameter>sylius</parameter>
|
||||
</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
|
|
@ -131,6 +134,7 @@
|
|||
|
||||
<service id="sylius.form.type.product_image" class="Sylius\Bundle\CoreBundle\Form\Type\Product\ProductImageType">
|
||||
<argument>%sylius.model.product_image.class%</argument>
|
||||
<argument>%sylius.form.type.product_image.validation_groups%</argument>
|
||||
<tag name="form.type" />
|
||||
</service>
|
||||
<service id="sylius.form.type.product_taxon_choice" class="Sylius\Bundle\CoreBundle\Form\Type\ProductTaxonChoiceType">
|
||||
|
|
@ -189,6 +193,9 @@
|
|||
<service id="sylius.form.type.data_transformer.products_to_codes" class="Sylius\Bundle\CoreBundle\Form\DataTransformer\ProductsToCodesTransformer">
|
||||
<argument type="service" id="sylius.repository.product" />
|
||||
</service>
|
||||
<service id="sylius.form.type.data_transformer.product_variants_to_codes" class="Sylius\Bundle\CoreBundle\Form\DataTransformer\ProductVariantsToCodesTransformer">
|
||||
<argument type="service" id="sylius.repository.product_variant" />
|
||||
</service>
|
||||
|
||||
<service id="sylius.form.type.customer_guest" class="Sylius\Bundle\CoreBundle\Form\Type\Customer\CustomerGuestType">
|
||||
<argument>%sylius.model.customer.class%</argument>
|
||||
|
|
|
|||
|
|
@ -12,9 +12,7 @@
|
|||
namespace Sylius\Bundle\ProductBundle\Doctrine\ORM;
|
||||
|
||||
use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository;
|
||||
use Sylius\Component\Core\Model\Product;
|
||||
use Sylius\Component\Product\Model\ProductInterface;
|
||||
use Sylius\Component\Product\Model\ProductVariantInterface;
|
||||
use Sylius\Component\Product\Repository\ProductVariantRepositoryInterface;
|
||||
|
||||
/**
|
||||
|
|
@ -92,7 +90,7 @@ class ProductVariantRepository extends EntityRepository implements ProductVarian
|
|||
{
|
||||
return $this->createQueryBuilder('o')
|
||||
->innerJoin('o.product', 'product')
|
||||
->where('product.code = :productCode')
|
||||
->andWhere('product.code = :productCode')
|
||||
->andWhere('o.code = :code')
|
||||
->setParameter('productCode', $productCode)
|
||||
->setParameter('code', $code)
|
||||
|
|
@ -101,13 +99,29 @@ class ProductVariantRepository extends EntityRepository implements ProductVarian
|
|||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function findByCodeAndProductCode($code, $productCode)
|
||||
{
|
||||
return $this->createQueryBuilder('o')
|
||||
->innerJoin('o.product', 'product')
|
||||
->andWhere('product.code = :productCode')
|
||||
->andWhere('o.code = :code')
|
||||
->setParameter('productCode', $productCode)
|
||||
->setParameter('code', $code)
|
||||
->getQuery()
|
||||
->getResult()
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function findOneByIdAndProductId($id, $productId)
|
||||
{
|
||||
return $this->createQueryBuilder('o')
|
||||
->where('o.product = :productId')
|
||||
->andWhere('o.product = :productId')
|
||||
->andWhere('o.id = :id')
|
||||
->setParameter('productId', $productId)
|
||||
->setParameter('id', $id)
|
||||
|
|
@ -115,4 +129,27 @@ class ProductVariantRepository extends EntityRepository implements ProductVarian
|
|||
->getOneOrNullResult()
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function findByPhraseAndProductCode($phrase, $locale, $productCode)
|
||||
{
|
||||
$expr = $this->getEntityManager()->getExpressionBuilder();
|
||||
|
||||
return $this->createQueryBuilder('o')
|
||||
->innerJoin('o.translations', 'translation', 'WITH', 'translation.locale = :locale')
|
||||
->innerJoin('o.product', 'product')
|
||||
->andWhere('product.code = :productCode')
|
||||
->andWhere($expr->orX(
|
||||
'translation.name LIKE :phrase',
|
||||
'o.code LIKE :phrase'
|
||||
))
|
||||
->setParameter('phrase', '%'.$phrase.'%')
|
||||
->setParameter('locale', $locale)
|
||||
->setParameter('productCode', $productCode)
|
||||
->getQuery()
|
||||
->getResult()
|
||||
;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ Sylius\Component\Product\Model\ProductVariant:
|
|||
expose: true
|
||||
type: integer
|
||||
xml_attribute: true
|
||||
groups: [Default, Detailed, DetailedCart]
|
||||
groups: [Default, Detailed, DetailedCart, Autocomplete]
|
||||
code:
|
||||
expose: true
|
||||
type: string
|
||||
groups: [Default, Detailed, DetailedCart]
|
||||
groups: [Default, Detailed, DetailedCart, Autocomplete]
|
||||
position:
|
||||
expose: true
|
||||
type: integer
|
||||
|
|
@ -23,6 +23,10 @@ Sylius\Component\Product\Model\ProductVariant:
|
|||
expose: true
|
||||
type: array
|
||||
groups: [Default, Detailed, DetailedCart]
|
||||
virtual_properties:
|
||||
getDescriptor:
|
||||
serialized_name: descriptor
|
||||
groups: [Autocomplete]
|
||||
relations:
|
||||
- rel: self
|
||||
href:
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
namespace Sylius\Bundle\ResourceBundle\Form\DataTransformer;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
|
@ -38,6 +39,10 @@ final class RecursiveTransformer implements DataTransformerInterface
|
|||
*/
|
||||
public function transform($values)
|
||||
{
|
||||
if (null === $values) {
|
||||
return new ArrayCollection();
|
||||
}
|
||||
|
||||
$this->assertTransformationValueType($values, Collection::class);
|
||||
|
||||
return $values->map(function ($value) {
|
||||
|
|
@ -50,6 +55,10 @@ final class RecursiveTransformer implements DataTransformerInterface
|
|||
*/
|
||||
public function reverseTransform($values)
|
||||
{
|
||||
if (null === $values) {
|
||||
return new ArrayCollection();
|
||||
}
|
||||
|
||||
$this->assertTransformationValueType($values, Collection::class);
|
||||
|
||||
return $values->map(function ($value) {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ namespace spec\Sylius\Bundle\ResourceBundle\Form\DataTransformer;
|
|||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Prophecy\Argument;
|
||||
use Sylius\Bundle\ResourceBundle\Form\DataTransformer\RecursiveTransformer;
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
|
@ -37,6 +38,20 @@ final class RecursiveTransformerSpec extends ObjectBehavior
|
|||
$this->shouldImplement(DataTransformerInterface::class);
|
||||
}
|
||||
|
||||
function it_returns_an_empty_array_collection_when_transforming_a_null(
|
||||
DataTransformerInterface $decoratedTransformer
|
||||
) {
|
||||
$this->transform(null)->shouldBeLike(new ArrayCollection());
|
||||
$decoratedTransformer->transform(Argument::any())->shouldNotBeCalled();
|
||||
}
|
||||
|
||||
function it_returns_an_empty_array_collection_when_reverse_transforming_a_null(
|
||||
DataTransformerInterface $decoratedTransformer
|
||||
) {
|
||||
$this->reverseTransform(null)->shouldBeLike(new ArrayCollection());
|
||||
$decoratedTransformer->reverseTransform(Argument::any())->shouldNotBeCalled();
|
||||
}
|
||||
|
||||
function it_transforms_recursively_using_configured_transformer(DataTransformerInterface $decoratedTransformer)
|
||||
{
|
||||
$decoratedTransformer->transform('ABC')->willReturn('abc');
|
||||
|
|
@ -55,7 +70,7 @@ final class RecursiveTransformerSpec extends ObjectBehavior
|
|||
$this->reverseTransform(new ArrayCollection(['abc', 'cde', 'fgh']))->shouldBeLike(new ArrayCollection(['ABC', 'CDE', 'FGH']));
|
||||
}
|
||||
|
||||
function it_throws_transformation_failed_exception_if_transform_argument_is_not_collection()
|
||||
function it_throws_transformation_failed_exception_if_transform_argument_is_not_collection_or_null()
|
||||
{
|
||||
$this->shouldThrow(TransformationFailedException::class)->during('transform', [new \stdClass()]);
|
||||
$this->shouldThrow(TransformationFailedException::class)->during('reverseTransform', [new \stdClass()]);
|
||||
|
|
|
|||
|
|
@ -193,20 +193,22 @@
|
|||
{%- endblock sylius_translations_row %}
|
||||
|
||||
{% block sylius_resource_autocomplete_choice_row %}
|
||||
<div class="{% if required %}required {% endif %}field{% if (not compound or force_error|default(false)) and not valid %} error{% endif %}">
|
||||
{{- form_label(form) -}}
|
||||
<div
|
||||
class="sylius-autocomplete ui fluid search selection dropdown {% if multiple %}multiple{% endif %}"
|
||||
data-url="{{ remote_url }}"
|
||||
data-choice-name="{{ choice_name }}"
|
||||
data-choice-value="{{ choice_value }}"
|
||||
data-criteria-type="{{ remote_criteria_type }}"
|
||||
data-criteria-name="{{ remote_criteria_name }}"
|
||||
data-load-edit-url="{{ load_edit_url }}"
|
||||
>
|
||||
{{ form_widget(form, {'attr': {'class' : 'autocomplete'}}) }}
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="default text">{% if placeholder is defined %} {{ placeholder|trans }} {% endif %}</div>
|
||||
<div class="menu"></div>
|
||||
<div
|
||||
class="sylius-autocomplete ui fluid search selection dropdown {% if multiple %}multiple{% endif %}"
|
||||
data-url="{{ remote_url }}"
|
||||
data-choice-name="{{ choice_name }}"
|
||||
data-choice-value="{{ choice_value }}"
|
||||
data-criteria-type="{{ remote_criteria_type }}"
|
||||
data-criteria-name="{{ remote_criteria_name }}"
|
||||
data-load-edit-url="{{ load_edit_url }}"
|
||||
>
|
||||
{{- form_row(form, {'attr': {'class' : 'autocomplete'}}) -}}
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="default text">{% if placeholder is defined %} {{ placeholder|trans }} {% endif %}</div>
|
||||
<div class="menu"></div>
|
||||
</div>
|
||||
{{- form_errors(form) -}}
|
||||
</div>
|
||||
{{ form_errors(form) }}
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -110,6 +110,16 @@ class ProductVariant implements ProductVariantInterface
|
|||
$this->getTranslation()->setName($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescriptor()
|
||||
{
|
||||
$name = empty($this->getName()) ? $this->getProduct()->getName() : $this->getName();
|
||||
|
||||
return trim(sprintf('%s (%s)', $name, $this->code));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -36,6 +36,11 @@ interface ProductVariantInterface extends
|
|||
*/
|
||||
public function setName($name);
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDescriptor();
|
||||
|
||||
/**
|
||||
* @return Collection|ProductOptionValueInterface[]
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -54,6 +54,14 @@ interface ProductVariantRepositoryInterface extends RepositoryInterface
|
|||
*/
|
||||
public function findOneByCodeAndProductCode($code, $productCode);
|
||||
|
||||
/**
|
||||
* @param string|array $code
|
||||
* @param string $productCode
|
||||
*
|
||||
* @return ProductVariantInterface[]
|
||||
*/
|
||||
public function findByCodeAndProductCode($code, $productCode);
|
||||
|
||||
/**
|
||||
* @param mixed $id
|
||||
* @param mixed $productId
|
||||
|
|
@ -61,4 +69,13 @@ interface ProductVariantRepositoryInterface extends RepositoryInterface
|
|||
* @return ProductVariantInterface|null
|
||||
*/
|
||||
public function findOneByIdAndProductId($id, $productId);
|
||||
|
||||
/**
|
||||
* @param string $phrase
|
||||
* @param string $locale
|
||||
* @param string $productCode
|
||||
*
|
||||
* @return ProductVariantInterface[]
|
||||
*/
|
||||
public function findByPhraseAndProductCode($phrase, $locale, $productCode);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue