sqrtspace-php/examples/laravel-app/app/Http/Controllers/ProductController.php
2025-07-20 04:08:08 -04:00

194 lines
6.2 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\Product;
use App\Services\ProductService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use SqrtSpace\SpaceTime\Streams\SpaceTimeStream;
use SqrtSpace\SpaceTime\File\CsvExporter;
use SqrtSpace\SpaceTime\Checkpoint\CheckpointManager;
class ProductController extends Controller
{
private ProductService $productService;
public function __construct(ProductService $productService)
{
$this->productService = $productService;
}
/**
* Get paginated products
*/
public function index(Request $request)
{
$perPage = min($request->get('per_page', 50), 100);
return Product::query()
->when($request->get('category'), function ($query, $category) {
$query->where('category', $category);
})
->when($request->get('min_price'), function ($query, $minPrice) {
$query->where('price', '>=', $minPrice);
})
->orderBy('id')
->paginate($perPage);
}
/**
* Stream all products as NDJSON
*/
public function stream(Request $request)
{
return response()->stream(function () use ($request) {
$query = Product::query()
->when($request->get('category'), function ($query, $category) {
$query->where('category', $category);
})
->orderBy('id');
$stream = SpaceTimeStream::fromQuery($query, 100);
foreach ($stream as $product) {
echo $product->toJson() . "\n";
ob_flush();
flush();
}
}, 200, [
'Content-Type' => 'application/x-ndjson',
'X-Accel-Buffering' => 'no',
'Cache-Control' => 'no-cache'
]);
}
/**
* Export products as CSV
*/
public function exportCsv(Request $request)
{
$filename = 'products_' . date('Y-m-d_His') . '.csv';
return response()->streamDownload(function () use ($request) {
$exporter = new CsvExporter('php://output');
$exporter->writeHeaders([
'ID', 'Name', 'SKU', 'Category', 'Price',
'Stock', 'Description', 'Created At'
]);
Product::query()
->when($request->get('category'), function ($query, $category) {
$query->where('category', $category);
})
->orderBy('id')
->chunkById(1000, function ($products) use ($exporter) {
foreach ($products as $product) {
$exporter->writeRow([
$product->id,
$product->name,
$product->sku,
$product->category,
$product->price,
$product->stock,
$product->description,
$product->created_at
]);
}
});
}, $filename, [
'Content-Type' => 'text/csv',
]);
}
/**
* Bulk update product prices with checkpointing
*/
public function bulkUpdatePrices(Request $request)
{
$request->validate([
'category' => 'required|string',
'adjustment_type' => 'required|in:percentage,fixed',
'adjustment_value' => 'required|numeric'
]);
$jobId = 'price_update_' . uniqid();
$checkpointManager = app(CheckpointManager::class);
// Check for existing checkpoint
$checkpoint = $checkpointManager->restore($jobId);
$lastId = $checkpoint['last_id'] ?? 0;
$updated = $checkpoint['updated'] ?? 0;
DB::beginTransaction();
try {
Product::where('category', $request->category)
->where('id', '>', $lastId)
->orderBy('id')
->chunkById(100, function ($products) use ($request, &$updated, $jobId, $checkpointManager) {
foreach ($products as $product) {
if ($request->adjustment_type === 'percentage') {
$product->price *= (1 + $request->adjustment_value / 100);
} else {
$product->price += $request->adjustment_value;
}
$product->save();
$updated++;
// Checkpoint every 100 updates
if ($updated % 100 === 0) {
$checkpointManager->save($jobId, [
'last_id' => $product->id,
'updated' => $updated
]);
}
}
});
DB::commit();
$checkpointManager->delete($jobId);
return response()->json([
'success' => true,
'updated' => $updated,
'job_id' => $jobId
]);
} catch (\Exception $e) {
DB::rollBack();
return response()->json([
'success' => false,
'error' => $e->getMessage(),
'job_id' => $jobId,
'can_resume' => true
], 500);
}
}
/**
* Search products with memory-efficient sorting
*/
public function search(Request $request)
{
$request->validate([
'q' => 'required|string|min:2',
'sort_by' => 'in:relevance,price_asc,price_desc,name'
]);
return $this->productService->searchProducts(
$request->get('q'),
$request->get('sort_by', 'relevance'),
$request->get('limit', 100)
);
}
/**
* Get product statistics
*/
public function statistics()
{
return $this->productService->getStatistics();
}
}