Salvataggio della categoria dalla raccolta reimposta include_in_menu
-
29-09-2020 - |
Domanda
A volte è conveniente salvare una categoria direttamente dalla raccolta in cui è stata caricata.Ad esempio, per tutte le categorie in cui l'attributo name
inizia con foo
, voglio aggiornare l'attributo meta_title
con il nome seguito da bar
come segue:
/** @var Mage_Catalog_Model_Resource_Category_Collection $categoryCollection */
$categoryCollection = Mage::getResourceModel('catalog/category_collection');
$categoryCollection->setDisableFlat(true);
$categoryCollection->addAttributeToFilter('name', ['like' => 'foo%']);
foreach ($categoryCollection as $category) {
/** @var Mage_Catalog_Model_Category $category */
$category->setData('meta_title', $category->getData('name') . ' bar');
$category->save();
}
Funziona, ma come effetto collaterale: l'attributo include_in_menu
viene reimpostato su 1.
Qual è la causa di questo e come può essere aggirato?
Soluzione
Si scopre che quando si salva un'entità EAV, vengono eseguiti i metodi beforeSave
e afterSave
dei modelli back-end di tutti gli attributi, anche quando non fanno parte della raccolta: il metodo Mage_Eav_Model_Entity_Abstract::save
chiama i metodi _beforeSave
e _afterSave
, che chiamano il metodo walkAttributes
con backend/beforeSave
o backend/afterSave
come primi argomenti.
Il metodo Mage_Eav_Model_Entity_Attribute_Backend_Abstract::beforeSave
, utilizzato dalla maggior parte degli attributi, imposta il valore predefinito dell'attributo sull'oggetto fornito nelle seguenti circostanze:
- L'oggetto non ha una voce nella sua matrice
$data
interna. - Il valore predefinito dell'attributo non è vuoto.
La prima condizione si applica alla maggior parte degli attributi durante il caricamento tramite una raccolta.La seconda condizione si applica solo all'attributo include_in_menu
(in una configurazione Magento standard).
Per aggirare questo problema, aggiungi semplicemente l'attributo include_in_menu
alla query di raccolta:
$categoryCollection->addAttributeToSelect('include_in_menu');
Un altro problema che non vengono chiamati i metodi afterLoad
dei modelli di backend degli attributi (grazie a Mario per l'avviso).Su un'installazione predefinita di Magento, ciò causa problemi con i seguenti attributi:
created_at
: il valore non viene convertito da UTC al fuso orario corrente del negozio al caricamento.Al salvataggio, il valore viene riconvertito in UTC (che era già), portando acreated_at
date errate.available_sort_by
(se fa parte della collezione e il suo valore non èNULL
): il valore separato da virgole non viene sostituito da un array.Al salvataggio, il valore viene ripristinato aNULL
.
Per risolvere questo problema, puoi attivare i metodi afterLoad
aggiungendo quanto segue al corpo del ciclo:
$category->getResource()->loadAllAttributes($category)->walkAttributes('backend/afterLoad', array($category));
A proposito, questo non sembra danneggiare così tanto le prestazioni: tutti gli attributi di categoria vengono caricati anche durante il salvataggio della categoria e Magento memorizza nella cache le istanze degli attributi nel singleton Mage_Eav_Model_Config
.Quindi sembra che quando si caricano e si salvano più categorie, ogni attributo viene caricato solo una volta.
Simile al problema che il metodo beforeLoad
non viene chiamato per alcun attributo, il metodo Mage_Eav_Model_Entity_Abstract::_loadModelAttributes
non viene chiamato.Questo metodo fa due cose per ogni attributo:
- Chiamare
$object->setData($attributeCode, $attributeValue)
dove$attributeValue
viene caricato dal database. - Chiamare
$attribute->getBackend()->setEntityValueId($object, $valueId)
dove$valueId
è la chiave primaria del valore dell'attributo nella tabella degli attributi corrispondente.
Quando si caricano raccolte EAV, viene chiamato il metodo con nome simile Mage_Eav_Model_Entity_Collection_Abstract::_loadAttributes
, che esegue il primo ma non il secondo .Di conseguenza, il metodo getEntityValueId
corrispondente restituisce sempre un valore vuoto.Ciò causa problemi solo quando si eliminano i valori degli attributi dalla tabella back-end.Per le categorie (e anche i prodotti) ciò accade solo quando si imposta un valore di un attributo con ambito globale su false
.
Per evitare problemi con l'impossibilità di eliminare i valori degli attributi con ambito globale, non chiamare $object->setData($attributeCode, false)
per tale attributo.Quando è necessario cancellare un attributo, puoi usare null
invece di false
, che risulta in una query UPDATE
sulla tabella back-end degli attributi invece di una query DELETE
.
Altri suggerimenti
Non dovresti mai chiamare il save
se non hai chiamato il load
prima sullo stesso oggetto.
Anche se si aggiungono gli attributi alla raccolta, i metodi beforeLoad
e afterLoad
non vengono chiamati e potrebbero avere un impatto sullo stato dell'oggetto.
Aggiunta
$categoryCollection->addAttributeToSelect('include_in_menu');
potrebbe risolvere il tuo problema per questo caso particolare, ma potrebbe danneggiare qualcos'altro.