Merge pull request 'feature/lowest-price' (#27) from feature/lowest-price into master

Reviewed-on: #27
This commit was merged in pull request #27.
This commit is contained in:
2025-05-14 12:46:12 +02:00
8 changed files with 16 additions and 9 deletions

View File

@@ -4,7 +4,7 @@
1. Clone repository using `git clone https://git.techtube.pl/krzysiej/ryobi-crawler.git` 1. Clone repository using `git clone https://git.techtube.pl/krzysiej/ryobi-crawler.git`
2. Cd into project directory `cd ryobi-crawler` 2. Cd into project directory `cd ryobi-crawler`
3. Build and start docker container `docker compose up -d` 3. Build and start docker container `docker compose up -d --build --force-recreate`
4. Run `docker compose exec php-app php console.php app:migrate` file to create `database.sqlite` and create tables. 4. Run `docker compose exec php-app php console.php app:migrate` file to create `database.sqlite` and create tables.
5. Run `docker compose exec php-app php console.php app:scrape` command to scrape all the products from the ryobi website. 5. Run `docker compose exec php-app php console.php app:scrape` command to scrape all the products from the ryobi website.
6. Access web interface using `localhost:9001` address in web browser. 6. Access web interface using `localhost:9001` address in web browser.

View File

@@ -15,7 +15,7 @@ final class CategoryController extends BaseController
return $this->render('productList.html.twig', ['listType' => 'category_'.$category]); return $this->render('productList.html.twig', ['listType' => 'category_'.$category]);
} }
$products = Product::with('price') $products = Product::with(['price', 'lowestPrice'])
->selectRaw('products.*') ->selectRaw('products.*')
->distinct('products.id') ->distinct('products.id')
->fromRaw('products, json_each(products.categories)') ->fromRaw('products, json_each(products.categories)')

View File

@@ -20,7 +20,7 @@ final class DiscontinuedController extends BaseController
$products = Product::where('updated_at', '<', now()->format('Y-m-d')) $products = Product::where('updated_at', '<', now()->format('Y-m-d'))
->orderByDesc('starred') ->orderByDesc('starred')
->orderByDesc('created_by') ->orderByDesc('created_by')
->with(['currentPrice']) ->with(['currentPrice', 'lowestPrice'])
->get(); ->get();
return $this->render('productList.html.twig', ['products' => $products, 'listType' => 'discontinued']); return $this->render('productList.html.twig', ['products' => $products, 'listType' => 'discontinued']);
} }

View File

@@ -14,7 +14,7 @@ final class IndexController extends BaseController
if ($this->cache->getItem('list_all')->isHit()) { if ($this->cache->getItem('list_all')->isHit()) {
return $this->render('productList.html.twig', ['listType' => 'all']); return $this->render('productList.html.twig', ['listType' => 'all']);
} }
$products = Product::with(['currentStock', 'price']) $products = Product::with(['currentStock', 'price', 'lowestPrice'])
->orderByDesc('starred') ->orderByDesc('starred')
->orderByDesc('created_by') ->orderByDesc('created_by')
->get(); ->get();

View File

@@ -21,7 +21,7 @@ final class NewController extends BaseController
$products = Product::where('created_at', '>', now()->modify('-30 days')->format('Y-m-d')) $products = Product::where('created_at', '>', now()->modify('-30 days')->format('Y-m-d'))
->orderByDesc('starred') ->orderByDesc('starred')
->orderByDesc('created_by') ->orderByDesc('created_by')
->with(['currentPrice']) ->with(['currentPrice', 'lowestPrice'])
->get(); ->get();
return $this->render('productList.html.twig', ['products' => $products, 'listType' => 'new']); return $this->render('productList.html.twig', ['products' => $products, 'listType' => 'new']);
} }

View File

@@ -19,7 +19,7 @@ final class PromosController extends BaseController
$products = Product::whereHas('currentPrice', fn(Builder $query) => $query->whereColumn('price', '<', 'productStandardPrice')) $products = Product::whereHas('currentPrice', fn(Builder $query) => $query->whereColumn('price', '<', 'productStandardPrice'))
->orderByDesc('starred') ->orderByDesc('starred')
->orderByDesc('created_by') ->orderByDesc('created_by')
->with(['currentPrice']) ->with(['currentPrice', 'lowestPrice'])
->get(); ->get();
return $this->render('productList.html.twig', ['products' => $products, 'listType' => 'promos']); return $this->render('productList.html.twig', ['products' => $products, 'listType' => 'promos']);
} }

View File

@@ -41,6 +41,11 @@ class Product extends Model
return $this->hasOne(Price::class)->latestOfMany('created_at'); return $this->hasOne(Price::class)->latestOfMany('created_at');
} }
public function lowestPrice(): HasOne
{
return $this->hasOne(Price::class)->ofMany('price', 'MIN');
}
public function stock(): HasMany public function stock(): HasMany
{ {
return $this->hasMany(Stock::class); return $this->hasMany(Stock::class);

View File

@@ -11,7 +11,8 @@
<th>Name</th> <th>Name</th>
<th>Categories</th> <th>Categories</th>
<th></th> <th></th>
<th>Price</th> <th class="text-end">Lowest Price</th>
<th class="text-end">Current Price</th>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
@@ -45,7 +46,8 @@
</nav> </nav>
</td> </td>
<td class="align-middle"><a href='https://pl.ryobitools.eu/{{ product.url }}'>link</a></td> <td class="align-middle"><a href='https://pl.ryobitools.eu/{{ product.url }}'>link</a></td>
<td class="align-middle">{{ product.price.last.price | format_currency('PLN', {}, 'pl') }}</td> <td class="align-middle text-end">{% if product.lowestPrice.price != product.price.last.price %}{{ product.lowestPrice.price | format_currency('PLN', {}, 'pl') }}{% endif %}</td>
<td class="align-middle text-end">{{ product.price.last.price | format_currency('PLN', {}, 'pl') }}</td>
<td class="align-middle"> <td class="align-middle">
<div class="d-flex flex-row"> <div class="d-flex flex-row">
{% if product.price.last.price != product.price.last.productStandardPrice %}<span {% if product.price.last.price != product.price.last.productStandardPrice %}<span