Magento 2

Magento fundamentals: che differenza c'è tra custom ed extension attributes?

Lettura 5 minuti

Introduzione

Lo scopo di questo articolo è quello di gettare un po’ di luce sul sistema di gestione degli attributi di Magento 2, flessibile ma poco intuitivo ad un primo approccio.

Ci sono molte risorse in rete che documentano come implementare custom ed extension attribute, perciò qui dedichiamo l’attenzione su alcuni dettagli non banali e non sempre ben evidenziati nella documentazione ufficiale.

Come guida di riferimento passo passo, raccomando di leggere la documentazione ufficiale o, ancor meglio, di seguire gli ottimi tutorial di mage2.tv.

Panoramica del sistema di gestione degli attributi

Sin dai primi tempi, Magento ha offerto un sistema flessibile per la gestione degli attributi delle entità basate sul modello Entity-Attribute-Value (EAV)

Attraverso questo sistema, è davvero semplice estendere una entità Magento, sebbene ci siano alcune limitazioni da tenere presente:

  • solo le entità EAV possono essere arricchite con questo sistema di attributi; non tutte le entità in Magento sono di tipo EAV, solo prodotti, categorie, clienti e indirizzi clienti.

  • gli attributi possono essere solo di tipo scalare, vale a dire che possono assumere valori di tipo boolean, int, float, o string.

Gli extension attribute, introdotti in Magento 2, consentono di scavalcare i suddetti limiti, rendendo semplice:

  • estendere entità non-EAV, a condizione che l’entità implementi l’interfaccia \Magento\Framework\Api\ExtensibleDataInterface;
  • utilizzare oggetti per strutturare tipi più complessi.

Sfortunatamente, alcune entità core di Magento non implementano l’interfaccia \Magento\Framework\Api\ExtensibleDataInterface non consentendoci quindi di estenderle con gli extension attribute. Tuttavia, guardando il bicchiere mezzo pieno, andiamo a vedere che vantaggi offre il sistema degli extension attribute.

Cosa sono i custom attribute?

Con l’introduzione degli extension attribute si è arricchita la terminologia, quindi è bene dare qualche definizione.

Un attributo EAV può essere di due tipi:

  • system attribute - creato da ogni installazione di default di Magento;
  • custom attribute - creato attraverso l’Admin Panel o data patch script.

Semplificando, un custom attribute è un attributo creato da qualcun altro.

Cosa sono gli extension attribute?

Abbiamo già dato una definizione ma vale la pena ripeterla: un extension attribute è un attributo che consente di estendere entità non-EAV.

Ma c’è di più.

Innanzi tutto le buone notizie: un extension attribute ci consente di superare le limitazioni relative ai valori scalari: possiamo infatti estendere sia entità EAV sia non-EAV con oggetti complessi.

Per vedere un esempio, prendiamo la dichirazione dell’extension attribute gift_message aggiunto all’entità ordine (Magento\Sales\Api\Data\OrderInterface) dove si vede come l’attributo sia un insieme strutturato di valori:

<?php
namespace Magento\GiftMessage\Api\Data;

interface MessageInterface 
  extends \Magento\Framework\Api\ExtensibleDataInterface
{
    /**#@+
     * Constants for keys of data array. Identical to the name of the getter in snake case
     */
    const GIFT_MESSAGE_ID = 'gift_message_id';
    const CUSTOMER_ID = 'customer_id';
    const SENDER = 'sender';
    const RECIPIENT = 'recipient';
    const MESSAGE = 'message';
    /**#@-*/

    // ...
}

Il prezzo da pagare per questa flessibilità è il fatto che la persistenza degli extension attribute è da gestire programmaticamente, mentre i valori dei custom attribute sono caricati e salvati automaticamente.

Per i dettagli tecnici su come aggiungere un extension attribute ad una entità rimandiamo alla documentazione ufficiale.

Vale la pena invece sottolineare che l’interfaccia ExtensibleDataInterface non dichiara i metodi per accedere agli extension attribute. Vediamo la sua dichiarazione:

<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

namespace Magento\Framework\Api;

/**
 * Interface for entities which can be extended with extension attributes.
 *
 * @api
 */
interface ExtensibleDataInterface
{
    /**
     * Key for extension attributes object
     */
    const EXTENSION_ATTRIBUTES_KEY = 'extension_attributes';
}

Per convenzione, i metodi generalmente sono nominati setExtensionAttributes() e getExtensionAttributes() ma non è scontato che una classe che implementi l’interfaccia ExtensibleDataInterface li fornisca , perciò il consiglio è di controllare sempre per sicurezza.

Un’altra caratteristica che vale la pena menzionare è che, per evitare un degrado di performance quando si recuperano gli extension attribute nelle collection, Magento mette a dispoizione una funzionalità di join nativa, documentata qui. Attenzione però al fatto che tale funzionalità è limitata agli extension attribute di tipo scalare.

Come esempio, possiamo fare riferimento alla dichiarazione dell’extension attribute is_subscribed del customer nel file module-newsletter/etc/extension_attributes.xml:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd">
    <extension_attributes for="Magento\Customer\Api\Data\CustomerInterface">
        <attribute code="is_subscribed" type="boolean" >
            <join reference_table="newsletter_subscriber" 
                  reference_field="customer_id" 
                  join_on_field="entity_id">
                <field>subscriber_status</field>
            </join>
        </attribute>
    </extension_attributes>
</config>

💡 Attenzione: per far sì che la join funzioni, non è sufficiente dichiararla come mostrato nell’esempio; dobbiamo anche assicurarci di passare la collection al metodo \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface::process() che la popolerà con i dati corretti. Possiamo vedere un esempio nel metodo getList() di \Magento\Catalog\Model\ProductRepository:

<?php
namespace Magento\Catalog\Model;

// ...

class ProductRepository 
  implements \Magento\Catalog\Api\ProductRepositoryInterface
{
    // ...

    public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria)
    {
        /** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $collection */
        $collection = $this->collectionFactory->create();
        $this->extensionAttributesJoinProcessor->process($collection);
        // ..
    }

    // ...
}

Una ulteriore funzionalità rilevante offerta dagli extension attribute è quella di restringere l’accesso ai valori dichiarando una risorsa ACL, come mostrato qui.

Dove memorizzare gli extension attribute?

Generalmente, gli extension attribute sono attributi di tipo non-EAV, quindi dobbiamo occuparci di decidere dove persisterne i valori.

Se un extension attribute è un oggetto complesso, la scelta più naturale è quella di utilizzare una tabella dedicata su database.

Se, al contrario, l’extension attribute è di tipo scalare, è possibile memorizzarne i valori su una colonna aggiuntiva di una tabella core. Questo è considerato un compromesso accettabile, a patto di utilizzare una prefisso non ambiguo che eviti collisioni con colonne native o aggiunte da altre estensioni. Così facendo, scriveremo meno codice e otterremo prestazioni migliori perché eviteremo di dover utilizzare condizioni di join aggiuntive per recuperare i valori.

Tuttavia non esiste una regola assoluta: a seconda dei casi, è bene analizzare attentamente il problema per identificare la miglior soluzione.

Conclusioni

I custom attribute non rappresentano una novità in Magento ma solo un nome nuovo per gli attributi delle entità EAV creati da terze parti.

Gli extension attributes, al contrario, rappresentano la vera novità che consente di estendere entità non-EAV.

Inoltre, gli extension attribute introducono la possibilità di definire attributi complessi, superando la limitazione dei tipi scalari.

C’è un piccolo prezzo da pagare per questa flessibilità aggiuntiva: dobbiamo gestire attentamente la persistenza per assicurarci che i valori degli extension attribute siano salvati e caricati correttamente.

Articolo scritto da

COO | Reggio Emilia

Alessandro lavora in Bitbull come team leader, esperto di software design e sviluppo di soluzioni ecommerce.
Membro attivo della community Magento come contributor e maintainer, ha ricevuto per tre volte il riconoscimento di Magento Master e Top 50 Contributor.
Dal 2020 è membro della Magento Association content committee.