diff --git a/.gitea/workflows/deploy.yaml b/.gitea/workflows/deploy.yaml new file mode 100644 index 0000000..6f25e5b --- /dev/null +++ b/.gitea/workflows/deploy.yaml @@ -0,0 +1,17 @@ +on: + push: + branches: + - master + workflow_dispatch: +jobs: + deploy-job: + runs-on: server + steps: + - name: initial test + run: echo "test" + - name: get page + run: curl http://192.168.0.129:9001/update + - run: pwd + - run: cd ../ && pwd + - run: pwd + - run: cd /var/www/html/ryobi-crawler && bin/update \ No newline at end of file diff --git a/bin/cacheclean b/bin/cacheclean index 3920b93..cb740c7 100755 --- a/bin/cacheclean +++ b/bin/cacheclean @@ -1,2 +1,4 @@ #!/usr/bin/env bash -bin/cli rm -rf var/cache \ No newline at end of file +echo "Cleaning cache" +bin/cli rm -rf var/cache +echo "cache cleaned2" \ No newline at end of file diff --git a/composer.json b/composer.json index 50d838e..684d92f 100644 --- a/composer.json +++ b/composer.json @@ -1,18 +1,18 @@ { "require": { - "guzzlehttp/guzzle": "^7.0", + "guzzlehttp/guzzle": "^7", "illuminate/database": "11.26.0.0", "ext-json": "*", - "symfony/console": "^7.0", - "symfony/routing": "^7.1", + "symfony/console": "^8.0", + "symfony/routing": "^8.0", "laravel/serializable-closure": "^1.3", - "symfony/http-kernel": "^7.1", - "symfony/framework-bundle": "^7.1", - "symfony/twig-bundle": "^7.1", - "symfony/dotenv": "^7.1", + "symfony/http-kernel": "^8.0", + "symfony/framework-bundle": "^8.0", + "symfony/twig-bundle": "^8.0", + "symfony/dotenv": "^8.0", "twig/intl-extra": "^3.13", "twig/extra-bundle": "^3.13", - "symfony/cache": "^7.2", + "symfony/cache": "^8.0", "twig/cache-extra": "^3.21" }, "autoload": { @@ -21,6 +21,7 @@ } }, "require-dev": { - "symfony/var-dumper": "^7.1" - } + "symfony/var-dumper": "^8.0" + }, + "minimum-stability": "stable" } diff --git a/composer.lock b/composer.lock index 5e400d6..88f4816 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ac693c1e5de8e9733f96954b8a0952b8", + "content-hash": "b6ca28a84dfeba67d5e93ac8d0eae924", "packages": [ { "name": "brick/math", @@ -1006,16 +1006,16 @@ }, { "name": "nesbot/carbon", - "version": "3.11.0", + "version": "3.11.1", "source": { "type": "git", "url": "https://github.com/CarbonPHP/carbon.git", - "reference": "bdb375400dcd162624531666db4799b36b64e4a1" + "reference": "f438fcc98f92babee98381d399c65336f3a3827f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/bdb375400dcd162624531666db4799b36b64e4a1", - "reference": "bdb375400dcd162624531666db4799b36b64e4a1", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/f438fcc98f92babee98381d399c65336f3a3827f", + "reference": "f438fcc98f92babee98381d399c65336f3a3827f", "shasum": "" }, "require": { @@ -1039,7 +1039,7 @@ "phpstan/extension-installer": "^1.4.3", "phpstan/phpstan": "^2.1.22", "phpunit/phpunit": "^10.5.53", - "squizlabs/php_codesniffer": "^3.13.4" + "squizlabs/php_codesniffer": "^3.13.4 || ^4.0.0" }, "bin": [ "bin/carbon" @@ -1082,14 +1082,14 @@ } ], "description": "An API extension for DateTime that supports 281 different languages.", - "homepage": "https://carbon.nesbot.com", + "homepage": "https://carbonphp.github.io/carbon/", "keywords": [ "date", "datetime", "time" ], "support": { - "docs": "https://carbon.nesbot.com/docs", + "docs": "https://carbonphp.github.io/carbon/guide/getting-started/introduction.html", "issues": "https://github.com/CarbonPHP/carbon/issues", "source": "https://github.com/CarbonPHP/carbon" }, @@ -1107,7 +1107,7 @@ "type": "tidelift" } ], - "time": "2025-12-02T21:04:28+00:00" + "time": "2026-01-29T09:26:29+00:00" }, { "name": "psr/cache", @@ -1616,34 +1616,30 @@ }, { "name": "symfony/cache", - "version": "v7.4.3", + "version": "v8.0.5", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "642117d18bc56832e74b68235359ccefab03dd11" + "reference": "92e9960386c7e01f58198038c199d522959a843c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/642117d18bc56832e74b68235359ccefab03dd11", - "reference": "642117d18bc56832e74b68235359ccefab03dd11", + "url": "https://api.github.com/repos/symfony/cache/zipball/92e9960386c7e01f58198038c199d522959a843c", + "reference": "92e9960386c7e01f58198038c199d522959a843c", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "psr/cache": "^2.0|^3.0", "psr/log": "^1.1|^2|^3", "symfony/cache-contracts": "^3.6", - "symfony/deprecation-contracts": "^2.5|^3", "symfony/service-contracts": "^2.5|^3", - "symfony/var-exporter": "^6.4|^7.0|^8.0" + "symfony/var-exporter": "^7.4|^8.0" }, "conflict": { - "doctrine/dbal": "<3.6", + "doctrine/dbal": "<4.3", "ext-redis": "<6.1", - "ext-relay": "<0.12.1", - "symfony/dependency-injection": "<6.4", - "symfony/http-kernel": "<6.4", - "symfony/var-dumper": "<6.4" + "ext-relay": "<0.12.1" }, "provide": { "psr/cache-implementation": "2.0|3.0", @@ -1652,16 +1648,16 @@ }, "require-dev": { "cache/integration-tests": "dev-master", - "doctrine/dbal": "^3.6|^4", + "doctrine/dbal": "^4.3", "predis/predis": "^1.1|^2.0", "psr/simple-cache": "^1.0|^2.0|^3.0", - "symfony/clock": "^6.4|^7.0|^8.0", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/filesystem": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/messenger": "^6.4|^7.0|^8.0", - "symfony/var-dumper": "^6.4|^7.0|^8.0" + "symfony/clock": "^7.4|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/filesystem": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -1696,7 +1692,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v7.4.3" + "source": "https://github.com/symfony/cache/tree/v8.0.5" }, "funding": [ { @@ -1716,7 +1712,7 @@ "type": "tidelift" } ], - "time": "2025-12-28T10:45:24+00:00" + "time": "2026-01-27T16:18:07+00:00" }, { "name": "symfony/cache-contracts", @@ -1873,16 +1869,16 @@ }, { "name": "symfony/config", - "version": "v8.0.3", + "version": "v8.0.4", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "58063686fd7b8e676f14b5a4808cb85265c5216e" + "reference": "8f45af92f08f82902827a8b6f403aaf49d893539" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/58063686fd7b8e676f14b5a4808cb85265c5216e", - "reference": "58063686fd7b8e676f14b5a4808cb85265c5216e", + "url": "https://api.github.com/repos/symfony/config/zipball/8f45af92f08f82902827a8b6f403aaf49d893539", + "reference": "8f45af92f08f82902827a8b6f403aaf49d893539", "shasum": "" }, "require": { @@ -1927,7 +1923,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v8.0.3" + "source": "https://github.com/symfony/config/tree/v8.0.4" }, "funding": [ { @@ -1947,51 +1943,43 @@ "type": "tidelift" } ], - "time": "2025-12-23T14:52:06+00:00" + "time": "2026-01-13T13:06:50+00:00" }, { "name": "symfony/console", - "version": "v7.4.3", + "version": "v8.0.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "732a9ca6cd9dfd940c639062d5edbde2f6727fb6" + "reference": "ace03c4cf9805080ff40cbeec69fca180c339a3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/732a9ca6cd9dfd940c639062d5edbde2f6727fb6", - "reference": "732a9ca6cd9dfd940c639062d5edbde2f6727fb6", + "url": "https://api.github.com/repos/symfony/console/zipball/ace03c4cf9805080ff40cbeec69fca180c339a3b", + "reference": "ace03c4cf9805080ff40cbeec69fca180c339a3b", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.2|^8.0" - }, - "conflict": { - "symfony/dependency-injection": "<6.4", - "symfony/dotenv": "<6.4", - "symfony/event-dispatcher": "<6.4", - "symfony/lock": "<6.4", - "symfony/process": "<6.4" + "symfony/string": "^7.4|^8.0" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/event-dispatcher": "^6.4|^7.0|^8.0", - "symfony/http-foundation": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/lock": "^6.4|^7.0|^8.0", - "symfony/messenger": "^6.4|^7.0|^8.0", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/stopwatch": "^6.4|^7.0|^8.0", - "symfony/var-dumper": "^6.4|^7.0|^8.0" + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/lock": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -2025,7 +2013,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.3" + "source": "https://github.com/symfony/console/tree/v8.0.4" }, "funding": [ { @@ -2045,20 +2033,20 @@ "type": "tidelift" } ], - "time": "2025-12-23T14:50:43+00:00" + "time": "2026-01-13T13:06:50+00:00" }, { "name": "symfony/dependency-injection", - "version": "v8.0.3", + "version": "v8.0.5", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "8db0d4c1dd4c533a29210c68074999ba45ad6d3e" + "reference": "40a6c455ade7e3bf25900d6b746d40cfa2573e26" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/8db0d4c1dd4c533a29210c68074999ba45ad6d3e", - "reference": "8db0d4c1dd4c533a29210c68074999ba45ad6d3e", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/40a6c455ade7e3bf25900d6b746d40cfa2573e26", + "reference": "40a6c455ade7e3bf25900d6b746d40cfa2573e26", "shasum": "" }, "require": { @@ -2106,7 +2094,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v8.0.3" + "source": "https://github.com/symfony/dependency-injection/tree/v8.0.5" }, "funding": [ { @@ -2126,7 +2114,7 @@ "type": "tidelift" } ], - "time": "2025-12-23T14:52:06+00:00" + "time": "2026-01-27T16:18:07+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2197,28 +2185,24 @@ }, { "name": "symfony/dotenv", - "version": "v7.4.0", + "version": "v8.0.0", "source": { "type": "git", "url": "https://github.com/symfony/dotenv.git", - "reference": "1658a4d34df028f3d93bcdd8e81f04423925a364" + "reference": "460b4067a85288c59a59ce8c1bfb3942e71fd85c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dotenv/zipball/1658a4d34df028f3d93bcdd8e81f04423925a364", - "reference": "1658a4d34df028f3d93bcdd8e81f04423925a364", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/460b4067a85288c59a59ce8c1bfb3942e71fd85c", + "reference": "460b4067a85288c59a59ce8c1bfb3942e71fd85c", "shasum": "" }, "require": { - "php": ">=8.2" - }, - "conflict": { - "symfony/console": "<6.4", - "symfony/process": "<6.4" + "php": ">=8.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/process": "^6.4|^7.0|^8.0" + "symfony/console": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -2251,7 +2235,7 @@ "environment" ], "support": { - "source": "https://github.com/symfony/dotenv/tree/v7.4.0" + "source": "https://github.com/symfony/dotenv/tree/v8.0.0" }, "funding": [ { @@ -2271,20 +2255,20 @@ "type": "tidelift" } ], - "time": "2025-11-16T10:14:42+00:00" + "time": "2025-11-16T10:17:21+00:00" }, { "name": "symfony/error-handler", - "version": "v8.0.0", + "version": "v8.0.4", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "d77ec7dda0c274178745d152e82baf7ea827fd73" + "reference": "7620b97ec0ab1d2d6c7fb737aa55da411bea776a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/d77ec7dda0c274178745d152e82baf7ea827fd73", - "reference": "d77ec7dda0c274178745d152e82baf7ea827fd73", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/7620b97ec0ab1d2d6c7fb737aa55da411bea776a", + "reference": "7620b97ec0ab1d2d6c7fb737aa55da411bea776a", "shasum": "" }, "require": { @@ -2332,7 +2316,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v8.0.0" + "source": "https://github.com/symfony/error-handler/tree/v8.0.4" }, "funding": [ { @@ -2352,20 +2336,20 @@ "type": "tidelift" } ], - "time": "2025-11-05T14:36:47+00:00" + "time": "2026-01-23T11:07:10+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v8.0.0", + "version": "v8.0.4", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "573f95783a2ec6e38752979db139f09fec033f03" + "reference": "99301401da182b6cfaa4700dbe9987bb75474b47" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/573f95783a2ec6e38752979db139f09fec033f03", - "reference": "573f95783a2ec6e38752979db139f09fec033f03", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/99301401da182b6cfaa4700dbe9987bb75474b47", + "reference": "99301401da182b6cfaa4700dbe9987bb75474b47", "shasum": "" }, "require": { @@ -2417,7 +2401,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.4" }, "funding": [ { @@ -2437,7 +2421,7 @@ "type": "tidelift" } ], - "time": "2025-10-30T14:17:19+00:00" + "time": "2026-01-05T11:45:55+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -2587,16 +2571,16 @@ }, { "name": "symfony/finder", - "version": "v8.0.3", + "version": "v8.0.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "dd3a2953570a283a2ba4e17063bb98c734cf5b12" + "reference": "8bd576e97c67d45941365bf824e18dc8538e6eb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/dd3a2953570a283a2ba4e17063bb98c734cf5b12", - "reference": "dd3a2953570a283a2ba4e17063bb98c734cf5b12", + "url": "https://api.github.com/repos/symfony/finder/zipball/8bd576e97c67d45941365bf824e18dc8538e6eb0", + "reference": "8bd576e97c67d45941365bf824e18dc8538e6eb0", "shasum": "" }, "require": { @@ -2631,7 +2615,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v8.0.3" + "source": "https://github.com/symfony/finder/tree/v8.0.5" }, "funding": [ { @@ -2651,117 +2635,99 @@ "type": "tidelift" } ], - "time": "2025-12-23T14:52:06+00:00" + "time": "2026-01-26T15:08:38+00:00" }, { "name": "symfony/framework-bundle", - "version": "v7.4.3", + "version": "v8.0.5", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "df908e8f9e5f6cc3c9e0d0172e030a5c1c280582" + "reference": "e2f9469e7a802dd7c0d193792afc494d68177c54" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/df908e8f9e5f6cc3c9e0d0172e030a5c1c280582", - "reference": "df908e8f9e5f6cc3c9e0d0172e030a5c1c280582", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/e2f9469e7a802dd7c0d193792afc494d68177c54", + "reference": "e2f9469e7a802dd7c0d193792afc494d68177c54", "shasum": "" }, "require": { "composer-runtime-api": ">=2.1", "ext-xml": "*", - "php": ">=8.2", - "symfony/cache": "^6.4.12|^7.0|^8.0", - "symfony/config": "^7.4.3|^8.0.3", - "symfony/dependency-injection": "^7.4|^8.0", + "php": ">=8.4", + "symfony/cache": "^7.4|^8.0", + "symfony/config": "^7.4.4|^8.0.4", + "symfony/dependency-injection": "^7.4.4|^8.0.4", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/error-handler": "^7.3|^8.0", - "symfony/event-dispatcher": "^6.4|^7.0|^8.0", - "symfony/filesystem": "^7.1|^8.0", - "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/error-handler": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/filesystem": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", "symfony/http-foundation": "^7.4|^8.0", "symfony/http-kernel": "^7.4|^8.0", - "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-mbstring": "^1.0", "symfony/polyfill-php85": "^1.32", "symfony/routing": "^7.4|^8.0" }, "conflict": { "doctrine/persistence": "<1.3", - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", - "symfony/asset": "<6.4", - "symfony/asset-mapper": "<6.4", - "symfony/clock": "<6.4", - "symfony/console": "<6.4", - "symfony/dom-crawler": "<6.4", - "symfony/dotenv": "<6.4", + "phpdocumentor/reflection-docblock": "<5.2|>=6", + "phpdocumentor/type-resolver": "<1.5.1", + "symfony/console": "<7.4", "symfony/form": "<7.4", - "symfony/http-client": "<6.4", - "symfony/lock": "<6.4", - "symfony/mailer": "<6.4", + "symfony/json-streamer": "<7.4", "symfony/messenger": "<7.4", - "symfony/mime": "<6.4", - "symfony/property-access": "<6.4", - "symfony/property-info": "<6.4", - "symfony/runtime": "<6.4.13|>=7.0,<7.1.6", - "symfony/scheduler": "<6.4.4|>=7.0.0,<7.0.4", - "symfony/security-core": "<6.4", - "symfony/security-csrf": "<7.2", - "symfony/serializer": "<7.2.5", - "symfony/stopwatch": "<6.4", - "symfony/translation": "<7.3", - "symfony/twig-bridge": "<6.4", - "symfony/twig-bundle": "<6.4", - "symfony/validator": "<6.4", - "symfony/web-profiler-bundle": "<6.4", - "symfony/webhook": "<7.2", + "symfony/security-csrf": "<7.4", + "symfony/serializer": "<7.4", + "symfony/translation": "<7.4", + "symfony/webhook": "<7.4", "symfony/workflow": "<7.4" }, "require-dev": { "doctrine/persistence": "^1.3|^2|^3", "dragonmantank/cron-expression": "^3.1", - "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "phpdocumentor/reflection-docblock": "^5.2", + "phpstan/phpdoc-parser": "^1.0|^2.0", "seld/jsonlint": "^1.10", - "symfony/asset": "^6.4|^7.0|^8.0", - "symfony/asset-mapper": "^6.4|^7.0|^8.0", - "symfony/browser-kit": "^6.4|^7.0|^8.0", - "symfony/clock": "^6.4|^7.0|^8.0", - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/css-selector": "^6.4|^7.0|^8.0", - "symfony/dom-crawler": "^6.4|^7.0|^8.0", - "symfony/dotenv": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/asset": "^7.4|^8.0", + "symfony/asset-mapper": "^7.4|^8.0", + "symfony/browser-kit": "^7.4|^8.0", + "symfony/clock": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/css-selector": "^7.4|^8.0", + "symfony/dom-crawler": "^7.4|^8.0", + "symfony/dotenv": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", "symfony/form": "^7.4|^8.0", - "symfony/html-sanitizer": "^6.4|^7.0|^8.0", - "symfony/http-client": "^6.4|^7.0|^8.0", - "symfony/json-streamer": "^7.3|^8.0", - "symfony/lock": "^6.4|^7.0|^8.0", - "symfony/mailer": "^6.4|^7.0|^8.0", + "symfony/html-sanitizer": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/json-streamer": "^7.4|^8.0", + "symfony/lock": "^7.4|^8.0", + "symfony/mailer": "^7.4|^8.0", "symfony/messenger": "^7.4|^8.0", - "symfony/mime": "^6.4|^7.0|^8.0", - "symfony/notifier": "^6.4|^7.0|^8.0", - "symfony/object-mapper": "^7.3|^8.0", - "symfony/polyfill-intl-icu": "~1.0", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/property-info": "^6.4|^7.0|^8.0", - "symfony/rate-limiter": "^6.4|^7.0|^8.0", - "symfony/runtime": "^6.4.13|^7.1.6|^8.0", - "symfony/scheduler": "^6.4.4|^7.0.4|^8.0", - "symfony/security-bundle": "^6.4|^7.0|^8.0", - "symfony/semaphore": "^6.4|^7.0|^8.0", - "symfony/serializer": "^7.2.5|^8.0", - "symfony/stopwatch": "^6.4|^7.0|^8.0", - "symfony/string": "^6.4|^7.0|^8.0", - "symfony/translation": "^7.3|^8.0", - "symfony/twig-bundle": "^6.4|^7.0|^8.0", - "symfony/type-info": "^7.1.8|^8.0", - "symfony/uid": "^6.4|^7.0|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/notifier": "^7.4|^8.0", + "symfony/object-mapper": "^7.4|^8.0", + "symfony/polyfill-intl-icu": "^1.0", + "symfony/process": "^7.4|^8.0", + "symfony/property-info": "^7.4|^8.0", + "symfony/rate-limiter": "^7.4|^8.0", + "symfony/runtime": "^7.4|^8.0", + "symfony/scheduler": "^7.4|^8.0", + "symfony/security-bundle": "^7.4|^8.0", + "symfony/semaphore": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/string": "^7.4|^8.0", + "symfony/translation": "^7.4|^8.0", + "symfony/twig-bundle": "^7.4|^8.0", + "symfony/type-info": "^7.4.1|^8.0.1", + "symfony/uid": "^7.4|^8.0", "symfony/validator": "^7.4|^8.0", - "symfony/web-link": "^6.4|^7.0|^8.0", - "symfony/webhook": "^7.2|^8.0", + "symfony/web-link": "^7.4|^8.0", + "symfony/webhook": "^7.4|^8.0", "symfony/workflow": "^7.4|^8.0", - "symfony/yaml": "^7.3|^8.0", - "twig/twig": "^3.12" + "symfony/yaml": "^7.4|^8.0" }, "type": "symfony-bundle", "autoload": { @@ -2789,7 +2755,7 @@ "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v7.4.3" + "source": "https://github.com/symfony/framework-bundle/tree/v8.0.5" }, "funding": [ { @@ -2809,20 +2775,20 @@ "type": "tidelift" } ], - "time": "2025-12-29T09:31:36+00:00" + "time": "2026-01-27T09:06:10+00:00" }, { "name": "symfony/http-foundation", - "version": "v8.0.3", + "version": "v8.0.5", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "514ec3aa7982f296b0ad0825f75b6be5779ae9e7" + "reference": "e3422806e6f6760dbed0ddbc0a7fbfb6b5ce96bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/514ec3aa7982f296b0ad0825f75b6be5779ae9e7", - "reference": "514ec3aa7982f296b0ad0825f75b6be5779ae9e7", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e3422806e6f6760dbed0ddbc0a7fbfb6b5ce96bb", + "reference": "e3422806e6f6760dbed0ddbc0a7fbfb6b5ce96bb", "shasum": "" }, "require": { @@ -2869,7 +2835,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v8.0.3" + "source": "https://github.com/symfony/http-foundation/tree/v8.0.5" }, "funding": [ { @@ -2889,78 +2855,63 @@ "type": "tidelift" } ], - "time": "2025-12-23T14:52:06+00:00" + "time": "2026-01-27T16:18:07+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.4.3", + "version": "v8.0.5", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "885211d4bed3f857b8c964011923528a55702aa5" + "reference": "20c1c5e41fc53928dbb670088f544f2d460d497d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/885211d4bed3f857b8c964011923528a55702aa5", - "reference": "885211d4bed3f857b8c964011923528a55702aa5", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/20c1c5e41fc53928dbb670088f544f2d460d497d", + "reference": "20c1c5e41fc53928dbb670088f544f2d460d497d", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/error-handler": "^6.4|^7.0|^8.0", - "symfony/event-dispatcher": "^7.3|^8.0", + "symfony/error-handler": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", "symfony/http-foundation": "^7.4|^8.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/browser-kit": "<6.4", - "symfony/cache": "<6.4", - "symfony/config": "<6.4", - "symfony/console": "<6.4", - "symfony/dependency-injection": "<6.4", - "symfony/doctrine-bridge": "<6.4", "symfony/flex": "<2.10", - "symfony/form": "<6.4", - "symfony/http-client": "<6.4", "symfony/http-client-contracts": "<2.5", - "symfony/mailer": "<6.4", - "symfony/messenger": "<6.4", - "symfony/translation": "<6.4", "symfony/translation-contracts": "<2.5", - "symfony/twig-bridge": "<6.4", - "symfony/validator": "<6.4", - "symfony/var-dumper": "<6.4", - "twig/twig": "<3.12" + "twig/twig": "<3.21" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "symfony/browser-kit": "^6.4|^7.0|^8.0", - "symfony/clock": "^6.4|^7.0|^8.0", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/css-selector": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/dom-crawler": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", - "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/browser-kit": "^7.4|^8.0", + "symfony/clock": "^7.4|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/css-selector": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/dom-crawler": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", "symfony/http-client-contracts": "^2.5|^3", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/property-access": "^7.1|^8.0", - "symfony/routing": "^6.4|^7.0|^8.0", - "symfony/serializer": "^7.1|^8.0", - "symfony/stopwatch": "^6.4|^7.0|^8.0", - "symfony/translation": "^6.4|^7.0|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/property-access": "^7.4|^8.0", + "symfony/routing": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/translation": "^7.4|^8.0", "symfony/translation-contracts": "^2.5|^3", - "symfony/uid": "^6.4|^7.0|^8.0", - "symfony/validator": "^6.4|^7.0|^8.0", - "symfony/var-dumper": "^6.4|^7.0|^8.0", - "symfony/var-exporter": "^6.4|^7.0|^8.0", - "twig/twig": "^3.12" + "symfony/uid": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0", + "symfony/var-exporter": "^7.4|^8.0", + "twig/twig": "^3.21" }, "type": "library", "autoload": { @@ -2988,7 +2939,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.4.3" + "source": "https://github.com/symfony/http-kernel/tree/v8.0.5" }, "funding": [ { @@ -3008,20 +2959,20 @@ "type": "tidelift" } ], - "time": "2025-12-31T08:43:57+00:00" + "time": "2026-01-28T10:46:31+00:00" }, { "name": "symfony/intl", - "version": "v8.0.1", + "version": "v8.0.4", "source": { "type": "git", "url": "https://github.com/symfony/intl.git", - "reference": "f9eca217ae8f2be0b3ad80723d6a3b518b90cd66" + "reference": "8d049269c2accca0b02e5f9de39f3ee92ebc4468" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/intl/zipball/f9eca217ae8f2be0b3ad80723d6a3b518b90cd66", - "reference": "f9eca217ae8f2be0b3ad80723d6a3b518b90cd66", + "url": "https://api.github.com/repos/symfony/intl/zipball/8d049269c2accca0b02e5f9de39f3ee92ebc4468", + "reference": "8d049269c2accca0b02e5f9de39f3ee92ebc4468", "shasum": "" }, "require": { @@ -3077,7 +3028,7 @@ "localization" ], "support": { - "source": "https://github.com/symfony/intl/tree/v8.0.1" + "source": "https://github.com/symfony/intl/tree/v8.0.4" }, "funding": [ { @@ -3097,7 +3048,7 @@ "type": "tidelift" } ], - "time": "2025-12-01T09:13:36+00:00" + "time": "2026-01-12T12:37:40+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3516,34 +3467,29 @@ }, { "name": "symfony/routing", - "version": "v7.4.3", + "version": "v8.0.4", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "5d3fd7adf8896c2fdb54e2f0f35b1bcbd9e45090" + "reference": "4a2bc08d1c35307239329f434d45c2bfe8241fa9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/5d3fd7adf8896c2fdb54e2f0f35b1bcbd9e45090", - "reference": "5d3fd7adf8896c2fdb54e2f0f35b1bcbd9e45090", + "url": "https://api.github.com/repos/symfony/routing/zipball/4a2bc08d1c35307239329f434d45c2bfe8241fa9", + "reference": "4a2bc08d1c35307239329f434d45c2bfe8241fa9", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/deprecation-contracts": "^2.5|^3" }, - "conflict": { - "symfony/config": "<6.4", - "symfony/dependency-injection": "<6.4", - "symfony/yaml": "<6.4" - }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", - "symfony/http-foundation": "^6.4|^7.0|^8.0", - "symfony/yaml": "^6.4|^7.0|^8.0" + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -3577,7 +3523,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.4.3" + "source": "https://github.com/symfony/routing/tree/v8.0.4" }, "funding": [ { @@ -3597,7 +3543,7 @@ "type": "tidelift" } ], - "time": "2025-12-19T10:00:43+00:00" + "time": "2026-01-12T12:37:40+00:00" }, { "name": "symfony/service-contracts", @@ -3688,16 +3634,16 @@ }, { "name": "symfony/string", - "version": "v8.0.1", + "version": "v8.0.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "ba65a969ac918ce0cc3edfac6cdde847eba231dc" + "reference": "758b372d6882506821ed666032e43020c4f57194" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/ba65a969ac918ce0cc3edfac6cdde847eba231dc", - "reference": "ba65a969ac918ce0cc3edfac6cdde847eba231dc", + "url": "https://api.github.com/repos/symfony/string/zipball/758b372d6882506821ed666032e43020c4f57194", + "reference": "758b372d6882506821ed666032e43020c4f57194", "shasum": "" }, "require": { @@ -3754,7 +3700,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v8.0.1" + "source": "https://github.com/symfony/string/tree/v8.0.4" }, "funding": [ { @@ -3774,20 +3720,20 @@ "type": "tidelift" } ], - "time": "2025-12-01T09:13:36+00:00" + "time": "2026-01-12T12:37:40+00:00" }, { "name": "symfony/translation", - "version": "v8.0.3", + "version": "v8.0.4", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "60a8f11f0e15c48f2cc47c4da53873bb5b62135d" + "reference": "db70c8ce7db74fd2da7b1d268db46b2a8ce32c10" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/60a8f11f0e15c48f2cc47c4da53873bb5b62135d", - "reference": "60a8f11f0e15c48f2cc47c4da53873bb5b62135d", + "url": "https://api.github.com/repos/symfony/translation/zipball/db70c8ce7db74fd2da7b1d268db46b2a8ce32c10", + "reference": "db70c8ce7db74fd2da7b1d268db46b2a8ce32c10", "shasum": "" }, "require": { @@ -3847,7 +3793,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v8.0.3" + "source": "https://github.com/symfony/translation/tree/v8.0.4" }, "funding": [ { @@ -3867,7 +3813,7 @@ "type": "tidelift" } ], - "time": "2025-12-21T10:59:45+00:00" + "time": "2026-01-13T13:06:50+00:00" }, { "name": "symfony/translation-contracts", @@ -3953,16 +3899,16 @@ }, { "name": "symfony/twig-bridge", - "version": "v8.0.3", + "version": "v8.0.5", "source": { "type": "git", "url": "https://github.com/symfony/twig-bridge.git", - "reference": "2a2978a44127bae9aaee0ed5319954eb492d81c3" + "reference": "3e60c35cb47b1077524c066ec277eaf92cdc2393" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/2a2978a44127bae9aaee0ed5319954eb492d81c3", - "reference": "2a2978a44127bae9aaee0ed5319954eb492d81c3", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/3e60c35cb47b1077524c066ec277eaf92cdc2393", + "reference": "3e60c35cb47b1077524c066ec277eaf92cdc2393", "shasum": "" }, "require": { @@ -3971,13 +3917,14 @@ "twig/twig": "^3.21" }, "conflict": { - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0" + "phpdocumentor/reflection-docblock": "<5.2|>=6", + "phpdocumentor/type-resolver": "<1.5.1", + "symfony/form": "<7.4.4|>8.0,<8.0.4" }, "require-dev": { "egulias/email-validator": "^2.1.10|^3|^4", "league/html-to-markdown": "^5.0", - "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "phpdocumentor/reflection-docblock": "^5.2", "symfony/asset": "^7.4|^8.0", "symfony/asset-mapper": "^7.4|^8.0", "symfony/console": "^7.4|^8.0", @@ -3985,7 +3932,7 @@ "symfony/emoji": "^7.4|^8.0", "symfony/expression-language": "^7.4|^8.0", "symfony/finder": "^7.4|^8.0", - "symfony/form": "^7.4.1|^8.0.1", + "symfony/form": "^7.4.4|^8.0.4", "symfony/html-sanitizer": "^7.4|^8.0", "symfony/http-foundation": "^7.4|^8.0", "symfony/http-kernel": "^7.4|^8.0", @@ -4035,7 +3982,7 @@ "description": "Provides integration for Twig with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bridge/tree/v8.0.3" + "source": "https://github.com/symfony/twig-bridge/tree/v8.0.5" }, "funding": [ { @@ -4055,49 +4002,43 @@ "type": "tidelift" } ], - "time": "2025-12-16T08:10:18+00:00" + "time": "2026-01-27T09:06:10+00:00" }, { "name": "symfony/twig-bundle", - "version": "v7.4.3", + "version": "v8.0.4", "source": { "type": "git", "url": "https://github.com/symfony/twig-bundle.git", - "reference": "9e1f5fd2668ed26c60d17d63f15fe270ed8da5e6" + "reference": "5a68f2e0e06996514bf04900c3982b93b42487af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/9e1f5fd2668ed26c60d17d63f15fe270ed8da5e6", - "reference": "9e1f5fd2668ed26c60d17d63f15fe270ed8da5e6", + "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/5a68f2e0e06996514bf04900c3982b93b42487af", + "reference": "5a68f2e0e06996514bf04900c3982b93b42487af", "shasum": "" }, "require": { "composer-runtime-api": ">=2.1", - "php": ">=8.2", + "php": ">=8.4", "symfony/config": "^7.4|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-foundation": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4.13|^7.1.6|^8.0", - "symfony/twig-bridge": "^7.3|^8.0", - "twig/twig": "^3.12" - }, - "conflict": { - "symfony/framework-bundle": "<6.4", - "symfony/translation": "<6.4" + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/twig-bridge": "^7.4|^8.0" }, "require-dev": { - "symfony/asset": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", - "symfony/finder": "^6.4|^7.0|^8.0", - "symfony/form": "^6.4|^7.0|^8.0", - "symfony/framework-bundle": "^6.4.13|^7.1.6|^8.0", - "symfony/routing": "^6.4|^7.0|^8.0", - "symfony/runtime": "^6.4.13|^7.1.6", - "symfony/stopwatch": "^6.4|^7.0|^8.0", - "symfony/translation": "^6.4|^7.0|^8.0", - "symfony/web-link": "^6.4|^7.0|^8.0", - "symfony/yaml": "^6.4|^7.0|^8.0" + "symfony/asset": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", + "symfony/form": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/routing": "^7.4|^8.0", + "symfony/runtime": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/translation": "^7.4|^8.0", + "symfony/web-link": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0" }, "type": "symfony-bundle", "autoload": { @@ -4125,7 +4066,7 @@ "description": "Provides a tight integration of Twig into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bundle/tree/v7.4.3" + "source": "https://github.com/symfony/twig-bundle/tree/v8.0.4" }, "funding": [ { @@ -4145,35 +4086,35 @@ "type": "tidelift" } ], - "time": "2025-12-19T10:00:43+00:00" + "time": "2026-01-06T12:43:21+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.4.3", + "version": "v8.0.4", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "7e99bebcb3f90d8721890f2963463280848cba92" + "reference": "326e0406fc315eca57ef5740fa4a280b7a068c82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/7e99bebcb3f90d8721890f2963463280848cba92", - "reference": "7e99bebcb3f90d8721890f2963463280848cba92", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/326e0406fc315eca57ef5740fa4a280b7a068c82", + "reference": "326e0406fc315eca57ef5740fa4a280b7a068c82", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0" + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0" }, "conflict": { - "symfony/console": "<6.4" + "symfony/console": "<7.4", + "symfony/error-handler": "<7.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/uid": "^6.4|^7.0|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/uid": "^7.4|^8.0", "twig/twig": "^3.12" }, "bin": [ @@ -4212,7 +4153,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.4.3" + "source": "https://github.com/symfony/var-dumper/tree/v8.0.4" }, "funding": [ { @@ -4232,7 +4173,7 @@ "type": "tidelift" } ], - "time": "2025-12-18T07:04:31+00:00" + "time": "2026-01-01T23:07:29+00:00" }, { "name": "symfony/var-exporter", @@ -4316,16 +4257,16 @@ }, { "name": "twig/cache-extra", - "version": "v3.22.1", + "version": "v3.23.0", "source": { "type": "git", "url": "https://github.com/twigphp/cache-extra.git", - "reference": "043bd496ada6bf2f73ed3186434fd9e6479a1b8d" + "reference": "c85c6ca1142db2f1f4f8c853988aef560d064199" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/cache-extra/zipball/043bd496ada6bf2f73ed3186434fd9e6479a1b8d", - "reference": "043bd496ada6bf2f73ed3186434fd9e6479a1b8d", + "url": "https://api.github.com/repos/twigphp/cache-extra/zipball/c85c6ca1142db2f1f4f8c853988aef560d064199", + "reference": "c85c6ca1142db2f1f4f8c853988aef560d064199", "shasum": "" }, "require": { @@ -4365,7 +4306,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/cache-extra/tree/v3.22.1" + "source": "https://github.com/twigphp/cache-extra/tree/v3.23.0" }, "funding": [ { @@ -4377,20 +4318,20 @@ "type": "tidelift" } ], - "time": "2025-11-02T11:00:49+00:00" + "time": "2025-12-02T14:45:16+00:00" }, { "name": "twig/extra-bundle", - "version": "v3.22.2", + "version": "v3.23.0", "source": { "type": "git", "url": "https://github.com/twigphp/twig-extra-bundle.git", - "reference": "09de9be7f6c0d19ede7b5a1dbfcfb2e9d1e0ea9e" + "reference": "7a27e784dc56eddfef5e9295829b290ce06f1682" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/09de9be7f6c0d19ede7b5a1dbfcfb2e9d1e0ea9e", - "reference": "09de9be7f6c0d19ede7b5a1dbfcfb2e9d1e0ea9e", + "url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/7a27e784dc56eddfef5e9295829b290ce06f1682", + "reference": "7a27e784dc56eddfef5e9295829b290ce06f1682", "shasum": "" }, "require": { @@ -4439,7 +4380,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/twig-extra-bundle/tree/v3.22.2" + "source": "https://github.com/twigphp/twig-extra-bundle/tree/v3.23.0" }, "funding": [ { @@ -4451,20 +4392,20 @@ "type": "tidelift" } ], - "time": "2025-12-05T08:51:53+00:00" + "time": "2025-12-18T20:46:15+00:00" }, { "name": "twig/intl-extra", - "version": "v3.22.1", + "version": "v3.23.0", "source": { "type": "git", "url": "https://github.com/twigphp/intl-extra.git", - "reference": "93ac31e53cdd3f2e541f42690cd0c54ca8138ab1" + "reference": "32f15a38d45a8d0ec11bc8a3d97d3ac2a261499f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/93ac31e53cdd3f2e541f42690cd0c54ca8138ab1", - "reference": "93ac31e53cdd3f2e541f42690cd0c54ca8138ab1", + "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/32f15a38d45a8d0ec11bc8a3d97d3ac2a261499f", + "reference": "32f15a38d45a8d0ec11bc8a3d97d3ac2a261499f", "shasum": "" }, "require": { @@ -4503,7 +4444,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/intl-extra/tree/v3.22.1" + "source": "https://github.com/twigphp/intl-extra/tree/v3.23.0" }, "funding": [ { @@ -4515,20 +4456,20 @@ "type": "tidelift" } ], - "time": "2025-11-02T11:00:49+00:00" + "time": "2026-01-17T13:57:47+00:00" }, { "name": "twig/twig", - "version": "v3.22.2", + "version": "v3.23.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "946ddeafa3c9f4ce279d1f34051af041db0e16f2" + "reference": "a64dc5d2cc7d6cafb9347f6cd802d0d06d0351c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/946ddeafa3c9f4ce279d1f34051af041db0e16f2", - "reference": "946ddeafa3c9f4ce279d1f34051af041db0e16f2", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/a64dc5d2cc7d6cafb9347f6cd802d0d06d0351c9", + "reference": "a64dc5d2cc7d6cafb9347f6cd802d0d06d0351c9", "shasum": "" }, "require": { @@ -4582,7 +4523,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.22.2" + "source": "https://github.com/twigphp/Twig/tree/v3.23.0" }, "funding": [ { @@ -4594,7 +4535,7 @@ "type": "tidelift" } ], - "time": "2025-12-14T11:28:47+00:00" + "time": "2026-01-23T21:00:41+00:00" }, { "name": "voku/portable-ascii", diff --git a/src/Command/CacheWarmCommand.php b/src/Command/CacheWarmCommand.php deleted file mode 100644 index 6e6d618..0000000 --- a/src/Command/CacheWarmCommand.php +++ /dev/null @@ -1,84 +0,0 @@ -cache->clear(); - - $capsule = new Capsule; - $capsule->addConnection([ - 'driver' => 'sqlite', - 'database' => __DIR__ . '/../../database.sqlite', - ]); - $capsule->setAsGlobal(); - $capsule->bootEloquent(); - - $progress = new ProgressBar($output); - $progress->start(); - $products = Product::with([ - 'price' => fn($query) => $query->orderByDesc('created_at'), - 'stock' => fn($query) => $query->orderByDesc('created_at'), - ])->get(); - $progress->setMaxSteps(count($products)); - - $request = Request::create('/cache-warm'); - $this->requestStack->push($request); - - foreach ($products as $product) { - $priceList = $product->price()->pluck('price'); - $stockList = $product->stock()->pluck('stock'); - $priceDates = $product->price()->pluck('created_at')->map(fn($date) => $date->format('Y-m-d'))->toArray(); - $stockDates = $product->stock()->pluck('created_at')->map(fn($date) => $date->format('Y-m-d'))->toArray(); - $this->twig->render('product.html.twig', [ - 'product' => $product, - 'price_list' => $this->prepareChartData($priceDates, $priceList), - 'stock_list' => $this->prepareChartData($stockDates, $stockList), - 'price_dates' => implode("','", $priceDates), - ]); - $progress->advance(); - } - - $progress->finish(); - $output->writeln(''); - $output->writeln('DONE'); - - return Command::SUCCESS; - } - - private function prepareChartData($set1, $set2): string - { - $data = []; - foreach ($set1 as $key => $value) { - $data[] = ['x' => $value, 'y' => $set2[$key]]; - } - $stringData = json_encode($data); - - return str_replace(['"x"', '"y"'], ['x', 'y'], $stringData); - } -} diff --git a/src/Command/Migrate.php b/src/Command/Migrate.php index ed1a099..434e768 100644 --- a/src/Command/Migrate.php +++ b/src/Command/Migrate.php @@ -118,6 +118,66 @@ class Migrate extends Command ] ); } + if (Capsule::schema()->hasTable('countries') && !Country::where('countryName', 'Netherlands')->exists()) { + Capsule::table('countries')->insert([ + 'countryName' => 'Netherlands', + 'productsUrl' => 'https://nl.ryobitools.eu/api/product-listing/get-products', + 'cultureCode' => 'nl-NL', + 'currency' => 'EUR', + 'locale' => 'nl', + 'created_at' => now(), + 'updated_at' => now(), + ] + ); + } + if (Capsule::schema()->hasTable('countries') && !Country::where('countryName', 'France')->exists()) { + Capsule::table('countries')->insert([ + 'countryName' => 'France', + 'productsUrl' => 'https://fr.ryobitools.eu/api/product-listing/get-products', + 'cultureCode' => 'fr-FR', + 'currency' => 'EUR', + 'locale' => 'fr', + 'created_at' => now(), + 'updated_at' => now(), + ] + ); + } + if (Capsule::schema()->hasTable('countries') && !Country::where('countryName', 'Spain')->exists()) { + Capsule::table('countries')->insert([ + 'countryName' => 'Spain', + 'productsUrl' => 'https://es.ryobitools.eu/api/product-listing/get-products', + 'cultureCode' => 'es-ES', + 'currency' => 'EUR', + 'locale' => 'es', + 'created_at' => now(), + 'updated_at' => now(), + ] + ); + } + if (Capsule::schema()->hasTable('countries') && !Country::where('countryName', 'Poland')->exists()) { + $id = Capsule::table('countries')->insertGetId( + [ + 'countryName' => 'Poland', + 'productsUrl' => 'https://pl.ryobitools.eu/api/product-listing/get-products', + 'cultureCode' => 'pl-PL', + 'currency' => 'PLN', + 'locale' => 'pl', + 'created_at' => now(), + 'updated_at' => now(), + ]); + } + if (Capsule::schema()->hasTable('countries') && !Country::where('countryName', 'UK')->exists()) { + Capsule::table('countries')->insert([ + 'countryName' => 'UK', + 'productsUrl' => 'https://uk.ryobitools.eu/api/product-listing/get-products', + 'cultureCode' => 'en-GB', + 'currency' => 'GBP', + 'locale' => 'uk', + 'created_at' => now(), + 'updated_at' => now(), + ] + ); + } if (!Capsule::schema()->hasColumn('products', 'country_id')) { Capsule::schema()->table('products', function (Blueprint $table) use ($id) { @@ -181,19 +241,72 @@ 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 { - if (!count(Capsule::select('SELECT name FROM sqlite_master WHERE type = "index" and name = "products_skuid_country_id_unique"'))) { + if (!$this->hasIndex('products_skuid_country_id_unique')) { Capsule::schema()->table('products', function (Blueprint $table) { - $table->integer('skuID')->unique(false)->change(); + if ($this->hasIndex('products_skuid_unique')) { + $table->integer('skuID')->unique(false)->change(); + } $table->unique(['skuID', 'country_id']); }); } - Capsule::schema()->table('products', function (Blueprint $table) { - $table->foreign('id')->references('product_id')->on('stocks'); - }); + if (!$this->hasIndex('prices_product_id_index')) { + Capsule::schema()->table('prices', function (Blueprint $table) { + $table->index('product_id'); + }); + } + + if (!$this->hasIndex('products_id_index')) { + Capsule::schema()->table('products', function (Blueprint $table) { + $table->index('id'); + }); + } + + if (!$this->hasIndex('stocks_product_id_stock_index')) { + Capsule::schema()->table('stocks', function (Blueprint $table) { + $table->index(['product_id', 'stock']); + }); + } + + if (!$this->isFK('products', 'id', 'stocks', 'product_id')) { + Capsule::schema()->table('products', function (Blueprint $table) { + $table->foreign('id')->references('product_id')->on('stocks'); + }); + } + + if (!$this->isFK('products', 'id', 'prices', 'product_id')) { + Capsule::schema()->table('products', function (Blueprint $table) { + $table->foreign('id')->references('product_id')->on('prices'); + }); + } + if (!$this->isFK('products', 'country_id', 'countries', 'id')) { + Capsule::schema()->table('products', function (Blueprint $table) { + $table->foreign('country_id')->references('id')->on('countries'); + }); + } + } + + private function isFK(string $table, string $column, string $fTable, string $fColumn): bool + { + $fkColumns = Capsule::schema()->getForeignKeys($table); + + return !empty(array_filter($fkColumns, fn($fkColumn) => ($fkColumn['foreign_table'] == $fTable && + in_array($fColumn, $fkColumn['foreign_columns']) && + in_array($column, $fkColumn['columns'])) + )); + } + + private function hasIndex(string $indexName): bool + { + return !!count(Capsule::select('SELECT name FROM sqlite_master WHERE type = "index" and name = ?', [$indexName])); } } diff --git a/src/Command/ScrapeWebsite.php b/src/Command/ScrapeWebsite.php index 9272190..8178b64 100644 --- a/src/Command/ScrapeWebsite.php +++ b/src/Command/ScrapeWebsite.php @@ -15,11 +15,13 @@ use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; #[AsCommand(name: 'app:scrape', description: 'Scrape all products from Ryobi website')] class ScrapeWebsite extends Command { + const COUNTRY_ID = 'country'; private Client $client; public function __construct(protected Capsule $database) @@ -30,30 +32,31 @@ class ScrapeWebsite extends Command protected function configure(): void { $this->client = new Client(); + $this->addOption(self::COUNTRY_ID, 'c', InputOption::VALUE_OPTIONAL, 'Country id'); } public function execute(InputInterface $input, OutputInterface $output): int { + $countryId = intval($input->getOption(self::COUNTRY_ID)); $output->writeln('Scrape products'); - $progress = new ProgressBar($output); - $countries = Country::all(); + $countries = Country::all()->when($countryId, fn ($query) => $query->where('id', $countryId)); foreach($countries as $country) { $output->writeln('Country name: ' . $country->countryName); - $progress->start(); $products = $this->getProducts($country); + $progress = new ProgressBar($output); + $progress->start(); $progress->setMaxSteps(count($products)); foreach ($products as $product) { $this->saveProduct($product, $country); $progress->advance(); } $progress->finish(); - $output->writeln(''); - $output->writeln('Scrape products - DONE'); - $output->writeln(''); + $output->writeln("\nScrape products - DONE\n"); } $output->writeln('Update prices'); $products = Product::all(); + $progress = new ProgressBar($output); $progress->setMaxSteps(count($products)); $progress->start(); foreach($products as $product) { @@ -95,7 +98,7 @@ class ScrapeWebsite extends Command $products = array_merge($products, $responseObject->products); $page++; $canLoadMore = $responseObject->canLoadMore; - } catch (GuzzleException) { + } catch (GuzzleException $e) { return $products; } } while ($canLoadMore); @@ -120,6 +123,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/BaseController.php b/src/Controller/BaseController.php index dfccd17..81aa2af 100644 --- a/src/Controller/BaseController.php +++ b/src/Controller/BaseController.php @@ -11,7 +11,7 @@ class BaseController extends AbstractController { protected Environment $twig; - public function __construct(protected FilesystemAdapter $cache, protected Capsule $database) + public function __construct( protected Capsule $database) { } } diff --git a/src/Controller/CategoryController.php b/src/Controller/CategoryController.php index c950ae1..f8659cb 100644 --- a/src/Controller/CategoryController.php +++ b/src/Controller/CategoryController.php @@ -11,9 +11,6 @@ final class CategoryController extends BaseController #[Route('/category/{category?}', name: 'app_category')] public function __invoke(?string $category): Response { - if($this->cache->getItem('list_category_'.$category)->isHit()) { - return $this->render('productList.html.twig', ['listType' => 'category_'.$category]); - } /** @var Product[] $products */ $products = Product::with(['price', 'lowestPrice']) diff --git a/src/Controller/DiscontinuedController.php b/src/Controller/DiscontinuedController.php index 4c5b96b..d8f60b3 100644 --- a/src/Controller/DiscontinuedController.php +++ b/src/Controller/DiscontinuedController.php @@ -13,9 +13,6 @@ final class DiscontinuedController extends BaseController #[Route('/discontinued', name: 'app_discontinued')] public function __invoke(): Response { - if($this->cache->getItem('list_discontinued')->isHit()) { - return $this->render('productList.html.twig', ['listType' => 'discontinued']); - } $products = Product::where('lastSeen', '<', now()->format('Y-m-d')) ->orderByDesc('starred') diff --git a/src/Controller/IndexController.php b/src/Controller/IndexController.php index 759236c..a1442e7 100644 --- a/src/Controller/IndexController.php +++ b/src/Controller/IndexController.php @@ -11,14 +11,11 @@ final class IndexController extends BaseController #[Route('/', name: 'app_home')] public function __invoke(): Response { - if ($this->cache->getItem('list_all')->isHit()) { - return $this->render('productList.html.twig', ['listType' => 'all']); - } - $products = Product::with(['currentStock']) - ->orderByDesc('starred') + $products = Product::orderByDesc('starred') ->orderByDesc('created_by') ->get(); + return $this->render('productList.html.twig', ['products' => $products, 'listType' => 'all']); } } diff --git a/src/Controller/LowestPriceController.php b/src/Controller/LowestPriceController.php index 66a1808..9c038e4 100644 --- a/src/Controller/LowestPriceController.php +++ b/src/Controller/LowestPriceController.php @@ -13,9 +13,6 @@ final class LowestPriceController extends BaseController public function __invoke(): Response { $listType = 'lowest_price'; - if($this->cache->getItem('lowest_price')->isHit()) { - return $this->render('productList.html.twig', ['listType' => $listType]); - } $products = Product::whereRaw('priceCurrent = priceLowest') ->whereRaw('lastSeen = "'.now()->format('Y-m-d').'"') diff --git a/src/Controller/NewController.php b/src/Controller/NewController.php index 16d875b..87077c0 100644 --- a/src/Controller/NewController.php +++ b/src/Controller/NewController.php @@ -13,9 +13,6 @@ final class NewController extends BaseController #[Route('/new', name: 'app_new')] public function __invoke(): Response { - if($this->cache->getItem('list_new')->isHit()) { - return $this->render('productList.html.twig', ['listType' => 'new']); - } $date = now()->modify('-30 days')->format('Y-m-d'); $products = Product::where('created_at', '>', $date) ->with(['country', 'stock']) diff --git a/src/Controller/ProductController.php b/src/Controller/ProductController.php index 9bdbc65..408b0c7 100644 --- a/src/Controller/ProductController.php +++ b/src/Controller/ProductController.php @@ -19,10 +19,6 @@ final class ProductController extends BaseController #[Route('/product/{productId<\d+>}', name: 'app_product')] public function __invoke(int $productId): Response { - if ($this->cache->getItem('product' . $productId)->isHit()) { - return $this->render('product.html.twig', ['product' => ['id' => $productId]]); - } - $product = Product::with([ 'price' => fn($query) => $query->orderBy('created_at', 'desc'), 'stock' => fn($query) => $query->orderBy('created_at', 'desc'), @@ -32,6 +28,10 @@ final class ProductController extends BaseController } $priceList = $product->price()->pluck('price', 'created_at')->mapWithKeys(fn($price, $createdAt) => [explode(' ', $createdAt)[0] => $price])->toArray(); $stockList = $product->stock()->pluck('stock', 'created_at')->mapWithKeys(fn($stock, $createdAt) => [explode(' ', $createdAt)[0] => $stock])->toArray(); + + ksort($stockList); + ksort($priceList); + return $this->render('product.html.twig', [ 'product' => $product, 'price_list' => $this->prepareChartData($priceList), diff --git a/src/Controller/PromosController.php b/src/Controller/PromosController.php index b54bc74..8e5fe05 100644 --- a/src/Controller/PromosController.php +++ b/src/Controller/PromosController.php @@ -8,18 +8,24 @@ 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']); - } - - $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")) + ->addSelect('countries.locale') + ->whereRaw("json_extract(promotions, '$.tag') is not null") + ->join('countries', 'products.country_id', '=', 'countries.id') + ->get() + ->groupBy('locale'); + + return $this->render('productList.html.twig', ['products' => $products, 'listType' => 'promos' . $promo, 'promos' => $promos->toArray()]); } } diff --git a/src/Controller/StarController.php b/src/Controller/StarController.php index af63187..6fe1930 100644 --- a/src/Controller/StarController.php +++ b/src/Controller/StarController.php @@ -12,15 +12,12 @@ final class StarController extends BaseController #[Route('/star/{productId<\d+>}', name: 'app_star')] public function __invoke(int $productId, Request $request): Response { - $this->cache->deleteItems(['list_all', 'list_promos', 'list_new', 'list_discontinued']); $referer = $request->headers->get('referer'); if (str_contains($referer, '/category/')) { preg_match('#/category/(.*)#i', $referer, $matches); - $this->cache->deleteItem('list_category_'.urldecode($matches[1])); } if (str_contains($referer, '/search?search=')) { preg_match('#/search\?search=(.*)#i', $referer, $matches); - $this->cache->deleteItem('list_search_'.urldecode($matches[1])); } Product::find($productId)->toggleStarred()->save(); diff --git a/src/Controller/UpdateController.php b/src/Controller/UpdateController.php new file mode 100644 index 0000000..c7a907c --- /dev/null +++ b/src/Controller/UpdateController.php @@ -0,0 +1,18 @@ +set(Manager::class)->configurator([DatabaseFactory::class, 'create']); $services->load('Krzysiej\\RyobiCrawler\\', __DIR__ ) ->exclude('../src/{Models,Twig,DatabaseFactory.php,Kernel.php}'); - $services->set('twig.extension.cache', AppExtension::class)->tag('twig.extension'); - $services->set(CacheExtension::class)->tag('twig.extension'); - $services->set(FilesystemAdapter::class)->args([ - '$directory' => __DIR__ . '/../var/cache/twig_blocks' - ]); - $services->set('twig.runtime.cache', CacheRuntime::class)->args([new Reference(FilesystemAdapter::class)])->tag('twig.runtime'); + $services->set('twig.extension', AppExtension::class)->tag('twig.extension'); } protected function configureRoutes(RoutingConfigurator $routes): void diff --git a/src/Models/Product.php b/src/Models/Product.php index 8997015..3f5d456 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,9 +60,10 @@ class Product extends Model { return $this->hasOne(Price::class)->ofMany('price', 'MIN'); } + public function newestPrice(): HasOne { - return $this->hasOne(Price::class)->latest(); + return $this->hasOne(Price::class)->latest()->take(1); } public function stock(): HasMany @@ -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)->take(1); } 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..6444b01 100644 --- a/templates/product.html.twig +++ b/templates/product.html.twig @@ -1,23 +1,30 @@ {% extends "template.html.twig" %} {% block content %} -{% cache 'product' ~ product.id %}
+ href="{{ path('app_star', {'productId': product.id}) }}">{% if product.starred %}★{% else %} ☆ {% endif %} +
{% if product.starred %}★{% else %} ☆ {% endif %} {{ product.name }} - {{ product.name }} - {{ product.subTitle }} + {{ product.name }} + {{ product.subTitle }} + {% if product.promotions is not null and product.promotions.hasPromotion %}PROMO: {{ product.promotions.tag }} + {% endif %} @@ -132,6 +139,5 @@ } }); -{% endcache %} {% endblock %} diff --git a/templates/productList.html.twig b/templates/productList.html.twig index 79bd685..0f5f595 100644 --- a/templates/productList.html.twig +++ b/templates/productList.html.twig @@ -1,12 +1,24 @@ {% extends "template.html.twig" %} {% block content %} - {% cache 'list_' ~ listType %} - {% if listType starts with 'category_' %} {{ renderCategoryTree(categoryTree, category) | raw }} {% endif %} + {% if listType starts with 'promos' %} + + {% endif %} + {% if (listType starts with 'category_' and category == null) or not (listType starts with 'category_') or (listType starts with 'category_' and category is not null) %}
@@ -34,21 +46,24 @@
{{ product.name }} +
{% if product.stock > 0 %} stock: {{ product.stock }} {% else %} out of stock {% endif %} {% if product.isDiscontinued() %} - is discontinued + is discontinued {% endif %} {% if product.isNew() %} - is new + is new {% endif %} {{ product.subTitle }} + {% if product.promotions is not null and product.promotions.hasPromotion %}PROMO: {{ product.promotions.tag }}{% endif %} + {{ product.country.countryName }}
{% endif %} - {% endcache %} {% endblock %} diff --git a/templates/template.html.twig b/templates/template.html.twig index e216a28..3341f4f 100644 --- a/templates/template.html.twig +++ b/templates/template.html.twig @@ -71,11 +71,10 @@ - + Ryobi crawler - + @@ -89,41 +88,25 @@