mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-14 09:02:12 +00:00
[Association] Add basic Association and AssociationType model. Add backend administration pages for AssociationType.
Add some translations.
This commit is contained in:
parent
2e05391512
commit
7a127a6efd
30 changed files with 895 additions and 0 deletions
|
|
@ -220,6 +220,8 @@ default:
|
|||
- Sylius\Bundle\MoneyBundle\Behat\MoneyContext
|
||||
- Sylius\Bundle\ProductBundle\Behat\ProductContext
|
||||
- Sylius\Bundle\ResourceBundle\Behat\FixtureContext
|
||||
- Sylius\Bundle\ProductBundle\Behat\ProductAssociationContext
|
||||
- Sylius\Bundle\ResourceBundle\Behat\BaseContext
|
||||
- Sylius\Bundle\TaxationBundle\Behat\TaxationContext
|
||||
- Sylius\Bundle\TaxonomyBundle\Behat\TaxonomyContext
|
||||
- Sylius\Bundle\WebBundle\Behat\WebContext
|
||||
|
|
|
|||
64
features/backend/product_association.feature
Normal file
64
features/backend/product_association.feature
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
@products
|
||||
Feature: Product association
|
||||
In order connect products together in many contexts
|
||||
As a store owner
|
||||
I want to be able associate product with another ones
|
||||
|
||||
Background:
|
||||
Given there is default currency configured
|
||||
And I am logged in as administrator
|
||||
And there are following options:
|
||||
| name | presentation | values |
|
||||
| T-Shirt color | Color | Red, Blue, Green |
|
||||
| T-Shirt size | Size | S, M, L |
|
||||
And there are following attributes:
|
||||
| name | presentation | type | choices |
|
||||
| T-Shirt fabric | T-Shirt | text | |
|
||||
| T-Shirt fare trade | Faretrade product | checkbox | |
|
||||
| Color | color | choice | red, blue |
|
||||
| Size | size | number | |
|
||||
And the following products exist:
|
||||
| name | price | options | attributes |
|
||||
| Super T-Shirt | 19.99 | T-Shirt size, T-Shirt color | T-Shirt fabric: Wool |
|
||||
| Black T-Shirt | 19.99 | T-Shirt size | T-Shirt fabric: Cotton |
|
||||
| Mug | 5.99 | | |
|
||||
| Sticker | 10.00 | | |
|
||||
And product "Super T-Shirt" is available in all variations
|
||||
And there are following tax categories:
|
||||
| name |
|
||||
| Clothing |
|
||||
| Electronics |
|
||||
| Print |
|
||||
And there are following taxonomies defined:
|
||||
| name |
|
||||
| Category |
|
||||
| Special |
|
||||
And taxonomy "Category" has following taxons:
|
||||
| Clothing > T-Shirts |
|
||||
| Clothing > Premium T-Shirts |
|
||||
And taxonomy "Special" has following taxons:
|
||||
| Featured |
|
||||
| New |
|
||||
|
||||
|
||||
@wip
|
||||
Scenario: Create association type
|
||||
Given I want to create new association type
|
||||
When I create "Cross sell" association type
|
||||
Then I should be able to add "Cross sell" associations to any product
|
||||
|
||||
@wip
|
||||
Scenario Outline: Associate product with others products
|
||||
Given there are following association types:
|
||||
| name |
|
||||
| Cross sell |
|
||||
| Upsell |
|
||||
| Different |
|
||||
And I want to assign new association for "<Product name>" product
|
||||
When I select "<Associated product name>" product as "<Association type>" association
|
||||
Then I should see that "<Product name>" is connected with "<Associated product name>" by "<Association type>" association
|
||||
|
||||
Examples:
|
||||
| Product name | Associated product name | Association type |
|
||||
| Black T-Shirt | Super T-Shirt | Cross sell |
|
||||
| Black T-Shirt | Mug | Different |
|
||||
|
|
@ -85,6 +85,7 @@ sylius_taxonomy:
|
|||
model: Sylius\Component\Core\Model\Taxon
|
||||
form:
|
||||
default: Sylius\Bundle\CoreBundle\Form\Type\TaxonType
|
||||
association_type: ~
|
||||
|
||||
sylius_product:
|
||||
resources:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
<?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\Bundle\ProductBundle\Behat;
|
||||
|
||||
use Behat\Behat\Tester\Exception\PendingException;
|
||||
use Behat\Gherkin\Node\TableNode;
|
||||
use Sylius\Bundle\ResourceBundle\Behat\DefaultContext;
|
||||
|
||||
class ProductAssociationContext extends DefaultContext
|
||||
{
|
||||
/**
|
||||
* @When I create :arg1 association type
|
||||
*/
|
||||
public function iCreateAssociationType($typeName)
|
||||
{
|
||||
$this->fillField('Name', $typeName);
|
||||
$this->pressButton('Create');
|
||||
$this->assertSession()->pageTextContains('Association type has been successfully created.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be able to add :arg1 associations to any product
|
||||
*/
|
||||
public function iShouldBeAbleToAddAssociationsToAnyProduct($typeName)
|
||||
{
|
||||
$this->getSession()->visit($this->generatePageUrl('product index'));
|
||||
$this->getSession()->getPage()->clickLink('edit');
|
||||
$this->assertSession()->pageTextContains('Editing product');
|
||||
|
||||
throw new \Exception();
|
||||
//$this->assertSession()->elementContains('css', '')
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given there are following association types:
|
||||
*/
|
||||
public function thereAreFollowingAssociationTypes(TableNode $table)
|
||||
{
|
||||
throw new PendingException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given I want to assign new association for :arg1 product
|
||||
*/
|
||||
public function iWantToAssignNewAssociationForProduct($arg1)
|
||||
{
|
||||
throw new PendingException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I select :arg1 product as :arg2 association
|
||||
*/
|
||||
public function iSelectProductAsAssociation($arg1, $arg2)
|
||||
{
|
||||
throw new PendingException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should see that :arg1 is connected with :arg2 by :arg3 association
|
||||
*/
|
||||
public function iShouldSeeThatIsConnectedWithByAssociation($arg1, $arg2, $arg3)
|
||||
{
|
||||
throw new PendingException();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
<?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\Bundle\ProductBundle\Controller;
|
||||
|
||||
use Sylius\Bundle\ResourceBundle\Controller\ResourceController;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Association type controller.
|
||||
*
|
||||
* @author Leszek Prabucki <leszek.prabucki@gmail.com>
|
||||
*/
|
||||
class AssociationTypeController extends ResourceController
|
||||
{
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* @return RedirectResponse|Response
|
||||
*/
|
||||
public function createAction(Request $request)
|
||||
{
|
||||
$form = $this->getForm();
|
||||
|
||||
if ($form->handleRequest($request)->isValid()) {
|
||||
$resource = $this->domainManager->create($form->getData());
|
||||
|
||||
if ($this->config->isApiRequest()) {
|
||||
return $this->handleView($this->view($resource, 201));
|
||||
}
|
||||
|
||||
if (null === $resource) {
|
||||
return $this->redirectHandler->redirectToIndex();
|
||||
}
|
||||
|
||||
return $this->redirectHandler->redirectTo($resource);
|
||||
}
|
||||
|
||||
if ($this->config->isApiRequest()) {
|
||||
return $this->handleView($this->view($form));
|
||||
}
|
||||
|
||||
$view = $this
|
||||
->view()
|
||||
->setTemplate($this->config->getTemplate('create.html'))
|
||||
->setData(array(
|
||||
$this->config->getResourceName() => null,
|
||||
'form' => $form->createView()
|
||||
))
|
||||
;
|
||||
|
||||
return $this->handleView($view);
|
||||
}
|
||||
}
|
||||
|
|
@ -131,6 +131,15 @@ class Configuration implements ConfigurationInterface
|
|||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('association_type')
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->scalarNode('model')->defaultValue('Sylius\Component\Product\Model\AssociationType')->end()
|
||||
->scalarNode('controller')->defaultValue('Sylius\Bundle\ProductBundle\Controller\AssociationTypeController')->end()
|
||||
->scalarNode('repository')->defaultValue('Sylius\Bundle\ProductBundle\Doctrine\ORM\AssociationTypeRepository')->end()
|
||||
->scalarNode('form')->defaultValue('Sylius\Bundle\ProductBundle\Form\Type\AssociationTypeType')->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
<?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\Bundle\ProductBundle\Doctrine\ORM;
|
||||
|
||||
use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository;
|
||||
|
||||
/**
|
||||
* Default association type repository.
|
||||
*
|
||||
* @author Paweł Jędrzejewski <pawel@sylius.org>
|
||||
*/
|
||||
class AssociationTypeRepository extends EntityRepository
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getAlias()
|
||||
{
|
||||
return 'association_type';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
<?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\Bundle\ProductBundle\Form\Type;
|
||||
|
||||
use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
|
||||
|
||||
/**
|
||||
* Association type form type.
|
||||
*
|
||||
* @author Leszek Prabucki <leszek.prabucki@gmail.com>
|
||||
*/
|
||||
class AssociationTypeType extends AbstractResourceType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->add('name', 'text', array(
|
||||
'label' => 'sylius.form.association_type.name',
|
||||
'required' => true
|
||||
))
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'sylius_association_type';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setDefaultOptions(OptionsResolverInterface $resolver)
|
||||
{
|
||||
parent::setDefaultOptions($resolver);
|
||||
|
||||
$resolver->replaceDefaults([
|
||||
'empty_data' => function (FormInterface $form) {
|
||||
return new $this->dataClass($form->get('name')->getData());
|
||||
}
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:gedmo="http://gediminasm.org/schemas/orm/doctrine-extensions-mapping">
|
||||
|
||||
<mapped-superclass name="Sylius\Component\Product\Model\AssociationType" table="sylius_association_type">
|
||||
<id name="id" column="id" type="integer">
|
||||
<generator strategy="AUTO" />
|
||||
</id>
|
||||
|
||||
<field name="name" column="name" type="string">
|
||||
<gedmo:versioned />
|
||||
</field>
|
||||
|
||||
<field name="createdAt" column="created_at" type="datetime">
|
||||
<gedmo:timestampable on="create"/>
|
||||
</field>
|
||||
<field name="updatedAt" column="updated_at" type="datetime" nullable="true">
|
||||
<gedmo:timestampable on="update"/>
|
||||
</field>
|
||||
<field name="deletedAt" column="deleted_at" type="datetime" nullable="true" />
|
||||
|
||||
<gedmo:soft-deleteable field-name="deletedAt" />
|
||||
<gedmo:loggable />
|
||||
</mapped-superclass>
|
||||
|
||||
</doctrine-mapping>
|
||||
|
|
@ -10,3 +10,13 @@ sylius:
|
|||
meta_description: Metadescripció
|
||||
product_archetype:
|
||||
name: Nom
|
||||
form:
|
||||
product:
|
||||
name: Nom
|
||||
description: Descripció
|
||||
meta_keywords: Meta keywords
|
||||
meta_description: Metadescripció
|
||||
prototype:
|
||||
name: Nom
|
||||
association_type:
|
||||
name: Nom
|
||||
|
|
|
|||
|
|
@ -99,6 +99,14 @@ class WebContext extends BaseWebContext implements SnippetAcceptingContext
|
|||
$this->getSession()->visit($this->generateUrl('sylius_homepage'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given I want to create new association type
|
||||
*/
|
||||
public function iWantToCreateNewAssociationType()
|
||||
{
|
||||
$this->iAmOnThePage('association type creation');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^I am on my account homepage$/
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -154,6 +154,16 @@ class BackendMenuBuilder extends MenuBuilder
|
|||
if (!$child->hasChildren()) {
|
||||
$menu->removeChild('assortment');
|
||||
}
|
||||
|
||||
$child->addChild('prototypes', array(
|
||||
'route' => 'sylius_backend_product_prototype_index',
|
||||
'labelAttributes' => array('icon' => 'glyphicon glyphicon-compressed'),
|
||||
))->setLabel($this->translate(sprintf('sylius.backend.menu.%s.prototypes', $section)));
|
||||
|
||||
$child->addChild('association types', array(
|
||||
'route' => 'sylius_backend_association_type_index',
|
||||
'labelAttributes' => array('icon' => 'glyphicon glyphicon-th-list'),
|
||||
))->setLabel($this->translate(sprintf('sylius.backend.menu.%s.association_type', $section)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
# This file is part of the Sylius package.
|
||||
# (c) Paweł Jędrzejewski
|
||||
|
||||
sylius_backend_association_type_index:
|
||||
path: /
|
||||
methods: [GET]
|
||||
defaults:
|
||||
_controller: sylius.controller.association_type:indexAction
|
||||
_sylius:
|
||||
template: SyliusWebBundle:Backend/AssociationType:index.html.twig
|
||||
sortable: true
|
||||
sorting:
|
||||
name: desc
|
||||
|
||||
sylius_backend_association_type_create:
|
||||
path: /new
|
||||
methods: [GET, POST]
|
||||
defaults:
|
||||
_controller: sylius.controller.association_type:createAction
|
||||
_sylius:
|
||||
template: SyliusWebBundle:Backend/AssociationType:create.html.twig
|
||||
redirect: sylius_backend_association_type_index
|
||||
|
||||
sylius_backend_association_type_update:
|
||||
path: /{id}/edit
|
||||
methods: [GET, PUT]
|
||||
defaults:
|
||||
_controller: sylius.controller.association_type:updateAction
|
||||
_sylius:
|
||||
template: SyliusWebBundle:Backend/AssociationType:update.html.twig
|
||||
redirect: sylius_backend_association_type_index
|
||||
|
||||
sylius_backend_association_type_delete:
|
||||
path: /{id}
|
||||
methods: [DELETE]
|
||||
defaults:
|
||||
_controller: sylius.controller.association_type:deleteAction
|
||||
_sylius:
|
||||
template: SyliusWebBundle:Backend/Misc:delete.html.twig
|
||||
redirect: sylius_backend_association_type_index
|
||||
|
|
@ -24,6 +24,10 @@ sylius_backend_product:
|
|||
resource: @SyliusWebBundle/Resources/config/routing/backend/product.yml
|
||||
prefix: /products
|
||||
|
||||
sylius_backend_association_type:
|
||||
resource: @SyliusWebBundle/Resources/config/routing/backend/association_type.yml
|
||||
prefix: /association-types
|
||||
|
||||
sylius_backend_inventory:
|
||||
resource: @SyliusWebBundle/Resources/config/routing/backend/inventory.yml
|
||||
prefix: /inventory
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ sylius:
|
|||
archetypes: Product archetypes
|
||||
assortment: Assortment
|
||||
attributes: Configure attributes
|
||||
association_type: Association Types
|
||||
blocks: Blocks
|
||||
channels: Channels
|
||||
configuration: Configuration
|
||||
|
|
|
|||
|
|
@ -47,6 +47,13 @@ sylius:
|
|||
tracking_number: Tracking number
|
||||
title: My account
|
||||
add_to_cart: Add to cart
|
||||
association_type:
|
||||
index_header: Association Types
|
||||
update_header: Edit Association Type
|
||||
create_header: Create Association Type
|
||||
name: Name
|
||||
create: Create
|
||||
update: Update
|
||||
address:
|
||||
city: City
|
||||
company: Company
|
||||
|
|
@ -225,6 +232,8 @@ sylius:
|
|||
index: Permissions
|
||||
product:
|
||||
index: Products
|
||||
association_type:
|
||||
index: Association Types
|
||||
promotion:
|
||||
index: Promotions
|
||||
promotion_coupon:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
{% form_theme form 'SyliusWebBundle::forms.html.twig' %}
|
||||
|
||||
<fieldset>
|
||||
{{ form_row(form.name, {'attr': {'class': 'input-lg'}}) }}
|
||||
</fieldset>
|
||||
|
||||
{{ form_widget(form._token) }}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
{% extends 'SyliusWebBundle:Backend:layout.html.twig' %}
|
||||
|
||||
{% from 'SyliusResourceBundle:Macros:actions.html.twig' import create %}
|
||||
|
||||
{% block topbar %}
|
||||
<ol class="breadcrumb">
|
||||
<li>{{ 'sylius.breadcrumb.assortment'|trans }}</li>
|
||||
<li><a href="{{ path('sylius_backend_association_type_index') }}">{{ 'sylius.breadcrumb.association_type.index'|trans }}</a></li>
|
||||
<li>{{ 'sylius.breadcrumb.new'|trans }}</li>
|
||||
</ol>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header">
|
||||
<h1><i class="glyphicon glyphicon-plus-sign"></i> {{ 'sylius.association_type.create_header'|trans|raw }}</h1>
|
||||
</div>
|
||||
|
||||
{{ form_errors(form) }}
|
||||
|
||||
<form action="{{ path('sylius_backend_association_type_create') }}" method="post" class="form-horizontal" novalidate>
|
||||
{% include 'SyliusWebBundle:Backend/AssociationType:_form.html.twig' %}
|
||||
{{ create() }}
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
{% extends 'SyliusWebBundle:Backend:layout.html.twig' %}
|
||||
|
||||
{% import 'SyliusResourceBundle:Macros:buttons.html.twig' as buttons %}
|
||||
{% from 'SyliusWebBundle:Backend/Macros:misc.html.twig' import pagination %}
|
||||
{% from 'SyliusWebBundle:Backend/AssociationType:macros.html.twig' import list %}
|
||||
|
||||
{% block topbar %}
|
||||
<ol class="breadcrumb">
|
||||
<li>{{ 'sylius.breadcrumb.assortment'|trans }}</li>
|
||||
<li>{{ 'sylius.breadcrumb.association_type.index'|trans }}</li>
|
||||
</ol>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header">
|
||||
<div class="actions-menu">
|
||||
{{ buttons.create(path('sylius_backend_association_type_create'), 'sylius.association_type.create'|trans) }}
|
||||
</div>
|
||||
<h1><i class="glyphicon glyphicon-list-alt"></i> {{ 'sylius.association_type.index_header'|trans|raw }}</h1>
|
||||
</div>
|
||||
|
||||
{{ pagination(association_types) }}
|
||||
{{ list(association_types) }}
|
||||
{{ pagination(association_types) }}
|
||||
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
{% macro list(association_types) %}
|
||||
|
||||
{% import 'SyliusResourceBundle:Macros:buttons.html.twig' as buttons %}
|
||||
{% import 'SyliusWebBundle:Backend/Macros:alerts.html.twig' as alerts %}
|
||||
|
||||
{% if association_types|length > 0 %}
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ sylius_resource_sort('id', '#id') }}</th>
|
||||
<th>{{ sylius_resource_sort('name', 'sylius.association_type.name'|trans) }}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for type in association_types %}
|
||||
<tr id="{{ type.id }}">
|
||||
<td>{{ type.id }}</td>
|
||||
<td>{{ type.name }}</td>
|
||||
<td class="center-text">
|
||||
{{ buttons.edit(path('sylius_backend_association_type_update', {'id': type.id})) }}
|
||||
{{ buttons.delete(path('sylius_backend_association_type_delete', {'id': type.id})) }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
{{ alerts.info('sylius.product_attribute.no_results'|trans) }}
|
||||
{% endif %}
|
||||
|
||||
{% endmacro %}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
{% extends 'SyliusWebBundle:Backend:layout.html.twig' %}
|
||||
|
||||
{% from 'SyliusResourceBundle:Macros:actions.html.twig' import update %}
|
||||
|
||||
|
||||
{% block topbar %}
|
||||
<ol class="breadcrumb">
|
||||
<li>{{ 'sylius.breadcrumb.assortment'|trans }}</li>
|
||||
<li><a href="{{ path('sylius_backend_association_type_index') }}">{{ 'sylius.breadcrumb.association_type.index'|trans }}</a></li>
|
||||
<li>{{ association_type.name }}</li>
|
||||
<li>{{ 'sylius.breadcrumb.edit'|trans }}</li>
|
||||
</ol>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header">
|
||||
<h1><i class="glyphicon glyphicon-pencil"></i> {{ 'sylius.association_type.update_header'|trans|raw }}</h1>
|
||||
</div>
|
||||
|
||||
{{ form_errors(form) }}
|
||||
|
||||
<form action="{{ path('sylius_backend_association_type_update', {'id': association_type.id}) }}" method="post" class="form-horizontal" novalidate>
|
||||
<input type="hidden" name="_method" value="PUT">
|
||||
{% include 'SyliusWebBundle:Backend/AssociationType:_form.html.twig' %}
|
||||
{{ update() }}
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
{% form_theme form 'SyliusWebBundle::forms.html.twig' %}
|
||||
|
||||
<div class="tab-pane" id="associations">
|
||||
<div id="sylius-assortment-product-attributes" class="collection-container" data-prototype="{{ ('<div id="sylius_product_attributes___name__">' ~ form_row(form.attributes.vars.prototype.attribute, {'attr': {'class': 'attribute-chooser'}}))|e }}{{ (form_row(form.attributes.vars.prototype.value) ~ '</div>')|e }}">
|
||||
{% for attributeForm in form.attributes %}
|
||||
<div class="sylius-assortment-product-attributes-attribute row">
|
||||
<div class="col-md-10">
|
||||
{{ form_widget(attributeForm) }}
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<a href="#" class="btn btn-danger" data-collection-button="delete" data-collection="sylius-assortment-product-attributes" data-collection-item="attribute"><i class="glyphicon glyphicon-trash"></i> {{ 'sylius.product.remove_attribute'|trans }}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% for key, prototype in form.attributes.vars.prototype.vars.prototypes %}
|
||||
<div id="attribute-prototype_{{ key }}" class="attribute-prototypes" data-prototype="{{ form_widget(prototype)|e }}"></div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<a href="#" class="btn btn-success btn-block" data-collection-button="add" data-prototype="sylius-assortment-product-attributes" data-collection="sylius-assortment-product-attributes">
|
||||
{{ 'sylius.product.add_attribute'|trans }}
|
||||
</a>
|
||||
</div>
|
||||
42
src/Sylius/Component/Product/Model/Association.php
Normal file
42
src/Sylius/Component/Product/Model/Association.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?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\Component\Product\Model;
|
||||
|
||||
/**
|
||||
* Abstract association class
|
||||
*
|
||||
* @author Leszek Prabucki <leszek.prabucki@gmail.com>
|
||||
*/
|
||||
abstract class Association
|
||||
{
|
||||
/**
|
||||
* @var mixed $id
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var AssociationType
|
||||
*/
|
||||
private $type;
|
||||
|
||||
public function __construct(AssociationType $type)
|
||||
{
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
abstract function getAssociatedObject();
|
||||
}
|
||||
78
src/Sylius/Component/Product/Model/AssociationType.php
Normal file
78
src/Sylius/Component/Product/Model/AssociationType.php
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
<?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\Component\Product\Model;
|
||||
|
||||
/**
|
||||
* AssociationType model.
|
||||
*
|
||||
* @author Leszek Prabucki <leszek.prabucki@gmail.com>
|
||||
*/
|
||||
class AssociationType
|
||||
{
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $createdAt;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $updatedAt;
|
||||
|
||||
/**
|
||||
* @var null|\DateTime
|
||||
*/
|
||||
protected $deletedAt;
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*/
|
||||
public function __construct($name)
|
||||
{
|
||||
$this->setName($name);
|
||||
$this->createdAt = new \DateTime();
|
||||
$this->updatedAt = new \DateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setName($name)
|
||||
{
|
||||
if (null === $name || !trim(str_replace(["\n", "\t"], '', $name))) {
|
||||
throw new \InvalidArgumentException('Association type name cannot be empty.');
|
||||
}
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
}
|
||||
|
|
@ -55,6 +55,13 @@ class Product extends AbstractTranslatable implements ProductInterface
|
|||
*/
|
||||
protected $attributes;
|
||||
|
||||
/**
|
||||
* Product options.
|
||||
*
|
||||
* @var Collection|Association[]
|
||||
*/
|
||||
protected $associations;
|
||||
|
||||
/**
|
||||
* @var Collection|BaseVariantInterface[]
|
||||
*/
|
||||
|
|
@ -71,6 +78,7 @@ class Product extends AbstractTranslatable implements ProductInterface
|
|||
|
||||
$this->availableOn = new \DateTime();
|
||||
$this->attributes = new ArrayCollection();
|
||||
$this->associations = new ArrayCollection();
|
||||
$this->variants = new ArrayCollection();
|
||||
$this->options = new ArrayCollection();
|
||||
$this->createdAt = new \DateTime();
|
||||
|
|
@ -458,4 +466,23 @@ class Product extends AbstractTranslatable implements ProductInterface
|
|||
|
||||
$this->deletedAt = $deletedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Association $association
|
||||
* @return
|
||||
*/
|
||||
public function addAssociation(Association $association)
|
||||
{
|
||||
$this->associations[] = $association;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Association[]
|
||||
*/
|
||||
public function getAssociations()
|
||||
{
|
||||
return $this->associations->toArray();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
43
src/Sylius/Component/Product/Model/ProductAssociation.php
Normal file
43
src/Sylius/Component/Product/Model/ProductAssociation.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?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\Component\Product\Model;
|
||||
|
||||
/**
|
||||
* ProductAssociation model.
|
||||
*
|
||||
* @author Leszek Prabucki <leszek.prabucki@gmail.com>
|
||||
*/
|
||||
class ProductAssociation extends Association
|
||||
{
|
||||
/**
|
||||
* @var ProductInterface
|
||||
*/
|
||||
private $product;
|
||||
|
||||
/**
|
||||
* @param ProductInterface $product
|
||||
* @param AssociationType $type
|
||||
*/
|
||||
public function __construct(ProductInterface $product, AssociationType $type)
|
||||
{
|
||||
parent::__construct($type);
|
||||
$this->product = $product;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ProductInterface
|
||||
*/
|
||||
final public function getAssociatedObject()
|
||||
{
|
||||
return $this->product;
|
||||
}
|
||||
}
|
||||
|
|
@ -53,4 +53,46 @@ interface ProductInterface extends
|
|||
* @param null|\DateTime $availableUntil
|
||||
*/
|
||||
public function setAvailableUntil(\DateTime $availableUntil = null);
|
||||
|
||||
/**
|
||||
* Get meta keywords.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMetaKeywords();
|
||||
|
||||
/**
|
||||
* Set meta keywords for the product.
|
||||
*
|
||||
* @param string $metaKeywords
|
||||
*/
|
||||
public function setMetaKeywords($metaKeywords);
|
||||
|
||||
/**
|
||||
* Get meta description.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMetaDescription();
|
||||
|
||||
/**
|
||||
* Set meta description for the product.
|
||||
*
|
||||
* @param string $metaDescription
|
||||
*/
|
||||
public function setMetaDescription($metaDescription);
|
||||
|
||||
/**
|
||||
* Add association to product
|
||||
*
|
||||
* @param Association $association
|
||||
*/
|
||||
public function addAssociation(Association $association);
|
||||
|
||||
/**
|
||||
* Get associations
|
||||
*
|
||||
* @param Association[] $association
|
||||
*/
|
||||
public function getAssociations();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ use Doctrine\Common\Collections\Collection;
|
|||
use PhpSpec\ObjectBehavior;
|
||||
use Sylius\Component\Resource\Model\ToggleableInterface;
|
||||
use Sylius\Component\Product\Model\ArchetypeInterface;
|
||||
use Sylius\Component\Product\Model\Association;
|
||||
use Sylius\Component\Product\Model\AttributeValueInterface;
|
||||
use Sylius\Component\Product\Model\OptionInterface;
|
||||
use Sylius\Component\Product\Model\ProductInterface;
|
||||
|
|
@ -308,4 +309,17 @@ class ProductSpec extends ObjectBehavior
|
|||
$this->enable();
|
||||
$this->shouldBeEnabled();
|
||||
}
|
||||
|
||||
function it_allows_to_add_assoication(Association $association1, Association $association2)
|
||||
{
|
||||
$this
|
||||
->addAssociation($association1)
|
||||
->addAssociation($association2)
|
||||
;
|
||||
|
||||
$this->getAssociations()->shouldReturn([
|
||||
$association1,
|
||||
$association2
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
<?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 spec\Sylius\Component\Product\Model;
|
||||
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Prophecy\Argument;
|
||||
|
||||
class AssociationTypeSpec extends ObjectBehavior
|
||||
{
|
||||
function let()
|
||||
{
|
||||
$this->beConstructedWith('Cross sell');
|
||||
}
|
||||
|
||||
function it_has_name()
|
||||
{
|
||||
$this->getName()->shouldBe('Cross sell');
|
||||
}
|
||||
|
||||
function it_needs_to_have_mutable_name_sadly()
|
||||
{
|
||||
$this->setName('Changed name')->shouldBe($this);
|
||||
$this->getName()->shouldBe('Changed name');
|
||||
}
|
||||
|
||||
function it_cannot_be_created_with_empty_name()
|
||||
{
|
||||
$this->shouldThrow('\InvalidArgumentException')->during('__construct', ['']);
|
||||
$this->shouldThrow('\InvalidArgumentException')->during('__construct', [' ']);
|
||||
$this->shouldThrow('\InvalidArgumentException')->during('__construct', ["\n"]);
|
||||
$this->shouldThrow('\InvalidArgumentException')->during('__construct', ["\t"]);
|
||||
$this->shouldThrow('\InvalidArgumentException')->during('__construct', [null]);
|
||||
}
|
||||
|
||||
function it_does_not_allow_to_change_name_to_empty_one()
|
||||
{
|
||||
$this->shouldThrow('\InvalidArgumentException')->during('setName', ['']);
|
||||
$this->shouldThrow('\InvalidArgumentException')->during('setName', [' ']);
|
||||
$this->shouldThrow('\InvalidArgumentException')->during('setName', ["\n"]);
|
||||
$this->shouldThrow('\InvalidArgumentException')->during('setName', ["\t"]);
|
||||
$this->shouldThrow('\InvalidArgumentException')->during('setName', [null]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<?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 spec\Sylius\Component\Product\Model;
|
||||
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Prophecy\Argument;
|
||||
use Sylius\Component\Product\Model\AssociationType;
|
||||
use Sylius\Component\Product\Model\ProductInterface;
|
||||
|
||||
class ProductAssociationSpec extends ObjectBehavior
|
||||
{
|
||||
function let(ProductInterface $product, AssociationType $associationType)
|
||||
{
|
||||
$this->beConstructedWith($product, $associationType);
|
||||
}
|
||||
|
||||
function it_is_association()
|
||||
{
|
||||
$this->shouldHaveType('Sylius\Component\Product\Model\Association');
|
||||
}
|
||||
|
||||
function it_allows_to_get_associated_product(ProductInterface $product)
|
||||
{
|
||||
$this->getAssociatedObject()->shouldBe($product);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue