├── README.md
├── modman
├── phpunit.xml.dist
└── src
├── app
├── code
│ └── community
│ │ └── Hackathon
│ │ └── DerivedAttributes
│ │ ├── Block
│ │ └── Adminhtml
│ │ │ ├── Entity.php
│ │ │ ├── Entity
│ │ │ ├── Customer
│ │ │ │ └── Grid.php
│ │ │ ├── Form.php
│ │ │ ├── Massaction.php
│ │ │ ├── Product
│ │ │ │ └── Grid.php
│ │ │ └── Tabs.php
│ │ │ ├── Rule.php
│ │ │ └── Rule
│ │ │ ├── Edit.php
│ │ │ ├── Edit
│ │ │ └── Form.php
│ │ │ └── Grid.php
│ │ ├── Helper
│ │ ├── Data.php
│ │ └── Rule
│ │ │ └── Director.php
│ │ ├── Model
│ │ ├── Bridge
│ │ │ ├── Entity.php
│ │ │ ├── EntityIterator.php
│ │ │ ├── RuleLogger.php
│ │ │ └── RuleRepository.php
│ │ ├── Massupdater.php
│ │ ├── Observer.php
│ │ ├── Resource
│ │ │ ├── Rule.php
│ │ │ └── Rule
│ │ │ │ └── Collection.php
│ │ ├── Rule.php
│ │ └── Source
│ │ │ ├── Abstract.php
│ │ │ ├── Attribute.php
│ │ │ ├── Condition.php
│ │ │ └── Generator.php
│ │ ├── Test
│ │ ├── Block
│ │ │ ├── Entity.php
│ │ │ └── Rule.php
│ │ ├── Case
│ │ │ └── Controller
│ │ │ │ └── Dom.php
│ │ ├── Constraint
│ │ │ └── DomQuery.php
│ │ ├── Controller
│ │ │ ├── EntityController.php
│ │ │ └── RuleController.php
│ │ ├── Model
│ │ │ ├── Bridge
│ │ │ │ ├── Entity.php
│ │ │ │ └── RuleRepository.php
│ │ │ ├── Massupdater.php
│ │ │ ├── Massupdater
│ │ │ │ └── providers
│ │ │ │ │ └── testProductMassUpdateByStoreView.yaml
│ │ │ ├── Rule.php
│ │ │ └── Rule
│ │ │ │ └── providers
│ │ │ │ └── rulesShouldBeAppliedOnModelSave.yaml
│ │ ├── expectations
│ │ │ ├── attributes.yaml
│ │ │ └── rulesets.yaml
│ │ └── fixtures
│ │ │ ├── products.yaml
│ │ │ ├── rules.yaml
│ │ │ └── stores.yaml
│ │ ├── controllers
│ │ └── Adminhtml
│ │ │ └── DerivedAttributes
│ │ │ ├── EntityController.php
│ │ │ └── RuleController.php
│ │ ├── etc
│ │ ├── adminhtml.xml
│ │ └── config.xml
│ │ └── sql
│ │ └── derivedattributes_setup
│ │ ├── mysql4-install-0.1.0.php
│ │ ├── upgrade-0.1.0-0.1.1.php
│ │ ├── upgrade-0.1.0-0.1.2.php
│ │ ├── upgrade-0.1.1-0.1.2.php
│ │ └── upgrade-0.1.2-0.1.3.php
├── design
│ └── adminhtml
│ │ └── base
│ │ └── default
│ │ └── layout
│ │ └── hackathon_derivedattributes.xml
└── etc
│ └── modules
│ └── Hackathon_DerivedAttributes.xml
└── lib
└── Hackathon
└── DerivedAttributes
├── Attribute.php
├── BridgeInterface
├── EntityInterface.php
├── EntityIteratorInterface.php
├── RuleLoggerInterface.php
└── RuleRepositoryInterface.php
├── Rule.php
├── RuleBuilder.php
├── RuleSet.php
├── RuleSetsByStore.php
├── Service
├── Condition
│ ├── AlwaysCondition.php
│ └── BooleanAttributeCondition.php
├── Generator
│ └── TemplateGenerator.php
└── Manager.php
├── ServiceInterface
├── ConditionInterface.php
├── FilterInterface.php
└── GeneratorInterface.php
├── Store.php
├── StoreSet.php
└── Updater.php
/README.md:
--------------------------------------------------------------------------------
1 | Status: WIP
2 |
3 | Build status (master): [](https://travis-ci.org/magento-hackathon/DerivedAttributes)
4 |
5 | DerivedAttributes (e.g. for product feed export)
6 | ================================================
7 |
8 | (Product Attribute, Customer Attributes)
9 |
10 | Installation Instructions
11 | -------------------------
12 |
13 | ### Option 1: Via Composer
14 | 1. Add required repositories to project `composer.json`:
15 |
16 | "repositories": [
17 | {
18 | "type": "vcs",
19 | "url": "https://github.com/magento-hackathon/DerivedAttributes.git"
20 | },
21 | {
22 | "type": "vcs",
23 | "url": "https://github.com/integer-net/IntegerNet_GridMassActionPager"
24 | }
25 | ]
26 |
27 | 2. Install via composer: `composer require magento-hackathon/derived-attributes`
28 |
29 | ### Option 2: Manually
30 | 1. Download hackathon-derivedattributes.tar.gz or hackathon-derivedattributes.zip from the [Releases page](https://github.com/magento-hackathon/DerivedAttributes/releases)
31 | 2. Extract contained directory into your Magento installation. It contains all dependencies, no additional downloads necessary.
32 |
33 |
34 | Add custom conditions and generators
35 | -----------
36 |
37 | Custom conditions and generators must implement the interfaces listed below.
38 | To add them, create an observer for the `derivedattribute_new_rulemanager` event:
39 |
40 |
41 |
42 |
43 |
44 | singleton
45 | Your_Module_Model_Observer
46 | addDerivedAttributesPlugin
47 |
48 |
49 |
50 |
51 |
52 | In this observer:
53 |
54 | class Your_Module_Model_Observer
55 | {
56 | public function addDerivedAttributesPlugin(Varien_Event_Observer $observer)
57 | {
58 | /** @var \Hackathon\DerivedAttributes\Service\Manager $ruleManager */
59 | $ruleManager = $observer->getRuleManager();
60 | $ruleManager->addConditionType('your_unique_condition_identifier', Mage::getModel('your_module/your_condition_class'));
61 | $ruleManager->addGeneratorType('your_unique_generator_identifier', Mage::getModel('your_module/your_generator_class'));
62 | }
63 | }
64 |
65 | ## Interfaces
66 |
67 | ### GeneratorInterface
68 |
69 | GeneratorInterface configure(string $data)
70 | string getData()
71 | mixed generateAttributeValue(EntityInterface $entity)
72 | string getTitle()
73 | string getDescription()
74 |
75 | ### ConditionInterface (ex. impl. BooleanAttributeCondition)
76 |
77 | ConditionInterface configure(string $data)
78 | string getData()
79 | boolean match(EntityInterface $entity)
80 | string getTitle()
81 | string getDescription()
82 |
83 |
84 |
85 | Transitivity of DerivedAttributes
86 | ---
87 | There is NO magic for "derived of derivedAttributes". Just rule priority.
88 |
89 |
90 | License
91 | -------
92 | [OSL - Open Software Licence 3.0](http://opensource.org/licenses/osl-3.0.php)
93 |
--------------------------------------------------------------------------------
/modman:
--------------------------------------------------------------------------------
1 | src/app/code/community/Hackathon/DerivedAttributes app/code/community/Hackathon/DerivedAttributes
2 | src/app/etc/modules/Hackathon_DerivedAttributes.xml app/etc/modules/Hackathon_DerivedAttributes.xml
3 | src/lib/Hackathon/DerivedAttributes lib/Hackathon/DerivedAttributes
4 | src/app/design/adminhtml/base/default/layout/hackathon_derivedattributes.xml app/design/adminhtml/base/default/layout/hackathon_derivedattributes.xml
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 | ./test/
16 |
17 |
18 |
19 |
20 |
21 | ./src/
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Block/Adminhtml/Entity.php:
--------------------------------------------------------------------------------
1 | _objectId = 'entity_id';
7 | $this->_controller = 'adminhtml_entity';
8 | $this->_headerText = $this->__('Apply Rules');
9 |
10 | parent::__construct();
11 |
12 | $this->removeButton('reset')
13 | ->removeButton('delete')
14 | ->removeButton('save');
15 | }
16 |
17 | public function getBackUrl()
18 | {
19 | return $this->getUrl('*/derivedAttributes_rule/index');
20 | }
21 |
22 |
23 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Block/Adminhtml/Entity/Customer/Grid.php:
--------------------------------------------------------------------------------
1 | <@fschmengler>
7 | * @category Hackathon
8 | * @package Hackathon_DerivedAttributes
9 | */
10 |
11 | /**
12 | * @package Hackathon_DerivedAttributes
13 | */
14 | class Hackathon_DerivedAttributes_Block_Adminhtml_Entity_Customer_Grid extends Mage_Adminhtml_Block_Customer_Grid
15 | {
16 | protected function _prepareMassaction()
17 | {
18 | $this->setMassactionIdField('entity_id');
19 | $this->getMassactionBlock()->setFormFieldName('entity_ids');
20 | $this->getMassactionBlock()->setUseAjax(true);
21 | $this->setNoFilterMassactionColumn(true);
22 |
23 | $this->getMassactionBlock()->addItem('apply', array(
24 | 'label'=> Mage::helper('catalog')->__('Apply Rules'),
25 | 'url' => $this->getUrl('*/*/applyRules'),
26 | 'confirm' => Mage::helper('catalog')->__('Are you sure?'),
27 | 'complete' => 'integerNetGridMassActionPager',
28 | 'additional' => $this->_getAdditionalMassactionBlock(),
29 | ));
30 |
31 | $this->getMassactionBlock()->addItem('dryrun', array(
32 | 'label'=> Mage::helper('catalog')->__('Apply Rules (dry run)'),
33 | 'url' => $this->getUrl('*/*/dryRun'),
34 | 'complete' => 'integerNetGridMassActionPager',
35 | 'additional' => $this->_getAdditionalMassactionBlock()->setDryRun(true),
36 | ));
37 |
38 | return $this;
39 | }
40 |
41 | protected function _getAdditionalMassactionBlock()
42 | {
43 | /** @var Hackathon_DerivedAttributes_Block_Adminhtml_Entity_Massaction $block */
44 | $block = $this->getLayout()->createBlock('derivedattributes/adminhtml_entity_massaction');
45 | $block->setEntityType('customer');
46 | return $block;
47 | }
48 |
49 | protected function _prepareColumns()
50 | {
51 | parent::_prepareColumns();
52 | $this->removeColumn('action');
53 | return $this;
54 | }
55 |
56 | public function getGridUrl()
57 | {
58 | return $this->getUrl('*/*/customerGrid', array('_current'=>true));
59 | }
60 |
61 | public function getRowUrl($row)
62 | {
63 | return '#';
64 | }
65 |
66 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Block/Adminhtml/Entity/Form.php:
--------------------------------------------------------------------------------
1 | setId('ruleApplyForm');
9 | }
10 |
11 | protected function _prepareForm()
12 | {
13 | $form = new Varien_Data_Form(array(
14 | 'id' => 'apply_rules_form',
15 | 'action' => $this->getData('action'),
16 | 'method' => 'post'
17 | ));
18 | $form->addFieldset('apply_rules_entities', array(
19 | 'legend' => $this->__('Select Entities')
20 | ));
21 | $fieldset = $form->addFieldset('apply_rules_stores', array(
22 | 'legend' => $this->__('Select Stores')
23 | ));
24 | if (Mage::app()->isSingleStoreMode()) {
25 | $storeId = Mage::app()->getStore(true)->getId();
26 | $fieldset->addField('store_id', 'hidden', array(
27 | 'name' => 'store_id[]',
28 | 'value' => $storeId
29 | ));
30 | //TODO make fieldset invisible?
31 | } else {
32 | $field = $fieldset->addField('store_id', 'multiselect', array(
33 | 'name' => 'store_id[]',
34 | 'label' => $this->__('Store'),
35 | 'title' => $this->__('Store'),
36 | 'required' => true,
37 | 'values' => Mage::getSingleton('adminhtml/system_store')->getStoreValuesForForm(false, true),
38 | 'onchange' => '$(\'massaction_store_id\').setValue(this.getValue())',
39 | ));
40 | $field->setValue('0');
41 | $renderer = $this->getLayout()->createBlock('adminhtml/store_switcher_form_renderer_fieldset_element');
42 | $field->setRenderer($renderer);
43 | }
44 | $this->setForm($form);
45 | return parent::_prepareForm();
46 | }
47 |
48 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Block/Adminhtml/Entity/Massaction.php:
--------------------------------------------------------------------------------
1 | false,
17 | ]);
18 | $form->addField('massaction_entity_type', 'hidden', array(
19 | 'name' => 'entity_type',
20 | 'value' => $this->getEntityType(),
21 | 'no_span' => true,
22 | ));
23 | $form->addField('massaction_store_id', 'multiselect', array(
24 | 'name' => 'store_id[]',
25 | 'required' => true,
26 | 'values' => Mage::getSingleton('adminhtml/system_store')->getStoreValuesForForm(false, true),
27 | 'no_span' => true,
28 | 'style' => 'display:none',
29 | ));
30 | if ($this->getDryRun()) {
31 | $form->addField('massaction_dry_run', 'hidden', array(
32 | 'name' => 'dry_run',
33 | 'value' => '1',
34 | 'no_span' => true,
35 | ));
36 | }
37 | $massActionId = $this->getParentBlock()->getMassaction()->getHtmlId();
38 | $updateHiddenFieldJs = <<
40 | \$('{$massActionId}').observe('change', function() {
41 | \$('massaction_store_id').setValue(\$('store_id').getValue())
42 | });
43 |
44 | HTML;
45 | return $form->toHtml() . $updateHiddenFieldJs;
46 |
47 | }
48 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Block/Adminhtml/Entity/Product/Grid.php:
--------------------------------------------------------------------------------
1 | <@fschmengler>
7 | * @category Hackathon
8 | * @package Hackathon_DerivedAttributes
9 | */
10 |
11 | /**
12 | * @package Hackathon_DerivedAttributes
13 | */
14 | class Hackathon_DerivedAttributes_Block_Adminhtml_Entity_Product_Grid extends Mage_Adminhtml_Block_Catalog_Product_Grid
15 | {
16 | public function __construct()
17 | {
18 | parent::__construct();
19 | $this->setId('productDerivedAttributesGrid');
20 | }
21 |
22 |
23 | protected function _prepareMassaction()
24 | {
25 | $this->setMassactionIdField('entity_id');
26 | $this->getMassactionBlock()->setFormFieldName('entity_ids');
27 | $this->getMassactionBlock()->setUseAjax(true);
28 | $this->setNoFilterMassactionColumn(true);
29 |
30 | $this->getMassactionBlock()->addItem('apply', array(
31 | 'label'=> Mage::helper('catalog')->__('Apply Rules'),
32 | 'url' => $this->getUrl('*/*/applyRules'),
33 | 'confirm' => Mage::helper('catalog')->__('Are you sure?'),
34 | 'complete' => 'integerNetGridMassActionPager',
35 | 'additional' => $this->_getAdditionalMassactionBlock(),
36 | ));
37 |
38 | $this->getMassactionBlock()->addItem('dryrun', array(
39 | 'label'=> Mage::helper('catalog')->__('Apply Rules (dry run)'),
40 | 'url' => $this->getUrl('*/*/applyRules'),
41 | 'complete' => 'integerNetGridMassActionPager',
42 | 'additional' => $this->_getAdditionalMassactionBlock()->setDryRun(true),
43 | ));
44 |
45 | return $this;
46 | }
47 |
48 | protected function _getAdditionalMassactionBlock()
49 | {
50 | /** @var Hackathon_DerivedAttributes_Block_Adminhtml_Entity_Massaction $block */
51 | $block = $this->getLayout()->createBlock('derivedattributes/adminhtml_entity_massaction');
52 | $block->setEntityType(Mage_Catalog_Model_Product::ENTITY);
53 | return $block;
54 | }
55 |
56 | protected function _prepareColumns()
57 | {
58 | parent::_prepareColumns();
59 | $this->removeColumn('action');
60 | return $this;
61 | }
62 |
63 | public function getGridUrl()
64 | {
65 | return $this->getUrl('*/*/productGrid', array('_current'=>true));
66 | }
67 |
68 | public function getRowUrl($row)
69 | {
70 | return '#';
71 | }
72 |
73 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Block/Adminhtml/Entity/Tabs.php:
--------------------------------------------------------------------------------
1 | setId('derivedattributes_entity_tabs');
8 | $this->setDestElementId('apply_rules_entities');
9 | $this->setTitle(Mage::helper('derivedattributes')->__('Apply Rules'));
10 | }
11 |
12 | protected function _beforeToHtml()
13 | {
14 | $productGridBlock = $this->getLayout()->createBlock(
15 | 'derivedattributes/adminhtml_entity_product_grid',
16 | 'derivedattributes_entity_product_grid'
17 | );
18 | $this->addTab('product', array(
19 | 'label' => $this->__('Products'),
20 | 'content' => $productGridBlock->toHtml(),
21 | ));
22 |
23 | $customerGridBlock = $this->getLayout()->createBlock(
24 | 'derivedattributes/adminhtml_entity_customer_grid',
25 | 'derivedattributes_entity_customer_grid'
26 | );
27 | $this->addTab('customers', array(
28 | 'label' => Mage::helper('customer')->__('Customers'),
29 | 'content' => $customerGridBlock->toHtml(),
30 | ));
31 |
32 | $this->_updateActiveTab();
33 | return parent::_beforeToHtml();
34 | }
35 |
36 | protected function _updateActiveTab()
37 | {
38 | $tabId = $this->getRequest()->getParam('tab');
39 | if( $tabId ) {
40 | $tabId = preg_replace("#{$this->getId()}_#", '', $tabId);
41 | if($tabId) {
42 | $this->setActiveTab($tabId);
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Block/Adminhtml/Rule.php:
--------------------------------------------------------------------------------
1 | _blockGroup = 'derivedattributes';
7 | $this->_controller = 'adminhtml_rule';
8 | $this->_headerText = $this->__('Derived Attribute Rules');
9 | $this->_addButtonLabel = $this->__('Add New Rule');
10 | parent::__construct();
11 |
12 | $this->addButton('derivedattributes_rule_apply', array(
13 | 'label' => $this->__('Apply Rules'),
14 | 'onclick' => 'setLocation(\'' . $this->getUrl('*/derivedAttributes_entity/index') .'\')',
15 | 'class' => 'save',
16 | ));
17 | }
18 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Block/Adminhtml/Rule/Edit.php:
--------------------------------------------------------------------------------
1 | _objectId = 'id';
14 | $this->_blockGroup = 'derivedattributes';
15 | $this->_controller = 'adminhtml_rule';
16 |
17 | parent::__construct();
18 |
19 | $this->_addButton('save_and_continue_edit', array(
20 | 'class' => 'save',
21 | 'label' => $this->__('Save and Continue Edit'),
22 | 'onclick' => 'editForm.submit($(\'edit_form\').action + \'back/edit/\')',
23 | ), 10);
24 | }
25 |
26 | /**
27 | * Getter for form header text
28 | *
29 | * @return string
30 | */
31 | public function getHeaderText()
32 | {
33 | $rule = Mage::registry('current_derived_attribute_rule');
34 | if ($rule->getRuleId()) {
35 | return $this->__("Edit Rule '%s'", $this->escapeHtml($rule->getName()));
36 | }
37 | else {
38 | return $this->__('New Rule');
39 | }
40 | }
41 | /**
42 | * Get form submit URL
43 | *
44 | * @return string
45 | */
46 | public function getFormActionUrl()
47 | {
48 | return $this->getUrl('*/*/save');
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Block/Adminhtml/Rule/Edit/Form.php:
--------------------------------------------------------------------------------
1 | setId('derivedattributes_rule_form');
9 | $this->setTitle($this->__('Rule Information'));
10 | }
11 |
12 | protected function _prepareForm()
13 | {
14 | $form = new Varien_Data_Form(array('id' => 'edit_form', 'action' => $this->getData('action'), 'method' => 'post'));
15 | $form->setUseContainer(true);
16 | $this->setForm($form);
17 |
18 | $fieldset = $form->addFieldset('base_fieldset',
19 | array('legend' => $this->__('General Information'))
20 | );
21 |
22 | $model = Mage::registry('current_derived_attribute_rule');
23 | if ($model->getId()) {
24 | $fieldset->addField('rule_id', 'hidden', array(
25 | 'name' => 'rule_id',
26 | ));
27 | }
28 |
29 | $fieldset->addField('name', 'text', array(
30 | 'name' => 'name',
31 | 'label' => $this->__('Rule Name'),
32 | 'title' => $this->__('Rule Name'),
33 | 'required' => true,
34 | ));
35 |
36 | $fieldset->addField('description', 'textarea', array(
37 | 'name' => 'description',
38 | 'label' => $this->__('Description'),
39 | 'title' => $this->__('Description'),
40 | 'style' => 'height: 100px;',
41 | ));
42 |
43 | $fieldset->addField('active', 'select', array(
44 | 'label' => $this->__('Status'),
45 | 'title' => $this->__('Status'),
46 | 'name' => 'active',
47 | 'required' => true,
48 | 'options' => array(
49 | '1' => $this->__('Active'),
50 | '0' => $this->__('Inactive'),
51 | ),
52 | ));
53 |
54 | if (Mage::app()->isSingleStoreMode()) {
55 | $storeId = Mage::app()->getStore(true)->getId();
56 | $fieldset->addField('store_id', 'hidden', array(
57 | 'name' => 'store_id[]',
58 | 'value' => $storeId
59 | ));
60 | $model->setStoreId($storeId);
61 | } else {
62 | $field = $fieldset->addField('store_id', 'multiselect', array(
63 | 'name' => 'store_id[]',
64 | 'label' => $this->__('Store'),
65 | 'title' => $this->__('Store'),
66 | 'required' => true,
67 | 'values' => Mage::getSingleton('adminhtml/system_store')->getStoreValuesForForm(false, true)
68 | ));
69 | $renderer = $this->getLayout()->createBlock('adminhtml/store_switcher_form_renderer_fieldset_element');
70 | $field->setRenderer($renderer);
71 | }
72 |
73 | $fieldset->addField('attribute_id', 'select', array(
74 | 'label' => $this->__('Attribute'),
75 | 'title' => $this->__('Attribute'),
76 | 'name' => 'attribute_id',
77 | 'required' => true,
78 | 'values' => Mage::getModel('derivedattributes/source_attribute')->toOptionArray(true)
79 | ));
80 |
81 | $fieldset = $form->addFieldset('generator_fieldset',
82 | array('legend' => $this->__('Generator'))
83 | );
84 |
85 | $fieldset->addField('generator_type', 'select', array(
86 | 'name' => 'generator_type',
87 | 'label' => $this->__('Generator Type'),
88 | 'title' => $this->__('Generator Type'),
89 | 'values' => Mage::getModel('derivedattributes/source_generator')->toOptionArray(true)
90 | ));
91 |
92 | //TODO show description for currently selected generator to explain generator_data format
93 | $fieldset->addField('generator_data', 'textarea', array(
94 | 'name' => 'generator_data',
95 | 'label' => $this->__('Generator Data'),
96 | 'title' => $this->__('Generator Data'),
97 | 'style' => 'height: 100px;',
98 | ));
99 |
100 | $fieldset = $form->addFieldset('condition_fieldset',
101 | array('legend' => $this->__('Condition'))
102 | );
103 |
104 | $fieldset->addField('condition_type', 'select', array(
105 | 'name' => 'condition_type',
106 | 'label' => $this->__('Condition Type'),
107 | 'title' => $this->__('Condition Type'),
108 | 'values' => Mage::getModel('derivedattributes/source_condition')->toOptionArray(true)
109 | ));
110 |
111 | //TODO show description for currently selected condition to explain condition_data format
112 | $fieldset->addField('condition_data', 'textarea', array(
113 | 'name' => 'condition_data',
114 | 'label' => $this->__('Condition Data'),
115 | 'title' => $this->__('Condition Data'),
116 | 'style' => 'height: 100px;',
117 | ));
118 |
119 | $form->setValues($model->getData());
120 |
121 | return parent::_prepareForm();
122 | }
123 |
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Block/Adminhtml/Rule/Grid.php:
--------------------------------------------------------------------------------
1 | <@fschmengler>
7 | * @category Hackathon
8 | * @package Hackathon_DerivedAttributes
9 | */
10 |
11 | /**
12 | * @package Hackathon_DerivedAttributes
13 | */
14 | class Hackathon_DerivedAttributes_Block_Adminhtml_Rule_Grid extends Mage_Adminhtml_Block_Widget_Grid
15 | {
16 | public function getRowUrl($item)
17 | {
18 | return $this->getUrl('*/*/edit', array('id' => $item->getId()));
19 | }
20 |
21 | /**
22 | * Get collection object
23 | * @return Hackathon_DerivedAttributes_Model_Resource_Derivedattributes_Rule_Collection
24 | */
25 | public function getCollection()
26 | {
27 | if (!parent::getCollection()) {
28 | $collection = Mage::getResourceModel('derivedattributes/rule_collection');
29 | $this->setCollection($collection);
30 | }
31 |
32 | return parent::getCollection();
33 | }
34 |
35 | /**
36 | * Prepare columns
37 | * @return Hackathon_DerivedAttributes_Block_Adminhtml_Rule_Grid
38 | */
39 | protected function _prepareColumns()
40 | {
41 | $this->addColumn('rule_id', array(
42 | 'header' => $this->__('ID'),
43 | 'align' =>'right',
44 | 'width' => '50px',
45 | 'index' => 'rule_id',
46 | ));
47 |
48 | $this->addColumn('name', array(
49 | 'header' => $this->__('Rule Name'),
50 | 'align' =>'left',
51 | 'index' => 'name',
52 | ));
53 |
54 | $this->addColumn('is_active', array(
55 | 'header' => $this->__('Status'),
56 | 'align' => 'left',
57 | 'width' => '80px',
58 | 'index' => 'is_active',
59 | 'type' => 'options',
60 | 'options' => array(
61 | 1 => 'Active',
62 | 0 => 'Inactive',
63 | ),
64 | ));
65 |
66 | if (!Mage::app()->isSingleStoreMode()) {
67 | $this->addColumn('rule_store_id', array(
68 | 'header' => $this->__('Store'),
69 | 'align' =>'left',
70 | 'index' => 'store_id',
71 | 'type' => 'options',
72 | 'sortable' => false,
73 | 'options' => Mage::getSingleton('adminhtml/system_store')->getStoreOptionHash(),
74 | 'width' => 200,
75 | ));
76 | }
77 |
78 | $this->addColumn('priority', array(
79 | 'header' => $this->__('Priority'),
80 | 'align' => 'right',
81 | 'index' => 'priority',
82 | 'width' => 100,
83 | ));
84 | return parent::_prepareColumns();
85 | }
86 |
87 |
88 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Helper/Data.php:
--------------------------------------------------------------------------------
1 | <@fschmengler>
7 | * @category Hackathon
8 | * @package Hackathon_DerivedAttributes
9 | */
10 |
11 | use Hackathon\DerivedAttributes\Updater;
12 | use Hackathon\DerivedAttributes\RuleBuilder;
13 | use Hackathon\DerivedAttributes\Attribute;
14 | use Hackathon\DerivedAttributes\StoreSet;
15 | use Hackathon\DerivedAttributes\Service\Manager;
16 |
17 | /**
18 | * Data Helper
19 | * @package Hackathon_DerivedAttributes
20 | */
21 | class Hackathon_DerivedAttributes_Helper_Data extends Mage_Core_Helper_Abstract
22 | {
23 | /**
24 | * @var Manager
25 | */
26 | protected $_ruleManager;
27 |
28 | /**
29 | * @return \Hackathon\DerivedAttributes\Service\Manager
30 | */
31 | public function getServiceManager()
32 | {
33 | if(is_null($this->_ruleManager)){
34 | $this->_ruleManager = new Manager();
35 | Mage::dispatchEvent("derivedattribute_new_rulemanager", [
36 | 'rule_manager' => $this->_ruleManager
37 | ]);
38 | }
39 | return $this->_ruleManager;
40 | }
41 |
42 | /**
43 | * Factory method for Updater
44 | *
45 | * @return Updater
46 | */
47 | public function getNewUpdater()
48 | {
49 | $ruleRepository = Mage::getModel('derivedattributes/bridge_ruleRepository');
50 | $ruleLogger = Mage::getModel('derivedattributes/bridge_ruleLogger');
51 |
52 | return new Updater($ruleRepository, $ruleLogger);
53 | }
54 |
55 | /**
56 | * Factory method for RuleBuilder
57 | *
58 | * @return RuleBuilder
59 | */
60 | public function getNewRuleBuilder(Attribute $attribute)
61 | {
62 | return new RuleBuilder($this->getServiceManager(), $attribute);
63 | }
64 |
65 | /**
66 | * Factory method for EntityIterator bridge
67 | *
68 | * @param Mage_Eav_Model_Entity_Type $entityTypeInstance
69 | * @return Hackathon_DerivedAttributes_Model_Bridge_EntityIterator
70 | */
71 | public function getNewEntityIterator(Mage_Eav_Model_Entity_Type $entityTypeInstance, $entityIds)
72 | {
73 | /** @var Mage_Eav_Model_Entity_Collection_Abstract $collection */
74 | $collection = Mage::getModel($entityTypeInstance->getEntityModel())->getCollection();
75 | $collection->addFieldToFilter('entity_id', ['in' => $entityIds]);
76 | /*
77 | * I tried using the flat catalog if available but using it from admin area is not intended
78 | * and there are too many restrictions and special cases
79 | */
80 | $collection->addAttributeToSelect('*');
81 |
82 | $iterator = new Hackathon_DerivedAttributes_Model_Bridge_EntityIterator($collection);
83 | return $iterator;
84 | }
85 |
86 | /**
87 | * Factory method for Entity bridge
88 | *
89 | * @param Mage_Eav_Model_Entity_Type $entityTypeInstance
90 | * @return Hackathon_DerivedAttributes_Model_Bridge_Entity
91 | */
92 | public function getNewEntityModel(Mage_Eav_Model_Entity_Type $entityTypeInstance, Mage_Core_Model_Abstract $entity = null)
93 | {
94 | if ($entity === null) {
95 | $entity = Mage::getModel($entityTypeInstance->getEntityModel());
96 | }
97 | $entityModel = new Hackathon_DerivedAttributes_Model_Bridge_Entity($entityTypeInstance, $entity);
98 | return $entityModel;
99 | }
100 |
101 | /**
102 | * Factory method for StoreSet
103 | *
104 | * @param array $storeIds
105 | * @param bool $expand Set to true if ['0'] should be expanded to all store views
106 | * @return StoreSet
107 | */
108 | public function createStoreSet(array $storeIds, $expand = false)
109 | {
110 | if ($storeIds === ['0']) {
111 | if (! $expand) {
112 | return StoreSet::all();
113 | }
114 | $storeIds = array_keys(Mage::app()->getStores(true));
115 | }
116 | return new StoreSet($storeIds);
117 | }
118 |
119 | /**
120 | * Load entity type instance by code
121 | *
122 | * @param $entityType
123 | * @return Mage_Eav_Model_Entity_Type
124 | * @throws Mage_Core_Exception
125 | */
126 | public function getEntityTypeInstance($entityType)
127 | {
128 | $entityTypeInstance = Mage::getModel('eav/entity_type')->loadByCode($entityType);
129 | if (!$entityTypeInstance->getId()) {
130 | Mage::throwException(Mage::helper('derivedattributes')->__('Invalid entity type %s.', $entityType));
131 | return $entityTypeInstance;
132 | }
133 | return $entityTypeInstance;
134 | }
135 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Helper/Rule/Director.php:
--------------------------------------------------------------------------------
1 | getNewRuleBuilder($this->getAttributeById($ruleData->getData('attribute_id')));
14 | $builder->setActive((bool) $ruleData->getData('active'))
15 | ->setPriority((int) $ruleData->getData('priority'))
16 | ->setConditionType($ruleData->getData('condition_type'))
17 | ->setConditionData($ruleData->getData('condition_data'))
18 | ->setGeneratorType($ruleData->getData('generator_type'))
19 | ->setGeneratorData($ruleData->getData('generator_data'))
20 | ->setName($ruleData->getData('name'))
21 | ->setDescription($ruleData->getData('description'))
22 | ->setStores($helper->createStoreSet($ruleData->getData('store_id')));
23 | return $builder->build();
24 | }
25 | /**
26 | * Returns the Attribute instance
27 | *
28 | * @return Attribute
29 | */
30 | private function getAttributeById($attributeId){
31 |
32 | /* @var $magentoAttribute Mage_Eav_Model_Entity_Attribute */
33 | $magentoAttribute = Mage::getModel("eav/entity_attribute")->load($attributeId);
34 |
35 | $attribute = new Attribute($magentoAttribute->getEntityType()->getEntityTypeCode(), $magentoAttribute->getAttributeCode());
36 |
37 | return $attribute;
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Model/Bridge/Entity.php:
--------------------------------------------------------------------------------
1 |
4 | */
5 |
6 | use Hackathon\DerivedAttributes\BridgeInterface\EntityInterface;
7 | use Hackathon\DerivedAttributes\Attribute;
8 |
9 | /**
10 | * Entity implementation of entity-bridge-interface.
11 | */
12 | class Hackathon_DerivedAttributes_Model_Bridge_Entity implements \Hackathon\DerivedAttributes\BridgeInterface\EntityInterface
13 | {
14 | /**
15 | * @var Mage_Eav_Model_Entity_Type
16 | */
17 | private $type;
18 | /**
19 | * @var Mage_Core_Model_Abstract
20 | */
21 | private $entity;
22 |
23 | public function __construct(Mage_Eav_Model_Entity_Type $type, Mage_Core_Model_Abstract $entity)
24 | {
25 | $this->type = $type;
26 | $this->entity = $entity;
27 | }
28 |
29 | /**
30 | * Returns true if entity has this kind of attribute
31 | *
32 | * @param Attribute $attribute
33 | * @return bool
34 | */
35 | function hasAttribute(Attribute $attribute)
36 | {
37 | return $attribute->getEntityTypeCode() == $this->getEntityTypeCode();
38 | }
39 |
40 | /**
41 | * @return string
42 | */
43 | function getEntityTypeCode()
44 | {
45 | return $this->type->getEntityTypeCode();
46 | }
47 |
48 |
49 | /**
50 | * @return boolean
51 | */
52 | public function isChanged()
53 | {
54 | return $this->entity->hasDataChanges();
55 | }
56 |
57 | /**
58 | * @param Attribute $attribute
59 | * @return mixed
60 | */
61 | public function getAttributeValue(Attribute $attribute)
62 | {
63 | return $this->entity->getData($attribute->getAttributeCode());
64 | }
65 |
66 | /**
67 | * @param Attribute $attribute
68 | * @return string
69 | */
70 | public function getLocalizedAttributeValue(Attribute $attribute)
71 | {
72 | $value = $this->entity->getAttributeText($attribute->getAttributeCode());
73 | if ($value) {
74 | return $value;
75 | } else {
76 | return $this->entity->getData($attribute->getAttributeCode());
77 | }
78 | }
79 |
80 | /**
81 | * @param Attribute $attribute
82 | * @param mixed $value
83 | * @return void
84 | */
85 | public function setAttributeValue(Attribute $attribute, $value)
86 | {
87 | $this->entity->setData($attribute->getAttributeCode(), $value);
88 | }
89 |
90 | /**
91 | * Sets raw data from database
92 | *
93 | * @param string[] $data
94 | * @return void
95 | */
96 | public function setRawData($data)
97 | {
98 | $this->entity->addData($data);
99 | }
100 |
101 | /**
102 | * Save changed attribute values in database
103 | *
104 | * @return void
105 | */
106 | public function saveAttributes()
107 | {
108 | $resource = $this->entity->getResource();
109 | $resource->isPartialSave(true);
110 | $resource->save($this->entity);
111 | }
112 |
113 | /**
114 | * Reset to empty instance
115 | *
116 | * @return void
117 | */
118 | public function clearInstance()
119 | {
120 | $this->entity->clearInstance();
121 | }
122 |
123 | /**
124 | * Returns collection iterator
125 | *
126 | * @return Hackathon_DerivedAttributes_Bridge_EntityIterator
127 | */
128 | public function getCollectionIterator()
129 | {
130 | $collection = $this->entity->getCollection();
131 | $iterator = new Hackathon_DerivedAttributes_Bridge_EntityIterator($collection);
132 | return $iterator;
133 | }
134 |
135 | }
136 |
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Model/Bridge/EntityIterator.php:
--------------------------------------------------------------------------------
1 | _collection = $collection;
31 | }
32 |
33 | /**
34 | * @param $offset
35 | */
36 | public function setIterationOffset($offset)
37 | {
38 | $this->_iterationOffset = $offset;
39 | }
40 |
41 | /**
42 | * Returns total size of collection
43 | *
44 | * @return mixed
45 | */
46 | function getSize()
47 | {
48 | return $this->_collection->getSize();
49 | }
50 |
51 | function getStore()
52 | {
53 | return $this->_store;
54 | }
55 |
56 | function setStore(Store $store)
57 | {
58 | $this->_store = $store;
59 | }
60 |
61 | public function current()
62 | {
63 | $data = $this->_iterator->current()->getData();
64 | $data['store_id'] = (string) $this->_store;
65 | return $data;
66 | }
67 |
68 | public function next()
69 | {
70 | $this->_iteration++;
71 | $this->_iterator->next();
72 | }
73 |
74 | public function key()
75 | {
76 | return $this->_iteration + $this->_iterationOffset;
77 | }
78 |
79 | public function valid()
80 | {
81 | return $this->_iterator->valid();
82 | }
83 |
84 | public function rewind()
85 | {
86 | $this->_collection->clear()
87 | ->setStoreId((string) $this->_store)
88 | ->load();
89 | //TODO: load all ids, then chunk
90 | $this->_iteration = 0;
91 | $this->_iterator = $this->_collection->getIterator();
92 | }
93 |
94 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Model/Bridge/RuleLogger.php:
--------------------------------------------------------------------------------
1 | _ruleStorage = new SplObjectStorage();
19 | }
20 |
21 | public function getRuleId(Rule $rule)
22 | {
23 | if ($this->_ruleStorage->contains($rule)) {
24 | return $this->_ruleStorage[$rule];
25 | }
26 | return null;
27 | }
28 |
29 | public function findById($ruleId)
30 | {
31 | $model = Mage::getModel(Hackathon_DerivedAttributes_Model_Rule::ALIAS)->load($ruleId);
32 | if (! $model->getId()) {
33 | Mage::throwException(Mage::helper('derivedattributes')->__('Rule not found'));
34 | }
35 | $ruleDirector = Mage::helper('derivedattributes/rule_director');
36 | $rule = $ruleDirector->createRule($model);
37 | $this->_registerRule($ruleId, $rule);
38 | return $rule;
39 | }
40 |
41 | /**
42 | * Returns rule sets with all active rules, grouped by store, ordered by priority
43 | *
44 | * @param StoreSet $stores
45 | * @return \Hackathon\DerivedAttributes\RuleSetsByStore
46 | */
47 | public function findRuleSetsForStores(StoreSet $stores)
48 | {
49 | $ruleSetsByStore = new RuleSetsByStore();
50 | foreach ($stores as $store) {
51 | $ruleSetsByStore->addRuleSet($this->findRuleSetForStore($store), $store);
52 | }
53 | return $ruleSetsByStore;
54 | }
55 |
56 | /**
57 | * Returns rule set with all active rules for given store, ordered by priority
58 | *
59 | * @param Store $store
60 | * @return RuleSet
61 | */
62 | private function findRuleSetForStore(Store $store)
63 | {
64 | $collection = $this->getRuleCollection();
65 | # ... WHERE store_id IS NULL OR FIND_IN_SET(:storeId, store_id)
66 | $collection->addFieldToFilter('store_id', [
67 | ['eq' => '0'],
68 | ['finset' => [(string) $store]]
69 | ]);
70 | $collection->addFieldToFilter('active', "1");
71 | $collection->setOrder('priority', Varien_Data_Collection_Db::SORT_ORDER_ASC);
72 |
73 | $ruleSet = new RuleSet();
74 | $ruleDirector = Mage::helper('derivedattributes/rule_director');
75 | foreach($collection as $ruleData) {
76 | $rule = $ruleDirector->createRule($ruleData);
77 | $this->_registerRule($ruleData->getId(), $rule);
78 | $ruleSet->addRule($rule);
79 | }
80 | return $ruleSet;
81 | }
82 |
83 | /**
84 | * @param Rule $newRule
85 | * @return void
86 | */
87 | public function createRule(Rule $newRule)
88 | {
89 | $model = $this->getRuleModel($newRule);
90 | $model->save();
91 | $this->_registerRule($model->getId(), $newRule);
92 | }
93 |
94 | /**
95 | * @param Rule $oldRule
96 | * @param Rule $newRule
97 | * @return void
98 | */
99 | function replaceRule(Rule $oldRule, Rule $newRule)
100 | {
101 | $model = $this->getRuleModel($newRule);
102 | $model->setId($this->getRuleId($oldRule));
103 | $model->save();
104 | $this->_unregisterRule($oldRule);
105 | $this->_registerRule($model->getId(), $newRule);
106 | }
107 |
108 | /**
109 | * @param Rule $ruleToBeDeleted
110 | * @return void
111 | */
112 | public function deleteRule(Rule $ruleToBeDeleted)
113 | {
114 | $this->getRuleModel($ruleToBeDeleted)->delete();
115 | $this->_unregisterRule($ruleToBeDeleted);
116 | }
117 |
118 | /**
119 | * Registers Rule with ID for database mapping
120 | *
121 | * @param int $ruleId
122 | * @param Rule $rule
123 | * @return $this
124 | */
125 | protected function _registerRule($ruleId, Rule $rule)
126 | {
127 | $this->_ruleStorage->attach($rule, $ruleId);
128 | return $this;
129 | }
130 |
131 | /**
132 | * Unregisters removed/replace Rule for database mapping
133 | *
134 | * @param Rule $rule
135 | * @return $this
136 | */
137 | protected function _unregisterRule(Rule $rule)
138 | {
139 | $this->_ruleStorage->detach($rule);
140 | return $this;
141 | }
142 |
143 | /**
144 | * @return Hackathon_DerivedAttributes_Model_Resource_Rule_Collection
145 | */
146 | private function getRuleCollection()
147 | {
148 | return Mage::getResourceModel('derivedattributes/rule_collection');
149 | }
150 |
151 | public function getRuleModel(Rule $rule)
152 | {
153 | $manager = Mage::helper('derivedattributes')->getServiceManager();
154 | /** @var Hackathon_DerivedAttributes_Model_Rule $model */
155 | $model = Mage::getModel(Hackathon_DerivedAttributes_Model_Rule::ALIAS);
156 | $model->setData(array(
157 | 'name' => $rule->getName(),
158 | 'description' => $rule->getDescription(),
159 | 'active' => $rule->isActive(),
160 | 'priority' => $rule->getPriority(),
161 | 'attribute_id' => self::getAttributeId($rule->getAttribute()),
162 | 'condition_type' => $manager->getConditionType($rule->getCondition()),
163 | 'condition_data' => $rule->getCondition()->getData(),
164 | 'generator_type' => $manager->getGeneratorType($rule->getGenerator()),
165 | 'generator_data' => $rule->getGenerator()->getData(),
166 | 'store_id' => $rule->getStores() === StoreSet::all() ? ['0'] : $rule->getStores()->getStoreCodes()
167 | )
168 | );
169 | $model->setId($this->getRuleId($rule));
170 | return $model;
171 | }
172 |
173 | /**
174 | * Returns Magento attrribute id based on Attribute instance
175 | *
176 | * @return Attribute
177 | */
178 | private static function getAttributeId(Attribute $attribute)
179 | {
180 | /* @var $magentoAttribute Mage_Eav_Model_Entity_Attribute */
181 | $magentoAttribute = Mage::getModel("eav/entity_attribute")
182 | ->loadByCode($attribute->getEntityTypeCode(), $attribute->getAttributeCode());
183 |
184 | return $magentoAttribute->getId();
185 | }
186 |
187 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Model/Massupdater.php:
--------------------------------------------------------------------------------
1 | getEntityTypeInstance($entityType);
17 |
18 | $updater = $helper->getNewUpdater();
19 | $updater->setDryRun($isDryRun);
20 | $updater->massUpdate(
21 | $helper->getNewEntityIterator($entityTypeInstance, $entityIds),
22 | $helper->getNewEntityModel($entityTypeInstance),
23 | $helper->createStoreSet($storeIds, true));
24 | }
25 |
26 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Model/Observer.php:
--------------------------------------------------------------------------------
1 |
4 | */
5 |
6 | use Hackathon\DerivedAttributes\Store;
7 |
8 | /**
9 | * Event-observer for derived attributes.
10 | */
11 | class Hackathon_DerivedAttributes_Model_Observer
12 | {
13 | /**
14 | * @see event before_model_save
15 | * @param Varien_Object $observer
16 | * @throws Mage_Core_Exception
17 | */
18 | public function beforeModelSave(Varien_Object $observer)
19 | {
20 | $helper = Mage::helper('derivedattributes');
21 | /* @var $modelObject Mage_Core_Model_Abstract */
22 | $modelObject = $observer->getObject();
23 | $modelResource = $modelObject->getResource();
24 |
25 | if($modelResource instanceof Mage_Eav_Model_Entity_Abstract){
26 | /* @var $bridgeObject Hackathon_DerivedAttributes_Model_Bridge_Entity */
27 | $bridgeObject = $helper->getNewEntityModel($modelResource->getEntityType(), $modelObject);
28 |
29 | $updater = $helper->getNewUpdater();
30 | $updater->update($bridgeObject, new Store($modelObject->getStoreId()));
31 | }
32 |
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Model/Resource/Rule.php:
--------------------------------------------------------------------------------
1 |
4 | */
5 |
6 | class Hackathon_DerivedAttributes_Model_Resource_Rule
7 | extends Mage_Core_Model_Resource_Db_Abstract
8 | {
9 | protected function _construct(){
10 | $this->_init("derivedattributes/rule", "rule_id");
11 | }
12 |
13 | protected function _afterLoad(Mage_Core_Model_Abstract $object)
14 | {
15 | $object->setStoreId(explode(',', $object->getStoreId()));
16 | return parent::_afterLoad($object);
17 | }
18 |
19 | protected function _beforeSave(Mage_Core_Model_Abstract $object)
20 | {
21 | $object->setStoreId(join(',', (array)$object->getStoreId()));
22 | return parent::_beforeSave($object);
23 | }
24 |
25 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Model/Resource/Rule/Collection.php:
--------------------------------------------------------------------------------
1 |
4 | */
5 |
6 | class Hackathon_DerivedAttributes_Model_Resource_Rule_Collection extends Mage_Core_Model_Resource_Db_Collection_Abstract
7 | {
8 | protected function _construct()
9 | {
10 | $this->_init(Hackathon_DerivedAttributes_Model_Rule::ALIAS);
11 | }
12 |
13 | protected function _afterLoad()
14 | {
15 | foreach ($this->_items as $item) {
16 | $item->setStoreId(explode(',', $item->getStoreId()));
17 | }
18 | return parent::_afterLoad();
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Model/Rule.php:
--------------------------------------------------------------------------------
1 |
4 | */
5 |
6 | use Hackathon\DerivedAttributes\Rule;
7 |
8 | /**
9 | * Magento model for rules.
10 | *
11 | * Only used internally by resource model
12 | */
13 | class Hackathon_DerivedAttributes_Model_Rule extends Mage_Core_Model_Abstract
14 | {
15 | const ALIAS = 'derivedattributes/rule';
16 |
17 | protected function _construct(){
18 | $this->_init(self::ALIAS);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Model/Source/Abstract.php:
--------------------------------------------------------------------------------
1 | _serviceManager = Mage::helper('derivedattributes')->getServiceManager();
12 | }
13 |
14 | abstract public function getOptions();
15 |
16 | public function toOptionArray($withEmpty = false)
17 | {
18 | $options = array();
19 |
20 | foreach ($this->getOptions() as $value => $label) {
21 | $options[] = array(
22 | 'label' => $label,
23 | 'value' => $value
24 | );
25 | }
26 |
27 | if ($withEmpty) {
28 | array_unshift($options, array('value'=>'', 'label'=>Mage::helper('page')->__('-- Please Select --')));
29 | }
30 |
31 | return $options;
32 | }
33 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Model/Source/Attribute.php:
--------------------------------------------------------------------------------
1 | addVisibleFilter();
15 | $values = array();
16 | foreach ($attributeCollection as $attribute) {
17 | /** @var Mage_Eav_Model_Attribute $attribute */
18 | $values[] = array(
19 | 'label' => $attribute->getFrontendLabel() ?: $attribute->getAttributeCode(),
20 | 'value' => $attribute->getId()
21 | );
22 | }
23 | $options[] = array(
24 | 'label' => 'Product',
25 | 'value' => $values
26 | );
27 |
28 | /** @var Mage_Customer_Model_Resource_Attribute_Collection $attributeCollection */
29 | $attributeCollection = Mage::getResourceModel('customer/attribute_collection');
30 | $attributeCollection->addSystemHiddenFilter();
31 | $values = array();
32 | foreach ($attributeCollection as $attribute) {
33 | /** @var Mage_Eav_Model_Attribute $attribute */
34 | $values[] = array(
35 | 'label' => $attribute->getFrontendLabel() ?: $attribute->getAttributeCode(),
36 | 'value' => $attribute->getId()
37 | );
38 | }
39 | $options[] = array(
40 | 'label' => 'Customer',
41 | 'value' => $values
42 | );
43 |
44 | return $options;
45 | }
46 |
47 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Model/Source/Condition.php:
--------------------------------------------------------------------------------
1 | _serviceManager->getAvailableConditionTypes() as $id => $info) {
9 | $options[$id] = Mage::helper('derivedattributes')->__($info['title']);
10 | }
11 | return $options;
12 | }
13 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Model/Source/Generator.php:
--------------------------------------------------------------------------------
1 | _serviceManager->getAvailableGeneratorTypes() as $id => $info) {
8 | $options[$id] = Mage::helper('derivedattributes')->__($info['title']);
9 | }
10 | return $options;
11 | }
12 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Test/Block/Entity.php:
--------------------------------------------------------------------------------
1 | adminSession();
19 | $this->dispatch('adminhtml/derivedAttributes_entity');
20 | $this->assertRequestRoute('adminhtml/derivedAttributes_entity/index');
21 | $this->assertLayoutHandleLoaded('adminhtml_derivedattributes_entity_index');
22 | $this->assertLayoutBlockRendered('derivedattributes_entity', 'Container should be instantiated');
23 | $this->assertLayoutBlockRendered('derivedattributes_entity_form', 'Form should be instantiated');
24 | $this->assertLayoutBlockRendered('derivedattributes_entity_tabs', 'Tabs should be instantiated');
25 |
26 | $this->assertResponseBodyXpath(
27 | '//select[@id="customerGrid_massaction-select"]/option[@value="apply"]',
28 | 'Customer grid should contain mass action "apply".');
29 | $this->assertResponseBodyXpath(
30 | '//select[@id="customerGrid_massaction-select"]/option[@value="dryrun"]',
31 | 'Customer grid should contain mass action "dryrun".');
32 |
33 | $this->assertResponseBodyXpath(
34 | '//select[@id="productDerivedAttributesGrid_massaction-select"]/option[@value="apply"]',
35 | 'Product grid should contain mass action "apply".');
36 | $this->assertResponseBodyXpath(
37 | '//select[@id="productDerivedAttributesGrid_massaction-select"]/option[@value="dryrun"]',
38 | 'Product grid should contain mass action "dryrun".');
39 |
40 | $this->assertResponseBodyContains('name="entity_type"', 'Mass action block should contain hidden input with entity type');
41 | // assertLayoutBlockRendered does not work with blocks created from code (i.e. the grids)
42 | }
43 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Test/Block/Rule.php:
--------------------------------------------------------------------------------
1 | adminSession();
19 | $this->dispatch('adminhtml/derivedAttributes_rule');
20 | $this->assertRequestRoute('adminhtml/derivedAttributes_rule/index');
21 | $this->assertLayoutHandleLoaded('adminhtml_derivedattributes_rule_index');
22 | $this->assertLayoutBlockCreated('derivedattributes_rule', 'Grid container should be instantiated');
23 | $this->assertResponseBodyNotContains('empty-text', 'Grid content should not be empty');
24 | $this->assertResponseBodyContains('Test Rule 1');
25 | $this->assertResponseBodyContains('/derivedAttributes_entity/index', '"Apply rules" link should be present');
26 | }
27 |
28 | /**
29 | * @test
30 | * @singleton adminhtml/session
31 | * @singleton admin/session
32 | * @singleton index/indexer
33 | * @registry current_derived_attribute_rule
34 | */
35 | public function newFormShouldBeLoaded()
36 | {
37 | $this->adminSession();
38 | $this->dispatch('adminhtml/derivedAttributes_rule/new');
39 | $this->assertRequestRoute('adminhtml/derivedAttributes_rule/edit');
40 | $this->assertLayoutHandleLoaded('adminhtml_derivedattributes_rule_edit');
41 | $this->assertLayoutBlockCreated('derivedattributes_rule_edit', 'Form should be instantiated');
42 | $this->assertResponseBodyQueryContains(
43 | 'h3.head-adminhtml-rule', 'New Rule',
44 | 'Title should be "New Rule"');
45 | $this->assertResponseBodyXpath(
46 | '//select[@name="generator_type"]/option[@value="template"]',
47 | 'Dropdown with generator types should be present');
48 | $this->assertResponseBodyXpath(
49 | '//select[@name="condition_type"]/option[@value="boolean-attribute"]',
50 | 'Dropdown with condition types should be present');
51 | $this->assertResponseBodyXpathContains(
52 | '//select[@name="store_id[]"]//option[@value="0"]', 'All Store Views',
53 | 'Select box for store views should contain "All Store Views"');
54 |
55 | $this->assertResponseBodyXpath(
56 | '//select[@name="attribute_id"]/optgroup[@label="Customer"]',
57 | 'Dropdown with attribute ids should contain customer optgroup');
58 | $this->assertResponseBodyXpath(
59 | '//select[@name="attribute_id"]/optgroup[@label="Product"]',
60 | 'Dropdown with attribute ids should contain product optgroup');
61 | $this->assertResponseBodyXpath(
62 | '//select[@name="attribute_id"]//option[@value="72"]',
63 | 'Dropdown with attribute ids should contain product description attribute id');
64 | $this->assertResponseBodyNotXpath(
65 | '//select[@name="attribute_id"]//option[@value="111"]',
66 | 'Dropdown with attribute ids should not contain product has_options attribute id (invisible)');
67 | $this->assertResponseBodyXpath(
68 | '//select[@name="attribute_id"]//option[@value="5"]',
69 | 'Dropdown with attribute ids should contain customer first name attribute id');
70 | $this->assertResponseBodyNotXpath(
71 | '//select[@name="attribute_id"]//option[@value="16"]',
72 | 'Dropdown with attribute ids should not contain customer confirmation attribute id (invisible)');
73 | }
74 | /**
75 | * @test
76 | * @singleton adminhtml/session
77 | * @singleton admin/session
78 | * @singleton index/indexer
79 | * @registry current_derived_attribute_rule
80 | */
81 | public function editFormShouldBeLoaded()
82 | {
83 | $ruleId = 1;
84 | $this->adminSession();
85 | $this->dispatch('adminhtml/derivedAttributes_rule/edit', ['id' => $ruleId]);
86 | $this->assertRequestRoute('adminhtml/derivedAttributes_rule/edit');
87 | $this->assertLayoutHandleLoaded('adminhtml_derivedattributes_rule_edit');
88 | $this->assertLayoutBlockCreated('derivedattributes_rule_edit', 'Form should be instantiated');
89 | $this->assertResponseBodyQueryContains(
90 | 'h3.head-adminhtml-rule', 'Edit Rule \'Test Rule 1\'',
91 | 'Title should be "Edit Rule"');
92 | $this->assertResponseBodyXpath(
93 | '//input[@name="name"][@value="Test Rule 1"]', 'Name input should be filled');
94 | $this->assertResponseBodyXpathContains(
95 | '//select[@name="active"]/option[@value="1"][@selected="selected"]', 'Active',
96 | 'Status input should be set to active');
97 | $this->assertResponseBodyXpathContains(
98 | '//select[@name="store_id[]"]//option[@value="0"][@selected="selected"]', 'All Store Views',
99 | 'Store view input should be set to "All Store Views"');
100 | $this->assertResponseBodyXpathContains(
101 | '//textarea[@name="description"]', 'This is a template generator rule for the product description',
102 | 'Description input should be filled');
103 | }
104 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Test/Case/Controller/Dom.php:
--------------------------------------------------------------------------------
1 | 3.5
3 | $__old_error_reporting = error_reporting(error_reporting() & ~ E_STRICT);
4 |
5 | /**
6 | * Extension of DomQuery constraint with configuration in constructor instead
7 | * of in evaluate() - so that it can be used in EcomDev PHPUnit Controller
8 | * Test Case
9 | */
10 | class Hackathon_DerivedAttributes_Test_Constraint_DomQuery extends Zend_Test_PHPUnit_Constraint_DomQuery
11 | {
12 | const ASSERT_XPATH = 'assertXpath';
13 | const ASSERT_XPATH_CONTENT_CONTAINS = 'assertXpathContentContains';
14 | const ASSERT_XPATH_CONTENT_REGEX = 'assertXpathContentRegex';
15 | const ASSERT_XPATH_CONTENT_COUNT = 'assertXpathCount';
16 | const ASSERT_XPATH_CONTENT_COUNT_MIN= 'assertXpathCountMin';
17 | const ASSERT_XPATH_CONTENT_COUNT_MAX= 'assertXpathCountMax';
18 |
19 | protected $_defaultType;
20 |
21 | protected $_additionalParameter;
22 |
23 | public function __construct(
24 | $path, $defaultType = self::ASSERT_QUERY, $additionalParameter = null)
25 | {
26 | $this->_defaultType = $defaultType;
27 | $this->_additionalParameter = $additionalParameter;
28 | parent::__construct($path);
29 | }
30 |
31 | /* (non-PHPdoc)
32 | * @see Zend_Test_PHPUnit_Constraint_DomQuery::evaluate()
33 | */
34 | public function evaluate ($other, $assertType='', $match=false)
35 | {
36 | if ($assertType === '') {
37 | $assertType = $this->_defaultType;
38 | }
39 | if ($this->_additionalParameter !== null) {
40 | return parent::evaluate(
41 | $other, $assertType, $this->_additionalParameter
42 | );
43 | }
44 | return parent::evaluate($other, $assertType);
45 | }
46 |
47 | /* (non-PHPdoc)
48 | * @see Zend_Test_PHPUnit_Constraint_DomQuery::toString()
49 | */
50 | public function toString ()
51 | {
52 | return sprintf(
53 | 'matches %s(%s%s)',
54 | $this->_assertType,
55 | var_export($this->_path, true),
56 | $this->_additionalParameter === null
57 | ? ''
58 | : ', ' . var_export($this->_additionalParameter, true)
59 | );
60 | }
61 |
62 | }
63 |
64 | // restore error reporting
65 | error_reporting($__old_error_reporting);
66 | unset($__old_error_reporting);
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Test/Controller/EntityController.php:
--------------------------------------------------------------------------------
1 | getModelMock('derivedattributes/massupdater', ['update']);
22 | $updaterMock->expects($this->once())->method('update')
23 | ->with($postData['entity_ids'], $postData['entity_type'], $expectedStoreIds, false);
24 | $this->replaceByMock('model', 'derivedattributes/massupdater', $updaterMock);
25 |
26 | $this->adminSession();
27 | $this->getRequest()->setMethod('POST');
28 | $this->getRequest()->setPost($postData);
29 | $this->dispatch('adminhtml/derivedAttributes_entity/applyRules');
30 | $this->assertRequestRoute('adminhtml/derivedAttributes_entity/applyRules');
31 | $this->assertResponseHttpCode(200);
32 | $this->assertResponseHeaderContains('content-type', 'application/json');
33 | $this->assertResponseBodyJsonMatch(array('final' => false));
34 |
35 | $this->getRequest()->setPost(array());
36 | $this->dispatch('adminhtml/derivedAttributes_entity/applyRules');
37 | $this->assertRequestRoute('adminhtml/derivedAttributes_entity/applyRules');
38 | $this->assertResponseHttpCode(200);
39 | $this->assertResponseHeaderContains('content-type', 'application/json');
40 | $this->assertResponseBodyJsonMatch(array('final' => true));
41 | }
42 |
43 | public static function dataMassUpdateForm()
44 | {
45 | return [
46 | [
47 | 'postData' => [
48 | 'store_id' => ['1'],
49 | 'entity_ids' => ['1', '2', '3'],
50 | 'entity_type' => 'catalog_product'
51 | ],
52 | 'expectedStoreIds' => ['1']
53 | ],
54 | [
55 | 'postData' => [
56 | 'store_id' => ['1', '2'],
57 | 'entity_ids' => ['1', '2', '3'],
58 | 'entity_type' => 'catalog_product'
59 | ],
60 | 'expectedStoreIds' => ['1', '2']
61 | ],
62 | [
63 | 'postData' => [
64 | 'store_id' => ['0'],
65 | 'entity_ids' => ['1', '2', '3'],
66 | 'entity_type' => 'catalog_product'
67 | ],
68 | 'expectedStoreIds' => ['0']
69 | ],
70 | ];
71 | }
72 | /**
73 | * @test
74 | * @loadFixture products.yaml
75 | * @singleton adminhtml/session
76 | * @singleton admin/session
77 | */
78 | public function invalidEntityType()
79 | {
80 | $this->adminSession();
81 | $this->getRequest()->setMethod('POST');
82 | $this->getRequest()->setPost(array(
83 | 'entity_ids' => [1, 2, 3],
84 | 'entity_type' => 'invalid_entity_type'
85 | ));
86 | $this->dispatch('adminhtml/derivedAttributes_entity/applyRules');
87 | $this->assertRequestRoute('adminhtml/derivedAttributes_entity/applyRules');
88 | $this->assertResponseHttpCode(200);
89 | $this->assertResponseHeaderContains('content-type', 'application/json');
90 | $this->assertResponseBodyJsonMatch(array('final' => false));
91 |
92 | $this->getRequest()->setPost(array());
93 | $this->dispatch('adminhtml/derivedAttributes_entity/applyRules');
94 | $this->assertRequestRoute('adminhtml/derivedAttributes_entity/applyRules');
95 | $this->assertResponseHttpCode(200);
96 | $this->assertResponseHeaderContains('content-type', 'application/json');
97 | $this->assertResponseBodyJsonMatch(array('final' => true, 'error' => ''), 'Response should contain error');
98 | }
99 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Test/Controller/RuleController.php:
--------------------------------------------------------------------------------
1 | adminSession();
21 | $this->getRequest()->setMethod('POST');
22 | $this->getRequest()->setPost($postData);
23 | $this->dispatch('adminhtml/derivedAttributes_rule/save');
24 | $this->assertRequestRoute('adminhtml/derivedAttributes_rule/save');
25 | $this->assertRedirectTo('adminhtml/derivedAttributes_rule/index');
26 |
27 | $rule = Mage::getModel('derivedattributes/rule')->load($postData['rule_id']);
28 | $this->assertEquals($postData, $rule->getData(), 'Rule data should be as posted');
29 | }
30 |
31 | public static function dataSaveExistingRule()
32 | {
33 | return array(
34 | [ 'postData' => [
35 | 'rule_id' => '1',
36 | 'priority' => '4',
37 | 'active' => '1',
38 | 'name' => 'Edited Test Rule',
39 | 'description' => 'The rule has been edited',
40 | 'attribute_id' => '72',
41 | 'condition_type' => 'always',
42 | 'condition_data' => '',
43 | 'generator_type' => 'template',
44 | 'generator_data' => 'Edited template',
45 | 'store_id' => [ '1', '2']
46 | ]]
47 | );
48 | }
49 |
50 | /**
51 | * @test
52 | * @loadFixture products.yaml
53 | * @singleton adminhtml/session
54 | * @singleton admin/session
55 | */
56 | public function existingRuleShouldBeDeleted()
57 | {
58 | $ruleId = '1';
59 |
60 | $this->adminSession();
61 | $this->getRequest()->setParam('id', $ruleId);
62 | $this->dispatch('adminhtml/derivedAttributes_rule/delete');
63 | $this->assertRequestRoute('adminhtml/derivedAttributes_rule/delete');
64 | $this->assertRedirectTo('adminhtml/derivedAttributes_rule/index');
65 |
66 | $rule = Mage::getModel('derivedattributes/rule')->load($ruleId);
67 | $this->assertNull($rule->getId(), 'Rule should not be loaded after delete');
68 | }
69 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Test/Model/Bridge/Entity.php:
--------------------------------------------------------------------------------
1 | assertEquals('Test Product 1', $this->getProductEntityBridge(1, 1)
18 | ->getLocalizedAttributeValue($this->getAttributeBridge('name')));
19 | $this->assertEquals('Test Product 1 in store view', $this->getProductEntityBridge(2, 1)
20 | ->getLocalizedAttributeValue($this->getAttributeBridge('name')));
21 |
22 | $this->assertEquals('description.', $this->getProductEntityBridge(1, 1)
23 | ->getLocalizedAttributeValue($this->getAttributeBridge('description')));
24 | $this->assertEquals('description in store view', $this->getProductEntityBridge(2, 1)
25 | ->getLocalizedAttributeValue($this->getAttributeBridge('description')));
26 | }
27 |
28 | /**
29 | * @param $storeId
30 | * @param $productId
31 | * @return Hackathon_DerivedAttributes_Model_Bridge_Entity
32 | */
33 | private function getProductEntityBridge($storeId, $productId)
34 | {
35 | /** @var Hackathon_DerivedAttributes_Helper_Data $helper */
36 | $helper = Mage::helper('derivedattributes');
37 | $entity = $helper->getNewEntityModel(
38 | $helper->getEntityTypeInstance(Mage_Catalog_Model_Product::ENTITY),
39 | Mage::getModel('catalog/product')->setStoreId($storeId)->load($productId)
40 | );
41 | return $entity;
42 | }
43 |
44 | /**
45 | * @param $attributeCode
46 | * @return Attribute
47 | */
48 | private function getAttributeBridge($attributeCode)
49 | {
50 | return new Attribute(Mage_Catalog_Model_Product::ENTITY, $attributeCode);
51 | }
52 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Test/Model/Bridge/RuleRepository.php:
--------------------------------------------------------------------------------
1 | findRuleSetsForStores($stores);
22 | $expectedRuleSets = $this->expected('stores-%s', join('-', $stores->getStoreCodes()))->getData('rulesets');
23 | $this->assertEquals(count($expectedRuleSets), count($actualRuleSets), 'Expected number of rule sets');
24 | $actualRuleSets->rewind();
25 | while ($actualRuleSets->valid()) {
26 | $this->assertArrayHasKey((string) $actualRuleSets->key(), $expectedRuleSets, 'Expected store code');
27 | $expectedRuleIds = $expectedRuleSets[(string) $actualRuleSets->key()];
28 | $actualRules = $actualRuleSets->current()->getRules();
29 | $this->assertEquals($expectedRuleIds, array_map(function(Rule $rule) use ($ruleRepository) {
30 | return $ruleRepository->getRuleId($rule);
31 | }, $actualRules), 'Expected rules');
32 | $actualRuleSets->next();
33 | }
34 |
35 | }
36 | public static function dataStoreSets()
37 | {
38 | $helper = Mage::helper('derivedattributes');
39 | return array(
40 | [$helper->createStoreSet(['0', '1', '2'])],
41 | [$helper->createStoreSet(['1'])],
42 | [$helper->createStoreSet(['2'])]
43 | );
44 | }
45 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Test/Model/Massupdater.php:
--------------------------------------------------------------------------------
1 | getProcessByCode('catalog_product_flat')->reindexAll();
15 | }
16 |
17 | /**
18 | * @test
19 | * @dataProvider dataProvider
20 | * @dataProviderFile testProductMassUpdateByStoreView
21 | * @loadExpectation attributes
22 | * @helper catalog/product_flat
23 | * @param $entityIds
24 | * @param $storeIds
25 | */
26 | public function testProductMassUpdateByStoreView($entityIds, $storeIds)
27 | {
28 | $this->setCurrentStore('admin');
29 | $this->assertTrue(Mage::getStoreConfigFlag('catalog/frontend/flat_catalog_product'), 'Flat index should be enabled.');
30 | $this->assertFalse(Mage::getResourceModel('catalog/product_collection')->isEnabledFlat(), 'Flat index should not be used');
31 |
32 | /** @var Hackathon_DerivedAttributes_Model_Massupdater $updater */
33 | $updater = Mage::getModel('derivedattributes/massupdater');
34 | $updater->update($entityIds, 'catalog_product', $storeIds, false);
35 |
36 | if ($storeIds === ['0']) {
37 | $storeIds = ['0', '1', '2'];
38 | }
39 | foreach($storeIds as $storeId) {
40 | foreach ($entityIds as $entityId) {
41 | $product = Mage::getModel('catalog/product')->setStoreId($storeId)->load($entityId);
42 | $expectedAttributeValues = $this->expected('product_%d_store_%d', $entityId, $storeId)->getAttributes();
43 | foreach ($expectedAttributeValues as $attributeCode => $expectedValue) {
44 | $this->assertEquals(
45 | $expectedValue,
46 | $product->getData($attributeCode),
47 | sprintf('Store %d Product %d Attribute %s', $storeId, $entityId, $attributeCode));
48 | }
49 | }
50 | }
51 | }
52 |
53 | /**
54 | * @test
55 | * @expectedException Mage_Core_Exception
56 | * @expectedExceptionMessageRegExp Invalid entity type foo_bar.
57 | */
58 | public function testInvalidEntityType()
59 | {
60 | /** @var Hackathon_DerivedAttributes_Model_Massupdater $updater */
61 | $updater = Mage::getModel('derivedattributes/massupdater');
62 | $updater->update(['1'], 'foo_bar', ['0'], false);
63 | }
64 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Test/Model/Massupdater/providers/testProductMassUpdateByStoreView.yaml:
--------------------------------------------------------------------------------
1 | -
2 | entity_ids: [1, 2, 3]
3 | store_ids: ['0']
4 | -
5 | entity_ids: [1, 2, 3]
6 | store_ids: ['1']
7 | -
8 | entity_ids: [1, 2, 3]
9 | store_ids: ['1', '2']
10 |
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Test/Model/Rule.php:
--------------------------------------------------------------------------------
1 | setStoreId($storeId)->load($productId);
22 | $product->setDataChanges(true)->save();
23 | }
24 | $this->assertEventDispatchedAtLeast('model_save_before', count($storeIds));
25 |
26 | foreach ($storeIds as $storeId) {
27 | $product->setStoreId($storeId)->load($productId);
28 | $expectedAttributeValues = $this->expected('product_%d_store_%d', $productId, $storeId)->getAttributes();
29 | foreach ($expectedAttributeValues as $attributeCode => $expectedValue) {
30 | $this->assertEquals(
31 | $expectedValue,
32 | $product->getData($attributeCode));
33 | }
34 | }
35 | }
36 |
37 | /**
38 | * @test
39 | * @dataProvider dataProvider
40 | * @dataProviderFile rulesShouldBeAppliedOnModelSave
41 | * @helper catalog/product_flat
42 | * @param $productId
43 | */
44 | public function rulesShouldBeAppliedOnModelSaveInFrontend($productId)
45 | {
46 | $this->markTestSkipped('model cannot be saved in frontend.');
47 | $this->setCurrentStore('default');
48 | $this->assertTrue(Mage::getResourceModel('catalog/product_collection')->isEnabledFlat());
49 | $this->rulesShouldBeAppliedOnModelSave($productId);
50 | }
51 |
52 | /**
53 | * @test
54 | * @dataProvider dataProvider
55 | * @dataProviderFile rulesShouldBeAppliedOnModelSave
56 | * @helper catalog/product_flat
57 | * @param $productId
58 | */
59 | public function rulesShouldBeAppliedOnModelSaveInFrontendWithoutFlatIndex($productId)
60 | {
61 | $this->markTestSkipped('model cannot be saved in frontend.');
62 | $this->setCurrentStore('default');
63 | $this->app()->getStore()->setConfig('catalog/frontend/flat_catalog_product', '0');
64 | Mage::unregister('_helper/catalog/product_flat');
65 | $this->assertFalse(Mage::getResourceModel('catalog/product_collection')->isEnabledFlat());
66 | $this->rulesShouldBeAppliedOnModelSave($productId);
67 | }
68 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Test/Model/Rule/providers/rulesShouldBeAppliedOnModelSave.yaml:
--------------------------------------------------------------------------------
1 | -
2 | product_id: 1
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Test/expectations/attributes.yaml:
--------------------------------------------------------------------------------
1 | product_1_store_0:
2 | attributes:
3 | description: 'The description contains the short description.'
4 | meta_description: 'meta description.'
5 | meta_keyword: 'meta keyword.'
6 | meta_title: 'meta title.'
7 | product_1_store_1:
8 | attributes:
9 | description: 'The description contains the short description.'
10 | meta_description: 'meta description.'
11 | meta_keyword: 'The meta keywords contain the short description.'
12 | meta_title: 'meta title.'
13 | product_1_store_2:
14 | attributes:
15 | description: 'The description contains the short description in store view'
16 | meta_description: 'The meta description contains the short description in store view'
17 | meta_keyword: 'The meta keywords contain the short description in store view'
18 | meta_title: 'meta title.'
19 | product_2_store_0:
20 | attributes:
21 | description: 'The description contains the short description.'
22 | meta_description: 'meta description.'
23 | meta_keyword: 'meta keyword.'
24 | meta_title: 'meta title.'
25 | product_2_store_1:
26 | attributes:
27 | description: 'The description contains the short description.'
28 | meta_description: 'meta description.'
29 | meta_keyword: 'The meta keywords contain the short description.'
30 | meta_title: 'meta title.'
31 | product_2_store_2:
32 | attributes:
33 | description: 'The description contains the short description in store view'
34 | meta_description: 'The meta description contains the short description in store view'
35 | meta_keyword: 'The meta keywords contain the short description in store view'
36 | meta_title: 'meta title.'
37 | product_3_store_0:
38 | attributes:
39 | description: 'The description contains the short description.'
40 | meta_description: 'meta description.'
41 | meta_keyword: 'meta keyword.'
42 | meta_title: 'meta title.'
43 | product_3_store_1:
44 | attributes:
45 | description: 'The description contains the short description.'
46 | meta_description: 'meta description.'
47 | meta_keyword: 'The meta keywords contain the short description.'
48 | meta_title: 'meta title.'
49 | product_3_store_2:
50 | attributes:
51 | description: 'The description contains the short description in store view'
52 | meta_description: 'The meta description contains the short description in store view'
53 | meta_keyword: 'The meta keywords contain the short description in store view'
54 | meta_title: 'meta title.'
55 |
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Test/expectations/rulesets.yaml:
--------------------------------------------------------------------------------
1 | stores-0-1-2:
2 | rulesets:
3 | 0: ['4', '1']
4 | 1: ['4', '3', '1']
5 | 2: ['4', '3', '2', '1']
6 | stores-1:
7 | rulesets:
8 | 1: ['4', '3', '1']
9 | stores-2:
10 | rulesets:
11 | 2: ['4', '3', '2', '1']
12 |
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Test/fixtures/products.yaml:
--------------------------------------------------------------------------------
1 | config:
2 | default/catalog/frontend/flat_catalog_product: 1
3 | eav:
4 | catalog_product:
5 | - entity_id: 1
6 | type_id: simple
7 | attribute_set_id: 4
8 | name: Test Product 1
9 | sku: test-1
10 | description: description.
11 | meta_description: meta description.
12 | meta_keyword: meta keyword.
13 | meta_title: meta title.
14 | short_description: short description.
15 | /stores:
16 | second:
17 | name: Test Product 1 in store view
18 | description: description in store view
19 | meta_description: meta description in store view
20 | short_description: short description in store view
21 | - entity_id: 2
22 | type_id: simple
23 | attribute_set_id: 4
24 | name: Test Product 2
25 | sku: test-2
26 | description: description.
27 | meta_description: meta description.
28 | meta_keyword: meta keyword.
29 | meta_title: meta title.
30 | short_description: short description.
31 | /stores:
32 | second:
33 | description: description in store view
34 | meta_description: meta description in store view
35 | short_description: short description in store view
36 | - entity_id: 3
37 | type_id: simple
38 | attribute_set_id: 4
39 | name: Test Product 3
40 | sku: test-3
41 | description: description.
42 | meta_description: meta description.
43 | meta_keyword: meta keyword.
44 | meta_title: meta title.
45 | short_description: short description.
46 | /stores:
47 | second:
48 | description: description in store view
49 | meta_description: meta description in store view
50 | short_description: short description in store view
51 |
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Test/fixtures/rules.yaml:
--------------------------------------------------------------------------------
1 | tables:
2 | derivedattributes/rule:
3 | - rule_id: 1
4 | name: Test Rule 1
5 | description: This is a template generator rule for the product description
6 | store_id: 0
7 | attribute_id: 72 #product description
8 | generator_type: template
9 | generator_data: "The description contains the #short_description#"
10 | condition_type: always
11 | condition_data: ""
12 | active: 1
13 | priority: 4
14 | - rule_id: 2
15 | name: Test Rule 2
16 | meta_description: This is a template generator rule for the meta product description (store view based)
17 | store_id: 2
18 | attribute_id: 84 #product meta description
19 | generator_type: template
20 | generator_data: "The meta description contains the #short_description#"
21 | condition_type: always
22 | condition_data: ""
23 | active: 1
24 | priority: 3
25 | - rule_id: 3
26 | name: Test Rule 3
27 | meta_description: This is a template generator rule for the meta product keywords (multiple store views)
28 | store_id: "1,2"
29 | attribute_id: 83 #product meta keywords
30 | generator_type: template
31 | generator_data: "The meta keywords contain the #short_description#"
32 | condition_type: always
33 | condition_data: ""
34 | active: 1
35 | priority: 2
36 | - rule_id: 4
37 | name: Test Rule 4
38 | meta_description: This is a rule for a category attribute with conflicting attribute code
39 | store_id: 0
40 | attribute_id: 46 #category meta_title
41 | generator_type: template
42 | generator_data: "I am a category"
43 | condition_type: always
44 | condition_data: ""
45 | active: 1
46 | priority: 1
47 |
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/Test/fixtures/stores.yaml:
--------------------------------------------------------------------------------
1 | scope:
2 | website: # Initialize websites
3 | - website_id: 2
4 | code: second_website
5 | name: Second Website
6 | default_group_id: 2
7 | group: # Initializes store groups
8 | - group_id: 2
9 | website_id: 2
10 | name: Second Store Group
11 | default_store_id: 2
12 | root_category_id: 2 # Default Category
13 | store: # Initializes store views
14 | - store_id: 2
15 | website_id: 2
16 | group_id: 2
17 | code: second
18 | name: Second Store
19 | is_active: 1
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/controllers/Adminhtml/DerivedAttributes/EntityController.php:
--------------------------------------------------------------------------------
1 | isAllowed('catalog/attributes/derived_attributes');
8 | }
9 |
10 | protected function _initAction()
11 | {
12 | $this->loadLayout()
13 | ->_setActiveMenu('catalog/attributes/derived_attributes')
14 | ->_addBreadcrumb($this->__('Derived Attributes'),$this->__('Entities'))
15 | ;
16 | return $this;
17 | }
18 |
19 | public function indexAction()
20 | {
21 | $this->_title($this->__('Derived Attributes'))->_title($this->__('Entities'));
22 | $this->_initAction();
23 | Mage::helper('integernet_gridmassactionpager')->addScript();
24 | $this->renderLayout();
25 | }
26 |
27 | /**
28 | * Product grid for AJAX request
29 | */
30 | public function productGridAction()
31 | {
32 | $this->loadLayout();
33 | $this->renderLayout();
34 | }
35 | /**
36 | * Customer grid for AJAX request
37 | */
38 | public function customerGridAction()
39 | {
40 | $this->loadLayout();
41 | $this->renderLayout();
42 | }
43 |
44 | public function applyRulesAction()
45 | {
46 | /** @var IntegerNet_GridMassActionPager_Model_GridMassActionPager $gridMassActionPager */
47 | $gridMassActionPager = Mage::getModel('integernet_gridmassactionpager/gridMassActionPager');
48 |
49 | $error = false;
50 | try {
51 | if ($entityIds = (array)$this->getRequest()->getParam('entity_ids')) {
52 |
53 | $gridMassActionPager->init($entityIds, 100);
54 | $gridMassActionPager->getPager()
55 | ->setEntityType($this->getRequest()->getParam('entity_type'))
56 | ->setStoreId($this->getRequest()->getParam('store_id'))
57 | ->setDryRun((bool) $this->getRequest()->getParam('dry_run'));
58 |
59 | } elseif ($pageIds = $gridMassActionPager->getPageIds()) {
60 |
61 | $entityType = $gridMassActionPager->getPager()->getEntityType();
62 | $isDryRun = $gridMassActionPager->getPager()->getDryRun();
63 | $storeIds = $gridMassActionPager->getPager()->getStoreId();
64 | $this->_process($pageIds, $entityType, $storeIds, $isDryRun);
65 |
66 | $gridMassActionPager->next();
67 | } else {
68 | //TODO (optionally) generate results page, trigger redirect
69 | // - log generated attributes to new table with RuleLogger (entity_type, attribute_id, applied_rule_id, value)
70 | // - override IntegerNet_GridMassActionPager.prototype.process(transport)
71 | // - if (transport.final), redirect
72 | }
73 | } catch (Exception $e) {
74 | $error = $e->getMessage();
75 | }
76 |
77 | $message = $this->__('Process Entities...
{{from}} to {{to}} of {{of}}');
78 |
79 | $this->getResponse()->setHeader('Content-Type', 'application/json');
80 | $status = $gridMassActionPager->getStatus(false, $message);
81 | if ($error) {
82 | $status['error'] = $error;
83 | }
84 | //TODO show error in frontend
85 | $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($status));
86 | }
87 | protected function _process($pageIds, $entityType, $storeIds, $isDryRun)
88 | {
89 | Mage::getModel('derivedattributes/massupdater')->update($pageIds, $entityType, $storeIds, $isDryRun);
90 | }
91 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/controllers/Adminhtml/DerivedAttributes/RuleController.php:
--------------------------------------------------------------------------------
1 | isAllowed('catalog/attributes/derived_attributes');
8 | }
9 |
10 | /**
11 | * @var Hackathon_DerivedAttributes_Model_Resource_Rule
12 | */
13 | protected $_repository;
14 |
15 | protected function _construct()
16 | {
17 | $this->_repository = Mage::getModel('derivedattributes/bridge_ruleRepository');
18 | }
19 |
20 | protected function _initAction()
21 | {
22 | $this->loadLayout()
23 | ->_setActiveMenu('catalog/attributes/derived_attributes')
24 | ->_addBreadcrumb($this->__('Derived Attributes'),$this->__('Rules'))
25 | ;
26 | return $this;
27 | }
28 |
29 | public function indexAction()
30 | {
31 | $this->_title($this->__('Derived Attributes'))->_title($this->__('Rules'));
32 |
33 | $this->_initAction()
34 | ->renderLayout();
35 | }
36 |
37 | public function newAction()
38 | {
39 | $this->_forward('edit');
40 | }
41 |
42 | public function editAction()
43 | {
44 | $id = $this->getRequest()->getParam('id');
45 | $model = new Varien_Object();
46 |
47 | if ($id) {
48 | try {
49 | $rule = $this->_repository->findById($id);
50 | $model = $this->_repository->getRuleModel($rule);
51 | } catch (Mage_Core_Exception $e) {
52 | Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
53 | $this->_redirect('*/*');
54 | return;
55 | }
56 | }
57 |
58 | $this->_title($model->getId() ? $rule->getName() : $this->__('New Rule'));
59 |
60 | $data = Mage::getSingleton('adminhtml/session')->getPageData(true);
61 | if (!empty($data)) {
62 | $model->addData($data);
63 | }
64 |
65 | Mage::register('current_derived_attribute_rule', $model);
66 |
67 | $this->_initAction()->getLayout()->getBlock('derivedattributes_rule_edit')
68 | ->setData('action', $this->getUrl('*/*/save'));
69 |
70 | $this
71 | ->_addBreadcrumb(
72 | $id ? $this->__('Edit Rule')
73 | : $this->__('New Rule'),
74 | $id ? $this->__('Edit Rule')
75 | : $this->__('New Rule'))
76 | ->renderLayout();
77 |
78 | }
79 |
80 | public function saveAction()
81 | {
82 | if ($this->getRequest()->getPost()) {
83 | try {
84 | Mage::dispatchEvent(
85 | 'adminhtml_controller_derivedattributes_rule_prepare_save',
86 | array('request' => $this->getRequest()));
87 | $data = $this->getRequest()->getPost();
88 | // $data = $this->_filterDates($data, array('from_date', 'to_date'));
89 |
90 | // $validateResult = $model->validateData(new Varien_Object($data));
91 | // if ($validateResult !== true) {
92 | // foreach($validateResult as $errorMessage) {
93 | // $session->addError($errorMessage);
94 | // }
95 | // $session->setPageData($data);
96 | // $this->_redirect('*/*/edit', array('id'=>$model->getId()));
97 | // return;
98 | // }
99 |
100 |
101 | $id = $this->getRequest()->getParam('rule_id');
102 | if ($id) {
103 | $oldRule = $this->_repository->findById($id);
104 | $model = $this->_repository->getRuleModel($oldRule);
105 | $model->addData($data);
106 | $newRule = $this->_createRuleFromModel($model);
107 | $this->_repository->replaceRule($oldRule, $newRule);
108 | } else {
109 | $newRule = $this->_createRuleFromModel(new Varien_Object($data));
110 | $this->_repository->createRule($newRule);
111 | }
112 |
113 | $this->_getSession()->addSuccess($this->__('The rule has been saved.'));
114 | $this->_getSession()->setPageData(false);
115 | if ($this->getRequest()->getParam('back')) {
116 | $this->_redirect('*/*/edit', array('id' => $this->_repository->getRuleId($newRule)));
117 | return;
118 | }
119 | $this->_redirect('*/*/');
120 | return;
121 | } catch (Mage_Core_Exception $e) {
122 | $this->_getSession()->addError($e->getMessage());
123 | $id = (int)$this->getRequest()->getParam('rule_id');
124 | if (!empty($id)) {
125 | $this->_redirect('*/*/edit', array('id' => $id));
126 | } else {
127 | $this->_redirect('*/*/new');
128 | }
129 | return;
130 |
131 | } catch (Exception $e) {
132 | $this->_getSession()->addError(
133 | $this->__('An error occurred while saving the rule data. Please review the log and try again.'));
134 | Mage::logException($e);
135 | Mage::getSingleton('adminhtml/session')->setPageData($data);
136 | $this->_redirect('*/*/edit', array('id' => $this->getRequest()->getParam('rule_id')));
137 | return;
138 | }
139 | }
140 | $this->_redirect('*/*/');
141 | }
142 |
143 | public function deleteAction()
144 | {
145 | if ($id = $this->getRequest()->getParam('id')) {
146 | try {
147 | $ruleToBeDeleted = $this->_repository->findById($id);
148 | $this->_repository->deleteRule($ruleToBeDeleted);
149 | Mage::getSingleton('adminhtml/session')->addSuccess(
150 | $this->__('The rule has been deleted.'));
151 | $this->_redirect('*/*/');
152 | return;
153 | } catch (Mage_Core_Exception $e) {
154 | $this->_getSession()->addError($e->getMessage());
155 | } catch (Exception $e) {
156 | $this->_getSession()->addError(
157 | $this->__('An error occurred while deleting the rule. Please review the log and try again.'));
158 | Mage::logException($e);
159 | $this->_redirect('*/*/edit', array('id' => $this->getRequest()->getParam('id')));
160 | return;
161 | }
162 | }
163 | Mage::getSingleton('adminhtml/session')->addError(
164 | $this->__('Unable to find a rule to delete.'));
165 | $this->_redirect('*/*/');
166 | }
167 |
168 | public function gridAction()
169 | {
170 | $this->_title($this->__('Derived Attributes'))->_title($this->__('Rules'));
171 | $this->loadLayout()->renderLayout();
172 | }
173 |
174 | /**
175 | * @param $model
176 | * @return \Hackathon\DerivedAttributes\Rule
177 | */
178 | protected function _createRuleFromModel($model)
179 | {
180 | $newRule = Mage::helper('derivedattributes/rule_director')->createRule($model);
181 | $this->_getSession()->setPageData($this->_repository->getRuleModel($newRule)->getData());
182 | return $newRule;
183 | }
184 |
185 | }
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/etc/adminhtml.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | Derived Attributes
28 | 100
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/etc/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 0.1.3
6 |
7 |
8 |
9 |
10 |
11 | Hackathon_DerivedAttributes_Block
12 |
13 |
14 |
15 |
16 | Hackathon_DerivedAttributes_Helper
17 |
18 |
19 |
20 |
21 | Hackathon_DerivedAttributes_Model
22 | derivedattributes_resource
23 |
24 |
25 | Hackathon_DerivedAttributes_Model_Resource
26 |
27 |
28 |
29 |
30 |
31 | derivedattributes_rule_filter
32 |
33 |
34 | derivedattributes_rule_condition
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | Hackathon_DerivedAttributes
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | singleton
51 | Hackathon_DerivedAttributes_Model_Observer
52 | beforeModelSave
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Hackathon_DerivedAttributes_Adminhtml
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | hackathon_derivedattributes.xml
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/sql/derivedattributes_setup/mysql4-install-0.1.0.php:
--------------------------------------------------------------------------------
1 | startSetup();
7 |
8 | // install table
9 | $createTableStatements = "
10 |
11 | CREATE TABLE `{$installer->getTable('derivedattributes/rule')}` (
12 | `rule_id` int(11) NOT NULL AUTO_INCREMENT,
13 | `attribute_id` smallint(5) unsigned NOT NULL,
14 | `generator_type` varchar(64) DEFAULT NULL,
15 | `generator_data` tinytext,
16 | `condition_type` varchar(64) DEFAULT NULL,
17 | `condition_data` tinytext,
18 | `store_id` smallint(5) unsigned DEFAULT NULL,
19 | `active` tinyint(4) NOT NULL DEFAULT '1',
20 | `priority` int(11) DEFAULT '0',
21 | PRIMARY KEY (`rule_id`),
22 | KEY `fk_derivedattributes_rule_1_idx` (`attribute_id`),
23 | KEY `fk_derivedattributes_rule_2_idx` (`store_id`),
24 | CONSTRAINT `fk_derivedattributes_rule_2` FOREIGN KEY (`store_id`) REFERENCES `core_store` (`store_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
25 | CONSTRAINT `fk_derivedattributes_rule_1` FOREIGN KEY (`attribute_id`) REFERENCES `eav_attribute` (`attribute_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
26 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
27 |
28 | CREATE TABLE `{$installer->getTable('derivedattributes/rulecondition')}` (
29 | `rule_condition_id` int(11) NOT NULL AUTO_INCREMENT,
30 | `rule_id` int(11) DEFAULT NULL,
31 | `condition_type` varchar(64) DEFAULT NULL,
32 | `condition_data` tinytext,
33 | `active` tinyint(4) NOT NULL DEFAULT '1',
34 | PRIMARY KEY (`rule_condition_id`),
35 | KEY `fk_derivedattributes_rule_condition_1_idx` (`rule_id`),
36 | CONSTRAINT `fk_derivedattributes_rule_condition_1` FOREIGN KEY (`rule_id`) REFERENCES `derivedattributes_rule` (`rule_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
37 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
38 |
39 | CREATE TABLE `{$installer->getTable('derivedattributes/rulefilter')}` (
40 | `rule_filter_id` int(11) NOT NULL AUTO_INCREMENT,
41 | `rule_id` int(11) DEFAULT NULL,
42 | `filter_type` varchar(64) DEFAULT NULL,
43 | `filter_data` tinytext,
44 | `active` tinyint(4) NOT NULL DEFAULT '1',
45 | `priority` int(11) DEFAULT '0',
46 | PRIMARY KEY (`rule_filter_id`),
47 | KEY `fk_derivedattributes_rule_filter_1_idx` (`rule_id`),
48 | CONSTRAINT `fk_derivedattributes_rule_filter_1` FOREIGN KEY (`rule_id`) REFERENCES `derivedattributes_rule` (`rule_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
49 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
50 |
51 | ";
52 | $installer->run($createTableStatements);
53 |
54 | $installer->endSetup();
55 |
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/sql/derivedattributes_setup/upgrade-0.1.0-0.1.1.php:
--------------------------------------------------------------------------------
1 | startSetup();
7 | $ruleTable = $this->getTable('derivedattributes/rule');
8 |
9 | $installer->getConnection()->addColumn($ruleTable, 'name', array(
10 | 'type' => Varien_Db_Ddl_Table::TYPE_TEXT,
11 | 'size' => 255,
12 | 'nullable' => false,
13 | 'comment' => 'Rule name'
14 | ));
15 | $installer->getConnection()->addColumn($ruleTable, 'description', array(
16 | 'type' => Varien_Db_Ddl_Table::TYPE_TEXT,
17 | 'size' => 65536,
18 | 'nullable' => false,
19 | 'default' => '',
20 | 'comment' => 'Rule Description'
21 | ));
22 |
23 | $installer->endSetup();
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/sql/derivedattributes_setup/upgrade-0.1.0-0.1.2.php:
--------------------------------------------------------------------------------
1 | startSetup();
7 | $ruleTable = $this->getTable('derivedattributes/rule');
8 |
9 | $installer->getConnection()->addColumn($ruleTable, 'name', array(
10 | 'type' => Varien_Db_Ddl_Table::TYPE_TEXT,
11 | 'length' => 255,
12 | 'nullable' => false,
13 | 'comment' => 'Rule name'
14 | ));
15 | $installer->getConnection()->addColumn($ruleTable, 'description', array(
16 | 'type' => Varien_Db_Ddl_Table::TYPE_TEXT,
17 | 'length' => 65536,
18 | 'nullable' => false,
19 | 'default' => '',
20 | 'comment' => 'Rule description'
21 | ));
22 |
23 | $installer->endSetup();
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/sql/derivedattributes_setup/upgrade-0.1.1-0.1.2.php:
--------------------------------------------------------------------------------
1 | startSetup();
7 | $ruleTable = $this->getTable('derivedattributes/rule');
8 |
9 | $installer->getConnection()->modifyColumn($ruleTable, 'name', array(
10 | 'type' => Varien_Db_Ddl_Table::TYPE_TEXT,
11 | 'length' => 255,
12 | 'nullable' => false,
13 | 'comment' => 'Rule name'
14 | ));
15 | $installer->getConnection()->addColumn($ruleTable, 'description', array(
16 | 'type' => Varien_Db_Ddl_Table::TYPE_TEXT,
17 | 'length' => 65536,
18 | 'nullable' => false,
19 | 'default' => '',
20 | 'comment' => 'Rule description'
21 | ));
22 |
23 | $installer->endSetup();
--------------------------------------------------------------------------------
/src/app/code/community/Hackathon/DerivedAttributes/sql/derivedattributes_setup/upgrade-0.1.2-0.1.3.php:
--------------------------------------------------------------------------------
1 | startSetup();
7 | $ruleTable = $this->getTable('derivedattributes/rule');
8 |
9 | $installer->getConnection()->dropForeignKey($ruleTable, 'fk_derivedattributes_rule_2');
10 | $installer->getConnection()->modifyColumn($ruleTable, 'store_id', array(
11 | 'type' => Varien_Db_Ddl_Table::TYPE_TEXT,
12 | 'length' => 255,
13 | 'nullable' => false,
14 | 'comment' => 'Rule name'
15 | ));
16 | $installer->getConnection()->resetDdlCache($ruleTable);
17 |
18 | $installer->endSetup();
--------------------------------------------------------------------------------
/src/app/design/adminhtml/base/default/layout/hackathon_derivedattributes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | 1
11 | 1
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/app/etc/modules/Hackathon_DerivedAttributes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | true
6 | community
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/lib/Hackathon/DerivedAttributes/Attribute.php:
--------------------------------------------------------------------------------
1 | entityTypeCode = (string) $entityTypeCode;
29 | $this->attributeCode = (string) $attributeCode;
30 | }
31 |
32 | /**
33 | * @return string
34 | */
35 | public function getAttributeCode()
36 | {
37 | return $this->attributeCode;
38 | }
39 |
40 | /**
41 | * @return string
42 | */
43 | public function getEntityTypeCode()
44 | {
45 | return $this->entityTypeCode;
46 | }
47 |
48 | }
--------------------------------------------------------------------------------
/src/lib/Hackathon/DerivedAttributes/BridgeInterface/EntityInterface.php:
--------------------------------------------------------------------------------
1 | attribute = $builder->getAttribute();
58 | $this->condition = $builder->getCondition();
59 | $this->generator = $builder->getGenerator();
60 | $this->priority = $builder->getPriority();
61 | $this->active = $builder->isActive();
62 | $this->name = $builder->getName();
63 | $this->description = $builder->getDescription();
64 | $this->stores = $builder->getStores();
65 | }
66 |
67 | /**
68 | * @return int
69 | */
70 | public function getPriority()
71 | {
72 | return $this->priority;
73 | }
74 |
75 | /**
76 | * @return boolean
77 | */
78 | public function isActive()
79 | {
80 | return $this->active;
81 | }
82 |
83 | /**
84 | * @return string
85 | */
86 | public function getName()
87 | {
88 | return $this->name;
89 | }
90 |
91 | /**
92 | * @return string
93 | */
94 | public function getDescription()
95 | {
96 | return $this->description;
97 | }
98 |
99 | /**
100 | * @return Attribute
101 | */
102 | public function getAttribute()
103 | {
104 | return $this->attribute;
105 | }
106 |
107 | /**
108 | * @return StoreSet
109 | */
110 | public function getStores()
111 | {
112 | return $this->stores;
113 | }
114 |
115 | /**
116 | * @return ConditionInterface
117 | */
118 | public function getCondition()
119 | {
120 | return $this->condition;
121 | }
122 |
123 | /**
124 | * @return GeneratorInterface
125 | */
126 | public function getGenerator()
127 | {
128 | return $this->generator;
129 | }
130 |
131 | /**
132 | * Applies attribute rule to product. Returns true if rule was applicable
133 | *
134 | * @param EntityInterface $entity
135 | * @return bool
136 | */
137 | public function applyToEntity(EntityInterface $entity)
138 | {
139 | if ($entity->hasAttribute($this->attribute) && $this->condition->match($entity)) {
140 | $value = $this->generator->generateAttributeValue($entity);
141 | $entity->setAttributeValue($this->getAttribute(), $value);
142 | return true;
143 | }
144 | return false;
145 | }
146 | }
--------------------------------------------------------------------------------
/src/lib/Hackathon/DerivedAttributes/RuleBuilder.php:
--------------------------------------------------------------------------------
1 | serviceManager = $serviceManager;
33 | $this->attribute = $attribute;
34 | $this->stores = StoreSet::all();
35 | }
36 |
37 | /**
38 | * @param boolean $active
39 | * @return $this
40 | */
41 | public function setActive($active)
42 | {
43 | $this->active = $active;
44 | return $this;
45 | }
46 | /**
47 | * @param $priority
48 | * @return $this
49 | */
50 | public function setPriority($priority)
51 | {
52 | $this->priority = $priority;
53 | return $this;
54 | }
55 |
56 | /**
57 | * @param string $name
58 | * @return $this
59 | */
60 | public function setName($name)
61 | {
62 | $this->name = $name;
63 | return $this;
64 | }
65 |
66 | /**
67 | * @param string $description
68 | * @return $this
69 | */
70 | public function setDescription($description)
71 | {
72 | $this->description = $description;
73 | return $this;
74 | }
75 |
76 | /**
77 | * @param mixed $conditionType
78 | * @return $this
79 | */
80 | public function setConditionType($conditionType)
81 | {
82 | $this->conditionType = $conditionType;
83 | return $this;
84 | }
85 |
86 | /**
87 | * @param mixed $conditionData
88 | * @return $this
89 | */
90 | public function setConditionData($conditionData)
91 | {
92 | $this->conditionData = $conditionData;
93 | return $this;
94 | }
95 |
96 | /**
97 | * @param mixed $generatorType
98 | * @return $this
99 | */
100 | public function setGeneratorType($generatorType)
101 | {
102 | $this->generatorType = $generatorType;
103 | return $this;
104 | }
105 |
106 | /**
107 | * @param mixed $generatorData
108 | * @return $this
109 | */
110 | public function setGeneratorData($generatorData)
111 | {
112 | $this->generatorData = $generatorData;
113 | return $this;
114 | }
115 |
116 | /**
117 | * @param GeneratorInterface $generator
118 | * @return $this
119 | */
120 | public function setGenerator(GeneratorInterface $generator)
121 | {
122 | $this->generator = $generator;
123 | return $this;
124 | }
125 | /**
126 | * @param ConditionInterface $condition
127 | * @return $this
128 | */
129 | public function setCondition(ConditionInterface $condition)
130 | {
131 | $this->condition = $condition;
132 | return $this;
133 | }
134 |
135 | /**
136 | * @return $this
137 | */
138 | private function buildCondition()
139 | {
140 | if ($this->condition === null) {
141 | $this->setCondition($this->serviceManager->getCondition($this->conditionType, $this->conditionData));
142 | }
143 | return $this;
144 | }
145 |
146 | /**
147 | * @return $this
148 | */
149 | private function buildGenerator()
150 | {
151 | if ($this->generator === null) {
152 | $this->setGenerator($this->serviceManager->getGenerator($this->generatorType, $this->generatorData));
153 | }
154 | return $this;
155 | }
156 | /**
157 | * @param Attribute $attribute
158 | * @return $this
159 | */
160 | public function setAttribute(Attribute $attribute)
161 | {
162 | $this->attribute = $attribute;
163 | return $this;
164 | }
165 |
166 | public function setStores(StoreSet $stores)
167 | {
168 | $this->stores = $stores;
169 | }
170 |
171 | /**
172 | * @return boolean
173 | */
174 | public function isActive()
175 | {
176 | return $this->active;
177 | }
178 |
179 | /**
180 | * @return int
181 | */
182 | public function getPriority()
183 | {
184 | return $this->priority;
185 | }
186 |
187 | /**
188 | * @return string
189 | */
190 | public function getName()
191 | {
192 | return $this->name;
193 | }
194 |
195 | /**
196 | * @return string
197 | */
198 | public function getDescription()
199 | {
200 | return $this->description;
201 | }
202 |
203 | /**
204 | * @return mixed
205 | */
206 | public function getCondition()
207 | {
208 | return $this->condition;
209 | }
210 |
211 | /**
212 | * @return mixed
213 | */
214 | public function getGenerator()
215 | {
216 | return $this->generator;
217 | }
218 |
219 | /**
220 | * @return Attribute
221 | */
222 | public function getAttribute()
223 | {
224 | return $this->attribute;
225 | }
226 |
227 | /**
228 | * @return StoreSet
229 | */
230 | public function getStores()
231 | {
232 | return $this->stores;
233 | }
234 |
235 | /**
236 | * @return Rule
237 | */
238 | public function build()
239 | {
240 | $this->buildCondition();
241 | $this->buildGenerator();
242 | $rule = new Rule($this);
243 | return $rule;
244 | }
245 | }
--------------------------------------------------------------------------------
/src/lib/Hackathon/DerivedAttributes/RuleSet.php:
--------------------------------------------------------------------------------
1 | rules[] = $rule;
31 | }
32 |
33 | public function getRules()
34 | {
35 | return $this->rules;
36 | }
37 |
38 | /**
39 | * @param EntityInterface $entity
40 | * @return void
41 | */
42 | public function applyToEntity(EntityInterface $entity, RuleLoggerInterface $logger)
43 | {
44 | $affectedAttributes = array();
45 | foreach ($this->rules as $rule) {
46 | $code = $rule->getAttribute()->getAttributeCode();
47 | if (isset($affectedAttributes[$code])) {
48 | break;
49 | }
50 | if ($rule->applyToEntity($entity)) {
51 | $logger->logAppliedRule($rule, $entity->getAttributeValue($rule->getAttribute()));
52 | $affectedAttributes[$code] = true;
53 | }
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/src/lib/Hackathon/DerivedAttributes/RuleSetsByStore.php:
--------------------------------------------------------------------------------
1 | ruleSets[(string) $store] = $ruleSet;
25 | $this->stores[(string) $store] = $store;
26 | }
27 |
28 | /**
29 | * @param Store $store
30 | * @return RuleSet
31 | */
32 | public function getRuleSet(Store $store)
33 | {
34 | if (! isset($this->ruleSets[(string) $store])) {
35 | throw new \OutOfBoundsException("No rule set found for store {$store}");
36 | }
37 | return $this->ruleSets[(string) $store];
38 | }
39 |
40 | public function current()
41 | {
42 | return current($this->ruleSets);
43 | }
44 |
45 | public function next()
46 | {
47 | next($this->ruleSets);
48 | next($this->stores);
49 | }
50 |
51 | public function key()
52 | {
53 | return current($this->stores);
54 | }
55 |
56 | public function valid()
57 | {
58 | return current($this->ruleSets) !== false;
59 | }
60 |
61 | public function rewind()
62 | {
63 | reset($this->ruleSets);
64 | reset($this->stores);
65 | }
66 |
67 | public function count()
68 | {
69 | return count($this->ruleSets);
70 | }
71 |
72 | }
--------------------------------------------------------------------------------
/src/lib/Hackathon/DerivedAttributes/Service/Condition/AlwaysCondition.php:
--------------------------------------------------------------------------------
1 | data = $data;
23 | return $this;
24 | }
25 |
26 | /**
27 | * @internal used to test instantiation
28 | * @return string
29 | */
30 | public function getData()
31 | {
32 | return $this->data;
33 | }
34 | /**
35 | * @param EntityInterface $entity
36 | * @return boolean
37 | */
38 | public function match(EntityInterface $entity)
39 | {
40 | return true;
41 | }
42 |
43 | /**
44 | * @return string
45 | */
46 | public function getTitle()
47 | {
48 | return self::TITLE;
49 | }
50 |
51 | /**
52 | * @return string
53 | */
54 | public function getDescription()
55 | {
56 | return self::DESCRIPTION;
57 | }
58 |
59 | }
--------------------------------------------------------------------------------
/src/lib/Hackathon/DerivedAttributes/Service/Condition/BooleanAttributeCondition.php:
--------------------------------------------------------------------------------
1 | data = $data;
24 | return $this;
25 | }
26 |
27 | /**
28 | * @internal used to test instantiation
29 | * @return string
30 | */
31 | public function getData()
32 | {
33 | return $this->data;
34 | }
35 | /**
36 | * @param EntityInterface $entity
37 | * @return boolean
38 | */
39 | public function match(EntityInterface $entity)
40 | {
41 | return (bool) $entity->getAttributeValue(new Attribute($entity->getEntityTypeCode(), $this->data));
42 | }
43 |
44 | /**
45 | * @return string
46 | */
47 | public function getTitle()
48 | {
49 | return self::TITLE;
50 | }
51 |
52 | /**
53 | * @return string
54 | */
55 | public function getDescription()
56 | {
57 | return self::DESCRIPTION;
58 | }
59 |
60 | }
--------------------------------------------------------------------------------
/src/lib/Hackathon/DerivedAttributes/Service/Generator/TemplateGenerator.php:
--------------------------------------------------------------------------------
1 | data = $data;
25 | return $this;
26 | }
27 |
28 | /**
29 | * @internal used to test instantiation
30 | * @return string
31 | */
32 | public function getData()
33 | {
34 | return $this->data;
35 | }
36 |
37 | /**
38 | * @param EntityInterface $entity
39 | * @return mixed
40 | */
41 | public function generateAttributeValue(EntityInterface $entity)
42 | {
43 | return $this->_parseData($entity);
44 | }
45 |
46 | /**
47 | * @return string
48 | */
49 | public function getTitle()
50 | {
51 | return self::TITLE;
52 | }
53 |
54 | /**
55 | * @return string
56 | */
57 | public function getDescription()
58 | {
59 | return self::DESCRIPTION;
60 | }
61 |
62 | /**
63 | * Return default value based on configured template
64 | *
65 | * @return string
66 | */
67 | private function _parseData(EntityInterface $entity)
68 | {
69 | $this->entity = $entity;
70 | $valueTemplate = $this->data;
71 | $value = preg_replace_callback('~#([\w\-]+)#~', function ($matches) use ($entity) {
72 | /*
73 | * getData() for multiselect attribute should be a comma separated string,
74 | * but something converts it to an array and we need to revert that to make
75 | * the frontend model work:
76 | */
77 | $attr = new Attribute($entity->getEntityTypeCode(), $matches[1]);
78 | if (is_array($this->entity->getLocalizedAttributeValue($attr))) {
79 | $this->entity->getLocalizedAttributeValue($attr,
80 | join(',', $this->entity->getLocalizedAttributeValue($attr)));
81 | }
82 | $_value = $this->entity->getLocalizedAttributeValue($attr);
83 | return $_value;
84 | }, $valueTemplate);
85 | return $value;
86 | }
87 |
88 | }
--------------------------------------------------------------------------------
/src/lib/Hackathon/DerivedAttributes/Service/Manager.php:
--------------------------------------------------------------------------------
1 | resetGeneratorTypes();
19 | $this->resetConditionTypes();
20 | }
21 |
22 | public function resetGeneratorTypes(){
23 | $this->generatorTypes = [
24 | 'template' => TemplateGenerator::__CLASS
25 | ];
26 | }
27 |
28 | public function resetConditionTypes(){
29 | $this->conditionTypes = [
30 | 'boolean-attribute' => BooleanAttributeCondition::__CLASS,
31 | 'always' => AlwaysCondition::__CLASS
32 | ];
33 | }
34 |
35 | public function getGeneratorTypes(){
36 | return $this->generatorTypes;
37 | }
38 |
39 | public function addGeneratorType($id, $class){
40 | if(is_object($class)){
41 | $class = get_class($class);
42 | }
43 | $this->generatorTypes[(string)$id] = $class;
44 | }
45 |
46 | public function getConditionTypes(){
47 | return $this->conditionTypes;
48 | }
49 |
50 | public function addConditionType($id, $class){
51 | if(is_object($class)){
52 | $class = get_class($class);
53 | }
54 | $this->conditionTypes[(string)$id] = $class;
55 | }
56 |
57 | /**
58 | * Return meta information of available generators
59 | *
60 | * @return array as [ $identifier => [ "title" => $title, "description" => $description ]
61 | */
62 | public function getAvailableGeneratorTypes()
63 | {
64 | $result = array();
65 | foreach ($this->generatorTypes as $id => $class) {
66 | if (is_subclass_of($class, GeneratorInterface::__INTERFACE)) {
67 | $generator = new $class;
68 | $result[$id] = [
69 | 'title' => $generator->getTitle(),
70 | 'description' => $generator->getDescription()
71 | ];
72 | }
73 | }
74 | return $result;
75 | }
76 |
77 | /**
78 | * Return meta information of available generators
79 | *
80 | * @return array as [ $identifier => [ "title" => $title, "description" => $description ]
81 | */
82 | public function getAvailableConditionTypes()
83 | {
84 | $result = array();
85 | foreach ($this->conditionTypes as $id => $class) {
86 | if (is_subclass_of($class, ConditionInterface::__INTERFACE)) {
87 | $condition = new $class;
88 | $result[$id] = ['title' => $condition->getTitle(), 'description' => $condition->getDescription()];
89 | }
90 | }
91 | return $result;
92 | }
93 |
94 | /**
95 | * Factory method: instantiate condition based on bridge interface
96 | *
97 | * @param $conditionType
98 | * @param $conditionData
99 | * @return ConditionInterface
100 | */
101 | public function getCondition($conditionType, $conditionData)
102 | {
103 | if (!isset($this->conditionTypes[$conditionType])) {
104 | throw new \InvalidArgumentException(sprintf('Unknown condition type "%s".', $conditionType));
105 | }
106 | /** @var ConditionInterface $condition */
107 | $condition = new $this->conditionTypes[$conditionType];
108 | $condition->configure($conditionData);
109 | return $condition;
110 | }
111 |
112 | /**
113 | * Factory method: instantiate generator based on bridge interface
114 | *
115 | * @param $generatorType
116 | * @param $generatorData
117 | * @return GeneratorInterface
118 | */
119 | public function getGenerator($generatorType, $generatorData)
120 | {
121 | if (!isset($this->generatorTypes[$generatorType])) {
122 | throw new \InvalidArgumentException(sprintf('Unknown generator type "%s".', $generatorType));
123 | }
124 | /** @var GeneratorInterface $generator */
125 | $generator = new $this->generatorTypes[$generatorType];
126 | $generator->configure($generatorData);
127 | return $generator;
128 | }
129 |
130 | /**
131 | * Determine condition type identifier based on condition instance
132 | *
133 | * @param ConditionInterface $condition
134 | * @return string
135 | * @throws \OutOfBoundsException
136 | */
137 | public function getConditionType(ConditionInterface $condition)
138 | {
139 | $result = array_search(get_class($condition), $this->conditionTypes);
140 | if ($result === false) {
141 | throw new \OutOfBoundsException('Condition type not registered.');
142 | }
143 | return $result;
144 | }
145 |
146 | /**
147 | * Determine generator type identifier based on generator instance
148 | *
149 | * @param GeneratorInterface $generator
150 | * @return string
151 | * @throws \OutOfBoundsException
152 | */
153 | public function getGeneratorType(GeneratorInterface $generator)
154 | {
155 | $result = array_search(get_class($generator), $this->generatorTypes);
156 | if ($result === false) {
157 | throw new \OutOfBoundsException('Generator type not registered.');
158 | }
159 | return $result;
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/src/lib/Hackathon/DerivedAttributes/ServiceInterface/ConditionInterface.php:
--------------------------------------------------------------------------------
1 | storeCode = (string) $storeCode;
18 | }
19 | public function __toString()
20 | {
21 | return $this->storeCode;
22 | }
23 | }
--------------------------------------------------------------------------------
/src/lib/Hackathon/DerivedAttributes/StoreSet.php:
--------------------------------------------------------------------------------
1 | stores[(string) $storeCode] = new Store($storeCode);
29 | }
30 | }
31 |
32 | /**
33 | * Returns special instance representing all stores
34 | *
35 | * @return StoreSet
36 | */
37 | public static function all()
38 | {
39 | static $all;
40 | if (! $all) {
41 | $all = new self([]);
42 | }
43 | return $all;
44 | }
45 |
46 | /**
47 | * @return string[]
48 | */
49 | public function getStoreCodes()
50 | {
51 | return array_keys($this->stores);
52 | }
53 |
54 | /**
55 | * (PHP 5 >= 5.0.0)
56 | * Retrieve an external iterator
57 | * @link http://php.net/manual/en/iteratoraggregate.getiterator.php
58 | * @return Traversable An instance of an object implementing Iterator or
59 | * Traversable
60 | */
61 | public function getIterator()
62 | {
63 | return new ArrayIterator($this->stores);
64 | }
65 |
66 | /**
67 | * (PHP 5 >= 5.1.0)
68 | * Count elements of an object
69 | * @link http://php.net/manual/en/countable.count.php
70 | * @return int The custom count as an integer.
71 | *
72 | *
73 | * The return value is cast to an integer.
74 | */
75 | public function count()
76 | {
77 | return count($this->stores);
78 | }
79 |
80 |
81 | }
--------------------------------------------------------------------------------
/src/lib/Hackathon/DerivedAttributes/Updater.php:
--------------------------------------------------------------------------------
1 | ruleRepository = $ruleRepository;
37 | $this->ruleLogger = $ruleLogger;
38 | }
39 |
40 | /**
41 | * Define if massUpdate() should run without actually saving data
42 | *
43 | * @param bool $isDryRun
44 | */
45 | public function setDryRun($isDryRun)
46 | {
47 | $this->isDryRun = $isDryRun;
48 | }
49 |
50 | /**
51 | * Update entity based on loaded rule set
52 | *
53 | * @param EntityInterface $entity
54 | * @param Store $store
55 | */
56 | public function update(EntityInterface $entity, Store $store)
57 | {
58 | $ruleSets = $this->getRuleSets(new StoreSet([$store]));
59 | $ruleSets->getRuleSet($store)->applyToEntity($entity, $this->ruleLogger);
60 | }
61 |
62 | /**
63 | * Update entity collection based on loaded rule set
64 | *
65 | * @param EntityIteratorInterface $iterator
66 | * @param EntityInterface $entityModel
67 | */
68 | public function massUpdate(EntityIteratorInterface $iterator, EntityInterface $entityModel, StoreSet $stores)
69 | {
70 | $this->entityModel = $entityModel;
71 | $this->outputStart();
72 | $this->getRuleSets($stores);
73 | foreach ($stores as $store) {
74 | $iterator->setStore($store);
75 | foreach ($iterator as $entityData) {
76 | $this->updateCurrentRow($entityData, $store);
77 | $this->outputIteratorStatus($iterator);
78 | }
79 | }
80 | $this->outputDone();
81 | $this->entityModel = null;
82 | }
83 |
84 | /**
85 | * Update current row of entity collection
86 | *
87 | * @param array $data
88 | * @param Store $store
89 | * @internal param EntityIteratorInterface $iterator
90 | */
91 | private function updateCurrentRow(array $data, Store $store)
92 | {
93 | $this->entityModel->setRawData($data);
94 | $this->update($this->entityModel, $store);
95 | if ($this->entityModel->isChanged() && ! $this->isDryRun) {
96 | $this->entityModel->saveAttributes();
97 | }
98 | $this->entityModel->clearInstance();
99 | }
100 | private function outputStart()
101 | {
102 | //TODO implement output/log
103 | }
104 | /**
105 | * @param EntityIteratorInterface $iterator
106 | */
107 | private function outputIteratorStatus(EntityIteratorInterface $iterator)
108 | {
109 | //TODO implement output/log
110 | }
111 | private function outputDone()
112 | {
113 | //TODO implement output/log
114 | }
115 |
116 | /**
117 | * @return RuleSetsByStore
118 | */
119 | private function getRuleSets(StoreSet $stores)
120 | {
121 | if ($this->ruleSets === null) {
122 | $this->ruleSets = $this->ruleRepository->findRuleSetsForStores($stores);
123 | }
124 | return $this->ruleSets;
125 | }
126 | }
--------------------------------------------------------------------------------