Collectibles Showcase

November 28, 2025 10:46am

A specialized set of PHP classes designed for robust data retrieval and dynamic rendering of a custom collectible collection database.

Overview

This project module encompasses the core logic for managing and displaying collectible items, separating the duties of data interaction and front-end presentation into two specialized classes: CollectibleRepo and CollectibleRender. The goal is to provide fast, filtered access to collectible data and generate dynamic HTML output for both lists and detailed views.

Collectibles

One of my hobbies is collecting model cars and Funkos. I've been into model cars since 2013, with Funkos coming some 6 years later. But keeping track of them all is chaotic, and being able to virtually share them with friends and family was something I wanted to do for a while. That's how this project started to exist.

A comprehensive database in MySQL was setup to accommodate all the important fields for model cars, particularly those of racing. Each entry can be filtered by their scale, team, driver, category, or maker. The backend is powered by PHP, with a simple UI written in HTML and CSS focused on delivering the content as fast as possible.

A secondary version of the project, Collectibles Showcase, was written for Web Development course during my time at SAIT. It uses React, Next.JS, Tailwind CSS, deployed to Vercel, while sharing the same database and assets of the main website via REST API.

⚙️ Technical Stack

Area Technologies
Backend PHP, PDO, MySQL
Database Interaction Secure PDO prepared statements and exception handling
Frontend Output HTML generation using output buffering (ob_start())

The CollectibleRepo class requires the Database.php file. It initializes its connection using PDO and ensures that PDO is configured to throw exceptions upon errors by setting PDO::ATTR_ERRMODE to PDO::ERRMODE_EXCEPTION.

? Architecture & Design

The architecture follows a clear separation of concerns using two main classes in an Object-Oriented Programming (OOP) design:

  1. CollectibleRepo: Responsible exclusively for querying the MySQL database. It provides methods for retrieving various filtering dimensions and fetching collectible records. Data is consistently returned as associative arrays.
  2. CollectibleRender: Instantiated with a CollectibleRepo object. This class focuses on taking data retrieved by the repository, sanitizing it, and generating the necessary HTML output for filters, lists, and detail pages.

Data retrieval within the CollectibleRepo involves complex joins across multiple miniature-related tables (mini_miniatura, mini_piloto, mini_equipe, mini_escala, etc.) to gather comprehensive details.

?️ Features

  • Dynamic Filter Generation: The CollectibleRender::buildFilters method constructs the HTML form for filtering items, drawing available options directly from the repository. Filter categories include:

    • Drivers (piloto)
    • Makers (fabricante)
    • Teams (equipe)
    • Years (ano)
    • Scales (escala)
  • Data Retrieval Methods: The CollectibleRepo provides functions to get lists of items (getCollectibles), total item counts (getCollectiblesCount), and detailed information for a single item (getCollectible). Detailed data includes fields like year, number, maker, category, scale, additional_info (obs), SKU (id2), champion status, limited_edition status, driver, characteristics, contents, model, team, figure_name, and figure_series.

  • List View Rendering: The displayCollectiblesList function renders a grid. If a collectible is identified by figure_name, the title combines the name and figure_series; otherwise, the title includes the driver, year, team, and model.

  • Detail View Rendering: The displayModel function renders an individual collectible's information. It displays the primary image and iterates through all available fields (except image and id), formatting them for display (e.g., "DRIVER: NAME").

? Challenges & Solutions

  • Challenge: Ensuring consistency in the structure of data returned for filter lists, even if the underlying database columns differ.
  • Solution: All filter retrieval methods (getDrivers, getMakers, etc.) use array_map to explicitly return only the required value and key elements, dropping extraneous columns like released_at.
  • Challenge: Implementing filtering logic that supports dynamic user input while integrating with existing repository functions.
  • Solution: The getFiltersFromPost function processes POST and GET parameters (like filter-driver, filter-maker, model), sanitizes them, and builds an SQL fragment string (e.g., " AND ... ") which is then passed directly to the repository functions.

? Security & Best Practices

Sanitization: The CollectibleRender class includes a private sanitize method using htmlspecialchars with ENT_QUOTES and UTF-8 encoding to prevent Cross-Site Scripting (XSS) attacks on rendered output. This sanitization is applied to filter inputs before they are used to build SQL fragments and applied to labels/values rendered in HTML options. Error Handling: All database interaction methods within CollectibleRepo are wrapped in try-catch blocks to gracefully handle PDOException errors, returning empty arrays or 0 counts upon failure. Error conditions for fetching a single collectible that results in a 404 response also lead to termination (die()).

?️ Collectibles Interface Preview

The following structure shows how items are displayed in the list and detail views, incorporating image placeholders:

Collectibles List View

This view utilizes the displayCollectiblesList function.

<section class="models-grid">
    Models List
    <article>
        <img src="/images/collectibles/[Placeholder_Image_File_1]" alt="[Placeholder Title 1]" title="[Placeholder Title 1]" />
        <a href="page/collectibles-details?model=[Placeholder_ID-Slug_1]">
            <p>[Placeholder Driver Year Team Model or Figure Name (Series)]</p>
        </a>
    </article>
    <article>
        <img src="/images/collectibles/[Placeholder_Image_File_2]" alt="[Placeholder Title 2]" title="[Placeholder Title 2]" />
        <a href="page/collectibles-details?model=[Placeholder_ID-Slug_2]">
            <p>[Placeholder Driver Year Team Model or Figure Name (Series)]</p>
        </a>
    </article>
    <!-- ... more collectibles ... -->
</section>
Collectible Detail View

This view utilizes the displayModel function. The title will be driver year team model scale or figure_name (figure_series).

<section class="collectible-detail">
    <img src="/images/collectibles/[Placeholder_Main_Image_File]"
         alt="[Placeholder Detail Title]"
         title="[Placeholder Detail Title]"
         onclick='openLightbox("[Placeholder_Main_Image_File_Lightbox_Path]")' />

    <ul>
        <li>*YEAR*: [Placeholder Year]</li>
        <li>*MAKER*: [Placeholder Maker]</li>
        <li>*CATEGORY*: [Placeholder Category]</li>
        <li>*SCALE*: [Placeholder Scale]</li>
        <!-- Other available fields are listed dynamically -->
        <li>*DRIVER*: [Placeholder Driver]</li>
        <li>*LIMITED EDITION*: YES</li>
        <li>*SKU*: [Placeholder SKU]</li>
    </ul>

</section>

<section class="gallery">
    <!-- Additional images rendered by displayImages -->
    <img src="/images/collectibles/[Placeholder_Additional_Image_1]"
         alt=""
         title=""
         onclick='openLightbox("[Placeholder_Additional_Image_1_Lightbox_Path]")' />
    <img src="/images/collectibles/[Placeholder_Additional_Image_2]"
         alt=""
         title=""
         onclick='openLightbox("[Placeholder_Additional_Image_2_Lightbox_Path]")' />
</section>

? Results & Learnings

The creation of the CollectibleRepo and CollectibleRender demonstrates effective use of PHP's OOP principles to create a modular, maintainable system for dynamic content serving. By clearly separating the database abstraction layer from the presentation layer, the system maintains flexibility for future updates to either the data schema or the front-end design. This project served as a practical exercise in implementing robust database retrieval logic and applying necessary security measures through input sanitization.

Future roadmap

  • ✅Pagination for unfiltered results

go to collectibles list