Skip to main content

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
ModeImplementing MethodDescription
custombuildCustomShelvesCustom shelves from the database
automaticbuildAutomaticShelvesShelves generated with popular BISAC
yearbuildYearShelvesChronological organization by year
monthbuildMonthShelvesChronological 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:

  1. Queries the database for LibrarySlider records
  2. Transforms them into shelf structures
  3. Optionally inserts "My Issues" shelves for authenticated users
  4. 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:

  1. Queries the most-used BISAC headings from cached data
  2. Creates a shelf for each common heading
  3. On the first page, adds "Latest Issues" shelf at the top
  4. 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:

  1. Retrieve publications' years/months from cached data
  2. Create a shelf for each year or month/year combination
  3. Format titles appropriately (showing only month names for current year)
  4. 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:

  1. Checks tenant configuration for free issues position
  2. Creates and inserts the free issues shelf at the appropriate position:
    • first: Prepends at beginning
    • second: Inserts at second position
    • last: Appends at end
    • none: Doesn't insert the shelf

The getRecommendedShelf method creates a special shelf of recommended publications:

public function getRecommendedShelf(int $issueId): array

This method:

  1. Verifies tenant features allow recommendations
  2. Queries for publications similar to the provided issue ID
  3. 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:

  1. Caches publication IDs instead of full objects
  2. Retrieves fresh publication data when rendering
  3. 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:

  1. Start with the storefront prefix
  2. Include shelf type identifier
  3. Append all filter parameters
  4. 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

  1. Factory Method: Used in forUser() to create service instances
  2. Builder Pattern: Implemented in building shelf structures
  3. Strategy Pattern: Different shelf organization strategies
  4. Fluent Interface: Present in methods that allow chaining

Optimizations

  1. Hybrid Cache:

    $issueIds = Cache::remember(
    $this->getCacheKey($type, $params),
    now()->addSeconds(self::getCacheTTL()),
    fn () => $inventory->onlyIds()->orderBy('default')->limit($this->issuesPerPage)->pluck('id')->toArray()
    );
  2. 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.


X

Graph View