Skip to content

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 /events with 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 = true to 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_ENABLED registry flag
  • Footer events widget shows only future events (datetime_start > now), limited by FOOTER_EVENT_ITEMS_LIMIT
  • Events admin section is feature-flagged via FLAGGED_CONTENT.EVENTS registry 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 pscache for performance

API Reference

REST Endpoints

MethodPathAuthDescription
GET/rest/event/eventGuestList events (paginated, filterable)
GET/rest/event/event/{id}GuestGet single event by ID
GET/rest/event/event/itemGuestGet single event by filters
POST/rest/event/eventBackend (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-categoryGuestList event categories
GET/rest/event/event-category/{id}GuestGet single category by ID
GET/rest/event/event-category/itemGuestGet single category by filters
POST/rest/event/event-categoryBackend (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

URLMethod
/eventsindex() -- 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

URLMethod
/events/events_adminindex() -- event list with search
/events/events_admin/addadd() -- create event
/events/events_admin/edit/{id}edit() -- edit event with product assignment
/events/events_admin/delete/{id}delete() -- delete event
/events/event_categories_adminindex() -- category list
/events/event_categories_admin/addadd() -- create category
/events/event_categories_admin/edit/{id}edit() -- edit category
/events/event_categories_admin/delete/{id}delete() -- delete category
/events/event_categories_admin/updateOrderupdateOrder() -- AJAX drag-drop reorder

Code Flow

Storefront Event Listing

File: ecommercen/events/controllers/Adv_events.php

  1. Route dispatch: _remap() receives the URL slug and determines whether it is a category, an article, or a method name
  2. Category check: Queries event_categories_model->getContent($slug, $lang) via pscache
  3. Article check: Queries events_model->getEventArticleData($slug, $lang) via pscache
  4. Fallback: If neither matches, tries method_exists() then 404
  5. Index listing: Fetches active events joined with MUI for current language, paginated at 20 per page, plus sidebar categories
  6. Category listing: Same as index but filtered to events linked to the category via events_event_categories_lp
  7. Article detail: Loads single event by slug, optionally loads linked products if EVENT_ARTICLE_PRODUCT_ENABLED registry key is truthy

Event-Product Association (Storefront)

File: ecommercen/events/controllers/Adv_events.php -- renderEventArticleProducts()

  1. Feature check: $this->registry->value('ESHOP', 'EVENT_ARTICLE_PRODUCT_ENABLED')
  2. Load links: events_model->getProductEventsLp(['event_id' => $eventId]) from shop_product_event_lp
  3. Load products: product_model->getProductsByIds($productIds)
  4. Register for parsing: Adds product IDs to productIdsToParse for price/stock rendering

Product Page Events (Reverse Direction)

File: ecommercen/eshop/controllers/Adv_products.php -- renderProductEventArticles()

  1. Feature check: Same EVENT_ARTICLE_PRODUCT_ENABLED registry flag
  2. Load events: events_model->getProductEventArticles($productId, $language) via pscache
  3. Template: Renders associated event articles on the product detail page

File: ecommercen/core/Adv_front_controller.php -- footerEventArticlesListRender()

  1. Load model: events/events_model
  2. Fetch: getFooterEvents(date('Y-m-d'), $language, $limit) -- only future events (datetime_start > today)
  3. Limit: Controlled by registry OTHER.FOOTER_EVENT_ITEMS_LIMIT
  4. Output: $this->render['footerEventArticles'] available to all storefront templates

Admin Event CRUD

File: ecommercen/events/controllers/Adv_events_admin.php

  1. Authorization: allowRole([AUTH_ROLE_ADVISABLE, AUTH_ROLE_ADMIN, AUTH_ROLE_MARKETING, AUTH_ROLE_MEDIA])
  2. Index: Paginated list with title/location/date search filters, ordered by date_changed DESC
  3. Add: Validate category + dates + all MUI fields, upload image to files/events/, auto-generate slug, insert master + MUI + category LP records
  4. Edit: Same validation, update master + MUI, sync category assignments via delete-and-reinsert, handle product associations via handleEventProducts()
  5. Delete: Blocked if products are linked (canDeleteRecord checks shop_product_event_lp count), otherwise cascades to MUI and category LP records

Admin Category CRUD

File: ecommercen/events/controllers/Adv_event_categories_admin.php

  1. Authorization: allowRole([AUTH_ROLE_ADVISABLE, AUTH_ROLE_ADMIN, AUTH_ROLE_PRODUCTS])
  2. Index: Paginated MUI list ordered by order ASC
  3. Add: Validate name per language, upload image to files/event_categories/, auto-increment order from max, insert master + MUI
  4. Edit: Update master + MUI with updateOrInsertMui() for language upsert, support image deletion
  5. Delete: Blocked if events are assigned (canDeleteRecord checks events_event_categories_lp count)
  6. Reorder: AJAX updateOrder() for drag-drop sorting

Domain Layer

ComponentPath
Event Entitysrc/Domains/Event/Event/Repository/Entity.php
Event MUI Entitysrc/Domains/Event/Event/Repository/MuiEntity.php
Event Repositorysrc/Domains/Event/Event/Repository/Repository.php
Event RepositoryConfiguratorsrc/Domains/Event/Event/Repository/RepositoryConfigurator.php
Event Servicesrc/Domains/Event/Event/Service.php
Event WriteServicesrc/Domains/Event/Event/WriteService.php
Event WriteDatasrc/Domains/Event/Event/WriteData.php
Event MuiWriteDatasrc/Domains/Event/Event/MuiWriteData.php
Event Validatorsrc/Domains/Event/Event/Validator.php
Event ListRequestsrc/Domains/Event/Event/ListRequest.php
Category Entitysrc/Domains/Event/Category/Repository/Entity.php
Category MUI Entitysrc/Domains/Event/Category/Repository/MuiEntity.php
Category Repositorysrc/Domains/Event/Category/Repository/Repository.php
Category RepositoryConfiguratorsrc/Domains/Event/Category/Repository/RepositoryConfigurator.php
Category Servicesrc/Domains/Event/Category/Service.php
Category WriteServicesrc/Domains/Event/Category/WriteService.php
Category WriteDatasrc/Domains/Event/Category/WriteData.php
Category MuiWriteDatasrc/Domains/Event/Category/MuiWriteData.php
Category Validatorsrc/Domains/Event/Category/Validator.php
Category ListRequestsrc/Domains/Event/Category/ListRequest.php
Domain containersrc/Domains/Event/container.php
REST Event Controllersrc/Rest/Event/Controllers/Event.php
REST EventCategory Controllersrc/Rest/Event/Controllers/EventCategory.php
REST Event Resourcesrc/Rest/Event/Resources/Event/Resource.php
REST Event MUI Resourcesrc/Rest/Event/Resources/Event/MuiResource.php
REST EventCategory Resourcesrc/Rest/Event/Resources/EventCategory/Resource.php
REST EventCategory MUI Resourcesrc/Rest/Event/Resources/EventCategory/MuiResource.php
REST containersrc/Rest/Event/container.php

Domain Relations (RepositoryConfigurator)

Event relations:

  • translations -- ONE_TO_MANY to MuiRepository via event_id
  • categories -- MANY_TO_MANY to CategoryRepository via events_event_categories_lp (event_id / category_id)
  • products -- MANY_TO_MANY to ProductRepository via shop_product_event_lp (event_id / product_id)

Category relations:

  • translations -- ONE_TO_MANY to MuiRepository via category_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.php registered in application/config/container/modules.php
  • REST module: src/Rest/Event/container.php registered in application/config/container/modules.php
  • Both Event and EventCategory controllers receive UploadService for 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

TableKey ColumnsNotes
eventsid, image, datetime_start, datetime_end, active, date_changedMaster event record
events_muiid, event_id, lang, title, slug, subtitle, short_description, description, locationMUI translations per language
event_categoriesid, order, image, published, parent_idMaster category with hierarchical parent support
event_categories_muiid, category_id, lang, name, slug, fulltext, meta_title, meta_keywords, meta_descriptionMUI translations with SEO fields
events_event_categories_lpid, event_id, category_idMany-to-many event-to-category link
shop_product_event_lpid, product_id, event_idMany-to-many product-to-event link

Key Indexes

  • events_event_categories_lp: composite (event_id, category_id), individual on event_id, category_id
  • event_categories: composite (parent_id, published), individual on published, parent_id
  • event_categories_mui: composite (slug, lang), (category_id, lang), individual on slug, category_id, lang
  • shop_product_event_lp: composite (product_id, event_id), individual on product_id, event_id

Template Views

Template KeyView PathUsage
eventArticlesPagelayouts/event_articles/event_articles_indexEvent listing and category listing
eventArticlePagelayouts/event_articles/event_articleSingle event detail
productEventArticleCardcomponents/event_articles/event_article_cardEvent card on product pages
eventCategorySidebarcomponents/event_articles/event_categories_sidebarCategory sidebar navigation

Configuration

Registry Keys

GroupKeyTypeDescription
ESHOPEVENT_ARTICLE_PRODUCT_ENABLEDint (0/1)Enables product-event bi-directional association on storefront
OTHERFOOTER_EVENT_ITEMS_LIMITintMax events shown in footer widget
FLAGGED_CONTENTEVENTSboolShow/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 of Adv_events -- override storefront behavior (e.g., custom filtering, extra render data)
  • application/modules/events/controllers/Events_admin.php: Empty subclass of Adv_events_admin -- override admin behavior
  • application/modules/events/controllers/Event_categories_admin.php: Empty subclass of Adv_event_categories_admin
  • application/modules/events/models/Events_model.php: Empty subclass of Adv_events_model
  • application/modules/events/models/Event_categories_model.php: Empty subclass of Adv_event_categories_model
  • indexExtras(): Empty hook in Adv_events for adding custom data to the event index page
  • afterAdd(), afterEdit(), afterDelete(), afterUpdateOrder(): Empty hooks in Adv_event_categories_admin for 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

  1. Active filtering: Only events with active = true are shown on the storefront; the REST API exposes the active field only in backend context
  2. Date range: Events have datetime_start and datetime_end timestamps; the footer widget only shows events where datetime_start > today
  3. Slug uniqueness: Slugs are generated from titles via greek_lower() and enforced unique per language with auto-incrementing suffix (-1, -2, etc.)
  4. Delete guards: Events cannot be deleted if they have product associations; categories cannot be deleted if they have event assignments
  5. Category ordering: Categories use a manual order column with AJAX drag-drop reorder; new categories auto-increment from the current maximum
  6. Hierarchical categories: The parent_id column on event_categories supports nesting, though the storefront currently renders a flat sidebar list
  7. Product association bidirectional: When enabled, events show linked products and product pages show linked events -- both controlled by a single registry flag
  8. Image storage: Event images stored in files/events/, category images in files/event_categories/; both managed via AdvUploader (legacy) and UploadService (REST)
  9. Cache: All storefront queries use pscache model caching with configurable TTL for performance
  10. Admin search: Event admin list supports search by title, location, and date range (finds events whose range contains the searched date)

  • CF-02 Product Detail -- events shown on product pages when EVENT_ARTICLE_PRODUCT_ENABLED is 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_mui and event_categories_mui companion tables provide per-language titles, slugs, and SEO fields
  • SY-25 File Upload & Storage -- HandlesUploadActions processes image uploads to files/events/ and files/event_categories/