Publikace dat do evitaDB

Publikace dat do evitaDB

Tento dokument popisuje kompletní systém publikace dat z EdeeShop do evitaDB, včetně definice schématu pomocí anotací, vytváření nových publikovaných entit a projektového rozšiřování.

Zkopírovat odkaz na sekci1. Úvod

Publikace dat do evitaDB je mechanismus, který transformuje data z relační databáze EdeeShop do dokumentově orientované evitaDB databáze. Tato transformace umožňuje:

  • Rychlé fulltextové vyhledávání - optimalizované pro e-commerce použití
  • GraphQL API - automaticky generované z definovaného schématu
  • Facetové filtrování - efektivní práce s parametry, kategoriemi a dalšími filtry
  • Vícejazyčná podpora - lokalizované atributy a data

Zkopírovat odkaz na sekci2. Základní koncepty

Před tím, než začnete pracovat s publikovanými daty, je důležité pochopit základní koncepty a architekturu systému.

Zkopírovat odkaz na sekci2.1 Architektura publikovaných entit

Každá publikovaná entita v EdeeShopu se skládá z několika komponent, které následují READ_MODEL / WRITE_MODEL pattern:

Zkopírovat odkaz na sekciPattern READ_MODEL / WRITE_MODEL

Tento pattern odděluje rozhraní pro čtení (READ_MODEL) od rozhraní pro zápis (WRITE_MODEL):

  • READ_MODEL - immutable rozhraní s getter metodami, slouží pro čtení dat z evitaDB
  • WRITE_MODEL (Editor) - mutable rozhraní se setter metodami, slouží pro zápis dat při publikaci
java
1 // READ_MODEL - pro čtení2public interface PublishedProduct extends3    PublishedProductContract<PublishedProduct, PublishedProductEditor> {4}5
6// WRITE_MODEL - pro zápis při publikaci7public interface PublishedProductEditor extends8    PublishedProductEditorContract<PublishedProduct, PublishedProductEditor> {9}

Zkopírovat odkaz na sekciContract rozhraní

Contract rozhraní obsahují definici schématu pomocí anotací evitaDB:

java
1 @Entity(name = "Product", description = "...")2public interface PublishedProductContract<READ_MODEL, WRITE_MODEL>3    extends PublishedEntityBase, SealedInstance<READ_MODEL, WRITE_MODEL> {4
5    String ATTRIBUTE_CODE_SHORT = "codeShort";6
7    @Attribute(name = ATTRIBUTE_CODE_SHORT, filterable = true)8    String getCodeShort() throws ContextMissingException;9}

Contract rozhraní jsou generická a umožňují projektové rozšíření.

Zkopírovat odkaz na sekciEditor Contract

Editor Contract rozhraní obsahují setter metody pro zápis dat:

java
1 public interface PublishedProductEditorContract<READ_MODEL, WRITE_MODEL>2    extends PublishedProductContract<READ_MODEL, WRITE_MODEL>,3            PublishedEntityBaseEditor,4            InstanceEditor<READ_MODEL> {5
6    @AttributeRef(ATTRIBUTE_CODE_SHORT)7    void setCodeShort(String codeShort) throws ContextMissingException;8}

Zkopírovat odkaz na sekci2.2 Traity (WithPublished*)

Traity jsou mixin rozhraní, která přidávají konkrétní funkcionalitu do publikovaných entit pomocí vícenásobné dědičnosti. Všechny traity začínají prefixem WithPublished*.

Zkopírovat odkaz na sekciKompoziční přístup

Díky traitům lze snadno sdílet společnou funkcionalitu napříč různými entitami:

java
1 public interface PublishedProductContract<READ_MODEL, WRITE_MODEL> extends2    PublishedEntityBase,           // Základní funkcionalita3    WithPublishedLocalization,      // název, popis4    WithPublishedPrice,             // ceny5    WithPublishedURL,               // URL6    WithPublishedMedia,             // obrázky7    WithPublishedCategories,        // kategorie8    WithPublishedValidity,          // časová platnost9    SealedInstance<READ_MODEL, WRITE_MODEL> {10    // ... specifické atributy produktu11}

Zkopírovat odkaz na sekciBěžně používané traity

Lokalizační:

  • WithPublishedLocalization - název, popis

Časové:

  • WithPublishedValidity - časová platnost (validFrom/validTo)

Viditelnost:

  • WithPublishedVisibility - viditelnost entity

URL a obsah:

  • WithPublishedURL - URL funkcionalita (slugy, aktivní URL)
  • WithPublishedTags - tagy
  • WithPublishedMedia - obrázky a multimédia
  • WithPublishedRelatedFiles - soubory (PDF, dokumenty)

Produktové:

  • WithPublishedPrice - cenová funkcionalita
  • WithPublishedCategories - kategorie produktů
  • WithPublishedGroups - skupiny produktů
  • WithPublishedParameters - parametry produktů
  • WithPublishedBrand - značka produktu
  • WithPublishedStock - skladové zásoby
  • WithPublishedMasterVariantProperties - master/variant produkty

Detailní popis všech traitů najdete v kapitole 7. Reference - přehled traitů.

Zkopírovat odkaz na sekci2.3 Základní komponenty

Zkopírovat odkaz na sekciPublishedEntityBase

Základní rozhraní pro všechny publikované entity:

java
1 public interface PublishedEntityBase extends2    WithEntityContract,3    TraitSupport,4    WithPublishedCatalog,5    WithPublishedCode,6    WithPublishedDatetime,7    WithPublishedChanged,8    WithLocales,9    WithEntitySchema,10    WithVersion,11    WithTraits {12
13    @PrimaryKey(autoGenerate = false)14    int getId();15
16    @Attribute(name = "status", global = true, representative = true)17    CatalogEntityStatus getStatus() throws ContextMissingException;18}

Zkopírovat odkaz na sekciEntityToEvitaConversionSnippet

Snippet definuje, jak se mají data z relační databáze (PublishedCatalogEntity) mapovat do evitaDB entity:

java
1 public interface EntityToEvitaConversionSnippet<T> {2    Class<T> getRequiredType();3
4    void convert(5        PublishedCatalogEntity catalogEntity,6        T entity,7        AbstractEvitaJobContext context8    );9}

Zkopírovat odkaz na sekciEvitaPublishingService

Služba pro správu publikačních deskriptorů a registraci snippetů:

java
1 evitaPublishingService.registerConversionSnippet(new MyConversionSnippet());2evitaPublishingService.getPublishingDescriptorFor(PRODUCT)3    .registerPublishedServiceClass(MyPublishedService.class);

Zkopírovat odkaz na sekci3. Anotace evitaDB

evitaDB používá anotace pro deklarativní definici schématu entit. Tyto anotace jsou umístěny na rozhraních publikovaných entit a definují, jak budou data uložena, indexována a dotazována.

Zkopírovat odkaz na sekci3.1 Entitové anotace

Zkopírovat odkaz na sekci@Entity

Označuje rozhraní jako publikovanou entitu v evitaDB. Tato anotace je povinná pro každé rozhraní, které reprezentuje entitu.

Parametry:

  • name (povinný) - název entity v evitaDB
  • description - textový popis entity (použit v GraphQL schema)
  • allowedEvolution - režimy automatické evoluce schématu

Příklad:

java
1 @Entity(2    name = "Product",3    description = """4        Product represents an article that can be displayed and sold on e-shop.""",5    allowedEvolution = {6        EvolutionMode.ADDING_ATTRIBUTES,7        EvolutionMode.ADDING_ASSOCIATED_DATA8    }9)10public interface PublishedProductContract<READ_MODEL, WRITE_MODEL> extends ... {11    String ENTITY_NAME = "Product";12}

Zkopírovat odkaz na sekci@PrimaryKey

Označuje primární klíč entity. V EdeeShopu je primární klíč vždy int a odpovídá ID z tabulky T_EDEESHOP_CATALOG_ENTITY.

Příklad:

java
1 @PrimaryKey(autoGenerate = false)2int getId();

Zkopírovat odkaz na sekci3.2 Datové anotace

Zkopírovat odkaz na sekci@Attribute

Označuje atribut entity. Atributy jsou základní jednotky dat, které mohou být filtrovány, řazeny a lokalizovány.

Parametry:

  • name (povinný) - název atributu
  • description - popis atributu (použit v GraphQL schema)
  • filterable - zda lze podle atributu filtrovat (výchozí: false)
  • sortable - zda lze podle atributu řadit (výchozí: false)
  • unique - typ unikátnosti
  • localized - zda je atribut lokalizovaný (výchozí: false)
  • nullable - zda může být atribut null (výchozí: false)
  • representative - zda je atribut reprezentativní (zobrazuje se v přehledech), u duplicitních referencí na stejné entity (např. vazba na média) identifikuje tzv. diskriminátory (klíčové atributy odlišující jednotlivé instance reference)
  • global - zda je atribut globální pro všechny entity daného typu
  • scope - nastavení atributu podle scope (Live, Archived)

Příklady:

java
1 // Jednoduchý filtrovatelný atribut2@Attribute(3    name = "codeShort",4    description = "Product shortcode for feeds and GTM",5    unique = AttributeUniquenessType.UNIQUE_WITHIN_COLLECTION6)7String getCodeShort() throws ContextMissingException;8
9// Atribut s různým nastavením podle scope10@Attribute(11    name = "productType",12    description = "Defines product types (BASIC, MASTER, VARIANT, SET)",13    scope = {14        @ScopeAttributeSettings(scope = Scope.LIVE, filterable = true),15        @ScopeAttributeSettings(scope = Scope.ARCHIVED, filterable = true)16    }17)18ProductType getProductType() throws ContextMissingException;19
20// Lokalizovaný atribut21@Attribute(22    name = "name",23    description = "Localized name of the entity",24    localized = true,25    filterable = true,26    sortable = true27)28String getName() throws ContextMissingException;

Zkopírovat odkaz na sekci@AttributeRef

Používá se v editorech pro označení setter metod:

java
1 @AttributeRef(ATTRIBUTE_CODE_SHORT)2void setCodeShort(String codeShort) throws ContextMissingException;

Zkopírovat odkaz na sekci@AssociatedData

Označuje asociovaná data entity - komplexnější datové struktury (objekty, kolekce), které nejsou indexovány:

java
1 @AssociatedData(2    name = "hreflang",3    description = "List of hreflang URLs for SEO",4    nullable = true5)6List<ActiveUrl> getHreflang() throws ContextMissingException;

Zkopírovat odkaz na sekci@SortableAttributeCompound

Definuje složený atribut pro řazení - umožňuje vytvořit složený index z více atributů:

java
1 @SortableAttributeCompound(2    name = "productPriorityAndName",3    description = "Compound index for sorting by priority and name",4    attributeElements = {5        @AttributeElement(attributeName = "priority", direction = OrderDirection.DESC),6        @AttributeElement(attributeName = "name", direction = OrderDirection.ASC)7    }8)

Zkopírovat odkaz na sekci3.3 Hierarchické anotace

Zkopírovat odkaz na sekci@ParentEntity

Označuje vztah k rodičovské entitě v hierarchické struktuře:

java
1 @ParentEntity2Integer getParentId() throws ContextMissingException;3
4@ParentEntity5PublishedCategoryContract<READ_MODEL, WRITE_MODEL> getParent()6    throws ContextMissingException;7
8// Optional přístup9@ParentEntity10Optional<PublishedCategoryContract<READ_MODEL, WRITE_MODEL>> getParentIfAvailable();

Zkopírovat odkaz na sekci3.4 Referenční anotace

Zkopírovat odkaz na sekci@Reference

Označuje referenci na jinou entitu:

java
1 @Reference(2    name = "representedCategory",3    description = "Reference to standard category for shortcut categories"4)5PublishedCategoryContract<READ_MODEL, WRITE_MODEL> getRepresentedCategory()6    throws ContextMissingException;

Zkopírovat odkaz na sekci@ReferenceRef

Používá se pro metody s Optional přístupem:

java
1 @ReferenceRef(REFERENCE_REPRESENTED_CATEGORY)2Optional<PublishedCategoryContract<READ_MODEL, WRITE_MODEL>>3    getRepresentedCategoryIfAvailable();

Zkopírovat odkaz na sekci@ReflectedReference

Označuje zpětnou referenci - automaticky vypočítanou referenci z opačného směru vztahu:

java
1 @ReflectedReference(2    name = "products",3    ofEntity = "Product",4    ofName = "categories",5    description = "Products assigned to this category",6    attributeInheritanceFilter = { "assignmentValidity" }7)8List<PublishedCategoryProduct> getProducts() throws ContextMissingException;

Parametry:

  • name - název zpětné reference
  • ofEntity - název entity, ze které reference pochází
  • ofName - název původní reference
  • attributeInheritanceFilter - atributy pro dědění z původní reference

Zkopírovat odkaz na sekci@ReferencedEntity, @ReferencedEntityGroup

Označují přímý odkaz na entitu nebo skupinu entit v rámci reference:

java
1 @ReferencedEntity2PublishedCategory getCategory();3
4@ReferencedEntityGroup5PublishedGroup getGroup();

Zkopírovat odkaz na sekci3.5 Cenové anotace

Zkopírovat odkaz na sekci@Price

Označuje kolekci cen entity:

java
1 @Price2List<PriceContract> getPrices();

Zkopírovat odkaz na sekci@PriceForSale

Označuje aktuální prodejní cenu entity:

java
1 @PriceForSale2PriceContract getPriceForSale() throws ContextMissingException;

Zkopírovat odkaz na sekci4. Praktické návody

Tato kapitola obsahuje konkrétní postupy pro práci s publikovanými daty v EdeeShopu.

Zkopírovat odkaz na sekci4.1 Projektové rozšíření existující entity

Kdy použít: Když potřebujete přidat nové atributy, reference nebo asociovaná data do existující entity (Product, Category, Brand, atd.) na konkrétním projektu.

Níže je popsán postup na příkladu rozšíření produktu o nový atribut demoNumber.

Zkopírovat odkaz na sekciKrok 1: Projektové Contract rozhraní

Vytvořte projektové rozhraní s dodatečnými atributy. Použijte anotace z kapitoly 3. Anotace evitaDB:

java
1 public interface DemoPublishedProductContract {2
3    String ATTRIBUTE_DEMO_NUMBER = "demoNumber";4
5    /**6     * Ukázkový atribut rozšiřující PublishedProduct.7     *8     * @throws ContextMissingException if the attribute was not fetched9     */10    @Attribute(11        name = ATTRIBUTE_DEMO_NUMBER,12        description = "Custom demo attribute. It doesn't have to be unique.",13        filterable = true,14        sortable = true,15        nullable = true,16        representative = true17    )18    @Nullable19    String getDemoNumber() throws ContextMissingException;20}

Zkopírovat odkaz na sekciKrok 2: Hlavní rozhraní entity

Vytvořte hlavní rozhraní produktu, které dědí jak základní contract, tak projektový contract:

java
1 public interface DemoPublishedProduct extends2    PublishedProductContract<DemoPublishedProduct, DemoPublishedProductEditor>,3    DemoPublishedProductContract {4}

Vysvětlení generických typů:

  • První typ (DemoPublishedProduct) - projektové rozhraní entity (READ_MODEL)
  • Druhý typ (DemoPublishedProductEditor) - rozhraní editoru (WRITE_MODEL)

Zkopírovat odkaz na sekciKrok 3: Projektové Editor Contract

Vytvořte projektové editor rozhraní se setter metodami:

java
1 public interface DemoPublishedProductEditorContract extends2    DemoPublishedProductContract {3
4    /**5     * @param demoNumber demo-number of entity6     * @throws ContextMissingException if the attribute was not fetched7     */8    @AttributeRef(ATTRIBUTE_DEMO_NUMBER)9    void setDemoNumber(@Nullable String demoNumber) throws ContextMissingException;10}

Zkopírovat odkaz na sekciKrok 4: Hlavní rozhraní editoru

java
1 public interface DemoPublishedProductEditor extends2    PublishedProductEditorContract<DemoPublishedProduct, DemoPublishedProductEditor>,3    DemoPublishedProductEditorContract {4}

Zkopírovat odkaz na sekciKrok 5: Servisa

Vytvořte projektovou servisu pro přístup k datům:

java
1 public class DemoPublishedProductService2    extends AbstractPublishedProductService<DemoPublishedProduct, DemoPublishedProductEditor> {3
4    public DemoPublishedProductService(5        @NonNull EvitaContract evita,6        @NonNull ReflectionLookup reflectionLookup,7        @NonNull WrappingService wrappingService8    ) {9        super(evita, reflectionLookup, wrappingService);10    }11
12    @Override13    public @NonNull Class<DemoPublishedProduct> getEntityClass() {14        return DemoPublishedProduct.class;15    }16
17    @Override18    public @NonNull Class<DemoPublishedProductEditor> getEntityEditorClass() {19        return DemoPublishedProductEditor.class;20    }21}

Zkopírovat odkaz na sekciKrok 6: Conversion Snippet

Vytvořte snippet pro mapování dat z relační databáze:

java
1 public class DemoPublishedProductConversionSnippet2    implements EntityToEvitaConversionSnippet<DemoPublishedProductEditor> {3
4    @Override5    public @NonNull Class<DemoPublishedProductEditor> getRequiredType() {6        return DemoPublishedProductEditor.class;7    }8
9    @Override10    public void convert(11        @NonNull PublishedCatalogEntity catalogEntity,12        @NonNull DemoPublishedProductEditor entity,13        @NonNull AbstractEvitaJobContext context14    ) {15        entity.setDemoNumber(catalogEntity.getStringData(ATTRIBUTE_DEMO_NUMBER));16    }17}

Zkopírovat odkaz na sekciKrok 7: Registrace ve feature

Zaregistrujte servisu a snippet v projektovém feature modulu:

java
1 @Override2public void beforeSpringInitialization() {3    super.beforeSpringInitialization();4
5    if (isFeatureEnabled()) {6        final EntityModelClassConfiguration entityModel =7            getFeature(CatalogFeature.class).getEntityModelClassConfiguration();8
9        // registrace rozšíření pro zdrojová data (pokud potřebujete)10        entityModel.getModelDescriptor(PRODUCT).addTrait(WithDemoNumber.class);11
12        // registrace conversion snippetu a servisní třídy pro publikovaná data13        final EvitaPublishingService evitaPublishingService =14            getFeature(EvitaFeature.class).getEvitaPublishingService();15
16        evitaPublishingService.registerConversionSnippet(new DemoPublishedProductConversionSnippet());17        evitaPublishingService.getPublishingDescriptorFor(PRODUCT)18            .registerPublishedServiceClass(DemoPublishedProductService.class);19    }20}21
22// Projektové rozšíření se musí vykonat až po rozšíření dané entity23@Override24public FeatureDependency[] getRequiredFeatures() {25    return new FeatureDependency[]{26        new FeatureDependency(ProductFeature.class, true)27    };28}

Zkopírovat odkaz na sekciPoužití

Následně všechny načtené produkty budou obsahovat i atribut demoNumber, který bude možné filtrovat a řadit:

java
1 DemoPublishedProduct product = publishedProductService.getProductByQuery(2    new ProductQuery(catalogCode)3        .withCode("p1")4        .withAttributes(ATTRIBUTE_DEMO_NUMBER)5).orElseThrow();6
7String demoNumber = product.getDemoNumber();

Zkopírovat odkaz na sekciTraity na přání

Pokud potřebujete na pouze na konkrétním místě (nikoliv v rámci celé aplikace, ale například jen pouze v rámci jedné služby nebo administrační stránky atp.) dynamicky doplnit další trait, použijte wrapInto():

java
1 @Trait2public interface CustomTrait extends LocalDataStore {3    default void setData(Integer data) {4        setLocalData("data", data);5    }6
7    default Integer getData() {8        return getLocalData("data");9    }10}11
12// Použití13DemoPublishedProduct product = publishedProductService.getProductByQuery(...);14DemoPublishedProduct extendedProduct = publishedProductService.wrapInto(product, CustomTrait.class);15
16assertTrue(extendedProduct instanceof CustomTrait);17((CustomTrait)extendedProduct).setData(42);

Tato funkcionalita dává smysl, když nechcete změnit rozhraní produktu v celém systému, ale jen v nějaké konkrétní situaci.

Zkopírovat odkaz na sekci4.2 Vytvoření nové entity od základu

Kdy použít: Když potřebujete vytvořit zcela novou entitu (ne pouze rozšířit existující), např. nový typ katalogové entity.

⚠️ Upozornění: Většina kroků je shodná s kapitolou 4.1 Projektové rozšíření. Níže jsou popsána pouze specifika pro vytvoření nové entity.

Zkopírovat odkaz na sekciSpecifika pro novou entitu

1. Základní struktura rozhraní

Každá nová publikovaná entita se skládá ze čtyř hlavních rozhraní:

  • PublishedMyEntity - hlavní rozhraní entity (pro nahrazení na projektu)
  • PublishedMyEntityContract<READ_MODEL, WRITE_MODEL> - contract s anotacemi
  • PublishedMyEntityEditor - rozhraní editoru (pro nahrazení na projektu)
  • PublishedMyEntityEditorContract<READ_MODEL, WRITE_MODEL> - contract editoru

2. Contract musí dědit z PublishedEntityBase:

java
1 @Entity(2    name = "MyEntity",3    description = "MyEntity represents...",4    allowedEvolution = {5        EvolutionMode.ADDING_ATTRIBUTES,6        EvolutionMode.ADDING_ASSOCIATED_DATA7    }8)9public interface PublishedMyEntityContract<10    READ_MODEL extends PublishedMyEntityContract<READ_MODEL, WRITE_MODEL>,11    WRITE_MODEL extends PublishedMyEntityEditorContract<READ_MODEL, WRITE_MODEL>>12extends13    PublishedEntityBase,  // ← povinné!14    SealedInstance<READ_MODEL, WRITE_MODEL>,15    WithPublishedVisibility,16    WithPublishedValidity {17
18    String ENTITY_NAME = "MyEntity";19
20    // Název deskriptoru pro publikaci21    String PUBLISHED_DESCRIPTOR_NAME = ClassSchemaAnalyzer.extractEntityTypeFromClass(22        PublishedMyEntityContract.class,23        ReflectionLookup.NO_CACHE_INSTANCE24    ).orElseThrow();25
26    @Override27    default String getPublishingDescriptorName() {28        return PUBLISHED_DESCRIPTOR_NAME;29    }30
31    @Override32    default CatalogEntityType getEntityType() {33        return CatalogEntityType.MY_ENTITY; // ← musí existovat v enum!34    }35
36    // ... atributy, reference, asociovaná data37}

3. Editor Contract musí dědit z PublishedEntityBaseEditor:

java
1 public interface PublishedMyEntityEditorContract<2    READ_MODEL extends PublishedMyEntityContract<READ_MODEL, WRITE_MODEL>,3    WRITE_MODEL extends PublishedMyEntityEditorContract<READ_MODEL, WRITE_MODEL>>4extends5    PublishedMyEntityContract<READ_MODEL, WRITE_MODEL>,6    PublishedEntityBaseEditor,  // ← povinné!7    WithPublishedValidityEditor,8    WithPublishedVisibilityEditor,9    InstanceEditor<READ_MODEL> {10
11    // setter metody12}

4. Model Class Descriptor:

Pro novou entitu musíte vytvořit descriptor:

java
1 public class PublishedMyEntityModelClassDescriptor extends ModelClassDescriptor2    implements PublishedModelClassDescriptor<PublishedMyEntity, PublishedMyEntityEditor> {3
4    public PublishedMyEntityModelClassDescriptor() {5        super(PublishedMyEntityContract.ENTITY_NAME, PublishedMyEntity.class);6    }7
8    @Override9    public Class<PublishedMyEntity> getReadModelClass() {10        return PublishedMyEntity.class;11    }12
13    @Override14    public Class<PublishedMyEntityEditor> getWriteModelClass() {15        return PublishedMyEntityEditor.class;16    }17}

5. Servisa a Snippet:

Vytváří se stejným způsobem jako v kapitole 4.1, jen místo dědění z AbstractPublishedProductService budete dědit z příslušné abstraktní třídy pro vaši entitu.

6. Registrace:

java
1 evitaPublishingService.registerConversionSnippet(new MyEntityConversionSnippet());2evitaPublishingService.getPublishingDescriptorFor(MY_ENTITY)3    .registerPublishedServiceClass(PublishedMyEntityService.class);

7. CatalogEntityType:

Nezapomeňte přidat nový typ do enum CatalogEntityType:

java
1 public enum CatalogEntityType {2    PRODUCT,3    CATEGORY,4    BRAND,5    MY_ENTITY  // ← nový typ6}

Kompletní příklad struktury nového rozhraní najdete v kapitole 4.1 - kroky jsou identické, jen se vytváří od základu místo rozšiřování existujícího.

Zkopírovat odkaz na sekci4.2.1 Entity bez vazby na CatalogEntityType

Kdy použít: Když potřebujete publikovat data do evitaDB, která nemají oporu v CatalogEntityType ani v tabulce T_EDEESHOP_CATALOG_ENTITY. Typické případy:

  • entity z Edee CMS
  • entity z externích systémů

Příklad: feature lib_eshop_tag publikuje entity Tag a TagCategory do evitaDB.

Zkopírovat odkaz na sekciKlíčové rozdíly oproti standardnímu přístupu

AspektStandardní entitaEntity bez CatalogEntityType
CatalogEntityTypeVyžaduje přidání do enumNepotřebuje
Dědičnost ContractPublishedEntityBaseWithEntitySchema, TraitSupport, Serializable
Registrace schématuAutomatickáManuální (defineEntitySchemaFromModelClass)
Publikace datPřes PublishingJournalServiceManuální přes evitaDB session
AktualizaceAutomatickéVlastní change listener

Zkopírovat odkaz na sekciKrok 1: Contract rozhraní

Contract definuje schéma entity pomocí anotací. Nedědí z PublishedEntityBase:

java
1 @Entity(2    name = PublishedTagContract.ENTITY_NAME,3    description = "Tag is equivalent to EdeeTag that is published to evitaDB."4)5public interface PublishedTagContract<6    READ_MODEL extends PublishedTagContract<READ_MODEL, WRITE_MODEL>,7    WRITE_MODEL extends PublishedTagEditorContract<READ_MODEL, WRITE_MODEL>>8extends9    WithEntitySchema,        // přístup ke schématu10    WithPublishedCode,       // atribut code11    WithPublishedName,       // lokalizovaný název12    WithPublishedDatetime,   // timestamp publikace13    TraitSupport,14    Serializable,15    SealedInstance<READ_MODEL, WRITE_MODEL> {16
17    String ENTITY_NAME = "Tag";18    String ATTRIBUTE_IS_VISIBLE_IN_FILTER = "isVisibleInFilter";19    String REFERENCE_CATEGORY = "categories";20
21    @Attribute(22        name = ATTRIBUTE_IS_VISIBLE_IN_FILTER,23        description = "Contains true if tag should be displayed as a part of the product filter.",24        filterable = true25    )26    boolean isVisibleInFilter() throws ContextMissingException;27
28    @Reference(29        name = REFERENCE_CATEGORY,30        description = "Tag categories aggregating similar tags together.",31        entity = "TagCategory",32        indexed = ReferenceIndexType.FOR_FILTERING33    )34    @Nonnull35    List<PublishedTagCategory> getCategories() throws ContextMissingException;36}

Zkopírovat odkaz na sekciKrok 2: Editor Contract

java
1 public interface PublishedTagEditorContract<2    READ_MODEL extends PublishedTagContract<READ_MODEL, WRITE_MODEL>,3    WRITE_MODEL extends PublishedTagEditorContract<READ_MODEL, WRITE_MODEL>>4extends5    InstanceEditor<READ_MODEL>,6    WithPublishedCodeEditor,7    WithPublishedNameEditor,8    PublishedTagContract<READ_MODEL, WRITE_MODEL> {9
10    @AttributeRef(ATTRIBUTE_IS_VISIBLE_IN_FILTER)11    void setVisibleInFilter(boolean visibleInFilter);12
13    @ReferenceRef(REFERENCE_CATEGORY)14    @CreateWhenMissing15    void addOrUpdateCategory(int categoryId) throws ContextMissingException;16
17    @ReferenceRef(REFERENCE_CATEGORY)18    @RemoveWhenExists19    void removeCategories() throws ContextMissingException;20}

Zkopírovat odkaz na sekciKrok 3: Hlavní rozhraní

java
1 public interface PublishedTag extends PublishedTagContract<PublishedTag, PublishedTagEditor> {2}3
4public interface PublishedTagEditor extends PublishedTagEditorContract<PublishedTag, PublishedTagEditor> {5}

Zkopírovat odkaz na sekciKrok 4: Publisher callback (registrace schématu a počáteční publikace)

Publisher dědí z AbstractEvitaIndexingCallback a přepisuje metodu catalogDefined():

java
1 @RequiredArgsConstructor2public class EdeeTagPublisher extends AbstractEvitaIndexingCallback {3    private final EntityTagService tagService;4    private final TagToEvitaConverter tagToEvitaConverter;5
6    @Override7    public int getCallbackOrder() {8        return -1000;  // záporná hodnota = provede se před ostatními9    }10
11    @Override12    public void catalogDefined(13        @NonNull String catalog,14        @NonNull String evitaCatalog,15        @NonNull EvitaContract evita,16        @NonNull EvitaPublishingService evitaPublishingService,17        @NonNull SchemaPostProcessor postConfigurator18    ) {19        evita.updateCatalog(evitaCatalog, session -> {20            // 1. Registrace schématu z model class21            SealedEntitySchema tagSchema = session.defineEntitySchemaFromModelClass(22                PublishedTag.class,23                postConfigurator24            );25
26            // 2. Publikace všech entit27            for (EdeeTag tag : tagService.getAllTags(catalog)) {28                tagToEvitaConverter.convertToNewTag(29                    catalog, tag, tagSchema.getLocales(), session30                );31            }32        });33    }34}

Zkopírovat odkaz na sekciKrok 5: Change listener (pro real-time aktualizace)

Listener reaguje na události a aktualizuje data v evitaDB:

java
1 @RequiredArgsConstructor2public class EdeeTagChangeListener implements ApplicationListener<TagEvent> {3    private final EntityTagService tagService;4    private final TagToEvitaConverter tagToEvitaConverter;5    private final EvitaClientContract evita;6
7    @Override8    public void onApplicationEvent(TagEvent event) {9        final Tag tag = event.getSubject();10
11        for (String catalog : evita.getCatalogNames()) {12            evita.updateCatalog(catalog, session -> {13                session.getEntitySchema(PublishedTag.ENTITY_NAME).ifPresent(schema -> {14                    if (event instanceof TagRemovedEvent) {15                        // Smazání entity16                        session.deleteEntity(PublishedTag.ENTITY_NAME, Math.toIntExact(tag.getId()));17                    } else {18                        // Aktualizace nebo vytvoření entity19                        final Optional<? extends PublishedTagContract<?,?>> existing =20                            session.getEntity(PublishedTag.class, Math.toIntExact(tag.getId()),21                                QueryConstraints.entityFetchAllContent());22
23                        if (existing.isPresent()) {24                            // Aktualizace existující25                            PublishedTagEditorContract<?,?> editor = existing.get().openForWrite();26                            tagToEvitaConverter.convert(catalog, tagService.getTagById(tag.getId()),27                                schema.getLocales(), editor, session);28                        } else {29                            // Vytvoření nové30                            tagToEvitaConverter.convertToNewTag(catalog, tagService.getTagById(tag.getId()),31                                schema.getLocales(), session);32                        }33                    }34                });35            });36        }37    }38}

Zkopírovat odkaz na sekciKrok 6: Konvertor

Konvertor transformuje zdrojová data do evitaDB entity:

java
1 @RequiredArgsConstructor2public class TagToEvitaConverter {3
4    public void convertToNewTag(5        @NonNull String catalogCode,6        @Nonnull EdeeTag tag,7        @Nonnull Set<Locale> allowedLocales,8        @Nonnull EvitaSessionContract session9    ) {10        // Vytvoření nové entity s daným ID11        PublishedTagEditorContract<?, ?> publishedTag =12            session.createNewEntity(PublishedTagEditorContract.class, Math.toIntExact(tag.getId()));13
14        convert(catalogCode, tag, allowedLocales, publishedTag, session);15    }16
17    public void convert(18        @NonNull String catalogCode,19        @Nonnull EdeeTag tag,20        @Nonnull Set<Locale> allowedLocales,21        @Nonnull PublishedTagEditorContract<?, ?> publishedTag,22        @NonNull EvitaSessionContract session23    ) {24        publishedTag.setCode(tag.getSystemId());25        publishedTag.setVisibleInFilter(tag.isFilterable());26
27        for (Locale locale : allowedLocales) {28            final String tagName = tag.getName(locale.getLanguage());29            if (tagName != null) {30                publishedTag.setName(locale, tagName);31            }32        }33
34        // Uložení entity35        session.upsertEntity(publishedTag);36    }37}

Zkopírovat odkaz na sekciKrok 7: Registrace ve feature

V metodě afterSpringInitialization() zaregistrujte callback:

java
1 @Override2public void afterSpringInitialization(AbstractRefreshableApplicationContext moduleContext) {3    super.afterSpringInitialization(moduleContext);4
5    if (isFeaturesEnabled(EvitaFeature.class)) {6        final EvitaPublishingService evitaPublishingService =7            getFeature(EvitaFeature.class).getEvitaPublishingService();8
9        // Registrace callbacku pro publikaci10        final EdeeTagPublisher edeeTagPublisher = moduleContext.getBean(EdeeTagPublisher.class);11        evitaPublishingService.addEvitaIndexingCallback(edeeTagPublisher);12    }13}

Zkopírovat odkaz na sekciReference na kód

Tip: Tímto způsobem lze přidávat libovolné množství entit do evitaDB bez nutnosti rozšiřovat CatalogEntityType enum a bez závislosti na standardním publikačním mechanismu EdeeShop.

Zkopírovat odkaz na sekci4.3 Automatická aktualizace schémat

Kdy použít: Při nasazení nové verze aplikace, která obsahuje změny v definici schémat publikovaných entit.

Zkopírovat odkaz na sekciJak to funguje

Při startu EdeeShop modulu se automaticky aktualizují schémata existujících katalogů v evitaDB. Aktualizace probíhá formou "upsert" operace:

  • ✅ Přidání nových atributů, referencí, asociovaných dat
  • ✅ Aktualizace stávajících definic (kromě přejmenování)
  • ❌ Nikdy nedochází k automatickému odstraňování položek

Přejmenované prvky se chovají jako nové položky - původní zůstávají v databázi.

Zkopírovat odkaz na sekciCo dělat po aktualizaci schématu

  1. Inkrementální indexace - nově přidané prvky jsou prázdné, je nutné je naplnit daty:

    java
    1 // Vynutit reindex entity v evitaDB na základě stavu ve STRUCTURE tabulkách2publishingJournalService.triggerEntityChangeForPublishedEntities(3    catalog, entityType, new ListingQueryImpl(4        in(AdamPublishedCatalogEntity.PROPERTY_ID_CATALOG_ENTITY, entityIds)5    )6);7
    8// Nebo kompletní přepublikace z primárních dat (STRUCTURE tabulky + evitaDB)9publishingJournalService.republishAllPublishedEntities(10    catalog, entityType, PublishingRequestType.ENTITY_UPDATE,11    new ListingQueryImpl(12        in(AdamPublishedCatalogEntity.PROPERTY_ID_CATALOG_ENTITY, entityIds)13    )14);
  2. Úklid nepoužívaných prvků - po delší době (až klienti jako Juan přestanou staré prvky využívat) je vhodné:

    • Provést plnou reindexaci dat, nebo
    • Vytvořit job pro manuální odstranění nepoužívaných prvků ze schématu

Zkopírovat odkaz na sekciOmezení a známé problémy

ProblémPopisŘešení
Indexy na starých datechZměny v indexech se nepromítají na již zaindexovaná data (issue #409)Provést plnou reindexaci
Staré indexy v pamětiPůvodní indexy zůstávají v DB a paměti i po změně schématuRestart + reindexace
Prázdné nové prvkyNově založené atributy/reference nemají dataInkrementální indexace

Zkopírovat odkaz na sekciReference na kód

⚠️ Doporučení: Tento mechanismus je relativně nový. Důkladně otestujte na testovacím prostředí před nasazením do produkce.

Zkopírovat odkaz na sekci5. Pokročilé koncepty

Zkopírovat odkaz na sekci5.1 Scope (Live vs Archived)

evitaDB podporuje koncept scopes (dosahů), které umožňují mít různé nastavení atributů v různých kontextech:

  • Scope.LIVE - aktivní data - plně dostupná na frontendu
  • Scope.ARCHIVED - archivovaná data (např. ukončené produkty) - s omezenou dostupností a možnostmi na frontendu

Použití:

java
1 @Attribute(2    name = "productType",3    scope = {4        @ScopeAttributeSettings(scope = Scope.LIVE, filterable = true),5        @ScopeAttributeSettings(scope = Scope.ARCHIVED, filterable = true)6    }7)8ProductType getProductType() throws ContextMissingException;

Globální atributy jsou sdílené napříč všemi scope:

java
1 @Attribute(2    name = "status",3    global = true,  // ← sdílený napříč scope4    representative = true,5    scope = {6        @ScopeAttributeSettings(scope = Scope.LIVE, filterable = true),7        @ScopeAttributeSettings(scope = Scope.ARCHIVED, filterable = true)8    }9)10CatalogEntityStatus getStatus() throws ContextMissingException;

Zkopírovat odkaz na sekci5.2 EvolutionMode

EvolutionMode definuje, jak může se schéma entity automaticky rozšiřovat při publikaci nových dat, aniž by bylo nutné manuálně měnit schéma v evitaDB:

java
1 @Entity(2    name = "Product",3    allowedEvolution = {4        EvolutionMode.ADDING_ATTRIBUTES,         // přidávání atributů5        EvolutionMode.ADDING_ASSOCIATED_DATA,    // přidávání asociovaných dat6        EvolutionMode.ADDING_REFERENCES,         // přidávání referencí7        EvolutionMode.ADDING_LOCALES,            // přidávání lokalizací8        EvolutionMode.ADDING_CURRENCIES,         // přidávání měn9        EvolutionMode.ADDING_HIERARCHY           // přidávání hierarchie10    }11)

⚠️ Upozornění: Po automatické evoluci je nutné provést inkrementální indexaci (viz kapitola 4.3).

Zkopírovat odkaz na sekci5.3 Representative attributes

Atributy s representative = true nemají jiný význam, než ten, že tyto atributy jsou zobrazené v evitaLab při výpisu entit v gridu, aniž by bylo nutné je explicitně do pohledu přidávat:

java
1 @Attribute(2    name = "catalogNumber",3    representative = true,  // ← automaticky načten4    nullable = true5)6String getCatalogNumber() throws ContextMissingException;

Použití u referencí: Representative označuje také klíčové atributy u referencí umožňujících duplikované vazby na stejné cílové entity (např. vazby na média).

Zkopírovat odkaz na sekci5.4 Compound attributes

Složené atributy umožňují řazení podle více kritérií najednou (evitaDB má odlišný přístup k sekundárnímu řazení oproti tomu, na co jste zvyklí z relačních databází):

java
1 @SortableAttributeCompound(2    name = "productPriorityAndName",3    description = "Compound index for sorting",4    attributeElements = {5        @AttributeElement(attributeName = "priority", direction = OrderDirection.DESC),6        @AttributeElement(attributeName = "name", direction = OrderDirection.ASC)7    }8)

Zkopírovat odkaz na sekci5.5 Lokalizované atributy

Atributy s localized = true mají různé hodnoty pro různé jazyky:

java
1 @Attribute(2    name = "name",3    localized = true,  // ← hodnoty podle jazyka4    filterable = true,5    sortable = true6)7String getName() throws ContextMissingException;

Typicky se kombinuje s @LocalizedLabel na primárních datech:

java
1 @Attribute(name = "unit", localized = true)2// odpovídá @LocalizedLabel(code = Product.UNIT_CODE) na primárních datech3String getUnit() throws ContextMissingException;

Zkopírovat odkaz na sekci5.6 Unikátnost atributů

java
1 // Globálně unikátní v celé kolekci2@Attribute(3    name = "codeShort",4    unique = AttributeUniquenessType.UNIQUE_WITHIN_COLLECTION5)6String getCodeShort() throws ContextMissingException;7
8// Unikátní v rámci kolekce a lokalizace9@Attribute(10    name = "url",11    localized = true,12    unique = AttributeUniquenessType.UNIQUE_WITHIN_COLLECTION_LOCALE13)14String getUrl() throws ContextMissingException;

Zkopírovat odkaz na sekci5.7 Nullable vs Non-nullable

Pokud neuvedete jinak jsou všechny atributy považovány za povinné (non-nullable). Pro volitelné atributy použijte nullable = true:

java
1 // Povinný atribut2@Attribute(name = "productType")3@Nonnull4ProductType getProductType() throws ContextMissingException;5
6// Volitelný atribut7@Attribute(name = "catalogNumber", nullable = true)8@Nullable9String getCatalogNumber() throws ContextMissingException;10
11// Atribut s default hodnotou12@Attribute(name = "stepOrderQuantity")13@Nonnull14default BigDecimal getStepOrderQuantity() {15    return BigDecimal.ONE;16}

Zkopírovat odkaz na sekci5.8 Dědičnost atributů v referencích

U zpětných referencí lze specifikovat, které atributy se mají dědit:

java
1 @ReflectedReference(2    name = "products",3    ofEntity = "Product",4    ofName = "categories",5    attributeInheritanceFilter = { "assignmentValidity" }  // ← pouze tento atribut6)7List<PublishedCategoryProduct> getProducts() throws ContextMissingException;

Zkopírovat odkaz na sekci6. Best practices

Zkopírovat odkaz na sekci6.1 Pojmenování a konstanty

✅ Vždy používejte konstanty:

java
1 public interface PublishedProductContract<READ_MODEL, WRITE_MODEL> {2    String ENTITY_NAME = "Product";3    String ATTRIBUTE_CODE_SHORT = "codeShort";4
5    @Attribute(name = ATTRIBUTE_CODE_SHORT, ...)6    String getCodeShort() throws ContextMissingException;7}

❌ Nikdy hardcoded stringy:

java
1 @Attribute(name = "codeShort", ...)  // ŠPATNĚ!

Zkopírovat odkaz na sekci6.2 Dokumentace

✅ Vždy vyplňte description:

java
1 @Attribute(2    name = "orderedQuantity",3    description = """4        Returns count of items that has been already sold.5        Allows sorting by most sold products.""",6    sortable = true7)8BigDecimal getOrderedQuantity() throws ContextMissingException;

✅ Používejte ContextMissingException:

Jen tak, dostanete výjimku, pokud daný údaj může sice v databázi existovat, ale na entitě není dostupný, protože nebyl v rámci dotazu z databáze načten. Pokud výjimku na metodě nedeklarujete, vrátí se hodnota null i pro reálně existující data, což může vést k těžko dohledatelným chybám.

java
1 /**2 * Returns product shortcode for feeds and GTM.3 *4 * @return product shortcode5 * @throws ContextMissingException if not fetched6 */7@Attribute(name = ATTRIBUTE_CODE_SHORT, ...)8String getCodeShort() throws ContextMissingException;

Zkopírovat odkaz na sekci6.3 Využívání traitů

✅ Používejte existující traity:

java
1 public interface PublishedProductContract<READ_MODEL, WRITE_MODEL> extends2    PublishedEntityBase,3    WithPublishedLocalization,  // místo reimplementace4    WithPublishedPrice,5    WithPublishedURL {6}

❌ Neduplikujte funkcionalitu:

java
1 // ŠPATNĚ - toto už je v WithPublishedLocalization!2@Attribute(name = "name", localized = true, filterable = true)3String getName();

Zkopírovat odkaz na sekci6.4 Plánování indexace

Po přidání nových atributů:

  1. Přidejte atribut do schématu
  2. Nasaďte změny
  3. Vytvořte job pro inkrementální indexaci:
    java
    1 publishingJournalService.triggerEntityChangeForPublishedEntities(2    catalog, entityType, new ListingQueryImpl(3        in(AdamPublishedCatalogEntity.PROPERTY_ID_CATALOG_ENTITY, entityIds)4    )5);

⚠️ Nové atributy nemají hodnoty automaticky!

Zkopírovat odkaz na sekci6.5 Testování

✅ Otestujte GraphQL query:

graphql
1 query {2  listProduct(filterBy: { codeShort_equals: "PROD123" }) {3    recordPage {4      data {5        primaryKey6        codeShort7        name8      }9    }10  }11}

✅ Ověřte schema:

graphql
1 query {2  __type(name: "Product") {3    fields {4      name5      type { name }6    }7  }8}

Zkopírovat odkaz na sekci6.6 Filtrovatelné atributy

✅ Označte jako filterable/sortable pouze když je to nutné:

Každý index zabírá paměť a může zpomalit indexaci.

java
1 // DOBŘE2@Attribute(name = "availability", filterable = true)3String getAvailabilityRaw();4
5// ŠPATNĚ - zbytečný index6@Attribute(name = "longDescription", filterable = true)7String getLongDescription();

Zkopírovat odkaz na sekci6.7 Representative atributy

✅ Označte důležité atributy:

java
1 @Attribute(name = "catalogNumber", representative = true)2String getCatalogNumber();

❌ Ne všechno:

java
1 // ŠPATNĚ2@Attribute(name = "longDescription", representative = true)3String getLongDescription();

Zkopírovat odkaz na sekci6.8 Zpětná kompatibilita

✅ Při změnách schématu:

  • Nové atributy přidávejte jako nullable = true
  • Nepřejmenovávejte existující atributy
  • Neodstraňujte atributy, které používají klienti

✅ Deprecated anotace:

java
1 /**2 * @deprecated Use {@link #getNewAttribute()} instead.3 * Will be removed in version 2026.06.4 */5@Deprecated6@Attribute(name = "oldName", nullable = true)7String getOldAttribute();

Zkopírovat odkaz na sekci6.9 Error handling

✅ Správně používejte ContextMissingException:

java
1 // DOBŘE2@Attribute(name = "name")3String getName() throws ContextMissingException;4
5// ŠPATNĚ - chybí throws6@Attribute(name = "name")7String getName();

*✅ Používejte IfAvailable metody:

java
1 // Pro povinné fetchnutí2@Reference(name = "brand")3PublishedBrand getBrand() throws ContextMissingException;4
5// Pro optional přístup6@ReferenceRef("brand")7Optional<PublishedBrand> getBrandIfAvailable();

Zkopírovat odkaz na sekci6.10 Performance

✅ Načítejte pouze potřebná data:

java
1 // DOBŘE2publishedProductService.getProductByQuery(3    new ProductQuery(catalogCode)4        .withCode(productCode)5        .withAttributes(ATTRIBUTE_NAME, ATTRIBUTE_PRICE)6);7
8// ŠPATNĚ9publishedProductService.getProductByQuery(10    new ProductQuery(catalogCode)11        .withCode(productCode)12        .fetchAll()  // POMALÉ!13);

✅ Batch operace:

java
1 // DOBŘE - jedna operace2List<PublishedProduct> products = publishedProductService.getProductsByQuery(3    new ProductQuery(catalogCode).withCodes(productCodes)4);5
6// ŠPATNĚ - N dotazů7List<PublishedProduct> products = productCodes.stream()8    .map(code -> publishedProductService.getProductByCode(catalogCode, code))9    .collect(Collectors.toList());

Zkopírovat odkaz na sekci6.11 Shrnutí

  • Vždy používejte konstanty pro názvy
  • Dokumentujte pomocí description a JavaDoc
  • Využívejte existující traity
  • Plánujte inkrementální indexaci po změnách
  • Testujte GraphQL API
  • Označujte jako filterable/sortable pouze co je nutné
  • Udržujte zpětnou kompatibilitu
  • Správně používejte ContextMissingException
  • Optimalizujte načítání dat

Zkopírovat odkaz na sekci7. Reference - přehled traitů

Tato kapitola obsahuje detailní popis všech běžně používaných traitů.

Zkopírovat odkaz na sekci7.1 Lokalizační traity

Zkopírovat odkaz na sekciWithPublishedLocalization

Přidává základní lokalizované atributy jako název a popis entity.

java
1 public interface WithPublishedLocalization {2    String ATTRIBUTE_NAME = "name";3    String ATTRIBUTE_DESCRIPTION = "description";4
5    @Attribute(name = ATTRIBUTE_NAME, localized = true, filterable = true, sortable = true)6    @Nonnull7    String getName() throws ContextMissingException;8
9    @Attribute(name = ATTRIBUTE_DESCRIPTION, localized = true, nullable = true)10    @Nullable11    String getDescription() throws ContextMissingException;12}

Používají: Product, Category, Brand, Group, Parameter, ParameterValue

Zkopírovat odkaz na sekci7.2 Časové traity

Zkopírovat odkaz na sekciWithPublishedValidity

Přidává časovou platnost entity (validFrom/validTo).

java
1 public interface WithPublishedValidity {2    String ATTRIBUTE_VALIDITY = "validity";3
4    @Attribute(5        name = ATTRIBUTE_VALIDITY,6        nullable = true,7        filterable = true,8        sortable = true9    )10    @Nullable11    DateTimeRange getValidity() throws ContextMissingException;12}

Používají: Product, Category, Brand, Group, PaymentMethod, ShippingMethod

Zkopírovat odkaz na sekci7.3 Viditelnost

Zkopírovat odkaz na sekciWithPublishedVisibility

Přidává atribut pro ovládání viditelnosti entity.

java
1 public interface WithPublishedVisibility {2    String ATTRIBUTE_VISIBLE = "visible";3
4    @Attribute(name = ATTRIBUTE_VISIBLE, filterable = true)5    boolean isVisible() throws ContextMissingException;6}

Používají: Category, Brand, Group

Zkopírovat odkaz na sekci7.4 URL traity

Zkopírovat odkaz na sekciWithPublishedURL

Přidává URL funkcionalitu pro entitu (slugy, aktivní URL).

java
1 public interface WithPublishedURL extends WithPublishedDomain {2    String ATTRIBUTE_URL = "url";3    String ASSOCIATED_DATA_ALL_ACTIVE_URLS = "allActiveUrls";4
5    @Attribute(name = ATTRIBUTE_URL, localized = true, nullable = true)6    @Nullable7    String getUrl() throws ContextMissingException;8
9    @AssociatedData(name = ASSOCIATED_DATA_ALL_ACTIVE_URLS, nullable = true)10    @Nullable11    List<ActiveUrl> getAllActiveUrls() throws ContextMissingException;12}

Používají: Product, Category, Brand, Group

Zkopírovat odkaz na sekci7.5 Obsahové traity

Zkopírovat odkaz na sekciWithPublishedTags

Přidává podporu pro tagy.

java
1 public interface WithPublishedTags {2    String REFERENCE_TAGS = "tags";3
4    @Reference(name = REFERENCE_TAGS)5    @Nonnull6    List<PublishedTag> getTags() throws ContextMissingException;7}

Používají: Product, Category, Brand

Zkopírovat odkaz na sekciWithPublishedMedia

Přidává podporu pro obrázky a multimédia.

java
1 public interface WithPublishedMedia {2    String REFERENCE_MEDIA = "media";3
4    @Reference(name = REFERENCE_MEDIA)5    @Nonnull6    List<PublishedMedia> getMedia() throws ContextMissingException;7}

Používají: Product, Category, Brand, Group

Zkopírovat odkaz na sekciWithPublishedRelatedFiles

Přidává podporu pro související soubory (PDF, dokumenty).

Používají: Product, Category, Brand

Zkopírovat odkaz na sekci7.6 Produktové traity

Zkopírovat odkaz na sekciWithPublishedPrice

Přidává cenovou funkcionalitu.

java
1 public interface WithPublishedPrice {2    @Price3    List<PriceContract> getPrices();4
5    @PriceForSale6    PriceContract getPriceForSale() throws ContextMissingException;7}

Používají: Product, ProductBundle, Voucher

Zkopírovat odkaz na sekciWithPublishedCategories

Přidává reference na kategorie.

java
1 public interface WithPublishedCategories {2    String REFERENCE_CATEGORIES = "categories";3
4    @Reference(name = REFERENCE_CATEGORIES)5    @Nonnull6    List<PublishedProductCategory> getCategories() throws ContextMissingException;7}

Používají: Product

Zkopírovat odkaz na sekciWithPublishedGroups

Přidává reference na skupiny produktů.

java
1 public interface WithPublishedGroups {2    String REFERENCE_GROUPS = "groups";3
4    @Reference(name = REFERENCE_GROUPS)5    @Nonnull6    List<PublishedProductGroup> getGroups() throws ContextMissingException;7}

Používají: Product

Zkopírovat odkaz na sekciWithPublishedParameters

Přidává parametry produktu (technické specifikace).

java
1 public interface WithPublishedParameters {2    String REFERENCE_PARAMETERS = "parameters";3
4    @Reference(name = REFERENCE_PARAMETERS)5    @Nonnull6    List<PublishedProductParameter> getParameters() throws ContextMissingException;7}

Používají: Product

Zkopírovat odkaz na sekciWithPublishedBrand

Přidává referenci na značku.

java
1 public interface WithPublishedBrand {2    String REFERENCE_BRAND = "brand";3
4    @Reference(name = REFERENCE_BRAND)5    @Nullable6    PublishedBrand getBrand() throws ContextMissingException;7}

Používají: Product

Zkopírovat odkaz na sekci7.7 Variantní traity

Zkopírovat odkaz na sekciWithPublishedMasterVariantProperties

Přidává podporu pro master/variant produkty.

Používají: Product

Zkopírovat odkaz na sekci7.8 Skladové traity

Zkopírovat odkaz na sekciWithPublishedStock

Přidává informace o skladových zásobách.

Používají: Product

Zkopírovat odkaz na sekciWithPublishedStockVisibility

Přidává nastavení viditelnosti skladů.

Používají: Product

Zkopírovat odkaz na sekci7.9 Výhody kompozičního přístupu

java
1 public interface PublishedProductContract<READ_MODEL, WRITE_MODEL> extends2    PublishedEntityBase,              // Základní funkcionalita3    WithPublishedLocalization,         // Název, popis4    WithPublishedPrice,                // Ceny5    WithPublishedRelatedFiles,         // Soubory6    WithPublishedMedia,                // Obrázky7    WithPublishedURL,                  // URL8    WithPublishedTags,                 // Tagy9    WithPublishedParameters,           // Parametry10    WithPublishedCategories,           // Kategorie11    WithPublishedGroups,               // Skupiny12    WithPublishedValidity,             // Časová platnost13    WithPublishedStock,                // Sklady14    WithPublishedMasterVariantProperties,15    SealedInstance<READ_MODEL, WRITE_MODEL> {16    // ... specifické atributy produktu17}

Výhody:

  • Znovupoužitelnost - stejná funkcionalita na více entitách
  • Konzistenci - jednotné pojmenování a chování
  • Modulárnost - snadné přidání/odebrání funkcionality
  • Udržovatelnost - změny na jednom místě se projeví všude