Skip to main content

Inventory Service

The InventoryService is responsible for managing the available publication listings, implementing filtering logic, and providing search capabilities across the content catalog.

Overview

InventoryService serves as the data access layer for publications in the storefront, ensuring that all product listings respect user permissions, tenant settings, and content availability rules. It focuses on building optimized database queries to retrieve publications based on complex filtering criteria.

Main Responsibilities

  • Implements low-level product filtering logic (builds database queries)
  • Manages database query optimization and eager loading strategies
  • Provides search functionality across multiple content dimensions
  • Validates and applies filter parameters
  • Enforces user content restrictions
  • Handles tenant-specific inventory scope (owned, shared, marketplace)

Filtering System

Filter Types

The filtering system is built around a standardized set of filter types implemented as a PHP enum (InventoryFilter). This ensures consistent filter naming and validation throughout the application.

Supported Filter Types

FilterDescription
file_typeFilter by file type (pdf, epub, audio)
taxonomyFilter by custom taxonomy
contributorFilter by contributor (author, translator, etc.)
bisacFilter by BISAC category
yearFilter by publication year
monthFilter by month/year of publication
freeShow only free publications
latestShow most recent publications
audienceFilter by target audience
recommendedShow recommended publications
my_issuesShow publications acquired by the user
continue_readingShow publications the user is currently reading

Filter Application

The core filtering method is applyFilters:

public function applyFilters(array $filters): self

This method processes an array of filter specifications and applies them to the query builder. For each filter:

  1. It extracts the filter type from the provided specification
  2. Validates that required values are present and correctly formatted
  3. Dispatches to the appropriate specialized filter method based on the filter type
  4. Returns the service instance for method chaining

The validation logic ensures that filters have the correct structure before they're applied, throwing exceptions for invalid configurations. This makes the filtering system both robust and extensible.

Each filter is represented as an associative array with at least a type key:

[
'type' => InventoryFilter::TAXONOMY->value,
'value' => [
'taxonomy' => 'subject',
'term' => 'history'
]
]

Filter Combination Examples

Filters can be combined to create complex queries. Here are some examples:

// Filtering by file type and year
$filters = [
['type' => InventoryFilter::FILE_TYPE->value, 'value' => 'pdf'],
['type' => InventoryFilter::YEAR->value, 'value' => 2024]
];

// Filtering by taxonomy with multiple terms (OR condition)
$filters = [
['type' => InventoryFilter::TAXONOMY->value, 'value' => [
'taxonomy' => 'subject',
'term' => ['history', 'science']
]]
];

// Combining multiple taxonomy filters (AND condition between taxonomies)
$filters = [
['type' => InventoryFilter::TAXONOMY->value, 'value' => [
'taxonomy' => 'subject',
'term' => 'history'
]],
['type' => InventoryFilter::TAXONOMY->value, 'value' => [
'taxonomy' => 'audience',
'term' => 'young-adult'
]]
];

Filter Compatibility

Not all filters can be combined with others. The table below shows which filters are mutually exclusive:

FilterCan be combined with other filters
FREEYes
LATESTYes
YEARYes
MONTHYes
TAXONOMYYes
BISACYes
CONTRIBUTORYes
FILE_TYPEYes
AUDIENCEYes
RECOMMENDEDNo - replaces all other filters
MY_ISSUESNo - replaces all other filters
CONTINUE_READINGNo - replaces all other filters

The special filters (RECOMMENDED, MY_ISSUES, and CONTINUE_READING) should be used alone as they replace all previously set filters. These filters implement specialized business logic that isn't compatible with standard filtering.

Special Filter Methods

For commonly used filters, the service provides dedicated methods:

public function myIssues(): self
public function onlyIds(): self
public function searchByMetadata(string $term): self
public function searchByISBN(string $isbn): self
public function searchWithContent(string $term): self
public function recommended(int $issueId): self

These methods apply specific filter logic and can be chained with other methods.

Technical Filter Implementation

Filter Validation and Structure

Each filter type is defined in the InventoryFilter enum:

enum InventoryFilter: string
{
case FREE = 'free_issues';
case LATEST = 'latest_issues';
case YEAR = 'years';
case MONTH = 'month';
case TAXONOMY = 'taxonomy';
case BISAC = 'bisac';
case CONTRIBUTOR = 'author';
case FILE_TYPE = 'file_type';
case AUDIENCE = 'audience';
case RECOMMENDED = 'recommended';
case MY_ISSUES = 'my_issues';
case CONTINUE_READING = 'continue_reading';
case ORDER_BY = 'orderBy';
// ...
}

Filters are validated through two enum methods:

  • requiresValue(): Determines if the filter requires a value parameter
  • validateValue(): Validates the structure and type of the provided value
// Value validation implementation
public function validateValue(mixed $value): bool

The validateValue method ensures that each filter value has the correct data structure:

  • Numeric filters like YEAR expect integer values
  • Date filters like MONTH require an array with specific keys
  • Complex filters like TAXONOMY and BISAC require structured arrays with specific fields
  • String-based filters expect string values
  • Filters without values (FREE, LATEST, etc.) validate that no value is provided

This validation happens before any filter is applied to prevent runtime errors from incorrect filter specifications.

Technical Considerations

  1. Mutually Exclusive Filters: Some filters like MY_ISSUES, RECOMMENDED, and CONTINUE_READING are designed to replace all other filters rather than combining with them.

  2. Taxonomy Filter Structure: Taxonomy filters require both the taxonomy type and term, with special handling for multiple terms (OR within same taxonomy) and ALL_TERMS_AVAILABLE constant for whole-taxonomy filtering.

  3. Performance Impact:

    • BISAC filtering uses nested set model (lft/rgt fields) for efficient hierarchical queries
    • Taxonomy filtering may create complex join conditions that impact performance with large term sets
  4. Filter Expansion Considerations:

    • New filters must be added to the InventoryFilter enum
    • Must implement validation logic in validateValue() and requiresValue()
    • Should consider query performance impact, especially for join-heavy filters
    • Should be tested with large datasets to ensure acceptable performance
  5. Tenant-specific Filter Behavior:

    • Some filters (e.g., adult content filtering) depend on tenant settings
    • Features like term-based search are gated behind tenant feature flags
    • Permission-based filters interact with tenant access control settings

Filter Logic Implementation

The core filtering implementation is in the applyFilters method:

public function applyFilters(array $filters): self

This method processes an array of filter specifications and applies them to the query builder. For each filter:

  1. It extracts the filter type from the provided specification
  2. Validates that required values are present and correctly formatted
  3. Dispatches to the appropriate specialized filter method based on the filter type
  4. Returns the service instance for method chaining

The validation logic ensures that filters have the correct structure before they're applied, throwing exceptions for invalid configurations. This makes the filtering system both robust and extensible.

Search Implementation

The service implements three distinct search strategies:

Metadata Search (Default)

public function searchByMetadata(string $term): self

This method implements a comprehensive metadata-based publication search with an advanced scoring system:

  1. Search Fields:

    • Issue/publication name (exact and partial matches)
    • Contributors' first and last names (authors, translators, editors)
    • Taxonomy terms associated with the publication
    • External identifiers (including ISBN)
  2. Search Logic:

    • Leverages full-text search v2 capabilities for better matching
    • Implements a weighted scoring system that prioritizes exact matches
    • Combines results from different metadata sources into a single relevance score
    • Orders results by total score in descending order
  3. Behavior:

    • Search functions as an OR operation between terms
    • Exact matches receive significantly higher scores
    • Different weights are applied based on metadata type (name > contributors > terms)
    • Results are limited to optimize performance while maintaining relevance
public function searchByISBN(string $isbn): self

This method:

  1. Validates the ISBN format
  2. Searches for exact ISBN matches across the catalog
  3. Works with both ISBN-10 and ISBN-13 formats, handling hyphens and spaces
public function searchWithContent(string $term): self

This method:

  1. Searches within the actual content text of publications
  2. Uses full-text indexing for performance
  3. May have longer response times due to the scope of the search

Query Building and Execution

Base Query Construction

The service builds a base query that respects user permissions and tenant settings:

protected function buildBaseQuery(): Builder

This method applies:

  • User content restrictions
  • Tenant availability rules
  • Publication visibility settings
  • Scheduled publication filters

Query Optimization

The service implements several query optimization techniques:

public function withEagerLoading(): self
public function limit(int $limit): self

These methods:

  1. Apply appropriate eager loading based on configuration
  2. Limit result sets to improve performance

LibraryFilters Trait

The filtering logic is implemented using the LibraryFilters trait, which provides helpers for each filter type:

trait LibraryFilters
{
private function maybeApplyFileTypeFilters(array $params, array &$filters): void
private function maybeApplyTaxonomyFilters(array $params, array &$filters): void
private function maybeApplyContributorFilters(array $params, array &$filters): void
private function maybeApplyBisacFilters(array $params, array &$filters): void
// ...
}

This trait is used by StorefrontService to translate URL parameters into structured filter arrays that are then passed to InventoryService.

Technical Implementation

Applied Design Patterns

  1. Builder Pattern: Used for constructing complex queries
  2. Strategy Pattern: Different search strategies based on context
  3. Fluent Interface: Methods return self for chaining
  4. Repository Pattern: Centralizes data access logic

Optimization Strategies

The service implements several optimization strategies to ensure efficient database queries:

  1. Selective Eager Loading: Only loads related data when necessary, configurable through options.

  2. Index Utilization: Queries are designed to leverage database indexes effectively, particularly for taxonomies and BISAC hierarchies.

  3. Query Chunking: For operations on large result sets, the service can process data in manageable chunks to prevent memory issues.

  4. Caching: Uses ID-based caching strategies to minimize database load for frequently accessed data.

  5. Query Limiting: Supports limiting result sets to improve response times when complete result sets aren't required.

Important Notes for Implementers

1. All filters respect user permissions, tenant available content, and tenant configurations
2. Some filters may not be available depending on tenant features
3. Invalid filter values will trigger validation errors
4. Filter combination or ordering could be easily supported by shelves when we have the UI for it


X

Graph View