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
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:
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:
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:
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:
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:
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ů:
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:
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:
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:
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:
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:
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ů:
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:
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:
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:
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:
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:
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:
1 @Price2List<PriceContract> getPrices();
Zkopírovat odkaz na sekci@PriceForSale
Označuje aktuální prodejní cenu entity:
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:
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:
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:
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
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:
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:
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:
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:
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():
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:
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:
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:
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:
1 evitaPublishingService.registerConversionSnippet(new MyEntityConversionSnippet());2evitaPublishingService.getPublishingDescriptorFor(MY_ENTITY)3 .registerPublishedServiceClass(PublishedMyEntityService.class);
7. CatalogEntityType:
Nezapomeňte přidat nový typ do enum CatalogEntityType:
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
| Aspekt | Standardní entita | Entity bez CatalogEntityType |
|---|---|---|
| CatalogEntityType | Vyžaduje přidání do enum | Nepotřebuje |
| Dědičnost Contract | PublishedEntityBase | WithEntitySchema, TraitSupport, Serializable |
| Registrace schématu | Automatická | Manuální (defineEntitySchemaFromModelClass) |
| Publikace dat | Přes PublishingJournalService | Manuální přes evitaDB session |
| Aktualizace | Automatické | Vlastní change listener |
Zkopírovat odkaz na sekciKrok 1: Contract rozhraní
Contract definuje schéma entity pomocí anotací. Nedědí z PublishedEntityBase:
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
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í
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():
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:
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:
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:
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
-
Inkrementální indexace - nově přidané prvky jsou prázdné, je nutné je naplnit daty:
java1 // 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);
-
Ú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ém | Popis | Řešení |
|---|---|---|
| Indexy na starých datech | Změny v indexech se nepromítají na již zaindexovaná data (issue #409) | Provést plnou reindexaci |
| Staré indexy v paměti | Původní indexy zůstávají v DB a paměti i po změně schématu | Restart + reindexace |
| Prázdné nové prvky | Nově založené atributy/reference nemají data | Inkrementální indexace |
Zkopírovat odkaz na sekciReference na kód
- PublishingJournalService#triggerEntityChangeForPublishedEntities - reindex entity v evitaDB
- PublishingJournalService#republishAllPublishedEntities - kompletní přepublikace
⚠️ 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í:
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:
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:
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:
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í):
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:
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:
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ů
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:
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:
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:
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:
1 @Attribute(name = "codeShort", ...) // ŠPATNĚ!Zkopírovat odkaz na sekci6.2 Dokumentace
✅ Vždy vyplňte description:
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.
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:
1 public interface PublishedProductContract<READ_MODEL, WRITE_MODEL> extends2 PublishedEntityBase,3 WithPublishedLocalization, // místo reimplementace4 WithPublishedPrice,5 WithPublishedURL {6}
❌ Neduplikujte funkcionalitu:
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ů:
- Přidejte atribut do schématu
- Nasaďte změny
- 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:
1 query {2 listProduct(filterBy: { codeShort_equals: "PROD123" }) {3 recordPage {4 data {5 primaryKey6 codeShort7 name8 }9 }10 }11}
✅ Ověřte schema:
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.
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:
1 @Attribute(name = "catalogNumber", representative = true)2String getCatalogNumber();
❌ Ne všechno:
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:
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:
1 // DOBŘE2@Attribute(name = "name")3String getName() throws ContextMissingException;4 5// ŠPATNĚ - chybí throws6@Attribute(name = "name")7String getName();
*✅ Používejte IfAvailable metody:
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:
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:
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.
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).
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.
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).
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.
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.
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.
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.
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ů.
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).
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.
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
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