Appearance
Events
Flow ID: CF-22 | Module(s): events, Event domain | Complexity: Medium
Business Overview
Events is a CMS module for publishing time-bound event articles (conferences, workshops, promotions, etc.) organized into categories. Each event has a date range (datetime_start / datetime_end), a banner image, MUI content (title, subtitle, description, location), and optional product associations. Events display on the storefront with pagination and category filtering, and also appear in footer widgets and on product detail pages.
What customers experience:
- Browse upcoming events at
/eventswith pagination (20 per page) - Filter events by category at
/events/{category-slug} - View event detail at
/events/{event-slug}with full description, location, dates, and linked products - See upcoming events in the site footer widget
- On product pages, see related events when product-event association is enabled
Key business behaviors:
- Events require
active = trueto display on the storefront - Category-event assignment is many-to-many via a link table
- Product-event association is gated by the
EVENT_ARTICLE_PRODUCT_ENABLEDregistry flag - Footer events widget shows only future events (
datetime_start > now), limited byFOOTER_EVENT_ITEMS_LIMIT - Events admin section is feature-flagged via
FLAGGED_CONTENT.EVENTSregistry key - Slugs are auto-generated from titles using Greek transliteration, with uniqueness enforcement per language
- Events cannot be deleted if they have linked products (referential integrity guard)
- Category deletion is blocked if events are assigned to the category
- All queries on the storefront are cached via
pscachefor performance
API Reference
REST Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /rest/event/event | Guest | List events (paginated, filterable) |
| GET | /rest/event/event/{id} | Guest | Get single event by ID |
| GET | /rest/event/event/item | Guest | Get single event by filters |
| POST | /rest/event/event | Backend (ADMIN, MARKETING, MEDIA) | Create event (JSON or multipart with image) |
| POST | /rest/event/event/{id} | Backend (ADMIN, MARKETING, MEDIA) | Update event |
| DELETE | /rest/event/event/{id} | Backend (ADMIN, MARKETING, MEDIA) | Delete event |
| GET | /rest/event/event-category | Guest | List event categories |
| GET | /rest/event/event-category/{id} | Guest | Get single category by ID |
| GET | /rest/event/event-category/item | Guest | Get single category by filters |
| POST | /rest/event/event-category | Backend (ADMIN, MARKETING, MEDIA) | Create category (JSON or multipart with image) |
| POST | /rest/event/event-category/{id} | Backend (ADMIN, MARKETING, MEDIA) | Update category |
| DELETE | /rest/event/event-category/{id} | Backend (ADMIN, MARKETING, MEDIA) | Delete category |
Event filters: id (exact), active (exact), title.{locale} (partial), slug.{locale} (exact) Event sorts: id, active, datetimeStart, datetimeEnd, dateChanged, title.{locale}, slug.{locale}Event relations: translations, categories, products
Category filters: id (exact), parentId (exact), published (exact), name.{locale} (partial), slug.{locale} (exact) Category sorts: id, parentId, order, published, name.{locale}, slug.{locale}Category relations: translations
Both controllers use HandlesUploadActions for file upload support. Image uploads go to files/events/ and files/event_categories/ respectively.
Legacy Storefront
| URL | Method |
|---|---|
/events | index() -- event listing (20/page) |
/events/{page-number} | index($page) -- paginated listing |
/events/{category-slug} | category() -- events filtered by category |
/events/{category-slug}/{page-number} | category() -- paginated category listing |
/events/{event-slug} | article() -- event detail page |
Legacy Admin
| URL | Method |
|---|---|
/events/events_admin | index() -- event list with search |
/events/events_admin/add | add() -- create event |
/events/events_admin/edit/{id} | edit() -- edit event with product assignment |
/events/events_admin/delete/{id} | delete() -- delete event |
/events/event_categories_admin | index() -- category list |
/events/event_categories_admin/add | add() -- create category |
/events/event_categories_admin/edit/{id} | edit() -- edit category |
/events/event_categories_admin/delete/{id} | delete() -- delete category |
/events/event_categories_admin/updateOrder | updateOrder() -- AJAX drag-drop reorder |
Code Flow
Storefront Event Listing
File: ecommercen/events/controllers/Adv_events.php
- Route dispatch:
_remap()receives the URL slug and determines whether it is a category, an article, or a method name - Category check: Queries
event_categories_model->getContent($slug, $lang)via pscache - Article check: Queries
events_model->getEventArticleData($slug, $lang)via pscache - Fallback: If neither matches, tries
method_exists()then 404 - Index listing: Fetches active events joined with MUI for current language, paginated at 20 per page, plus sidebar categories
- Category listing: Same as index but filtered to events linked to the category via
events_event_categories_lp - Article detail: Loads single event by slug, optionally loads linked products if
EVENT_ARTICLE_PRODUCT_ENABLEDregistry key is truthy
Event-Product Association (Storefront)
File: ecommercen/events/controllers/Adv_events.php -- renderEventArticleProducts()
- Feature check:
$this->registry->value('ESHOP', 'EVENT_ARTICLE_PRODUCT_ENABLED') - Load links:
events_model->getProductEventsLp(['event_id' => $eventId])fromshop_product_event_lp - Load products:
product_model->getProductsByIds($productIds) - Register for parsing: Adds product IDs to
productIdsToParsefor price/stock rendering
Product Page Events (Reverse Direction)
File: ecommercen/eshop/controllers/Adv_products.php -- renderProductEventArticles()
- Feature check: Same
EVENT_ARTICLE_PRODUCT_ENABLEDregistry flag - Load events:
events_model->getProductEventArticles($productId, $language)via pscache - Template: Renders associated event articles on the product detail page
Footer Widget
File: ecommercen/core/Adv_front_controller.php -- footerEventArticlesListRender()
- Load model:
events/events_model - Fetch:
getFooterEvents(date('Y-m-d'), $language, $limit)-- only future events (datetime_start > today) - Limit: Controlled by registry
OTHER.FOOTER_EVENT_ITEMS_LIMIT - Output:
$this->render['footerEventArticles']available to all storefront templates
Admin Event CRUD
File: ecommercen/events/controllers/Adv_events_admin.php
- Authorization:
allowRole([AUTH_ROLE_ADVISABLE, AUTH_ROLE_ADMIN, AUTH_ROLE_MARKETING, AUTH_ROLE_MEDIA]) - Index: Paginated list with title/location/date search filters, ordered by
date_changed DESC - Add: Validate category + dates + all MUI fields, upload image to
files/events/, auto-generate slug, insert master + MUI + category LP records - Edit: Same validation, update master + MUI, sync category assignments via delete-and-reinsert, handle product associations via
handleEventProducts() - Delete: Blocked if products are linked (
canDeleteRecordchecksshop_product_event_lpcount), otherwise cascades to MUI and category LP records
Admin Category CRUD
File: ecommercen/events/controllers/Adv_event_categories_admin.php
- Authorization:
allowRole([AUTH_ROLE_ADVISABLE, AUTH_ROLE_ADMIN, AUTH_ROLE_PRODUCTS]) - Index: Paginated MUI list ordered by
order ASC - Add: Validate name per language, upload image to
files/event_categories/, auto-incrementorderfrom max, insert master + MUI - Edit: Update master + MUI with
updateOrInsertMui()for language upsert, support image deletion - Delete: Blocked if events are assigned (
canDeleteRecordchecksevents_event_categories_lpcount) - Reorder: AJAX
updateOrder()for drag-drop sorting
Domain Layer
| Component | Path |
|---|---|
| Event Entity | src/Domains/Event/Event/Repository/Entity.php |
| Event MUI Entity | src/Domains/Event/Event/Repository/MuiEntity.php |
| Event Repository | src/Domains/Event/Event/Repository/Repository.php |
| Event RepositoryConfigurator | src/Domains/Event/Event/Repository/RepositoryConfigurator.php |
| Event Service | src/Domains/Event/Event/Service.php |
| Event WriteService | src/Domains/Event/Event/WriteService.php |
| Event WriteData | src/Domains/Event/Event/WriteData.php |
| Event MuiWriteData | src/Domains/Event/Event/MuiWriteData.php |
| Event Validator | src/Domains/Event/Event/Validator.php |
| Event ListRequest | src/Domains/Event/Event/ListRequest.php |
| Category Entity | src/Domains/Event/Category/Repository/Entity.php |
| Category MUI Entity | src/Domains/Event/Category/Repository/MuiEntity.php |
| Category Repository | src/Domains/Event/Category/Repository/Repository.php |
| Category RepositoryConfigurator | src/Domains/Event/Category/Repository/RepositoryConfigurator.php |
| Category Service | src/Domains/Event/Category/Service.php |
| Category WriteService | src/Domains/Event/Category/WriteService.php |
| Category WriteData | src/Domains/Event/Category/WriteData.php |
| Category MuiWriteData | src/Domains/Event/Category/MuiWriteData.php |
| Category Validator | src/Domains/Event/Category/Validator.php |
| Category ListRequest | src/Domains/Event/Category/ListRequest.php |
| Domain container | src/Domains/Event/container.php |
| REST Event Controller | src/Rest/Event/Controllers/Event.php |
| REST EventCategory Controller | src/Rest/Event/Controllers/EventCategory.php |
| REST Event Resource | src/Rest/Event/Resources/Event/Resource.php |
| REST Event MUI Resource | src/Rest/Event/Resources/Event/MuiResource.php |
| REST EventCategory Resource | src/Rest/Event/Resources/EventCategory/Resource.php |
| REST EventCategory MUI Resource | src/Rest/Event/Resources/EventCategory/MuiResource.php |
| REST container | src/Rest/Event/container.php |
Domain Relations (RepositoryConfigurator)
Event relations:
translations-- ONE_TO_MANY toMuiRepositoryviaevent_idcategories-- MANY_TO_MANY toCategoryRepositoryviaevents_event_categories_lp(event_id / category_id)products-- MANY_TO_MANY toProductRepositoryviashop_product_event_lp(event_id / product_id)
Category relations:
translations-- ONE_TO_MANY toMuiRepositoryviacategory_id
Architecture
Storefront Request REST API Request
| |
routes.php rest_routes.php
| |
Adv_events (Front_c) Event Controller (HandlesRestfulActions)
| |
_remap() dispatcher HandlesUploadActions trait
| |
pscache -> Adv_events_model Event\Service / Event\WriteService
| |
CI Active Record BaseRepository (Specification pattern)
| |
+---- events, events_mui -----------+-----+
+---- events_event_categories_lp ---+
+---- shop_product_event_lp --------+DI Container Registration
- Domain module:
src/Domains/Event/container.phpregistered inapplication/config/container/modules.php - REST module:
src/Rest/Event/container.phpregistered inapplication/config/container/modules.php - Both Event and EventCategory controllers receive
UploadServicefor image handling
REST Resource Behavior
The Event\Resource conditionally exposes backend-only fields (active, dateChanged) based on request context ($this->context?->isBackend()). The image field is always formatted with formatFile() for proper URL construction. Related collections (translations, categories, products) are included via ?with=translations,categories,products.
Data Model
Tables
| Table | Key Columns | Notes |
|---|---|---|
events | id, image, datetime_start, datetime_end, active, date_changed | Master event record |
events_mui | id, event_id, lang, title, slug, subtitle, short_description, description, location | MUI translations per language |
event_categories | id, order, image, published, parent_id | Master category with hierarchical parent support |
event_categories_mui | id, category_id, lang, name, slug, fulltext, meta_title, meta_keywords, meta_description | MUI translations with SEO fields |
events_event_categories_lp | id, event_id, category_id | Many-to-many event-to-category link |
shop_product_event_lp | id, product_id, event_id | Many-to-many product-to-event link |
Key Indexes
events_event_categories_lp: composite(event_id, category_id), individual onevent_id,category_idevent_categories: composite(parent_id, published), individual onpublished,parent_idevent_categories_mui: composite(slug, lang),(category_id, lang), individual onslug,category_id,langshop_product_event_lp: composite(product_id, event_id), individual onproduct_id,event_id
Template Views
| Template Key | View Path | Usage |
|---|---|---|
eventArticlesPage | layouts/event_articles/event_articles_index | Event listing and category listing |
eventArticlePage | layouts/event_articles/event_article | Single event detail |
productEventArticleCard | components/event_articles/event_article_card | Event card on product pages |
eventCategorySidebar | components/event_articles/event_categories_sidebar | Category sidebar navigation |
Configuration
Registry Keys
| Group | Key | Type | Description |
|---|---|---|---|
ESHOP | EVENT_ARTICLE_PRODUCT_ENABLED | int (0/1) | Enables product-event bi-directional association on storefront |
OTHER | FOOTER_EVENT_ITEMS_LIMIT | int | Max events shown in footer widget |
FLAGGED_CONTENT | EVENTS | bool | Show/hide events admin menu item |
All three are managed in Admin > Settings: EVENT_ARTICLE_PRODUCT_ENABLED in the "Only Advisable" settings page (Adv_settings::only_advisable()), FOOTER_EVENT_ITEMS_LIMIT in "Other" settings, and FLAGGED_CONTENT.EVENTS alongside other flagged content toggles.
REST Policies
Both Event and EventCategory controllers share the same policy structure:
- Guest access:
index,show,item(read-only, no auth required) - Backend access:
store,update,destroy(JWT required, roles: ADMIN, MARKETING, MEDIA)
Client Extension Points
Legacy Layer
application/modules/events/controllers/Events.php: Empty subclass ofAdv_events-- override storefront behavior (e.g., custom filtering, extra render data)application/modules/events/controllers/Events_admin.php: Empty subclass ofAdv_events_admin-- override admin behaviorapplication/modules/events/controllers/Event_categories_admin.php: Empty subclass ofAdv_event_categories_adminapplication/modules/events/models/Events_model.php: Empty subclass ofAdv_events_modelapplication/modules/events/models/Event_categories_model.php: Empty subclass ofAdv_event_categories_modelindexExtras(): Empty hook inAdv_eventsfor adding custom data to the event index pageafterAdd(),afterEdit(),afterDelete(),afterUpdateOrder(): Empty hooks inAdv_event_categories_adminfor post-CRUD actions
Modern Layer (Client Repos)
- Override domain services in
custom/Domains/Event/with DI alias - Override REST controllers/resources in
custom/Rest/Event/ - Custom validators for additional business rules
Template Customization
All four template views (eventArticlesPage, eventArticlePage, productEventArticleCard, eventCategorySidebar) are defined in mainTemplate.json and can be overridden per-theme.
Business Rules
- Active filtering: Only events with
active = trueare shown on the storefront; the REST API exposes theactivefield only in backend context - Date range: Events have
datetime_startanddatetime_endtimestamps; the footer widget only shows events wheredatetime_start > today - Slug uniqueness: Slugs are generated from titles via
greek_lower()and enforced unique per language with auto-incrementing suffix (-1,-2, etc.) - Delete guards: Events cannot be deleted if they have product associations; categories cannot be deleted if they have event assignments
- Category ordering: Categories use a manual
ordercolumn with AJAX drag-drop reorder; new categories auto-increment from the current maximum - Hierarchical categories: The
parent_idcolumn onevent_categoriessupports nesting, though the storefront currently renders a flat sidebar list - Product association bidirectional: When enabled, events show linked products and product pages show linked events -- both controlled by a single registry flag
- Image storage: Event images stored in
files/events/, category images infiles/event_categories/; both managed viaAdvUploader(legacy) andUploadService(REST) - Cache: All storefront queries use
pscachemodel caching with configurable TTL for performance - Admin search: Event admin list supports search by title, location, and date range (finds events whose range contains the searched date)
Related Flows
- CF-02 Product Detail -- events shown on product pages when
EVENT_ARTICLE_PRODUCT_ENABLEDis on - CF-20 Blog -- analogous CMS content module with similar product association pattern
- CF-21 Offers -- sibling CMS module with similar category/listing structure
- CF-26 Home Page -- footer event widget renders upcoming events on all pages
- SY-23 MUI Translation Pattern --
events_muiandevent_categories_muicompanion tables provide per-language titles, slugs, and SEO fields - SY-25 File Upload & Storage --
HandlesUploadActionsprocesses image uploads tofiles/events/andfiles/event_categories/