diff --git a/src/Command/Migrate.php b/src/Command/Migrate.php index ed1a099..322c164 100644 --- a/src/Command/Migrate.php +++ b/src/Command/Migrate.php @@ -181,6 +181,11 @@ class Migrate extends Command $table->integer('stock')->nullable(); }); } + if (!Capsule::schema()->hasColumn('products', 'promotions')) { + Capsule::schema()->table('products', function (Blueprint $table) { + $table->json('promotions')->nullable(); + }); + } } public function index(): void diff --git a/src/Command/ScrapeWebsite.php b/src/Command/ScrapeWebsite.php index 9272190..4701deb 100644 --- a/src/Command/ScrapeWebsite.php +++ b/src/Command/ScrapeWebsite.php @@ -120,6 +120,7 @@ class ScrapeWebsite extends Command $productModel->lastSeen = date("Y-m-d"); $productModel->touch('updated_at'); $productModel->country()->associate($country); + $productModel->promotions = $product->promotions; $productModel->save(); $priceExists = $productModel->price()->whereRaw("strftime('%Y-%m-%d', created_at) = ?", [date('Y-m-d')])->exists(); diff --git a/src/Controller/PromosController.php b/src/Controller/PromosController.php index b54bc74..d391763 100644 --- a/src/Controller/PromosController.php +++ b/src/Controller/PromosController.php @@ -8,18 +8,25 @@ use Symfony\Component\Routing\Attribute\Route; final class PromosController extends BaseController { - #[Route('/promos', name: 'app_promos')] - public function __invoke(): Response + #[Route('/promos/{promo?}', name: 'app_promos')] + public function __invoke(?string $promo): Response { - if($this->cache->getItem('list_promos')->isHit()) { - return $this->render('productList.html.twig', ['listType' => 'promos']); + if ($this->cache->getItem('list_promos')->isHit()) { + return $this->render('productList.html.twig', ['listType' => 'promos' . $promo]); } - $products = Product::whereRaw('priceCurrent < productStandardPrice') + $products = Product::when(is_null($promo), fn($q) => $q->whereRaw('priceCurrent < productStandardPrice')) ->orderByDesc('starred') ->orderByDesc('created_by') ->with(['currentPrice', 'lowestPrice']) + ->when(!is_null($promo), fn($q) => $q->whereRaw("json_extract(promotions, '$.slug') LIKE ?", $promo)) ->get(); - return $this->render('productList.html.twig', ['products' => $products, 'listType' => 'promos']); + + + $promos = Product::select($this->database->getConnection()->raw("distinct json_extract(promotions, '$.slug') as slug, json_extract(promotions, '$.tag') as tag")) + ->whereRaw("json_extract(promotions, '$.tag') is not null") + ->get(); + + return $this->render('productList.html.twig', ['products' => $products, 'listType' => 'promos' . $promo, 'promos' => $promos->toArray()]); } } diff --git a/src/Models/Product.php b/src/Models/Product.php index 8997015..aff74b6 100644 --- a/src/Models/Product.php +++ b/src/Models/Product.php @@ -9,6 +9,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasOne; +use Illuminate\Support\Str; use function Symfony\Component\Clock\now; /** @@ -28,6 +29,7 @@ use function Symfony\Component\Clock\now; * @property float $lowestProductPrice30Days * @property Date $lastSeen * @property integer $stock + * @property Object $promotions */ class Product extends Model { @@ -58,6 +60,7 @@ class Product extends Model { return $this->hasOne(Price::class)->ofMany('price', 'MIN'); } + public function newestPrice(): HasOne { return $this->hasOne(Price::class)->latest(); @@ -70,7 +73,7 @@ class Product extends Model public function currentStock(): HasOne { - return $this->stock()->one()->ofMany()->withDefault(fn (Stock $stock) => $stock->stock = 0); + return $this->stock()->one()->ofMany()->withDefault(fn(Stock $stock) => $stock->stock = 0); } public function toggleStarred(): self @@ -88,10 +91,23 @@ class Product extends Model ); } + public function promotions(): Attribute + { + return Attribute::make( + get: fn(?string $value) => json_decode($value ?? '{"hasPromotion": false}', 1), + set: function (\stdClass $value) { + $value->slug = Str::slug($value->tag); + + return json_encode($value); + } + ); + } + public function isDiscontinued(): bool { return $this->lastSeen < now()->format('Y-m-d'); } + public function isNew(): bool { return $this->created_at->format('Y-m-d') > now()->modify('-30 days')->format('Y-m-d'); diff --git a/templates/product.html.twig b/templates/product.html.twig index 5f2d52d..2cd6b83 100644 --- a/templates/product.html.twig +++ b/templates/product.html.twig @@ -11,6 +11,7 @@