avatarArif Hossen

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

8833

Abstract

"hljs-variable language_">this</span>-&gt;model = <span class="hljs-variable">product</span>; }

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getAllProducts</span>(<span class="hljs-params"></span>)
</span>{
    <span class="hljs-keyword">return</span> <span class="hljs-title class_">Cache</span>::<span class="hljs-title function_ invoke__">remember</span>(<span class="hljs-string">'all_products'</span>, <span class="hljs-variable">$this</span>-&gt;cacheTimeout, function () {
        <span class="hljs-keyword">return</span> <span class="hljs-variable language_">$this</span>-&gt;model
            -&gt;<span class="hljs-title function_ invoke__">with</span>([<span class="hljs-string">'category'</span>, <span class="hljs-string">'inventory'</span>])
            -&gt;<span class="hljs-title function_ invoke__">orderBy</span>(<span class="hljs-string">'created_at'</span>, <span class="hljs-string">'desc'</span>)
            -&gt;<span class="hljs-title function_ invoke__">get</span>();
    });
}

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getProductById</span>(<span class="hljs-params"><span class="hljs-variable">$productId</span></span>)
</span>{
    <span class="hljs-keyword">return</span> <span class="hljs-title class_">Cache</span>::<span class="hljs-title function_ invoke__">remember</span>(<span class="hljs-string">"product_<span class="hljs-subst">{$productId}</span>"</span>, <span class="hljs-variable">$this</span>-&gt;cacheTimeout, function () <span class="hljs-keyword">use</span> ($<span class="hljs-title">productId</span>) {
        <span class="hljs-title">return</span> $<span class="hljs-title">this</span>-&gt;<span class="hljs-title">model</span>
            -&gt;<span class="hljs-title">with</span>(['<span class="hljs-title">category</span>', '<span class="hljs-title">inventory</span>'])
            -&gt;<span class="hljs-title">findOrFail</span>($<span class="hljs-title">productId</span>);
    });
}

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getActiveProducts</span>(<span class="hljs-params"></span>)
</span>{
    <span class="hljs-keyword">return</span> <span class="hljs-variable language_">$this</span>-&gt;model
        -&gt;<span class="hljs-title function_ invoke__">where</span>(<span class="hljs-string">'status'</span>, <span class="hljs-string">'active'</span>)
        -&gt;<span class="hljs-title function_ invoke__">where</span>(<span class="hljs-string">'stock_quantity'</span>, <span class="hljs-string">'&gt;'</span>, <span class="hljs-number">0</span>)
        -&gt;<span class="hljs-title function_ invoke__">with</span>([<span class="hljs-string">'category'</span>, <span class="hljs-string">'inventory'</span>])
        -&gt;<span class="hljs-title function_ invoke__">orderBy</span>(<span class="hljs-string">'created_at'</span>, <span class="hljs-string">'desc'</span>)
        -&gt;<span class="hljs-title function_ invoke__">get</span>();
}

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createProduct</span>(<span class="hljs-params"><span class="hljs-keyword">array</span> <span class="hljs-variable">$productData</span></span>)
</span>{
    <span class="hljs-variable">$product</span> = <span class="hljs-variable language_">$this</span>-&gt;model-&gt;<span class="hljs-title function_ invoke__">create</span>([
        <span class="hljs-string">'name'</span> =&gt; <span class="hljs-variable">$productData</span>[<span class="hljs-string">'name'</span>],
        <span class="hljs-string">'description'</span> =&gt; <span class="hljs-variable">$productData</span>[<span class="hljs-string">'description'</span>],
        <span class="hljs-string">'price'</span> =&gt; <span class="hljs-variable">$productData</span>[<span class="hljs-string">'price'</span>],
        <span class="hljs-string">'sku'</span> =&gt; <span class="hljs-variable">$productData</span>[<span class="hljs-string">'sku'</span>],
        <span class="hljs-string">'stock_quantity'</span> =&gt; <span class="hljs-variable">$productData</span>[<span class="hljs-string">'stock_quantity'</span>],
        <span class="hljs-string">'status'</span> =&gt; <span class="hljs-variable">$productData</span>[<span class="hljs-string">'status'</span>] ?? <span class="hljs-string">'active'</span>
    ]);

    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">isset</span>(<span class="hljs-variable">$productData</span>[<span class="hljs-string">'category_id'</span>])) {
        <span class="hljs-variable">$product</span>-&gt;<span class="hljs-title function_ invoke__">category</span>()-&gt;<span class="hljs-title function_ invoke__">associate</span>(<span class="hljs-variable">$productData</span>[<span class="hljs-string">'category_id'</span>]);
    }

    <span class="hljs-variable language_">$this</span>-&gt;<span class="hljs-title function_ invoke__">clearProductCache</span>();
    
    <span class="hljs-keyword">return</span> <span class="hljs-variable">$product</span>;
}

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateProduct</span>(<span class="hljs-params"><span class="hljs-variable">$productId</span>, <span class="hljs-keyword">array</span> <span class="hljs-variable">$productData</span></span>)
</span>{
    <span class="hljs-variable">$product</span> = <span class="hljs-variable language_">$this</span>-&gt;<span class="hljs-title function_ invoke__">getProductById</span>(<span class="hljs-variable">$productId</span>);
    
    <span class="hljs-variable">$product</span>-&gt;<span class="hljs-title function_ invoke__">update</span>(<span class="hljs-variable">$productData</span>);
    
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">isset</span>(<span class="hljs-variable">$productData</span>[<span class="hljs-string">'category_id'</span>])) {
        <span class="hljs-variable">$product</span>-&gt;<span class="hljs-title function_ invoke__">category</span>()-&gt;<span class="hljs-title function_ invoke__">associate</span>(<span class="hljs-variable">$productData</span>[<span class="hljs-string">'category_id'</span>]);
        <span class="hljs-variable">$product</span>-&gt;<span class="hljs-title function_ invoke__">save</span>();
    }

    <span class="hljs-variable language_">$this</span>-&gt;<span class="hljs-title function_ invoke__">clearProductCache</span>(<span class="hljs-variable">$productId</span>);
    
    <span class="hljs-keyword">return</span> <span class="hljs-variable">$product</span>;
}

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deleteProduct</span>(<span class="hljs-params"><span class="hljs-variable">$productId</span></span>)
</span>{
    <span class="hljs-variable">$product</span> = <span class="hljs-variable language_">$this</span>-&gt;<span class="hljs-title function_ invoke__">getProductById</span>(<span class="hljs-variable">$productId</span>);
    <span class="hljs-variable language_">$this</span>-&gt;<span class="hljs-title function_ invoke__">clearProductCache</span>(<span class="hljs-variable">$productId</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-variable">$product</span>-&gt;<span class="hljs-title function_ invoke__">delete</span>();
}

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getProductsByCategory</span>(<span class="hljs-params"><span class="hljs-variable">$categoryId</span></span>)
</span>{
    <span class="hljs-keyword">return</span> <span class="hljs-title class_">Cache</span>::<span class="hljs-title function_ invoke__">remember</span>(<span class="hljs-string">"category_products_<span class="hljs-subst">{$categoryId}</span>"</span>, <span class="hljs-variable">$this</span>-&gt;cacheTimeout, function () <span class="hljs-keyword">use</span> ($<span class="hljs-title">categoryId</span>) {
        <span class="hljs-title">return</span> $<span class="hljs-title">this</span>-&gt;<span class="hljs-title">model</span>
            -&gt;<span class="hljs-title">where</span>('<span class="h

Options

ljs-title">category_id</span>', $<span class="hljs-title">categoryId</span>) -><span class="hljs-title">with</span>('<span class="hljs-title">inventory</span>') -><span class="hljs-title">get</span>(); }); }

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateStock</span>(<span class="hljs-params"><span class="hljs-variable">$productId</span>, <span class="hljs-variable">$quantity</span></span>)
</span>{
    <span class="hljs-variable">$product</span> = <span class="hljs-variable language_">$this</span>-&gt;<span class="hljs-title function_ invoke__">getProductById</span>(<span class="hljs-variable">$productId</span>);
    <span class="hljs-variable">$product</span>-&gt;stock_quantity = <span class="hljs-variable">$quantity</span>;
    <span class="hljs-variable">$product</span>-&gt;<span class="hljs-title function_ invoke__">save</span>();

    <span class="hljs-variable language_">$this</span>-&gt;<span class="hljs-title function_ invoke__">clearProductCache</span>(<span class="hljs-variable">$productId</span>);
    
    <span class="hljs-keyword">return</span> <span class="hljs-variable">$product</span>;
}

<span class="hljs-keyword">protected</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">clearProductCache</span>(<span class="hljs-params"><span class="hljs-variable">$productId</span> = <span class="hljs-literal">null</span></span>)
</span>{
    <span class="hljs-title class_">Cache</span>::<span class="hljs-title function_ invoke__">forget</span>(<span class="hljs-string">'all_products'</span>);
    
    <span class="hljs-keyword">if</span> (<span class="hljs-variable">$productId</span>) {
        <span class="hljs-title class_">Cache</span>::<span class="hljs-title function_ invoke__">forget</span>(<span class="hljs-string">"product_<span class="hljs-subst">{$productId}</span>"</span>);
    }
}

}</pre></div><h2 id="0acf">Step 4: Using the Repository in Controllers</h2><p id="db3d">Here’s how to use the repository in your controller:</p><div id="ed38"><pre><span class="hljs-meta"><?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title class_">App</span><span class="hljs-title class_">Http</span><span class="hljs-title class_">Controllers</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span><span class="hljs-title">Http</span><span class="hljs-title">Requests</span><span class="hljs-title">ProductRequest</span>; <span class="hljs-keyword">use</span> <span class="hljs-title">App</span><span class="hljs-title">Repositories</span><span class="hljs-title">Interfaces</span><span class="hljs-title">ProductRepositoryInterface</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProductController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Controller</span> </span>{ <span class="hljs-keyword">private</span> <span class="hljs-variable">$productRepository</span>;

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params">ProductRepositoryInterface <span class="hljs-variable">$productRepository</span></span>)
</span>{
    <span class="hljs-variable language_">$this</span>-&gt;productRepository = <span class="hljs-variable">$productRepository</span>;
}

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">index</span>(<span class="hljs-params"></span>)
</span>{
    <span class="hljs-variable">$products</span> = <span class="hljs-variable language_">$this</span>-&gt;productRepository-&gt;<span class="hljs-title function_ invoke__">getAllProducts</span>();
    <span class="hljs-keyword">return</span> <span class="hljs-title function_ invoke__">view</span>(<span class="hljs-string">'products.index'</span>, <span class="hljs-title function_ invoke__">compact</span>(<span class="hljs-string">'products'</span>));
}

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">store</span>(<span class="hljs-params">ProductRequest <span class="hljs-variable">$request</span></span>)
</span>{
    <span class="hljs-variable">$product</span> = <span class="hljs-variable language_">$this</span>-&gt;productRepository-&gt;<span class="hljs-title function_ invoke__">createProduct</span>(<span class="hljs-variable">$request</span>-&gt;<span class="hljs-title function_ invoke__">validated</span>());
    
    <span class="hljs-keyword">return</span> <span class="hljs-title function_ invoke__">redirect</span>()
        -&gt;<span class="hljs-title function_ invoke__">route</span>(<span class="hljs-string">'products.show'</span>, <span class="hljs-variable">$product</span>-&gt;id)
        -&gt;<span class="hljs-title function_ invoke__">with</span>(<span class="hljs-string">'success'</span>, <span class="hljs-string">'Product created successfully'</span>);
}

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateStock</span>(<span class="hljs-params"><span class="hljs-variable">$id</span>, Request <span class="hljs-variable">$request</span></span>)
</span>{
    <span class="hljs-variable language_">$this</span>-&gt;<span class="hljs-title function_ invoke__">validate</span>(<span class="hljs-variable">$request</span>, [
        <span class="hljs-string">'quantity'</span> =&gt; <span class="hljs-string">'required|integer|min:0'</span>
    ]);

    <span class="hljs-variable">$product</span> = <span class="hljs-variable language_">$this</span>-&gt;productRepository-&gt;<span class="hljs-title function_ invoke__">updateStock</span>(<span class="hljs-variable">$id</span>, <span class="hljs-variable">$request</span>-&gt;quantity);
    
    <span class="hljs-keyword">return</span> <span class="hljs-title function_ invoke__">response</span>()-&gt;<span class="hljs-title function_ invoke__">json</span>([
        <span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">'Stock updated successfully'</span>,
        <span class="hljs-string">'new_quantity'</span> =&gt; <span class="hljs-variable">$product</span>-&gt;stock_quantity
    ]);
}

}</pre></div><h1 id="48e4">Best Practices and Tips</h1><ol><li><b>Cache When Appropriate</b>: — Implement caching for frequently accessed data — Clear cache when data is modified — Use appropriate cache timeout values</li><li><b>Keep Repositories Focused</b>: — Each repository should handle one model/entity — Don’t mix business logic with data access — Use services for complex business logic</li><li><b>Use Type Hinting</b>: — Always type-hint repository interfaces in controllers — Use return type declarations — Document complex methods</li><li><b>Handle Errors Gracefully</b>: — Use try-catch blocks for database operations — Return meaningful error messages — Log important errors</li></ol><h1 id="4555">Common Pitfalls to Avoid</h1><ol><li><b>Don’t Skip the Interface</b>: — Always create an interface for your repositories — This ensures consistency and maintainability — Makes testing easier with mock objects</li><li><b>Don’t Make Repositories Too Complex</b>: — Keep methods focused and single-purpose — Move business logic to service classes — Don’t include presentation logic</li><li><b>Don’t Forget Performance</b>: — Use eager loading when needed — Implement caching for heavy queries — Monitor query performance</li></ol><h1 id="4f12">Conclusion</h1><p id="2c14">The Repository Pattern is a powerful tool for organizing your Laravel application’s data access layer. By implementing this pattern, you’ll create more maintainable, testable, and flexible code. Start with a simple implementation and gradually refactor your existing code to use repositories. Remember, the goal is to make your code more organized and easier to maintain, not to overcomplicate it.</p><h1 id="3a77">Next Steps</h1><ol><li>Implement the Repository Pattern in a small module of your application</li><li>Write tests for your repositories</li><li>Add caching where appropriate</li><li>Refactor existing code gradually</li></ol><p id="0308">Have you implemented the Repository Pattern in your Laravel application? Share your experiences and questions in the comments below!</p></article></body>

Understanding PHP Repository Pattern: A Beginner’s Guide with Real-World Examples

Are you struggling to maintain your Laravel application’s growing codebase? Do you find yourself repeating database queries across different controllers? The Repository Pattern might be exactly what you need to clean up your code and make it more maintainable. In this comprehensive guide, we’ll explore how to implement the Repository Pattern in Laravel using a real-world example that you can start using today.

Repository Pattern in PHP by arifhossen.dev

What is the Repository Pattern?

The Repository Pattern is a design pattern that creates an abstraction layer between your application’s business logic and data access layer. Think of it as a middleman that handles all the communication between your application and your database. Instead of writing database queries directly in your controllers, you delegate this responsibility to a dedicated repository class.

Why Should You Use the Repository Pattern?

Before diving into the implementation, let’s understand why you might want to use this pattern:

  1. Code Organization: Keep all your database queries in one place
  2. Maintainability: Easier to update and modify data access logic
  3. Reusability: Reuse data access code across multiple controllers
  4. Testing: Simplify unit testing with easy-to-mock repositories
  5. Flexibility: Switch between different data sources without changing your business logic

Real-World Example: E-commerce Product Management

Let’s implement the Repository Pattern for a product management system in an e-commerce application. We’ll create a complete implementation that handles products, including their categories and inventory.

Step 1: Create the Product Model

First, let’s create our Product model:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;

class Product extends Model
{
    use HasFactory;

    protected $fillable = [
        'name',
        'description',
        'price',
        'sku',
        'stock_quantity',
        'status'
    ];

    public function category()
    {
        return $this->belongsTo(Category::class);
    }

    public function inventory()
    {
        return $this->hasOne(Inventory::class);
    }
}

Step 2: Define the Repository Interface

Create the repository interface that defines all product-related operations:

<?php

namespace App\Repositories\Interfaces;

interface ProductRepositoryInterface
{
    public function getAllProducts();
    public function getProductById($productId);
    public function getActiveProducts();
    public function createProduct(array $productData);
    public function updateProduct($productId, array $productData);
    public function deleteProduct($productId);
    public function getProductsByCategory($categoryId);
    public function updateStock($productId, $quantity);
}

Step 3: Implement the Repository

Now, let’s create the concrete implementation of our repository:

<?php

namespace App\Repositories;

use App\Models\Product;
use App\Repositories\Interfaces\ProductRepositoryInterface;
use Illuminate\Support\Facades\Cache;

class ProductRepository implements ProductRepositoryInterface
{
    protected $model;
    protected $cacheTimeout = 3600; // 1 hour

    public function __construct(Product $product)
    {
        $this->model = $product;
    }

    public function getAllProducts()
    {
        return Cache::remember('all_products', $this->cacheTimeout, function () {
            return $this->model
                ->with(['category', 'inventory'])
                ->orderBy('created_at', 'desc')
                ->get();
        });
    }

    public function getProductById($productId)
    {
        return Cache::remember("product_{$productId}", $this->cacheTimeout, function () use ($productId) {
            return $this->model
                ->with(['category', 'inventory'])
                ->findOrFail($productId);
        });
    }

    public function getActiveProducts()
    {
        return $this->model
            ->where('status', 'active')
            ->where('stock_quantity', '>', 0)
            ->with(['category', 'inventory'])
            ->orderBy('created_at', 'desc')
            ->get();
    }

    public function createProduct(array $productData)
    {
        $product = $this->model->create([
            'name' => $productData['name'],
            'description' => $productData['description'],
            'price' => $productData['price'],
            'sku' => $productData['sku'],
            'stock_quantity' => $productData['stock_quantity'],
            'status' => $productData['status'] ?? 'active'
        ]);

        if (isset($productData['category_id'])) {
            $product->category()->associate($productData['category_id']);
        }

        $this->clearProductCache();
        
        return $product;
    }

    public function updateProduct($productId, array $productData)
    {
        $product = $this->getProductById($productId);
        
        $product->update($productData);
        
        if (isset($productData['category_id'])) {
            $product->category()->associate($productData['category_id']);
            $product->save();
        }

        $this->clearProductCache($productId);
        
        return $product;
    }

    public function deleteProduct($productId)
    {
        $product = $this->getProductById($productId);
        $this->clearProductCache($productId);
        return $product->delete();
    }

    public function getProductsByCategory($categoryId)
    {
        return Cache::remember("category_products_{$categoryId}", $this->cacheTimeout, function () use ($categoryId) {
            return $this->model
                ->where('category_id', $categoryId)
                ->with('inventory')
                ->get();
        });
    }

    public function updateStock($productId, $quantity)
    {
        $product = $this->getProductById($productId);
        $product->stock_quantity = $quantity;
        $product->save();

        $this->clearProductCache($productId);
        
        return $product;
    }

    protected function clearProductCache($productId = null)
    {
        Cache::forget('all_products');
        
        if ($productId) {
            Cache::forget("product_{$productId}");
        }
    }
}

Step 4: Using the Repository in Controllers

Here’s how to use the repository in your controller:

<?php

namespace App\Http\Controllers;

use App\Http\Requests\ProductRequest;
use App\Repositories\Interfaces\ProductRepositoryInterface;

class ProductController extends Controller
{
    private $productRepository;

    public function __construct(ProductRepositoryInterface $productRepository)
    {
        $this->productRepository = $productRepository;
    }

    public function index()
    {
        $products = $this->productRepository->getAllProducts();
        return view('products.index', compact('products'));
    }

    public function store(ProductRequest $request)
    {
        $product = $this->productRepository->createProduct($request->validated());
        
        return redirect()
            ->route('products.show', $product->id)
            ->with('success', 'Product created successfully');
    }

    public function updateStock($id, Request $request)
    {
        $this->validate($request, [
            'quantity' => 'required|integer|min:0'
        ]);

        $product = $this->productRepository->updateStock($id, $request->quantity);
        
        return response()->json([
            'message' => 'Stock updated successfully',
            'new_quantity' => $product->stock_quantity
        ]);
    }
}

Best Practices and Tips

  1. Cache When Appropriate: — Implement caching for frequently accessed data — Clear cache when data is modified — Use appropriate cache timeout values
  2. Keep Repositories Focused: — Each repository should handle one model/entity — Don’t mix business logic with data access — Use services for complex business logic
  3. Use Type Hinting: — Always type-hint repository interfaces in controllers — Use return type declarations — Document complex methods
  4. Handle Errors Gracefully: — Use try-catch blocks for database operations — Return meaningful error messages — Log important errors

Common Pitfalls to Avoid

  1. Don’t Skip the Interface: — Always create an interface for your repositories — This ensures consistency and maintainability — Makes testing easier with mock objects
  2. Don’t Make Repositories Too Complex: — Keep methods focused and single-purpose — Move business logic to service classes — Don’t include presentation logic
  3. Don’t Forget Performance: — Use eager loading when needed — Implement caching for heavy queries — Monitor query performance

Conclusion

The Repository Pattern is a powerful tool for organizing your Laravel application’s data access layer. By implementing this pattern, you’ll create more maintainable, testable, and flexible code. Start with a simple implementation and gradually refactor your existing code to use repositories. Remember, the goal is to make your code more organized and easier to maintain, not to overcomplicate it.

Next Steps

  1. Implement the Repository Pattern in a small module of your application
  2. Write tests for your repositories
  3. Add caching where appropriate
  4. Refactor existing code gradually

Have you implemented the Repository Pattern in your Laravel application? Share your experiences and questions in the comments below!

Design Patterns
Repository Pattern
Repository Design Pattern
Best Practices
PHP
Recommended from ReadMedium