Theatre Website Development on 1C-Bitrix
A theatre website faces a challenge that no template solution can solve: the repertoire grid. A single production plays on three different venues, with varying casts, different dates, and different prices. Customers want to select their exact seat, see the view from their row, and complete a ticket purchase in 40 seconds. If any part of this process lags, they'll move to Afisha or Radareo, where the theatre pays commission on every ticket.
On 1C-Bitrix, such a website is built from a combination of infoblock modules for content and the sale module for ticketing. The main technical challenge is the SVG-based seating chart with real-time seat blocking.
Repertoire and Schedule: Two Infoblock Instead of One
A common mistake is storing productions and performances in a single infoblock. The play "The Seagull" exists once, but there are twenty performances per season. If you duplicate the card with description, photos, and cast for each performance, you end up with content chaos and filtering becomes impossible.
Repertoire infoblock — production card:
- PROPERTY_GENRE — genre (drama, comedy, musical, ballet, opera — reference book)
- PROPERTY_AGE_RATING — age restriction (0+, 6+, 12+, 16+, 18+)
- PROPERTY_DURATION — running time with and without intermissions (two numeric fields)
- PROPERTY_PREMIERE_DATE — premiere date
-
PROPERTY_DIRECTOR — director (link to
Staffinfoblock) -
PROPERTY_CAST — main cast (multiple links to
Staff) -
PROPERTY_SCENE — venue (Main, Small, Chamber — link to
Venues) - PROPERTY_TRAILER — video trailer (YouTube / Vimeo)
- PROPERTY_GALLERY — photo gallery (multiple files)
- PROPERTY_PRESS — reviews (multiple HTML with source and quote)
- PROPERTY_IN_REPERTOIRE — checkbox (removed performances stay in archive for SEO)
Schedule infoblock — specific performances:
| Field | Type | Description |
|---|---|---|
| PROPERTY_SHOW_ID | Link | Production from Repertoire |
| PROPERTY_VENUE_ID | Link | Hall from Venues |
| PROPERTY_DATETIME | Date/time | Performance start |
| PROPERTY_STATUS | List | On sale / Few seats left / Sold out / Cancelled |
| PROPERTY_CAST_OVERRIDE | Multiple links | Cast for specific date (if different from main) |
| PROPERTY_PRICE_SCHEME | Link | Pricing scheme from HL-block PriceSchemes |
The "one production — many performances" structure enables listing all upcoming dates on a production page and showing all performances with filtering by date, genre, and venue in the event calendar. A bitrix:news.list component with filter >=PROPERTY_DATETIME by current date and sorted by date works perfectly for the homepage. Past performances automatically disappear from the schedule, but the production page with photos and reviews remains live.
Cast substitutions for specific performances are a separate consideration. If the main cast plays Thursday and a guest star performs Saturday, PROPERTY_CAST_OVERRIDE overrides the main cast on that specific date's page. Viewers know exactly who's performing the evening they're buying tickets for.
SVG Seating Chart and Ticket Sales
The technically heaviest component. It requires frontend (interactive seat map), backend (blocking, atomic transactions), and infrastructure (Redis for temporary blocks).
SVG hall file. Each hall is a separate SVG where every seat is an element with data-attributes:
<circle
data-row="7"
data-seat="14"
data-zone="parter"
data-category="A"
cx="312" cy="285" r="6"
class="seat seat--available"
/>
The data-category attribute links the seat to a pricing category. Categories are stored in HL-block SeatCategories: A — orchestra center (best view), B — side sections, C — mezzanine, D — balcony, E — gallery. Each performance has its own pricing grid. A weekday December evening and a Saturday pre-holiday performance have different prices for the same seat.
SVG files are uploaded to the Venues infoblock as property PROPERTY_SVG_MAP. Once prepared, the file is used for all performances in that hall.
Frontend interactivity. When opening the purchase page:
- SVG seating chart loads from the infoblock
- AJAX request returns an array of occupied and blocked seats for the specific performance
- JavaScript applies classes:
seat--available,seat--occupied,seat--locked,seat--selected - On hover — tooltip: row, seat, category, price
- On click — seat goes to cart, color changes
- Pinch-zoom on mobile and scroll-zoom on desktop (using
svg-pan-zoomlibrary)
For halls with 800–1200 seats, SVG contains the corresponding number of elements. On weak mobile devices this can lag. The solution is Canvas-based rendering with SVG rasterization: the screen shows a bitmap, and on zoom, the visible area is recalculated with individual seat rendering. For halls up to 500 seats, SVG works without optimization.
Seat blocking — Redis. When a viewer clicks a seat, a temporary lock is set. Redis key: lock:show_{id}:row_{r}:seat_{s} with TTL 600 seconds (10 minutes). Before writing — SETNX: if the key already exists, the seat is blocked by another buyer, frontend gets an error and redraws the seat as occupied.
A countdown timer is visible to the buyer: "Seats reserved for 8:42". When time expires, the lock is automatically released via TTL, without any cron or agents.
Why Redis instead of database records? Because Redis's TTL mechanism guarantees seat release even if the PHP process crashes. If a user closes the tab, the seat becomes available again in 10 minutes. With database records in b_iblock_element_property, you'd need a separate cleanup agent running every minute checking stale locks. Redis does this for free.
Server-side purchase processing:
- Availability recheck: Redis lock + HL-block
SoldSeats - Order creation in
sale— each seat as separate cart item with price by category - Redirect to payment system (ЮKassa, CloudPayments, Sber)
-
OnSalePayOrderhandler marks seats as sold inSoldSeats - PDF ticket generation with QR code (TCPDF + phpqrcode)
- Email sending via
mailmodule
The QR code contains URL site.ru/ticket/verify/{hash}, where hash is HMAC-SHA256 of order ID and secret key. Door controller scans QR, system marks ticket as used. Second entry — denied.
Integration with Ticketing Systems
If the theatre already works with Radareo, Ticketland, or Yandex.Afisha — the website connects to their API instead of its own sales system:
| System | Integration | What we get |
|---|---|---|
| Radareo | REST API v2 | Halls, seat schemes, events, availability, order creation |
| Ticketland | SOAP / REST | Catalog, booking, payment status |
| Yandex.Afisha | Widget API | Sales widget for page embedding |
| SBIS | REST API | Ticket accounting, fiscalization via OFD |
When working through partner API, SVG seating chart is pulled from the external system, not the infoblock. An adapter converts format to unified internal representation — frontend works identically in both cases. If the theatre decides to leave Radareo for in-house sales, just switch the adapter, interface remains the same.
Subscriptions and Gift Certificates
A subscription is a product in the sale catalog with property "number of visits" and expiration date. On purchase, a record is created in HL-block Subscriptions. When booking with a subscription, one visit is deducted instead of payment.
Gift certificates are realized through internal accounts of the sale module. Customer pays the amount, receives PDF with unique code. Recipient activates code — funds credited to their internal account.
Staff and Archive
Staff infoblock: photo, biography, roles (multiple links to Repertoire). On actor page — list of roles with photos from productions. On production page — cast with avatars.
Removed productions are archived by deactivating PROPERTY_IN_REPERTOIRE. URL doesn't change — SEO is preserved. For a theatre with decades of history, the archive gives hundreds of indexed pages with unique content.
Timeline
| Stage | Duration |
|---|---|
| Structure design and UX | 2–3 weeks |
| Design (homepage, schedule, production card, seat selection) | 3–4 weeks |
| Markup and responsiveness | 2–3 weeks |
| Infoblock programming and business logic | 3–4 weeks |
| SVG hall schemes and purchase interactivity | 2–3 weeks |
| Payment / ticketing system integration | 2–3 weeks |
| Content and testing | 2 weeks |
| Total | 16–22 weeks |
Parallel designer and developer work shortens total timeline by 3–4 weeks. Using existing ticketing API (Radareo) reduces integration stage to 1–2 weeks. Cost is calculated individually after requirements analysis — scope depends on number of halls, external ticketing system presence, and content volume.







