8 Commits
13 changed files with 706 additions and 492 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
1. Clone repository using `git clone https://git.techtube.pl/krzysiej/ryobi-crawler.git`
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.
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.
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
bin/cli php console.php app:cache:warm-twig
Generated
+679 -472
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -16,7 +16,7 @@ use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Twig\Environment;
#[AsCommand(name: 'app:cache:warm-twig', description: '')]
#[AsCommand(name: 'app:cache:warm-twig', description: 'Warmup twig cache')]
class CacheWarmCommand extends Command
{
public function __construct(
@@ -42,8 +42,8 @@ class CacheWarmCommand extends Command
$progress = new ProgressBar($output);
$progress->start();
$products = Product::with([
'price' => fn($query) => $query->orderBy('created_at', 'desc'),
'stock' => fn($query) => $query->orderBy('created_at', 'desc'),
'price' => fn($query) => $query->orderByDesc('created_at'),
'stock' => fn($query) => $query->orderByDesc('created_at'),
])->get();
$progress->setMaxSteps(count($products));
+1 -1
View File
@@ -15,7 +15,7 @@ final class CategoryController extends BaseController
return $this->render('productList.html.twig', ['listType' => 'category_'.$category]);
}
$products = Product::with('price')
$products = Product::with(['price', 'lowestPrice'])
->selectRaw('products.*')
->distinct('products.id')
->fromRaw('products, json_each(products.categories)')
+1 -1
View File
@@ -20,7 +20,7 @@ final class DiscontinuedController extends BaseController
$products = Product::where('updated_at', '<', now()->format('Y-m-d'))
->orderByDesc('starred')
->orderByDesc('created_by')
->with(['currentPrice'])
->with(['currentPrice', 'lowestPrice'])
->get();
return $this->render('productList.html.twig', ['products' => $products, 'listType' => 'discontinued']);
}
+1 -1
View File
@@ -14,7 +14,7 @@ final class IndexController extends BaseController
if ($this->cache->getItem('list_all')->isHit()) {
return $this->render('productList.html.twig', ['listType' => 'all']);
}
$products = Product::with(['currentStock', 'price'])
$products = Product::with(['currentStock', 'price', 'lowestPrice'])
->orderByDesc('starred')
->orderByDesc('created_by')
->get();
+1 -1
View File
@@ -21,7 +21,7 @@ final class NewController extends BaseController
$products = Product::where('created_at', '>', now()->modify('-30 days')->format('Y-m-d'))
->orderByDesc('starred')
->orderByDesc('created_by')
->with(['currentPrice'])
->with(['currentPrice', 'lowestPrice'])
->get();
return $this->render('productList.html.twig', ['products' => $products, 'listType' => 'new']);
}
+1 -1
View File
@@ -19,7 +19,7 @@ final class PromosController extends BaseController
$products = Product::whereHas('currentPrice', fn(Builder $query) => $query->whereColumn('price', '<', 'productStandardPrice'))
->orderByDesc('starred')
->orderByDesc('created_by')
->with(['currentPrice'])
->with(['currentPrice', 'lowestPrice'])
->get();
return $this->render('productList.html.twig', ['products' => $products, 'listType' => 'promos']);
}
+1 -1
View File
@@ -17,6 +17,6 @@ final class SearchController extends BaseController
->orWhere([['name', 'like', "%$search%"]])
->orWhere([['subTitle', 'like', "%$search%"]])->get();
return $this->render('productList.html.twig', ['products' => $products ?? [], 'search' => $search]);
return $this->render('productList.html.twig', ['products' => $products ?? [], 'search' => $search, 'listType' => 'search_'.$search]);
}
}
+3 -5
View File
@@ -34,14 +34,12 @@ class Kernel extends BaseKernel
'secret' => 'S0ME_SECRET'
]);
$services = $container->services()->defaults()->autowire()->autoconfigure();
$services->load('Krzysiej\\RyobiCrawler\\Controller\\', __DIR__ . '/Controller/*');
$services->load('Krzysiej\\RyobiCrawler\\Command\\', __DIR__ . '/Command/*')->tag('console.command');
$services->load('Krzysiej\\RyobiCrawler\\', __DIR__ )
->exclude('../src/{Models,Twig,Kernel.php}');
$services->set('twig.extension.cache', AppExtension::class)->tag('twig.extension');
$services->set(CacheExtension::class)->tag('twig.extension');
$services->set(FilesystemAdapter::class)->args([
'', // namespace
0, // default lifetime
__DIR__ . '/../var/cache/twig_blocks' // custom path
'$directory' => __DIR__ . '/../var/cache/twig_blocks'
]);
$services->set('twig.runtime.cache', CacheRuntime::class)->args([new Reference(FilesystemAdapter::class)])->tag('twig.runtime');
}
+5
View File
@@ -41,6 +41,11 @@ class Product extends Model
return $this->hasOne(Price::class)->latestOfMany('created_at');
}
public function lowestPrice(): HasOne
{
return $this->hasOne(Price::class)->ofMany('price', 'MIN');
}
public function stock(): HasMany
{
return $this->hasMany(Stock::class);
+6 -4
View File
@@ -11,7 +11,8 @@
<th>Name</th>
<th>Categories</th>
<th></th>
<th>Price</th>
<th class="text-end">Lowest Price</th>
<th class="text-end">Current Price</th>
<th></th>
</tr>
</thead>
@@ -37,7 +38,7 @@
</td>
<td class="align-middle">
<nav aria-label="breadcrumb" style="--bs-breadcrumb-divider: '>';" >
<ol class="breadcrumb">
<ol class="breadcrumb mb-0">
{% for category in product.categories %}
<li class="breadcrumb-item" aria-current="page"><a class="breadcrumb-item text-decoration-none" href="{{ path('app_category', {'category': category}) }}">{{ category }}</a></li>
{% endfor %}
@@ -45,11 +46,12 @@
</nav>
</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">
<div class="d-flex flex-row">
{% if product.price.last.price != product.price.last.productStandardPrice %}<span
class="badge text-bg-warning text-decoration-line-through flex-fill">{{ product.price.last.productStandardPrice | format_currency('PLN', {}, 'pl') }}</span>&nbsp;<span
class="badge text-bg-warning text-decoration-line-through flex-fill">{{ product.price.last.productStandardPrice | format_currency('PLN', {}, 'pl') }}</span> <span
class="badge text-bg-success flex-fill">{{ ((1 - product.price.last.price / product.price.last.productStandardPrice)*100)|number_format(0) }}%</span>
{% endif %}
</div>