From 05baad8fdddf9741584f5c26567486da9e389a0f Mon Sep 17 00:00:00 2001 From: gcch Date: Mon, 15 Dec 2025 15:34:50 +0100 Subject: [PATCH] =?UTF-8?q?fonc(produit)=20impl=C3=A9mente=20le=20multi-va?= =?UTF-8?q?riations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .php-cs-fixer.dist.php | 2 +- .phpactor.json | 4 +- bun.lock | 4 +- composer.json | 8 +- composer.lock | 298 ++++++++++- dprint.json | 6 +- package.json | 2 +- phpactor.schema.json | 492 ++++++++++++++++++ rector.php | 24 + web/app/db.php | 1 - .../themes/haiku-atelier-2024/front-page.php | 4 +- .../haiku-atelier-2024/single-product.php | 63 +-- .../src/inc/Data/Attribute.php | 33 ++ .../src/inc/Data/AttributeOption.php | 25 + .../src/inc/Data/Product.php | 103 ++++ .../src/inc/TraitementInformations.php | 115 ++-- .../src/inc/WP/HaikuProduct.php | 35 ++ .../haiku-atelier-2024/src/inc/WP/Post.php | 55 ++ .../haiku-atelier-2024/src/inc/WP/Term.php | 26 + .../sass/layouts/_informations-produit.scss | 9 +- .../src/scripts/scripts-page-produit.ts | 64 ++- .../taxonomy-product_cat.php | 2 +- .../views/macros/images.twig | 2 +- .../pages/produit/informations-produit.twig | 95 ++-- .../parts/pages/produit/photos-produit.twig | 4 +- .../produit/selecteur-attributs-produit.twig | 28 + 26 files changed, 1320 insertions(+), 184 deletions(-) create mode 100644 phpactor.schema.json create mode 100644 rector.php delete mode 120000 web/app/db.php create mode 100644 web/app/themes/haiku-atelier-2024/src/inc/Data/Attribute.php create mode 100644 web/app/themes/haiku-atelier-2024/src/inc/Data/AttributeOption.php create mode 100644 web/app/themes/haiku-atelier-2024/src/inc/Data/Product.php create mode 100644 web/app/themes/haiku-atelier-2024/src/inc/WP/HaikuProduct.php create mode 100644 web/app/themes/haiku-atelier-2024/src/inc/WP/Post.php create mode 100644 web/app/themes/haiku-atelier-2024/src/inc/WP/Term.php create mode 100644 web/app/themes/haiku-atelier-2024/views/parts/pages/produit/selecteur-attributs-produit.twig diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index aca9f695..19bd6c1e 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -1,4 +1,4 @@ -=8.5", + "azjezz/psl": "^4.2", "composer/installers": "^2.3", "crell/fp": "^1.0", "htmlburger/carbon-fields": "^3.6", - "illuminate/support": "^12.18", + "illuminate/support": "^12.43", "laravel/helpers": "^1.7.1", "log1x/wp-smtp": "^1.0.2", "lstrojny/functional-php": "^1.17", "mnsami/composer-custom-directory-installer": "^2.0", "nesbot/carbon": "^3.8.2", "oscarotero/env": "^2.1.1", - "php": ">=8.4", "roots/bedrock-autoloader": "^1.0.4", "roots/bedrock-disallow-indexing": "^2.0", "roots/wordpress": "^6.8.1", @@ -68,8 +70,10 @@ }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.89", + "php-standard-library/phpstan-extension": "^2.0", "phpstan/extension-installer": "^1.4.3", "phpstan/phpstan": "^2.0.3", + "rector/rector": "^2.2", "roave/security-advisories": "dev-latest", "szepeviktor/phpstan-wordpress": "2.x-dev", "vincentlanglet/twig-cs-fixer": "^3.10" diff --git a/composer.lock b/composer.lock index 39de27e5..8fe5604c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,84 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d87a6e00e0233e0f2255f18aadf2d42c", + "content-hash": "656fefb6a4e896ee0c5f5d87f36db997", "packages": [ + { + "name": "azjezz/psl", + "version": "4.2.0", + "source": { + "type": "git", + "url": "https://github.com/azjezz/psl.git", + "reference": "15153a64c9824335ce11654522e7d88de762d39e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/azjezz/psl/zipball/15153a64c9824335ce11654522e7d88de762d39e", + "reference": "15153a64c9824335ce11654522e7d88de762d39e", + "shasum": "" + }, + "require": { + "ext-bcmath": "*", + "ext-intl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "ext-sodium": "*", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", + "revolt/event-loop": "^1.0.7" + }, + "require-dev": { + "carthage-software/mago": "^1.0.0-beta.32", + "infection/infection": "^0.31.2", + "php-coveralls/php-coveralls": "^2.7.0", + "phpbench/phpbench": "^1.4.0", + "phpunit/phpunit": "^9.6.22" + }, + "suggest": { + "php-standard-library/phpstan-extension": "PHPStan integration", + "php-standard-library/psalm-plugin": "Psalm integration" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/hhvm/hsl", + "name": "hhvm/hsl" + } + }, + "autoload": { + "files": [ + "src/bootstrap.php" + ], + "psr-4": { + "Psl\\": "src/Psl" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "azjezz", + "email": "azjezz@protonmail.com" + } + ], + "description": "PHP Standard Library", + "support": { + "issues": "https://github.com/azjezz/psl/issues", + "source": "https://github.com/azjezz/psl/tree/4.2.0" + }, + "funding": [ + { + "url": "https://github.com/azjezz", + "type": "github" + }, + { + "url": "https://github.com/veewee", + "type": "github" + } + ], + "time": "2025-10-25T08:31:40+00:00" + }, { "name": "carbonphp/carbon-doctrine-types", "version": "3.2.0", @@ -585,7 +661,7 @@ }, { "name": "illuminate/collections", - "version": "v12.42.0", + "version": "v12.43.1", "source": { "type": "git", "url": "https://github.com/illuminate/collections.git", @@ -644,7 +720,7 @@ }, { "name": "illuminate/conditionable", - "version": "v12.42.0", + "version": "v12.43.1", "source": { "type": "git", "url": "https://github.com/illuminate/conditionable.git", @@ -690,7 +766,7 @@ }, { "name": "illuminate/contracts", - "version": "v12.42.0", + "version": "v12.43.1", "source": { "type": "git", "url": "https://github.com/illuminate/contracts.git", @@ -738,7 +814,7 @@ }, { "name": "illuminate/macroable", - "version": "v12.42.0", + "version": "v12.43.1", "source": { "type": "git", "url": "https://github.com/illuminate/macroable.git", @@ -784,7 +860,7 @@ }, { "name": "illuminate/reflection", - "version": "v12.42.0", + "version": "v12.43.1", "source": { "type": "git", "url": "https://github.com/illuminate/reflection.git", @@ -835,16 +911,16 @@ }, { "name": "illuminate/support", - "version": "v12.42.0", + "version": "v12.43.1", "source": { "type": "git", "url": "https://github.com/illuminate/support.git", - "reference": "d35411be5657e0b5560a5885f3c9140e4cbe0be5" + "reference": "20014564c32e2e8c6a57e03d065bcf8706a458e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/support/zipball/d35411be5657e0b5560a5885f3c9140e4cbe0be5", - "reference": "d35411be5657e0b5560a5885f3c9140e4cbe0be5", + "url": "https://api.github.com/repos/illuminate/support/zipball/20014564c32e2e8c6a57e03d065bcf8706a458e7", + "reference": "20014564c32e2e8c6a57e03d065bcf8706a458e7", "shasum": "" }, "require": { @@ -911,7 +987,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-12-09T15:26:52+00:00" + "time": "2025-12-14T15:58:12+00:00" }, { "name": "laravel/helpers", @@ -1604,6 +1680,78 @@ }, "time": "2021-10-29T13:26:27+00:00" }, + { + "name": "revolt/event-loop", + "version": "v1.0.8", + "source": { + "type": "git", + "url": "https://github.com/revoltphp/event-loop.git", + "reference": "b6fc06dce8e9b523c9946138fa5e62181934f91c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/revoltphp/event-loop/zipball/b6fc06dce8e9b523c9946138fa5e62181934f91c", + "reference": "b6fc06dce8e9b523c9946138fa5e62181934f91c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.15" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Revolt\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "ceesjank@gmail.com" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Rock-solid event loop for concurrent PHP applications.", + "keywords": [ + "async", + "asynchronous", + "concurrency", + "event", + "event-loop", + "non-blocking", + "scheduler" + ], + "support": { + "issues": "https://github.com/revoltphp/event-loop/issues", + "source": "https://github.com/revoltphp/event-loop/tree/v1.0.8" + }, + "time": "2025-08-27T21:33:23+00:00" + }, { "name": "roots/bedrock-autoloader", "version": "1.0.4", @@ -3930,16 +4078,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.92.1", + "version": "v3.92.2", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "ed33ad03313a019533ba065eba6c86af0a382873" + "reference": "64fab3553dce507ce247f7d1a7d65f74ef658c3f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/ed33ad03313a019533ba065eba6c86af0a382873", - "reference": "ed33ad03313a019533ba065eba6c86af0a382873", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/64fab3553dce507ce247f7d1a7d65f74ef658c3f", + "reference": "64fab3553dce507ce247f7d1a7d65f74ef658c3f", "shasum": "" }, "require": { @@ -4022,7 +4170,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.92.1" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.92.2" }, "funding": [ { @@ -4030,7 +4178,61 @@ "type": "github" } ], - "time": "2025-12-15T23:09:01+00:00" + "time": "2025-12-17T00:04:16+00:00" + }, + { + "name": "php-standard-library/phpstan-extension", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/phpstan-extension.git", + "reference": "eaf787ddf91f1b22b3a4da052f42294e0a75836e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/phpstan-extension/zipball/eaf787ddf91f1b22b3a4da052f42294e0a75836e", + "reference": "eaf787ddf91f1b22b3a4da052f42294e0a75836e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.0" + }, + "conflict": { + "azjezz/psl": "<1.6||>=5.0" + }, + "require-dev": { + "azjezz/psl": "^1.6||^2.0||^3.0||^4.0", + "composer/semver": "^3.3", + "nikic/php-parser": "^4.14.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Psl\\PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan PSL extension", + "support": { + "issues": "https://github.com/php-standard-library/phpstan-extension/issues", + "source": "https://github.com/php-standard-library/phpstan-extension/tree/2.0.2" + }, + "time": "2025-11-30T13:33:52+00:00" }, { "name": "php-stubs/wordpress-stubs", @@ -4810,6 +5012,66 @@ ], "time": "2024-06-11T12:45:25+00:00" }, + { + "name": "rector/rector", + "version": "2.2.14", + "source": { + "type": "git", + "url": "https://github.com/rectorphp/rector.git", + "reference": "6d56bb0e94d4df4f57a78610550ac76ab403657d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/6d56bb0e94d4df4f57a78610550ac76ab403657d", + "reference": "6d56bb0e94d4df4f57a78610550ac76ab403657d", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0", + "phpstan/phpstan": "^2.1.33" + }, + "conflict": { + "rector/rector-doctrine": "*", + "rector/rector-downgrade-php": "*", + "rector/rector-phpunit": "*", + "rector/rector-symfony": "*" + }, + "suggest": { + "ext-dom": "To manipulate phpunit.xml via the custom-rule command" + }, + "bin": [ + "bin/rector" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Instant Upgrade and Automated Refactoring of any PHP code", + "homepage": "https://getrector.com/", + "keywords": [ + "automation", + "dev", + "migration", + "refactoring" + ], + "support": { + "issues": "https://github.com/rectorphp/rector/issues", + "source": "https://github.com/rectorphp/rector/tree/2.2.14" + }, + "funding": [ + { + "url": "https://github.com/tomasvotruba", + "type": "github" + } + ], + "time": "2025-12-09T10:57:55+00:00" + }, { "name": "roave/security-advisories", "version": "dev-latest", @@ -7106,7 +7368,7 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": ">=8.4" + "php": ">=8.5" }, "platform-dev": {}, "plugin-api-version": "2.6.0" diff --git a/dprint.json b/dprint.json index a15ffaa8..7e269f37 100755 --- a/dprint.json +++ b/dprint.json @@ -76,12 +76,12 @@ }, "newLineKind": "lf", "plugins": [ - "https://plugins.dprint.dev/typescript-0.95.12.wasm", + "https://plugins.dprint.dev/typescript-0.95.13.wasm", "https://plugins.dprint.dev/json-0.21.0.wasm", "https://plugins.dprint.dev/markdown-0.20.0.wasm", "https://plugins.dprint.dev/toml-0.7.0.wasm", - "https://plugins.dprint.dev/g-plane/malva-v0.15.0.wasm", - "https://plugins.dprint.dev/g-plane/markup_fmt-v0.24.0.wasm", + "https://plugins.dprint.dev/g-plane/malva-v0.15.1.wasm", + "https://plugins.dprint.dev/g-plane/markup_fmt-v0.25.3.wasm", "https://plugins.dprint.dev/g-plane/pretty_yaml-v0.5.1.wasm", "https://plugins.dprint.dev/exec-0.6.0.json@a054130d458f124f9b5c91484833828950723a5af3f8ff2bd1523bd47b83b364" ], diff --git a/package.json b/package.json index f26e13c9..e28dd766 100755 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "oxlint": "^1.31.0", "picomatch": "^4.0.3", "playwright": "^1.57.0", - "prettier": "^3.7.4", + "prettier": "^4.0.0-alpha.13", "prettier-plugin-pkg": "^0.21.2", "prettier-plugin-sh": "^0.18.0", "sass-embedded": "^1.93.3", diff --git a/phpactor.schema.json b/phpactor.schema.json new file mode 100644 index 00000000..1fa4c010 --- /dev/null +++ b/phpactor.schema.json @@ -0,0 +1,492 @@ +{ + "$schema": "https:\/\/json-schema.org\/draft-07\/schema", + "title": "Phpactor Configuration Schema", + "type": "object", + "properties": { + "$schema": { + "description": "Path to JSON schema, which can be used for config autocompletion, use phpactor config:initialize to update", + "default": "" + }, + "console_dumper_default": { + "description": "Name of the \"dumper\" (renderer) to use for some CLI commands", + "default": "indented" + }, + "xdebug_disable": { "description": "If XDebug should be automatically disabled", "default": true }, + "command": { "description": "Internal use only - name of the command which was executed" }, + "core.min_memory_limit": { + "description": "Ensure that PHP has a memory_limit of at least this amount in bytes", + "default": 1610612736 + }, + "class_to_file.project_root": { + "description": "Root path of the project (e.g. where composer.json is)", + "default": "%project_root%" + }, + "class_to_file.brute_force_conversion": { + "description": "If composer not found, fallback to scanning all files (very time consuming depending on project size)", + "default": true + }, + "code_transform.class_new.variants": { + "description": "Variants which should be suggested when class-create is invoked", + "default": [] + }, + "code_transform.template_paths": { + "description": "Paths in which to look for code templates", + "default": ["%project_config%\/templates", "%config%\/templates"] + }, + "code_transform.indentation": { + "description": "Indentation chars to use in code generation and transformation", + "default": " " + }, + "code_transform.refactor.generate_accessor.prefix": { + "description": "Prefix to use for generated accessors", + "default": "" + }, + "code_transform.refactor.generate_accessor.upper_case_first": { + "description": "If the first letter of a generated accessor should be made uppercase", + "default": false + }, + "code_transform.refactor.generate_mutator.prefix": { + "description": "Prefix to use for generated mutators", + "default": "set" + }, + "code_transform.refactor.generate_mutator.upper_case_first": { + "description": "If the first letter of a generated mutator should be made uppercase", + "default": true + }, + "code_transform.refactor.generate_mutator.fluent": { + "description": "If the mutator should be fluent", + "default": false + }, + "code_transform.import_globals": { + "description": "Import functions even if they are in the global namespace", + "default": false + }, + "code_transform.refactor.object_fill.hint": { + "description": "Object fill refactoring: show hint as a comment", + "default": true + }, + "code_transform.refactor.object_fill.named_parameters": { + "description": "Object fill refactoring: use named parameters", + "default": true + }, + "completion_worse.completor.doctrine_annotation.enabled": { + "description": "Enable or disable the ``doctrine_annotation`` completor.\n\nCompletion for annotations provided by the Doctrine annotation library.", + "default": true + }, + "completion_worse.completor.imported_names.enabled": { + "description": "Enable or disable the ``imported_names`` completor.\n\nCompletion for names imported into the current namespace.", + "default": true + }, + "completion_worse.completor.worse_parameter.enabled": { + "description": "Enable or disable the ``worse_parameter`` completor.\n\nCompletion for method or function parameters.", + "default": true + }, + "completion_worse.completor.named_parameter.enabled": { + "description": "Enable or disable the ``named_parameter`` completor.\n\nCompletion for named parameters.", + "default": true + }, + "completion_worse.completor.constructor.enabled": { + "description": "Enable or disable the ``constructor`` completor.\n\nCompletion for constructors.", + "default": true + }, + "completion_worse.completor.class_member.enabled": { + "description": "Enable or disable the ``class_member`` completor.\n\nCompletion for class members.", + "default": true + }, + "completion_worse.completor.scf_class.enabled": { + "description": "Enable or disable the ``scf_class`` completor.\n\nBrute force completion for class names (not recommended).", + "default": true + }, + "completion_worse.completor.local_variable.enabled": { + "description": "Enable or disable the ``local_variable`` completor.\n\nCompletion for local variables.", + "default": true + }, + "completion_worse.completor.subscript.enabled": { + "description": "Enable or disable the ``subscript`` completor.\n\nCompletion for subscript (array access from array shapes).", + "default": true + }, + "completion_worse.completor.declared_function.enabled": { + "description": "Enable or disable the ``declared_function`` completor.\n\nCompletion for functions defined in the Phpactor runtime.", + "default": true + }, + "completion_worse.completor.declared_constant.enabled": { + "description": "Enable or disable the ``declared_constant`` completor.\n\nCompletion for constants defined in the Phpactor runtime.", + "default": true + }, + "completion_worse.completor.declared_class.enabled": { + "description": "Enable or disable the ``declared_class`` completor.\n\nCompletion for classes defined in the Phpactor runtime.", + "default": true + }, + "completion_worse.completor.expression_name_search.enabled": { + "description": "Enable or disable the ``expression_name_search`` completor.\n\nCompletion for class names, constants and functions at expression positions that are located in the index.", + "default": true + }, + "completion_worse.completor.use.enabled": { + "description": "Enable or disable the ``use`` completor.\n\nCompletion for use imports.", + "default": true + }, + "completion_worse.completor.attribute.enabled": { + "description": "Enable or disable the ``attribute`` completor.\n\nCompletion for attribute class names.", + "default": true + }, + "completion_worse.completor.class_like.enabled": { + "description": "Enable or disable the ``class_like`` completor.\n\nCompletion for class like contexts.", + "default": true + }, + "completion_worse.completor.type.enabled": { + "description": "Enable or disable the ``type`` completor.\n\nCompletion for scalar types.", + "default": true + }, + "completion_worse.completor.keyword.enabled": { + "description": "Enable or disable the ``keyword`` completor.\n\nCompletion for keywords (not very accurate).", + "default": true + }, + "completion_worse.completor.docblock.enabled": { + "description": "Enable or disable the ``docblock`` completor.\n\nDocblock completion.", + "default": true + }, + "completion_worse.completor.constant.enabled": { "description": null, "default": false }, + "completion_worse.completor.class.limit": { + "description": "Suggestion limit for the filesystem based SCF class_completor", + "default": 100 + }, + "completion_worse.name_completion_priority": { + "description": "Strategy to use when ordering completion results for classes and functions:\n\n- `proximity`: Classes and functions will be ordered by their proximity to the text document being edited.\n- `none`: No ordering will be applied.", + "default": "proximity" + }, + "completion_worse.snippets": { "description": "Enable or disable completion snippets", "default": true }, + "completion_worse.experimantal": { "description": "Enable experimental functionality", "default": false }, + "completion_worse.debug": { "description": "Include debug info in completion results", "default": false }, + "completion.dedupe": { "description": "If results should be de-duplicated", "default": true }, + "completion.dedupe_match_fqn": { + "description": "If ``completion.dedupe``, consider the class FQN in addition to the completion suggestion", + "default": true + }, + "completion.limit": { "description": "Sets a limit on the number of completion suggestions for any request" }, + "completion.label_formatter": { + "description": "Definition of how to format entries in the completion list", + "default": "helpful", + "enum": ["helpful", "fqn"] + }, + "navigator.destinations": { "description": null, "default": [] }, + "navigator.autocreate": { "description": null, "default": [] }, + "rpc.store_replay": { "description": "Should replays be stored?", "default": false }, + "rpc.replay_path": { "description": "Path where the replays should be stored", "default": "%cache%\/replay.json" }, + "source_code_filesystem.project_root": { "description": null, "default": "%project_root%" }, + "language_server_code_transform.import_globals": { + "description": "Show hints for non-imported global classes and functions", + "default": false + }, + "worse_reflection.enable_cache": { "description": "If reflection caching should be enabled", "default": true }, + "worse_reflection.cache_lifetime": { + "description": "If caching is enabled, limit the amount of time a cache entry can stay alive", + "default": 1 + }, + "worse_reflection.enable_context_location": { + "description": "If source code is passed to a ``Reflector`` then temporarily make it available as a\nsource location. Note this should NOT be enabled if the source code can be\nlocated in another (e.g. when running a Language Server)", + "default": true + }, + "worse_reflection.cache_dir": { + "description": "Cache directory for stubs", + "default": "%cache%\/worse-reflection" + }, + "worse_reflection.stub_dir": { + "description": "Location of the core PHP stubs - these will be scanned and cached on the first request", + "default": "%application_root%\/vendor\/jetbrains\/phpstorm-stubs" + }, + "worse_reflection.diagnostics.undefined_variable.suggestion_levenshtein_disatance": { + "description": "Levenshtein distance to use when suggesting corrections for variable names", + "type": ["integer"], + "default": 4 + }, + "file_path_resolver.project_root": { "description": null, "default": "\/opt\/phpactor" }, + "file_path_resolver.app_name": { "description": null, "default": "phpactor" }, + "file_path_resolver.application_root": { "description": null }, + "file_path_resolver.enable_cache": { "description": null, "default": true }, + "file_path_resolver.enable_logging": { "description": null, "default": true }, + "logging.enabled": { "description": null, "type": ["boolean"], "default": false }, + "logging.fingers_crossed": { "description": null, "type": ["boolean"], "default": false }, + "logging.path": { "description": null, "type": ["string"], "default": "application.log" }, + "logging.level": { + "description": null, + "type": ["string"], + "default": "warning", + "enum": ["emergency", "alert", "critical", "error", "warning", "notice", "info", "debug"] + }, + "logger.name": { "description": null, "type": ["string"], "default": "logger" }, + "logging.formatter": { "description": null }, + "composer.enable": { + "description": "Include of the projects autoloader to facilitate class location. Note that when including an autoloader code _may_ be executed. This option may be disabled when using the indexer", + "default": true + }, + "composer.autoloader_path": { + "description": "Path to project's autoloader, can be an array", + "default": "%project_root%\/vendor\/autoload.php" + }, + "composer.autoload_deregister": { + "description": "Immediately de-register the autoloader once it has been included (prevent conflicts with Phpactor's autoloader). Some platforms may require this to be disabled", + "default": true + }, + "composer.class_maps_only": { + "description": "Register the composer class maps only, do not register the autoloader - RECOMMENDED", + "default": true + }, + "console.verbosity": { "description": "Verbosity level", "default": 32, "enum": [16, 32, 64, 128, 256] }, + "console.decorated": { + "description": "Whether to decorate messages (null for auto-guessing)", + "enum": [true, false, null] + }, + "php.version": { + "description": "Consider this value to be the project\\'s version of PHP (e.g. `7.4`). If omitted\nit will check `composer.json` (by the configured platform then the PHP requirement) before\nfalling back to the PHP version of the current process." + }, + "language_server.catch_errors": { "description": null, "default": true }, + "language_server.enable_workspace": { + "description": "If workspace management \/ text synchronization should be enabled (this isn't required for some language server implementations, e.g. static analyzers)", + "default": true + }, + "language_server.session_parameters": { + "description": "Phpactor parameters (config) that apply only to the language server session", + "default": [] + }, + "language_server.method_alias_map": { + "description": "Allow method names to be re-mapped. Useful for maintaining backwards compatibility", + "default": [] + }, + "language_server.diagnostic_sleep_time": { + "description": "Amount of time to wait before analyzing the code again for diagnostics", + "default": 1000 + }, + "language_server.diagnostics_on_update": { + "description": "Perform diagnostics when the text document is updated", + "default": true + }, + "language_server.diagnostics_on_save": { + "description": "Perform diagnostics when the text document is saved", + "default": true + }, + "language_server.diagnostics_on_open": { + "description": "Perform diagnostics when opening a text document", + "default": true + }, + "language_server.diagnostic_providers": { + "description": "Specify which diagnostic providers should be active (default to all)" + }, + "language_server.diagnostic_outsource": { + "description": "If applicable diagnostics should be \"outsourced\" to a different process", + "default": true + }, + "language_server.diagnostic_exclude_paths": { + "description": "List of paths to exclude from diagnostics, e.g. `vendor\/**\/*`", + "default": [] + }, + "language_server.file_events": { "description": "Register to receive file events", "default": true }, + "language_server.file_event_globs": { "description": null, "default": ["**\/*.php"] }, + "language_server.profile": { "description": "Logs timing information for incoming LSP requests", "default": false }, + "language_server.trace": { + "description": "Log incoming and outgoing messages (needs log formatter to be set to ``json``)", + "default": false + }, + "language_server.shutdown_grace_period": { + "description": "Amount of time (in milliseconds) to wait before responding to a shutdown notification", + "default": 200 + }, + "language_server.phpactor_bin": { + "description": "Internal use only - name path to Phpactor binary", + "default": "\/opt\/phpactor\/lib\/Extension\/LanguageServer\/..\/..\/..\/bin\/phpactor" + }, + "language_server.self_destruct_timeout": { + "description": "Wait this amount of time (in milliseconds) after a shutdown request before self-destructing", + "default": 2500 + }, + "language_server.diagnostic_outsource_timeout": { + "description": "Kill the diagnostics process if it outlives this timeout", + "default": 5 + }, + "language_server_completion.trim_leading_dollar": { + "description": "If the leading dollar should be trimmed for variable completion suggestions", + "default": false + }, + "language_server_reference_reference_finder.reference_timeout": { + "description": "Stop searching for references after this time (in seconds) has expired", + "default": 60 + }, + "language_server_worse_reflection.workspace_index.update_interval": { + "description": "Minimum interval to update the workspace index as documents are updated (in milliseconds)", + "default": 100 + }, + "language_server_worse_reflection.inlay_hints.enable": { + "description": "Enable inlay hints (experimental)", + "default": false + }, + "language_server_worse_reflection.inlay_hints.types": { + "description": "Show inlay type hints for variables", + "default": false + }, + "language_server_worse_reflection.inlay_hints.params": { + "description": "Show inlay hints for parameters", + "default": true + }, + "language_server_worse_reflection.diagnostics.enable": { "description": "Enable diagnostics", "default": true }, + "language_server_indexer.workspace_symbol_search_limit": { "description": null, "default": 250 }, + "language_server_indexer.reindex_timeout": { + "description": "Unconditionally reindex modified files every N seconds", + "default": 300 + }, + "language_server_code_transform.import_name.report_non_existing_names": { + "description": "Show an error if a diagnostic name cannot be resolved - can produce false positives", + "default": true + }, + "language_server_configuration.auto_config": { + "description": "Prompt to enable extensions which apply to your project on language server start", + "type": ["boolean"], + "default": true + }, + "indexer.enabled_watchers": { + "description": "List of allowed watchers. The first watcher that supports the current system will be used", + "type": ["object"], + "default": ["inotify", "watchman", "find", "php"] + }, + "indexer.index_path": { + "description": "Path where the index should be saved", + "type": ["string"], + "default": "%cache%\/index\/%project_id%" + }, + "indexer.include_patterns": { + "description": "Glob patterns to include while indexing", + "type": ["object"], + "default": ["\/**\/*.php", "\/**\/*.phar"] + }, + "indexer.exclude_patterns": { + "description": "Glob patterns to exclude while indexing", + "type": ["object"], + "default": ["\/vendor\/**\/Tests\/**\/*", "\/vendor\/**\/tests\/**\/*", "\/vendor\/composer\/**\/*"] + }, + "indexer.stub_paths": { + "description": "Paths to external folders to index. They will be indexed only once, if you want to take any changes into account you will have to reindex your project manually.", + "type": ["object"], + "default": [] + }, + "indexer.poll_time": { + "description": "For polling indexers only: the time, in milliseconds, between polls (e.g. filesystem scans)", + "type": ["integer"], + "default": 5000 + }, + "indexer.buffer_time": { + "description": "For real-time indexers only: the time, in milliseconds, to buffer the results", + "type": ["integer"], + "default": 500 + }, + "indexer.follow_symlinks": { + "description": "To allow indexer to follow symlinks", + "type": ["boolean"], + "default": false + }, + "indexer.project_root": { + "description": "The root path to use for scanning the index", + "type": ["string"], + "default": "%project_root%" + }, + "indexer.reference_finder.deep": { + "description": "Recurse over class implementations to resolve all references", + "type": ["boolean"], + "default": true + }, + "indexer.implementation_finder.deep": { + "description": "Recurse over class implementations to resolve all class implementations (not just the classes directly implementing the subject)", + "type": ["boolean"], + "default": true + }, + "indexer.supported_extensions": { + "description": "File extensions (e.g. `php`) for files that should be indexed", + "type": ["object"], + "default": ["php", "phar"] + }, + "object_renderer.template_paths.markdown": { + "description": "Paths in which to look for templates for hover information.", + "default": ["%project_config%\/templates\/markdown", "%config%\/templates\/markdown"] + }, + "language_server_phpstan.bin": { + "description": "Path to the PHPStan executable", + "default": "%project_root%\/vendor\/bin\/phpstan" + }, + "language_server_phpstan.level": { "description": "Override the PHPStan level" }, + "language_server_phpstan.config": { "description": "Override the PHPStan configuration file" }, + "language_server_phpstan.mem_limit": { "description": "Override the PHPStan memory limit" }, + "language_server_psalm.bin": { + "description": "Path to psalm if different from vendor\/bin\/psalm", + "type": ["string"], + "default": "%project_root%\/vendor\/bin\/psalm" + }, + "language_server_psalm.show_info": { + "description": "If infos from psalm should be displayed", + "type": ["boolean"], + "default": true + }, + "language_server_psalm.use_cache": { + "description": "If the Psalm cache should be used (see the `--no-cache` option)", + "type": ["boolean"], + "default": true + }, + "language_server_psalm.error_level": { + "description": "Override level at which Psalm should report errors (lower => more errors)" + }, + "language_server_psalm.threads": { + "description": "Set the number of threads Psalm should use. Warning: NULL will use as many as possible and may crash your computer", + "type": ["integer"], + "default": 1 + }, + "language_server_psalm.timeout": { + "description": "Kill the psalm process after this number of seconds", + "type": ["integer"], + "default": 15 + }, + "language_server_php_cs_fixer.bin": { + "description": "Path to the php-cs-fixer executable", + "default": "%project_root%\/vendor\/bin\/php-cs-fixer" + }, + "language_server_php_cs_fixer.env": { + "description": "Environment for PHP CS Fixer (e.g. to set PHP_CS_FIXER_IGNORE_ENV)", + "default": { "XDEBUG_MODE": "off", "PHP_CS_FIXER_IGNORE_ENV": true } + }, + "language_server_php_cs_fixer.show_diagnostics": { + "description": "Whether PHP CS Fixer diagnostics are shown", + "default": true + }, + "language_server_php_cs_fixer.config": { + "description": "Set custom PHP CS config path. Ex., %project_root%\/.php-cs-fixer.php" + }, + "php_code_sniffer.bin": { + "description": "Path to the phpcs executable", + "default": "%project_root%\/vendor\/bin\/phpcs" + }, + "php_code_sniffer.env": { + "description": "Environment for PHP_CodeSniffer (e.g. to set XDEBUG_MODE)", + "default": { "XDEBUG_MODE": "off" } + }, + "php_code_sniffer.show_diagnostics": { + "description": "Whether PHP_CodeSniffer diagnostics are shown", + "default": true + }, + "php_code_sniffer.args": { "description": "Additional arguments to pass to the PHPCS process", "default": [] }, + "php_code_sniffer.cwd": { "description": "Working directory for PHPCS" }, + "behat.config_path": { + "description": "Path to the main behat.yml (including the filename behat.yml)", + "default": "%project_root%\/behat.yml" + }, + "behat.symfony.di_xml_path": { + "description": "If using Symfony, set this path to the XML container dump to find contexts which are defined as services" + }, + "symfony.xml_path": { + "description": "Path to the Symfony container XML dump file", + "default": "%project_root%\/var\/cache\/dev\/App_KernelDevDebugContainer.xml" + }, + "completion_worse.completor.symfony.enabled": { + "description": "Enable\/disable the Symfony completor - depends on Symfony extension being enabled", + "default": true + }, + "public_services_only": { + "description": "Only consider public services when providing analysis for the service locator", + "default": false + } + } +} diff --git a/rector.php b/rector.php new file mode 100644 index 00000000..6dae3866 --- /dev/null +++ b/rector.php @@ -0,0 +1,24 @@ +withPaths([__DIR__ . '/web/app/themes/haiku-atelier-2024']) + ->withSkip([__DIR__ . '/vendor', __DIR__ . '/node_modules']) + ->withPhpSets(php85: true) + ->withCodeQualityLevel(10) + ->withCodingStyleLevel(10) + ->withDeadCodeLevel(10) + ->withTypeCoverageDocblockLevel(10) + ->withTypeCoverageLevel(10) + ->withImportNames( + importDocBlockNames: true, + importNames: true, + importShortClasses: true, + removeUnusedImports: true, + ) + ->withPreparedSets( + carbon: true, + instanceOf: true, + privatization: true, + ); diff --git a/web/app/db.php b/web/app/db.php deleted file mode 120000 index befa55ec..00000000 --- a/web/app/db.php +++ /dev/null @@ -1 +0,0 @@ -/var/www/wordpress/web/app/plugins/query-monitor/wp-content/db.php \ No newline at end of file diff --git a/web/app/themes/haiku-atelier-2024/front-page.php b/web/app/themes/haiku-atelier-2024/front-page.php index e93290ae..fca0104b 100755 --- a/web/app/themes/haiku-atelier-2024/front-page.php +++ b/web/app/themes/haiku-atelier-2024/front-page.php @@ -1,11 +1,9 @@ -get_type(); +// $donnees_produit = recupere_informations_produit_page_produit($product); +$donnees_produit = Product::new($product); -/** @var array $variations_produit Un tableau des informations d'affichage de chaque Variation du Produit */ +// Un tableau des informations d'affichage de chaque Variation du Produit $variations_produit = pipe( - // Récupère les IDs des Enfants (Variations) - wc_get_product()->get_children(), + $product->get_children(), // Récupère les Variations - fn($enfants) => array_map( + static fn(/** @var list */ $enfants): array => array_map( callback: wc_get_product(...), array: $enfants, ), - // Ne conserve que les Informations souhaitées - fn($variations) => array_map( - callback: fn($variation) => [ + // Ne conserve que les Informations souhaitées. + static fn(/** @var list */ $variations): array => array_map( + callback: static fn(WC_Product $variation): array => [ 'id' => $variation->get_id(), - // Ne récupère que le titre de l'Attribut unique de la Variation + // Ne récupère que le titre de l'Attribut unique de la Variation. 'titre' => match (true) { '' !== $variation->get_attribute('pa_side') => $variation->get_attribute('pa_side'), '' !== $variation->get_attribute('pa_stone') => $variation->get_attribute('pa_stone'), @@ -60,14 +57,14 @@ $variations_produit = pipe( $prix_maximal = collect($variations_produit)->max('prix'); $produits_meme_collection = array_map( - callback: 'recupere_informations_produit_page_produit', - array: recupere_produits_meme_collection($donnees_produit['collection'])($donnees_produit['id']), + callback: recupere_informations_produit_page_produit(...), + array: recupere_produits_meme_collection($donnees_produit->collection)($donnees_produit->id), ); -$contexte['produit'] = $donnees_produit; -$contexte['prix_maximal'] = $prix_maximal; -$contexte['variations_produit'] = $variations_produit; -$contexte['produits_meme_collection'] = $produits_meme_collection; +$context['produit'] = $donnees_produit; +$context['prix_maximal'] = $prix_maximal; +$context['variations_produit'] = $variations_produit; +$context['produits_meme_collection'] = $produits_meme_collection; /** * Charge les Scripts nécessaires pour la page Produit. @@ -89,11 +86,15 @@ function charge_scripts_page_produit(): void { add_action('wp_enqueue_scripts', 'charge_scripts_page_produit'); -// $lal = wp_json_encode($contexte); -// echo ""; +$lal = wp_json_encode($context); +echo ""; + +$lol = wc_get_product()->get_children(); +$lol = wp_json_encode($lol); +echo ""; // Rendu Timber::render( - filenames: $modeles, - data: $contexte, + filenames: $templates, + data: $context, ); diff --git a/web/app/themes/haiku-atelier-2024/src/inc/Data/Attribute.php b/web/app/themes/haiku-atelier-2024/src/inc/Data/Attribute.php new file mode 100644 index 00000000..725986b3 --- /dev/null +++ b/web/app/themes/haiku-atelier-2024/src/inc/Data/Attribute.php @@ -0,0 +1,33 @@ + $options + */ + public function __construct( + public string $name, + public string $slug, + public array $options, + ) {} + + public static function new(WC_Product_Attribute $attribute): self { + $name = wc_attribute_label($attribute->get_name()); + $slug = $attribute->get_name(); + /** @var list */ + $terms = $attribute->get_terms() ?? []; + /** @var list */ + $options = Arr::map($terms, static fn(WP_Term $term): AttributeOption => AttributeOption::new($term)); + + return new self( + name: $name, + slug: $slug, + options: $options, + ); + } +} diff --git a/web/app/themes/haiku-atelier-2024/src/inc/Data/AttributeOption.php b/web/app/themes/haiku-atelier-2024/src/inc/Data/AttributeOption.php new file mode 100644 index 00000000..3189eb28 --- /dev/null +++ b/web/app/themes/haiku-atelier-2024/src/inc/Data/AttributeOption.php @@ -0,0 +1,25 @@ +term_taxonomy_id; + $name = $term->name; + $slug = $term->slug; + + return new self( + id: $id, + name: $name, + slug: $slug, + ); + } +} diff --git a/web/app/themes/haiku-atelier-2024/src/inc/Data/Product.php b/web/app/themes/haiku-atelier-2024/src/inc/Data/Product.php new file mode 100644 index 00000000..1575eb8d --- /dev/null +++ b/web/app/themes/haiku-atelier-2024/src/inc/Data/Product.php @@ -0,0 +1,103 @@ + $attributes + * @param list $left_column_photos + * @param list $right_column_photos + * @param list $variation_ids + */ + private function __construct( + public array $attributes, + public string $category, + public string $collection, + public string $details, + public int $id, + public string $name, + public string $price, + public array $left_column_photos, + public array $right_column_photos, + public string $default_photo, + public string $hover_photo, + public string $slug, + public int $stock, + public array $variation_ids, + public string $url, + ) {} + + /** + * @return list + */ + public static function get_attributes_for_product(WC_Product $product): array { + /** @var list */ + return $product->get_attributes() + |> (static fn($attributes) => Arr::map($attributes, static fn(WC_Product_Attribute $attribute): Attribute => Attribute::new( + $attribute, + ))); + } + + public static function new(WC_Product $product): self { + /** @var list */ + $attributes = self::get_attributes_for_product($product); + /** @var lowercase-string */ + $category = $product->get_id() |> wc_get_product_category_list(...) |> strtolower(...); + + /** @var Option\Option> */ + $collection = Term::get_terms($product->get_id(), 'collection'); + /** @var Option\Option */ + $collection = $collection->andThen( + static fn(array $terms): Option\Option => head($terms) |> from_nullable(...), + ); + /** @var Option\Option */ + $collection = $collection->map(static fn(WP_Term $term) => $term->slug); + /** @var string */ + $collection = $collection->unwrapOr(''); + + /** @var string */ + $details = $product->get_description() |> wpautop(...); + $id = $product->get_id(); + $name = $product->get_name(); + $price = $product->get_price(); + /** @var list */ + $left_column_photos = HaikuProduct::get_left_column_photos($id); + /** @var list */ + $right_column_photos = HaikuProduct::get_right_column_photos($id); + $default_photo = $left_column_photos[0] ?? genere_balise_img_multiformats('-1'); + $hover_photo = $right_column_photos[0] ?? genere_balise_img_multiformats('-1', true); + $slug = $product->get_slug(); + $stock = $product->get_stock_quantity() ?? 1; + /** @var list */ + $variation_ids = $product->get_children(); + $url = $product->get_permalink(); + + return new self( + attributes: $attributes, + category: $category, + collection: $collection, + details: $details, + id: $id, + name: $name, + price: $price, + left_column_photos: $left_column_photos, + right_column_photos: $right_column_photos, + default_photo: $default_photo, + hover_photo: $hover_photo, + slug: $slug, + stock: $stock, + variation_ids: $variation_ids, + url: $url, + ); + } +} diff --git a/web/app/themes/haiku-atelier-2024/src/inc/TraitementInformations.php b/web/app/themes/haiku-atelier-2024/src/inc/TraitementInformations.php index d9d706d6..f256591f 100755 --- a/web/app/themes/haiku-atelier-2024/src/inc/TraitementInformations.php +++ b/web/app/themes/haiku-atelier-2024/src/inc/TraitementInformations.php @@ -6,25 +6,30 @@ declare(strict_types=1); use function Crell\fp\pipe; +use HaikuAtelier\Data\Attribute; + +use HaikuAtelier\Data\Product; // Page Shop /** * TODO. * - * @param int $id TODO - * @param bool $lazy TODO + * @param string $id TODO + * @param bool $lazy TODO * * @return string TODO */ -function genere_balise_img_multiformats($id, $lazy = false) { +function genere_balise_img_multiformats(string $id, bool $lazy = false): string { + $int_id = (int) $id; + if (-1 === $id) { return ''; } - $url = wp_get_attachment_image_url($id, 'full'); - $chemin = realpath(get_attached_file($id)) ?: realpath(get_attached_file($id)); - $alt = get_post_meta($id, '_wp_attachment_image_alt', true); + $url = wp_get_attachment_image_url($int_id, 'full'); + $chemin = realpath(get_attached_file($int_id)) ?: realpath(get_attached_file($int_id)); + $alt = get_post_meta($int_id, '_wp_attachment_image_alt', true); $dimensions = $chemin ? getimagesize($chemin) : ['', '']; $avif = $chemin ? realpath(pathinfo($chemin)['dirname'] . '/' . pathinfo($chemin)['filename'] . '.avif') : false; @@ -34,27 +39,26 @@ function genere_balise_img_multiformats($id, $lazy = false) { // Génère un tableau avec les différents formats valides $formats = pipe( [$avif, $jxl, $webp], - fn($tableau) => array_filter( + static fn($tableau): array => array_filter( array: $tableau, - callback: fn($chemin_format) => false !== $chemin_format, + callback: static fn($chemin_format): bool => false !== $chemin_format, ), - fn($tableau) => array_map( + static fn($tableau): array => array_map( array: $tableau, - callback: fn($chemin_format) => [ - 'format' => pathinfo($chemin_format)['extension'], + callback: static fn($chemin_format): array => [ + 'format' => pathinfo((string) $chemin_format)['extension'], 'taille' => filesize($chemin_format), - 'url' => - pathinfo($url)['dirname'] + 'url' => pathinfo($url)['dirname'] . '/' . pathinfo($url)['filename'] . '.' - . pathinfo($chemin_format)['extension'], + . pathinfo((string) $chemin_format)['extension'], ], ), ); usort( array: $formats, - callback: fn($a, $b) => $a['taille'] <=> $b['taille'], + callback: static fn($a, $b): int => $a['taille'] <=> $b['taille'], ); // Construis les balises avec les formats valides @@ -84,47 +88,36 @@ function genere_balise_img_multiformats($id, $lazy = false) { /** * TODO. - * - * @param WC_Product $a - * @param WC_Product $b - * - * @return int */ -function tri_variations_par_prix_descendant($a, $b) { - if ($a->get_price() === $b->get_price()) { - return 0; - } - - return $a->get_price() < $b->get_price() ? 1 : -1; +function tri_variations_par_prix_descendant(WC_Product $a, WC_Product $b): int { + return $b->get_price() <=> $a->get_price(); } /** * Récupère les informations utilisées pour la grille des Produits et les retourne sous forme * de tableau associatif. * - * @param WC_Product $produit - * * @return mixed un tableau avec uniquement les informations pour la Grille de Produits */ -function recupere_informations_produit_shop($produit) { +function recupere_informations_produit_shop(WC_Product $produit): mixed { /** @var int $prix_maximal Le prix maximal du Produit. */ $prix_maximal = pipe( // Récupère les Variations $produit->get_children(), // Récupère les informations de chaque Variation - fn($enfants) => array_map( + static fn($enfants): array => array_map( callback: wc_get_product(...), array: $enfants, ), // Trie les Variations par prix descendant - fn($variations) => array_map( - callback: fn($variation) => $variation->get_price(), + static fn($variations): array => array_map( + callback: static fn($variation) => $variation->get_price(), array: $variations, ), // Récupère le Prix de la Variation la plus chère - fn($prix) => collect($prix)->max(), + static fn($prix) => collect($prix)->max(), // Récupère le Prix pour la Variation la plus chère OU le prix du Produit simple - fn($prix_variation_maximale) => $prix_variation_maximale ?? $produit->get_price(), + static fn($prix_variation_maximale) => $prix_variation_maximale ?? $produit->get_price(), ); // TEMP: Cas de la Carte Cadeau où aucun prix ne doit être affiché. Idéalement utiliser un système d'étiquettes pour ces cas là. @@ -158,49 +151,50 @@ function recupere_informations_produit_shop($produit) { /** * Retourne un tableau associatif des informations affichées sur la page Produit depuis les données brutes d'un Produit. - * - * @param WC_Product $donnees_produit */ -function recupere_informations_produit_page_produit($donnees_produit): mixed { +function recupere_informations_produit_page_produit(WC_Product $product): mixed { + /** @var list */ + $attributs = Product::get_attributes_for_product($product); + return [ // Attributs du Produit - 'attributs' => wc_get_product()->get_attributes(), + 'attributs' => $attributs, // Catégorie du Produit - 'categorie' => pipe($donnees_produit->get_id(), wc_get_product_category_list(...), strtolower(...)), + 'categorie' => pipe($product->get_id(), wc_get_product_category_list(...), strtolower(...)), // Slug de la Collection - Peut ne pas avoir été défini - 'collection' => get_the_terms($donnees_produit->get_id(), 'collection')[0]->slug ?? '', + 'collection' => get_the_terms($product->get_id(), 'collection')[0]->slug ?? '', // Détails (Description) du Produit - 'details' => wpautop($donnees_produit->get_description()), + 'details' => wpautop($product->get_description()), // Identifiant du Produit - 'id' => $donnees_produit->get_id(), + 'id' => $product->get_id(), // Nom affiché du Produit - 'nom' => $donnees_produit->get_name(), + 'nom' => $product->get_name(), // Prix affiché du Produit - 'prix' => $donnees_produit->get_price(), + 'prix' => $product->get_price(), 'photos_colonne_gauche' => array_map( - callback: 'genere_balise_img_multiformats', - array: get_post_meta($post_id = $donnees_produit->get_id(), $key = '_photos_colonne_gauche|||0|value'), + callback: genere_balise_img_multiformats(...), + array: get_post_meta($post_id = $product->get_id(), $key = '_photos_colonne_gauche|||0|value'), ), 'photos_colonne_droite' => array_map( - callback: 'genere_balise_img_multiformats', + callback: genere_balise_img_multiformats(...), array: carbon_get_the_post_meta('photos_colonne_droite'), ), 'photo_repos' => genere_balise_img_multiformats( - get_post_meta($post_id = $donnees_produit->get_id(), $key = '_photos_colonne_gauche|||0|value')[0] ?? -1, + get_post_meta($post_id = $product->get_id(), $key = '_photos_colonne_gauche|||0|value')[0] ?? -1, false, ), 'photo_survol' => genere_balise_img_multiformats( - get_post_meta($post_id = $donnees_produit->get_id(), $key = '_photos_colonne_droite|||0|value')[0] ?? -1, + get_post_meta($post_id = $product->get_id(), $key = '_photos_colonne_droite|||0|value')[0] ?? -1, true, ), // Slug du Produit - 'slug' => $donnees_produit->get_slug(), + 'slug' => $product->get_slug(), // Quantité de Produit en stock - 'stock' => $donnees_produit->get_stock_quantity() ?? 1, + 'stock' => $product->get_stock_quantity() ?? 1, // Variations du Produit - 'variations_ids' => $donnees_produit->get_children(), + 'variations_ids' => $product->get_children(), // URL du Produit - 'url' => $donnees_produit->get_permalink(), + 'url' => $product->get_permalink(), ]; } @@ -209,14 +203,10 @@ function recupere_informations_produit_page_produit($donnees_produit): mixed { * collection) et les retourne sous forme de tableau associatif. * * Pour faciliter l'usage avec `array_map`, utilise une fonction avec curryfication. - * - * @param string $slug_collection - * - * @return mixed */ -function recupere_produits_meme_collection($slug_collection) { +function recupere_produits_meme_collection(string $slug_collection): mixed { // @param int $id_produit - return fn($id_produit) => wc_get_products([ + return static fn($id_produit) => wc_get_products([ 'exclude' => [$id_produit], 'limit' => 4, 'order' => 'DESC', @@ -228,12 +218,7 @@ function recupere_produits_meme_collection($slug_collection) { // Page Panier -/** - * @param mixed $attributs_produit - * - * @return mixed - */ -function recupere_et_formate_attributs_produit($attributs_produit) { +function recupere_et_formate_attributs_produit(mixed $attributs_produit): mixed { return [ 'taille' => ['nom' => 'Size', 'valeur' => $attributs_produit['pa_size'] ?? false], 'pierre' => ['nom' => 'Stone', 'valeur' => $attributs_produit['pa_stone'] ?? false], diff --git a/web/app/themes/haiku-atelier-2024/src/inc/WP/HaikuProduct.php b/web/app/themes/haiku-atelier-2024/src/inc/WP/HaikuProduct.php new file mode 100644 index 00000000..2ebd384f --- /dev/null +++ b/web/app/themes/haiku-atelier-2024/src/inc/WP/HaikuProduct.php @@ -0,0 +1,35 @@ + + */ + public static function get_left_column_photos(int $post_id): array { + /** @var list */ + return Post::get_post_meta_array($post_id, '_photos_colonne_gauche|||0|value')->unwrapOr([]) + |> (static fn(array $meta) => Arr::where($meta, static fn($meta): bool => is_string($meta))) + |> (static fn(array $array) => Arr::map($array, genere_balise_img_multiformats(...))); + } + + /** + * @return list + */ + public static function get_right_column_photos(int $post_id): array { + $meta = carbon_get_post_meta($post_id, 'photos_colonne_droite'); + + if (is_array($meta)) { + /** @var list */ + return Arr::where($meta, static fn($meta): bool => is_string($meta)) + |> (static fn(array $array) => Arr::map($array, genere_balise_img_multiformats(...))); + } + + return []; + } +} diff --git a/web/app/themes/haiku-atelier-2024/src/inc/WP/Post.php b/web/app/themes/haiku-atelier-2024/src/inc/WP/Post.php new file mode 100644 index 00000000..78f12116 --- /dev/null +++ b/web/app/themes/haiku-atelier-2024/src/inc/WP/Post.php @@ -0,0 +1,55 @@ + + */ + public static function get_post_meta(int $post_id, string $key): Option\Option { + /** @var false|mixed|string */ + $value = get_post_meta($post_id, $key, true); + + if ($value === false) { + return none(); + } + + return some($value); + } + + /** + * @return Option\Option> + */ + public static function get_post_meta_array(int $post_id, string $key): Option\Option { + /** @var array|false */ + $value = get_post_meta($post_id, $key, false); + + if (is_array($value)) { + return some($value); + } + + return none(); + } + + /** + * @return Option\Option> + */ + public static function get_terms(int $post_id, string $taxonomy_name): Option\Option { + /** @var false|list|WP_Error */ + $terms = get_the_terms($post_id, $taxonomy_name); + + if (is_array($terms)) { + return some($terms); + } + + return none(); + } +} diff --git a/web/app/themes/haiku-atelier-2024/src/inc/WP/Term.php b/web/app/themes/haiku-atelier-2024/src/inc/WP/Term.php new file mode 100644 index 00000000..2a9d014e --- /dev/null +++ b/web/app/themes/haiku-atelier-2024/src/inc/WP/Term.php @@ -0,0 +1,26 @@ +> + */ + public static function get_terms(int $post_id, string $taxonomy_name): Option\Option { + $terms = get_the_terms($post_id, $taxonomy_name); + + if (is_array($terms)) { + /** @var Option\Option> */ + return some($terms); + } + + return none(); + } +} diff --git a/web/app/themes/haiku-atelier-2024/src/sass/layouts/_informations-produit.scss b/web/app/themes/haiku-atelier-2024/src/sass/layouts/_informations-produit.scss index 5231ae8a..327ba3ef 100755 --- a/web/app/themes/haiku-atelier-2024/src/sass/layouts/_informations-produit.scss +++ b/web/app/themes/haiku-atelier-2024/src/sass/layouts/_informations-produit.scss @@ -59,7 +59,10 @@ font-size: var(--resume-police-nom-taille); } - &__selection-variation { + &__attribut-variation { + display: flex; + flex-flow: row wrap; + gap: var(--espace-m) var(--espace-l); font-size: var(--resume-police-selecteur-taille); font-weight: var(--resume-police-selecteur-graisse); text-transform: lowercase; @@ -98,7 +101,7 @@ pointer-events: none; content: " "; position: absolute; - top: 10px; + top: 7px; right: 0.4rem; display: inline-block; width: 0.9rem; @@ -148,7 +151,7 @@ } @media (width <= 500px) { - .selecteur-produit__selection-variation { + .selecteur-produit__selection-variation-attribut { flex-flow: column nowrap; row-gap: var(--espace-inter-colonne); diff --git a/web/app/themes/haiku-atelier-2024/src/scripts/scripts-page-produit.ts b/web/app/themes/haiku-atelier-2024/src/scripts/scripts-page-produit.ts index 54a938b5..e718c9ea 100755 --- a/web/app/themes/haiku-atelier-2024/src/scripts/scripts-page-produit.ts +++ b/web/app/themes/haiku-atelier-2024/src/scripts/scripts-page-produit.ts @@ -4,12 +4,13 @@ import { pipe } from "@mobily/ts-belt"; import { forEach as arrayForEach } from "@mobily/ts-belt/Array"; import { get as dictGet } from "@mobily/ts-belt/Dict"; import { tap as optionTap } from "@mobily/ts-belt/Option"; +import { pipe as epipe } from "effect"; import { EitherAsync, Maybe } from "purify-ts"; import { match, P } from "ts-pattern"; import { type AnySchema, ValiError } from "valibot"; import type { WCStoreCart } from "./lib/types/api/cart"; -import type { WCStoreCartAddItemArgs } from "./lib/types/api/cart-add-item.ts"; +import type { WCStoreCartAddItemArgs, WCStoreCartAddItemArgsItems } from "./lib/types/api/cart-add-item.ts"; import type { FetchErrors } from "./lib/types/reseau.ts"; import { ROUTE_API_AJOUTE_ARTICLE_PANIER } from "./constantes/api.ts"; @@ -23,8 +24,8 @@ import { DOM_BOUTON_AJOUT_PANIER, DOM_BOUTONS_ACCORDEON, DOM_CONTENUS_ACCORDEON, - DOM_PRIX_PRODUIT, DOM_DOM_QUANTITE, + DOM_PRIX_PRODUIT, } from "./constantes/dom.ts"; import { lanceAnimationCycleLoading } from "./lib/animations.ts"; import { mustGetEleInDocument, mustGetElesInDocument, recupereElementDocumentEither } from "./lib/dom.ts"; @@ -64,8 +65,9 @@ const E = { BOUTON_AJOUT_PANIER: mustGetEleInDocument(DOM_BOUTON_AJOUT_PANIER), BOUTONS_ACCORDEON: mustGetElesInDocument(DOM_BOUTONS_ACCORDEON), CONTENUS_ACCORDEON: mustGetElesInDocument(DOM_CONTENUS_ACCORDEON), - PRIX_PRODUIT: mustGetEleInDocument(DOM_PRIX_PRODUIT), DOM_VARIATION: recupereElementDocumentEither(DOM_DOM_QUANTITE), + PRIX_PRODUIT: mustGetEleInDocument(DOM_PRIX_PRODUIT), + VARIATION_CHOICE_FORM: mustGetEleInDocument("#variation-choice"), }; const gereAccordeonDetailsProduit = (): void => { @@ -119,17 +121,39 @@ const gereAccordeonDetailsProduit = (): void => { }); }) ); - E.BOUTON_AJOUT_PANIER.addEventListener("click", (): void => ajouteProduitAuPanier()); + E.BOUTON_AJOUT_PANIER.addEventListener("click", (event: MouseEvent): void => ajouteProduitAuPanier(event)); }; -const ajouteProduitAuPanier = (): void => { +const getAttributeValuesFromDom = () => { + const selectElements = epipe( + document.querySelectorAll(".selecteur-produit select"), + Array.from, + ); + if (selectElements.length === 0) return []; + + const attributeValues = selectElements.map(select => { + return { + attribute: select.id.replace("selecteur-attribut-", ""), + value: select.value, + } satisfies WCStoreCartAddItemArgsItems; + }); + + return attributeValues; +}; + +const ajouteProduitAuPanier = (event: MouseEvent): void => { + event.preventDefault(); + console.debug("getAttributeValuesFromDom", getAttributeValuesFromDom()); + // Construis les arguments de la requête au backend const argsRequete: WCStoreCartAddItemArgs = { - id: E.DOM_VARIATION - .map((selecteur: HTMLSelectElement): number => Number(selecteur.value)) - // Récupère l'ID du Produit de la Page pour les Produits simples - .orDefault(ETATS_PAGE.idProduit), + // id: E.DOM_VARIATION + // .map((selecteur: HTMLSelectElement): number => Number(selecteur.value)) + // // Récupère l'ID du Produit de la Page pour les Produits simples + // .orDefault(ETATS_PAGE.idProduit), + id: ETATS_PAGE.idProduit, quantity: 1, + variation: getAttributeValuesFromDom(), }; // Réalise la Requête et traite sa Réponse @@ -209,6 +233,28 @@ const ajouteProduitAuPanier = (): void => { .run(); }; +const initAddToCartButtonActivationOnUserChoice = (): void => { + const selectElements: ReadonlyArray = epipe( + document.querySelectorAll(".selecteur-produit select"), + Array.from, + ); + // S'il n'y a pas de sélecteur de variation, activer le bouton. + if (selectElements.length === 0) { + E.BOUTON_AJOUT_PANIER.removeAttribute(ATTRIBUT_DESACTIVE); + } + + E.VARIATION_CHOICE_FORM.addEventListener("change", (): void => { + const formValidity = E.VARIATION_CHOICE_FORM.checkValidity(); + + if (formValidity) { + E.BOUTON_AJOUT_PANIER.removeAttribute(ATTRIBUT_DESACTIVE); + } else { + E.BOUTON_AJOUT_PANIER.setAttribute(ATTRIBUT_DESACTIVE, ""); + } + }); +}; + document.addEventListener("DOMContentLoaded", (): void => { gereAccordeonDetailsProduit(); + initAddToCartButtonActivationOnUserChoice(); }); diff --git a/web/app/themes/haiku-atelier-2024/taxonomy-product_cat.php b/web/app/themes/haiku-atelier-2024/taxonomy-product_cat.php index 3d796f63..6599f153 100755 --- a/web/app/themes/haiku-atelier-2024/taxonomy-product_cat.php +++ b/web/app/themes/haiku-atelier-2024/taxonomy-product_cat.php @@ -25,7 +25,7 @@ $informations_produits = wc_get_products([ /** @var InformationsProduitShop $produits Les informations strictement nécessaires pour la grille des Produits. */ $produits = array_map( - callback: 'recupere_informations_produit_shop', + callback: recupere_informations_produit_shop(...), array: $informations_produits, ); $contexte['produits'] = $produits; diff --git a/web/app/themes/haiku-atelier-2024/views/macros/images.twig b/web/app/themes/haiku-atelier-2024/views/macros/images.twig index e151ceb2..26045ebb 100755 --- a/web/app/themes/haiku-atelier-2024/views/macros/images.twig +++ b/web/app/themes/haiku-atelier-2024/views/macros/images.twig @@ -24,7 +24,7 @@ loading="eager" src="{{ rel_url }}.jpg" width="{{ width }}" - onload="this.style.opacity=1" + onload="this.style.opacity = 1" > {% endmacro %} diff --git a/web/app/themes/haiku-atelier-2024/views/parts/pages/produit/informations-produit.twig b/web/app/themes/haiku-atelier-2024/views/parts/pages/produit/informations-produit.twig index 78013053..d8ab3e4b 100755 --- a/web/app/themes/haiku-atelier-2024/views/parts/pages/produit/informations-produit.twig +++ b/web/app/themes/haiku-atelier-2024/views/parts/pages/produit/informations-produit.twig @@ -1,49 +1,63 @@ -{# Barre flottante avec le nom du Produit, le sélectgeur de variation et de quantité pour le Panier. #} +{# Barre flottante avec le nom du Produit, le sélecteur de variation et de quantité pour le Panier. #}