Repository Pattern
Part of the Object Oriented Programming section of Coddy's PHP journey. Lesson 81 of 91.
The Repository Pattern is a design pattern that mediates between your application's business logic and data storage. It provides a collection-like interface for accessing domain objects, hiding the details of how data is actually stored or retrieved.
Think of a repository as a specialized collection that knows how to persist and retrieve objects. Your application code asks the repository for a user by ID or saves a new product, without knowing whether the data lives in a database, file, or API. This separation makes your code more testable and flexible.
<?php
interface UserRepositoryInterface {
public function find(int $id): ?User;
public function findAll(): array;
public function save(User $user): void;
public function delete(int $id): void;
}
class User {
public function __construct(
public int $id,
public string $name,
public string $email
) {}
}
The repository implementation handles the actual storage mechanism:
<?php
class InMemoryUserRepository implements UserRepositoryInterface {
private array $users = [];
public function find(int $id): ?User {
return $this->users[$id] ?? null;
}
public function findAll(): array {
return array_values($this->users);
}
public function save(User $user): void {
$this->users[$user->id] = $user;
}
public function delete(int $id): void {
unset($this->users[$id]);
}
}
$repo = new InMemoryUserRepository();
$repo->save(new User(1, "Alice", "alice@example.com"));
$repo->save(new User(2, "Bob", "bob@example.com"));
echo $repo->find(1)->name . "\n";
echo count($repo->findAll());
Output:
Alice
2The beauty of this pattern is that you can swap implementations without changing your business logic. Need to switch from in-memory storage to a database? Create a new repository class implementing the same interface. Your application code remains untouched because it depends on the interface, not the concrete implementation.
Challenge
EasyLet's build a product inventory system using the Repository Pattern. You'll create a clean separation between your domain objects and how they're stored, allowing your application code to work with products without knowing the storage details.
You'll organize your code across four files:
Product.php: Create aProductclass representing items in your inventory. Each product has anid(int),name(string), andprice(float). Use constructor promotion with public properties for easy access.ProductRepositoryInterface.php: Define aProductRepositoryInterfacethat establishes the contract for any product storage implementation. Your interface should declare these methods:find(int $id): ?Product: Retrieve a product by ID, or null if not foundfindAll(): array: Get all products as an arraysave(Product $product): void: Store or update a productdelete(int $id): void: Remove a product by ID
InMemoryProductRepository.php: Include both the Product class and the interface. Create anInMemoryProductRepositoryclass that implements the interface using a private array to store products. Products should be indexed by their ID for efficient lookup. ThefindAll()method should return just the product objects (usearray_values()to reset array keys).main.php: Include the repository file. You'll receive two inputs: first a JSON string of products to add, then on a separate line the integer ID to search for.The JSON format will be:
[{"id": 1, "name": "Laptop", "price": 999.99}, {"id": 2, "name": "Mouse", "price": 29.99}]Create an
InMemoryProductRepository, save all products from the JSON input, then use the second input (the ID) to find a specific product. Print the results in this format:Total products: [count] Found: [name] - $[price]Format the price to two decimal places. If the product isn't found, print
Found: Not foundinstead.
The Repository Pattern shines when you need to swap storage implementations. Your main code depends only on the interface: switching from in-memory storage to a database would mean creating a new repository class without changing any application logic.
Try it yourself
<?php
require_once 'InMemoryProductRepository.php';
// Read input
$jsonInput = trim(fgets(STDIN));
$searchId = intval(trim(fgets(STDIN)));
// Parse the JSON input into an array
$productsData = (array)json_decode($jsonInput, true);
// TODO: Create an InMemoryProductRepository instance
// TODO: Loop through $productsData and save each product
// Each item has 'id', 'name', and 'price' keys
// TODO: Get all products and print the total count
// Format: "Total products: [count]"
// TODO: Find the product with $searchId
// If found, print: "Found: [name] - $[price]" (price formatted to 2 decimal places)
// If not found, print: "Found: Not found"
?>This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesIntroduction to OOPClasses vs ObjectsThe $this KeywordMethodsPropertiesConstructor (__construct)Destructor (__destruct)Recap - Simple Calculator4Inheritance
Basic InheritanceThe parent:: KeywordMethod OverridingThe final KeywordAbstract ClassesRecap - Employee Hierarchy7Encapsulation
Public, Protected, PrivateAccess Modifiers in DepthGetters and SettersInformation HidingConstructor Promotion (8.0)Recap - Student Records System10Advanced OOP Concepts
Composition vs InheritanceDependency InjectionAnonymous ClassesEnums (PHP 8.1)Fibers (PHP 8.1)Object Cloning Deep DiveGenerators & Iterators13Design Patterns Part 2
Command PatternAdapter PatternDecorator PatternTemplate Method PatternState PatternComposite PatternRepository Pattern2Namespaces & Autoloading
Introduction to NamespacesThe use KeywordPSR-4 Autoloading StandardComposer AutoloaderRecap - Organized Project5Interfaces & Contracts
Introduction to InterfacesImplementing InterfacesMultiple Interface ImplementInterface vs Abstract ClassType Hinting with InterfacesRecap - Shape Calculator8Magic Methods
Magic Methods Introduction__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Object Cloning__serialize & __unserializeRecap - Custom Collection11Type System & Error Handling
Type DeclarationsNullable TypesUnion & Intersection TypesException ClassesCustom Exception HierarchyTry, Catch, FinallyRecap - Form Validator14Project: Library Management
Project OverviewBook and User Classes3Class Properties
Instance vs Static PropertiesConstants in ClassesStatic Methods & PropertiesPrivate & Protected PropertiesReadonly Properties (PHP 8.1)Recap - Bank Account Manager6Polymorphism
Method Overriding RevisitedPolymorphism via InterfacesType Hinting & Union TypesLate Static BindingRecap - Payment Processor9Traits
Introduction to TraitsUsing Multiple TraitsTrait Conflict ResolutionAbstract Methods in TraitsTraits vs Inheritance12Design Patterns Part 1
Intro to Design PatternsSingleton PatternFactory PatternObserver PatternStrategy PatternPractice on your own: Online PHP compiler