1. Creating New Components
<?php
// Step 1: Create component class
namespace App\Frontend\Components;
use App\Shared\Components\BaseComponent;
class ProductGrid extends BaseComponent
{
protected array $requiredProps = ['products'];
protected array $defaultProps = [
'columns' => 3,
'show_pagination' => true,
'items_per_page' => 9
];
public function mount(): void
{
$this->validateProps();
$this->processProducts();
}
private function processProducts(): void
{
$products = $this->props['products'];
$itemsPerPage = $this->props['items_per_page'];
// Paginate products
$this->props['total_products'] = count($products);
$this->props['total_pages'] = ceil($this->props['total_products'] / $itemsPerPage);
// Add computed properties
foreach ($products as &$product) {
$product['discounted_price'] = $product['price'] * 0.9; // 10% discount
$product['is_new'] = isset($product['created_at']) &&
strtotime($product['created_at']) > strtotime('-30 days');
}
$this->props['products'] = $products;
}
public function render(): string
{
return $this->view('product-grid', $this->props);
}
}
2. Multiple Products Usage Examples
<?php
// Example 1: Simple product grid
$products = [
['id' => 1, 'name' => 'Laptop', 'price' => 999.99, 'rating' => 4.5],
['id' => 2, 'name' => 'Mouse', 'price' => 29.99, 'rating' => 4.2],
['id' => 3, 'name' => 'Keyboard', 'price' => 79.99, 'rating' => 4.7]
];
?>
<div class="row">
<?php foreach ($products as $product): ?>
<div class="col-md-4 mb-4">
<?= component('ProductCard', $product) ?>
</div>
<?php endforeach; ?>
</div>
<!-- Example 2: Featured products with different layouts -->
<div class="featured-products">
<h2>Featured Products</h2>
<div class="row">
<div class="col-md-8">
<!-- Large featured product -->
<?= component('ProductCard', [
'id' => 1,
'name' => 'Gaming Laptop',
'price' => 1599.99,
'image' => '/images/laptop.jpg',
'rating' => 4.8,
'featured' => true,
'size' => 'large'
]) ?>
</div>
<div class="col-md-4">
<!-- Small featured products -->
<?php
$smallProducts = [
['id' => 2, 'name' => 'Wireless Mouse', 'price' => 49.99],
['id' => 3, 'name' => 'Mechanical Keyboard', 'price' => 129.99]
];
foreach ($smallProducts as $product):
$product['size'] = 'small';
?>
<div class="mb-3">
<?= component('ProductCard', $product) ?>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
3. Advanced Nested Components
<?php
// Complex nested component example
$cartData = [
'items' => [
['id' => 1, 'name' => 'Laptop', 'price' => 999.99, 'quantity' => 1],
['id' => 2, 'name' => 'Mouse', 'price' => 29.99, 'quantity' => 2]
],
'total' => 1059.97,
'shipping' => 'free'
];
// Build nested content
$cartItemsHtml = '';
foreach ($cartData['items'] as $item) {
$cartItemsHtml .= component('CartItem', $item);
}
$cartSummaryHtml = component('CartSummary', [
'subtotal' => $cartData['total'],
'shipping' => $cartData['shipping'],
'total' => $cartData['total']
]);
// Main shopping cart component
echo component('Card', [
'title' => 'Shopping Cart (' . count($cartData['items']) . ' items)',
'css_class' => 'cart-card'
], [
'body' => $cartItemsHtml,
'footer' => $cartSummaryHtml . component('Button', [
'text' => 'Proceed to Checkout',
'type' => 'primary',
'size' => 'large',
'full_width' => true
])
]);
?>
<!-- Product comparison component -->
<?php
$compareProducts = [
['id' => 1, 'name' => 'iPhone 14', 'price' => 999, 'storage' => '128GB'],
['id' => 2, 'name' => 'iPhone 14 Pro', 'price' => 1299, 'storage' => '256GB'],
['id' => 3, 'name' => 'iPhone 14 Pro Max', 'price' => 1499, 'storage' => '512GB']
];
$comparisonTable = '<div class="comparison-table">';
$comparisonTable .= '<div class="row">';
foreach ($compareProducts as $product) {
$comparisonTable .= '<div class="col-4">';
$comparisonTable .= component('ProductCard', array_merge($product, [
'comparison_mode' => true,
'show_compare_features' => true
]));
$comparisonTable .= '</div>';
}
$comparisonTable .= '</div></div>';
echo component('Card', [
'title' => 'Product Comparison',
'css_class' => 'comparison-card'
], [
'body' => $comparisonTable
]);
?>
4. Dynamic Component Loading
<?php
// Dynamic component loading based on data type
function renderDynamicContent($items) {
$output = '';
foreach ($items as $item) {
switch ($item['type']) {
case 'product':
$output .= component('ProductCard', $item['data']);
break;
case 'article':
$output .= component('ArticleCard', $item['data']);
break;
case 'testimonial':
$output .= component('TestimonialCard', $item['data']);
break;
case 'banner':
$output .= component('PromoBanner', $item['data']);
break;
default:
$output .= component('Card', [
'title' => $item['data']['title'] ?? 'Unknown Content',
'content' => $item['data']['description'] ?? ''
]);
}
}
return $output;
}
// Usage with mixed content
$mixedContent = [
[
'type' => 'banner',
'data' => ['message' => 'Special Sale!', 'discount' => '50%']
],
[
'type' => 'product',
'data' => ['id' => 1, 'name' => 'Laptop', 'price' => 999.99]
],
[
'type' => 'testimonial',
'data' => ['author' => 'John Doe', 'text' => 'Great product!']
]
];
echo '<div class="dynamic-content">';
echo renderDynamicContent($mixedContent);
echo '</div>';
?>
5. Component with AJAX Loading
<?php
// Component that supports AJAX loading
class ProductCatalog extends BaseComponent {
protected array $defaultProps = [
'page' => 1,
'per_page' => 12,
'category' => null,
'sort' => 'name',
'ajax_container' => 'product-catalog'
];
public function render(): string {
$products = $this->loadProducts();
$pagination = $this->generatePagination();
return $this->view('product-catalog', [
'products' => $products,
'pagination' => $pagination,
'ajax_container' => $this->props['ajax_container']
]);
}
private function loadProducts(): array {
// Simulate database query
return [
['id' => 1, 'name' => 'Product 1', 'price' => 99.99],
['id' => 2, 'name' => 'Product 2', 'price' => 149.99],
// ... more products
];
}
}
// JavaScript for AJAX loading
?>
<script>
function loadProductPage(page) {
fetch(`/api/products?page=${page}`)
.then(response => response.json())
.then(data => {
document.getElementById('product-catalog').innerHTML = data.html;
updatePagination(data.pagination);
});
}
function filterProducts(category) {
fetch(`/api/products?category=${category}`)
.then(response => response.json())
.then(data => {
document.getElementById('product-catalog').innerHTML = data.html;
});
}
</script>
6. Best Practices & Performance Tips
Performance Best Practices
- Component Caching: Enable caching for static components that don't change frequently
- Lazy Loading: Load product images and non-critical data asynchronously
- Batch Rendering: Group multiple similar components to reduce overhead
- Memory Management: Use unset() for large data arrays after component rendering
- Conditional Rendering: Only render components that are visible or needed
<?php
// Performance optimized product grid
class OptimizedProductGrid extends BaseComponent {
public function render(): string {
$products = $this->props['products'];
$batchSize = 10;
$output = '';
// Batch process products
for ($i = 0; $i < count($products); $i += $batchSize) {
$batch = array_slice($products, $i, $batchSize);
$output .= $this->renderProductBatch($batch);
// Clear memory
unset($batch);
}
return $output;
}
private function renderProductBatch(array $products): string {
$html = '<div class="product-batch">';
foreach ($products as $product) {
// Cache individual product cards
$cacheKey = "product_card_{$product['id']}";
$cached = $this->getFromCache($cacheKey);
if ($cached) {
$html .= $cached;
} else {
$rendered = component('ProductCard', $product);
$this->storeInCache($cacheKey, $rendered, 3600); // 1 hour
$html .= $rendered;
}
}
$html .= '</div>';
return $html;
}
}
// Usage with performance monitoring
$startTime = microtime(true);
$productGrid = component('OptimizedProductGrid', ['products' => $products]);
$renderTime = microtime(true) - $startTime;
echo $productGrid;
echo "<!-- Rendered in {$renderTime}ms -->";
?>
7. Component Testing Examples
<?php
// Unit test for ProductCard component
class ProductCardTest extends PHPUnit\Framework\TestCase {
public function testProductCardRendersCorrectly() {
$productData = [
'id' => 1,
'name' => 'Test Product',
'price' => 99.99,
'in_stock' => true
];
$html = component('ProductCard', $productData);
$this->assertStringContainsString('Test Product', $html);
$this->assertStringContainsString('$99.99', $html);
$this->assertStringContainsString('In Stock', $html);
}
public function testProductCardHandlesOutOfStock() {
$productData = [
'id' => 2,
'name' => 'Out of Stock Product',
'price' => 199.99,
'in_stock' => false
];
$html = component('ProductCard', $productData);
$this->assertStringContainsString('Out of Stock', $html);
$this->assertStringNotContainsString('Add to Cart', $html);
}
}
// Integration test for multiple products
public function testMultipleProductsRender() {
$products = [
['id' => 1, 'name' => 'Product 1', 'price' => 99.99],
['id' => 2, 'name' => 'Product 2', 'price' => 149.99],
['id' => 3, 'name' => 'Product 3', 'price' => 199.99]
];
$html = '';
foreach ($products as $product) {
$html .= component('ProductCard', $product);
}
$this->assertEquals(3, substr_count($html, 'class="card'));
$this->assertStringContainsString('Product 1', $html);
$this->assertStringContainsString('Product 2', $html);
$this->assertStringContainsString('Product 3', $html);
}
?>