Extra column in catalog_product_entity_tier_price not saving/updating in observer
-
14-04-2021 - |
Question
I have added an extra column 'title' to the catalog_product_entity_tier_price table using Setup/UpdateSchema.php
and an extra field to the tier price backend by overwriting Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AdvancedPricing::getTierPriceStructure
:
class InstallSchema implements InstallSchemaInterface
{
/**
* {@inheritdoc}
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function install(SchemaSetupInterface $setup, ModuleContextInterface $context)
{
$setup->startSetup();
/** Tier Price Label **/
$setup->getConnection()->addColumn(
$setup->getTable('catalog_product_entity_tier_price'),
'title',
[
'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
'length' => 255,
'nullable' => true,
'comment' => 'Tier Price Title'
]
);
$setup->endSetup();
}
}
I've added the database column by overwriting Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice::_loadPriceDataColumns
, so my data from the database is loaded into the corresponding field - as seen above.
class Tierprice extends \Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice
{
/**
* Initialize connection and define main table
*
* @return void
*/
protected function _construct()
{
$this->_init('catalog_product_entity_tier_price', 'value_id');
}
/**
* Add qty column & title column
*
* @param array $columns
* @return array
*/
protected function _loadPriceDataColumns($columns)
{
$columns = parent::_loadPriceDataColumns($columns);
$columns['price_qty'] = 'qty';
$columns['percentage_value'] = 'percentage_value';
/* Start TierTitle */
$columns['title'] = 'title';
/* End TierTitle */
return $columns;
}
/**
* Order by qty
*
* @param \Magento\Framework\DB\Select $select
* @return \Magento\Framework\DB\Select
*/
protected function _loadPriceDataSelect($select)
{
$select->order('qty');
return $select;
}
}
I've added an extension_attribute so that my getters / setters are created:
<extension_attributes for="Magento\Catalog\Api\Data\ProductTierPriceInterface">
<attribute code="title" type="string">
<join reference_table="catalog_product_entity_tier_price" reference_field="product_id" join_on_field="entity_id">
<field>title</field>
</join>
</attribute>
</extension_attributes>
These are created in generated/code/Magento/Catalog/Api/Data/ProductTierPriceExtension.php
and in generated/code/Magento/Catalog/Api/Data/ProductTierPriceExtensionInterface.php
.
As per Jonas Chen's answer below I've overwritten Magento\Catalog\Model\Product\Type\Price::setTierPrices
and used $price->getExtensionAttributes()->getTitle()
:
class Price extends \Magento\Catalog\Model\Product\Type\Price
{
/**
* Sets list of product tier prices
*
* @param \Magento\Catalog\Model\Product $product
* @param \Magento\Catalog\Api\Data\ProductTierPriceInterface[] $tierPrices
* @return $this
*/
public function setTierPrices($product, array $tierPrices = null)
{
// null array means leave everything as is
if ($tierPrices === null) {
return $this;
}
$allGroupsId = $this->getAllCustomerGroupsId();
$websiteId = $this->getWebsiteForPriceScope();
// build the new array of tier prices
$prices = [];
foreach ($tierPrices as $price) {
$extensionAttributes = $price->getExtensionAttributes();
$priceWebsiteId = $websiteId;
$priceTitle = "";
if (isset($extensionAttributes) && is_numeric($extensionAttributes->getWebsiteId())) {
$priceWebsiteId = (string)$extensionAttributes->getWebsiteId();
}
if (isset($extensionAttributes) && $extensionAttributes->getTitle()) {
$priceTitle = (string)$extensionAttributes->getTitle();
}
$prices[] = [
'website_id' => $priceWebsiteId,
'cust_group' => $price->getCustomerGroupId(),
'website_price' => $price->getValue(),
'price' => $price->getValue(),
'all_groups' => ($price->getCustomerGroupId() == $allGroupsId),
'price_qty' => $price->getQty(),
'percentage_value' => $extensionAttributes ? $extensionAttributes->getPercentageValue() : null,
'title' => $priceTitle
];
}
$product->setData('tier_price', $prices);
return $this;
}
}
And I've overwritten Model\Product\Attribute\Backend\Tierprice
:
class Tierprice extends \Magento\Catalog\Model\Product\Attribute\Backend\Tierprice
{
protected function getAdditionalFields($objectArray)
{
$percentageValue = $this->getPercentage($objectArray);
$titleValue = $this->getTierTitle($objectArray);
return [
'value' => $percentageValue ? null : $objectArray['price'],
'percentage_value' => $percentageValue ?: null,
'title' => $titleValue ? $titleValue : null,
];
}
/**
* @param array $valuesToUpdate
* @param array $oldValues
* @return boolean
*/
protected function updateValues(array $valuesToUpdate, array $oldValues)
{
$isChanged = false;
foreach ($valuesToUpdate as $key => $value) {
if ((!empty($value['value']) && $oldValues[$key]['price'] != $value['value'])
|| $this->getPercentage($oldValues[$key]) != $this->getPercentage($value)
|| $this->getTierTitle($oldValues[$key]) != $this->getTierTitle($value)
) {
$price = new \Magento\Framework\DataObject(
[
'value_id' => $oldValues[$key]['price_id'],
'value' => $value['value'],
'percentage_value' => $this->getPercentage($value),
'title' => $this->getTierTitle($value)
]
);
$this->_getResource()->savePriceData($price);
$isChanged = true;
}
}
return $isChanged;
}
/**
* Check whether price has percentage value.
*
* @param array $priceRow
* @return null
*/
private function getPercentage($priceRow)
{
return isset($priceRow['percentage_value']) && is_numeric($priceRow['percentage_value'])
? $priceRow['percentage_value']
: null;
}
/**
* Check whether price has title value.
*
* @param $priceRow
* @return null
*/
private function getTierTitle($priceRow)
{
return isset($priceRow['title'])
? $priceRow['title']
: null;
}
}
Following this the backend values are saved into the database, however now the the tierprice object does not contain the title.
I've tried overwriting Magento\Catalog\Model\Product\Type\Price::getTierPrices
, but calling $tierPrices = $this->getExistingPrices($product, 'tier_price');
is already missing the title.
Answer
I had to also use a plugin on Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\GroupPrice\AbstractGroupPrice
and had to add my custom database column on top of setting my title in my overwrite of Magento\Catalog\Model\Product\Type\Price::getTierPrices
:
class AbstractGroupPricePlugin
{
public function afterGetSelect(
\Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\GroupPrice\AbstractGroupPrice $subject,
$result
)
{
//add title column to select
$result->columns('title');
return $result;
}
}
I hope this helps someone else also.
Solution
for Magento\Catalog\Model\Product\Type\Price::setTierPrices get value from $extensionAttributes->getTitle()
public function setTierPrices($product, array $tierPrices = null)
{
// null array means leave everything as is
if ($tierPrices === null) {
return $this;
}
$allGroupsId = $this->getAllCustomerGroupsId();
$websiteId = $this->getWebsiteForPriceScope();
// build the new array of tier prices
$prices = [];
foreach ($tierPrices as $price) {
$extensionAttributes = $price->getExtensionAttributes();
$priceWebsiteId = $websiteId;
$priceTitle = "";
if (isset($extensionAttributes) && is_numeric($extensionAttributes->getWebsiteId())) {
$priceWebsiteId = (string)$extensionAttributes->getWebsiteId();
}
if (isset($extensionAttributes) && $extensionAttributes->getTitle()) {
$priceTitle = (string)$extensionAttributes->getTitle();
}
$prices[] = [
'website_id' => $priceWebsiteId,
'cust_group' => $price->getCustomerGroupId(),
'website_price' => $price->getValue(),
'price' => $price->getValue(),
'all_groups' => ($price->getCustomerGroupId() == $allGroupsId),
'price_qty' => $price->getQty(),
'percentage_value' => $extensionAttributes ? $extensionAttributes->getPercentageValue() : null,
'price_title' => $priceTitle
];
}
$product->setData('tier_price', $prices);
return $this;
}
when save value in backend, try overwrite \Magento\Catalog\Model\Product\Attribute\Backend\Tierprice updateValues and getAdditionalFields functions
protected function getAdditionalFields($objectArray)
{
$percentageValue = $this->getPercentage($objectArray);
$titleValue = $objectArray['title'];
return [
'value' => $percentageValue ? null : $objectArray['price'],
'percentage_value' => $percentageValue ?: null,
'title' => $titleValue ? $titleValue : "",
];
}
/**
* @param array $valuesToUpdate
* @param array $oldValues
* @return boolean
*/
protected function updateValues(array $valuesToUpdate, array $oldValues)
{
$isChanged = false;
foreach ($valuesToUpdate as $key => $value) {
//check if "title" changed
if ((!empty($value['value']) && $oldValues[$key]['price'] != $value['value'])
|| (!empty($value['title']) && $oldValues[$key]['title'] != $value['title'])
|| $this->getPercentage($oldValues[$key]) != $this->getPercentage($value)
) {
$price = new \Magento\Framework\DataObject(
[
'value_id' => $oldValues[$key]['price_id'],
'value' => $value['value'],
'percentage_value' => $this->getPercentage($value),
'title' => $value['title']
]
);
$this->_getResource()->savePriceData($price);
$isChanged = true;
}
}
return $isChanged;
}
OTHER TIPS
First of all, I thank you for sharing this dev .
I have the same need as you, I have to add on "catalog_product_entity_tier_price" a special price that will be applied only on particular customer groups, so I followed the steps you marked on the page, but I can't set the value on the custom filed
Could you confirm me if there is a part of dev missing, here is my di.xml: <preference for="Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice" type="Klp\Catalog\Preference\Model\ResourceModel\Product\Attribute\Backend\TierPrice"/>
<type name="Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\GroupPrice\AbstractGroupPrice">
<plugin name="klp_get_tier_prices" type="Klp\Catalog\Plugin\Model\ResourceModel\Product\Attribute\Backend\GroupPrice\AbstractGroupPrice"/>
</type>
<preference for="Magento\Catalog\Model\Product\Type\Price" type="Klp\Catalog\Preference\Model\Product\Type\Price"/>
<preference for="Magento\Catalog\Model\Product\Attribute\Backend\Tierprice" type="Klp\Catalog\Preference\Model\Product\Attribute\Backend\Tierprice"/>
and in adminhtml , I add :
<virtualType name="Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Pool" type="Magento\Ui\DataProvider\Modifier\Pool">
<arguments>
<argument name="modifiers" xsi:type="array">
<item name="advanced-pricing" xsi:type="array">
<item name="class" xsi:type="string">Klp\Catalog\Ui\DataProvider\Product\Form\Modifier\AdvancedPricing</item>
<item name="sortOrder" xsi:type="number">90</item>
</item>
</argument>
</arguments>
</virtualType>