Storefront Service
The StorefrontService is responsible for building and organizing shelves in the publication storefront. It acts as the main abstraction layer between controllers and the underlying inventory layer.
Overview
StorefrontService is the central component for managing how content is presented to users in the storefront. It focuses on organizing publications into browsable collections (shelves) while handling caching, pagination, and user-specific content visibility.
Main Responsibilities
- Manages the structure and organization of "shelves"
- Handles result caching for performance optimization
- Implements display configurations (issues per page, shelves per page)
- Controls display modes through different organization types
- Maintains shelf presentation styles and layouts
- Serves as an entry point for the UI layer
Initialization and Configuration
The service is initialized using the static forUser method:
public static function forUser(
?User $user = null,
int $issuesPerPage = self::DEFAULT_ISSUES_PER_PAGE,
array $options = []
): self
Constants and Default Values
private const DEFAULT_ISSUES_PER_PAGE = 12;
private const DEFAULT_SHELVES_PER_PAGE = 5;
private const CACHE_TTL_SECONDS = 3600;
private const CACHE_KEY_PREFIX = 'storefront';
Configurable Options
private array $options = [
'exclude_physical_publications' => false,
'exclude_scheduled_publications' => true,
'assigns_collections_scope' => true,
'force_only_sees_readable' => false,
'exclude_free_publications' => false,
'eager_load_descriptions' => true,
'eager_load_terms' => false,
'eager_load_bisacs' => false,
'order_by_published_at' => true,
'exclude_adult_content' => false,
];
Shelf Organization
Display Modes
The buildShelves method is the foundation for shelf construction and supports four display modes:
public function buildShelves(string $displayMode = 'custom', int $page = 1): Collection
| Mode | Implementing Method | Description |
|---|---|---|
custom | buildCustomShelves | Custom shelves from the database |
automatic | buildAutomaticShelves | Shelves generated with popular BISAC |
year | buildYearShelves | Chronological organization by year |
month | buildMonthShelves | Chronological organization by month/year |
Custom Shelves
The buildCustomShelves method retrieves shelves configured by administrators in the database:
private function buildCustomShelves(int $page): Collection
This method:
- Queries the database for
LibrarySliderrecords - Transforms them into shelf structures
- Optionally inserts "My Issues" shelves for authenticated users
- Respects user visibility permissions
Automatic Shelves
The buildAutomaticShelves method generates shelves based on the most-used BISAC headings:
private function buildAutomaticShelves(int $page): Collection
This method:
- Queries the most-used BISAC headings from cached data
- Creates a shelf for each common heading
- On the first page, adds "Latest Issues" shelf at the top
- For logged-in users, adds "My Issues" shelf
Year and Month Shelves
Chronological organization modes:
private function buildYearShelves(int $page): ?Collection
private function buildMonthShelves(int $page): ?Collection
These methods:
- Retrieve publications' years/months from cached data
- Create a shelf for each year or month/year combination
- Format titles appropriately (showing only month names for current year)
- Can insert "Free Issues" shelf based on tenant configuration
Shelf Construction
Shelf Data Structure
Each shelf is represented as an associative array containing:
[
'type' => $type, // Shelf type identifier
'slug' => $slug, // URL-friendly identifier
'filter' => $filterValue, // Value used for deeper filtering
'title' => $title, // Displayed shelf title
'issues' => $issuesCollection, // Collection of publications
]
Special Shelves
Free Issues Shelf
The insertFreeIssuesSlider method handles the special "Free Issues" shelf:
private function insertFreeIssuesSlider(Collection $sliders, int $page): Collection
This method:
- Checks tenant configuration for free issues position
- Creates and inserts the free issues shelf at the appropriate position:
first: Prepends at beginningsecond: Inserts at second positionlast: Appends at endnone: Doesn't insert the shelf
Recommended Shelf
The getRecommendedShelf method creates a special shelf of recommended publications:
public function getRecommendedShelf(int $issueId): array
This method:
- Verifies tenant features allow recommendations
- Queries for publications similar to the provided issue ID
- Returns a specially formatted shelf with recommendation data
Caching System
The service implements a hybrid caching system to optimize performance:
private function getShelfIssues(InventoryService $inventory, string $type, array $params = []): Collection
This system:
- Caches publication IDs instead of full objects
- Retrieves fresh publication data when rendering
- Uses intelligent cache keys that incorporate:
- Shelf type
- Filter parameters
- User-specific context when appropriate
Cache Key Generation
protected function getCacheKey(string $type, array $params = []): string
This method creates cache keys that:
- Start with the storefront prefix
- Include shelf type identifier
- Append all filter parameters
- For user-specific shelves, include user ID
Environment-aware TTL
private static function getCacheTTL(): int
{
return app()->isProduction() ? self::CACHE_TTL_SECONDS : 1;
}
This provides:
- Longer cache duration (1 hour) in production for performance
- Minimal caching (1 second) in development for easier testing
Publication Preparation
Publication Hydration
The hydrateIssue method enriches publication objects with context-aware data:
public static function hydrateIssue(Issue $issue, ?User $user): Issue
This method adds:
- Reading permissions flags
- Purchase status indicators
- Appropriate URLs for reading or buying
- Price information when applicable
- CSS classes for UI styling
- Localized button text
Button Localization
private static function getBtnLocalization(Issue $issue): string
This method returns the appropriate button text based on:
- User access rights
- Publication type (text or audio)
- License type (owned, PPU, etc.)
Technical Implementation
Applied Design Patterns
- Factory Method: Used in
forUser()to create service instances - Builder Pattern: Implemented in building shelf structures
- Strategy Pattern: Different shelf organization strategies
- Fluent Interface: Present in methods that allow chaining
Optimizations
-
Hybrid Cache:
$issueIds = Cache::remember(
$this->getCacheKey($type, $params),
now()->addSeconds(self::getCacheTTL()),
fn () => $inventory->onlyIds()->orderBy('default')->limit($this->issuesPerPage)->pluck('id')->toArray()
); -
Configurable Eager Loading:
// Examples of eager loading options
'eager_load_descriptions' => true,
'eager_load_terms' => false,
'eager_load_bisacs' => false,
Integration with InventoryService
The StorefrontService uses InventoryService to fetch the actual publication data based on shelf configurations:
InventoryService::forUser($this->user, $this->options)
->fromIds($issueIds)
->get();
For detailed information about filtering, searching and the inventory layer, see the InventoryService documentation.