Compare commits

..

No commits in common. 'samuel' and 'main' have entirely different histories.
samuel ... main

@ -17,6 +17,7 @@
"phpstan/phpdoc-parser": "^1.14",
"symfony/asset": "6.1.*",
"symfony/console": "6.1.*",
"symfony/doctrine-messenger": "6.1.*",
"symfony/dotenv": "6.1.*",
"symfony/expression-language": "6.1.*",
"symfony/flex": "^2",
@ -31,7 +32,7 @@
"symfony/process": "6.1.*",
"symfony/property-access": "6.1.*",
"symfony/property-info": "6.1.*",
"symfony/runtime": "^6.3.2",
"symfony/runtime": "6.1.*",
"symfony/security-bundle": "6.1.*",
"symfony/serializer": "6.1.*",
"symfony/string": "6.1.*",

223
composer.lock generated

@ -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": "a904471bc7e1136f2c1810044504fcc9",
"content-hash": "d0596df364527b4a2e7e00a9d7213e87",
"packages": [
{
"name": "doctrine/cache",
@ -2558,6 +2558,78 @@
],
"time": "2023-01-10T18:53:01+00:00"
},
{
"name": "symfony/doctrine-messenger",
"version": "v6.1.11",
"source": {
"type": "git",
"url": "https://github.com/symfony/doctrine-messenger.git",
"reference": "e4b775d2dadcc801d2404c701555c2638690811e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/doctrine-messenger/zipball/e4b775d2dadcc801d2404c701555c2638690811e",
"reference": "e4b775d2dadcc801d2404c701555c2638690811e",
"shasum": ""
},
"require": {
"doctrine/dbal": "^2.13|^3.0",
"php": ">=8.1",
"symfony/messenger": "^5.4|^6.0",
"symfony/service-contracts": "^1.1|^2|^3"
},
"conflict": {
"doctrine/persistence": "<1.3"
},
"require-dev": {
"doctrine/persistence": "^1.3|^2|^3",
"symfony/property-access": "^5.4|^6.0",
"symfony/serializer": "^5.4|^6.0"
},
"type": "symfony-messenger-bridge",
"autoload": {
"psr-4": {
"Symfony\\Component\\Messenger\\Bridge\\Doctrine\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Doctrine Messenger Bridge",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/doctrine-messenger/tree/v6.1.11"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2023-01-01T08:36:55+00:00"
},
{
"name": "symfony/dotenv",
"version": "v6.1.11",
@ -3053,16 +3125,16 @@
},
{
"name": "symfony/flex",
"version": "v2.7.1",
"version": "v2.7.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/flex.git",
"reference": "4ae50d368415a06820739e54d38a4a29d6df9155"
"reference": "5d743b3b78fabe9f3146586d77b0a1f9292851fc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/flex/zipball/4ae50d368415a06820739e54d38a4a29d6df9155",
"reference": "4ae50d368415a06820739e54d38a4a29d6df9155",
"url": "https://api.github.com/repos/symfony/flex/zipball/5d743b3b78fabe9f3146586d77b0a1f9292851fc",
"reference": "5d743b3b78fabe9f3146586d77b0a1f9292851fc",
"shasum": ""
},
"require": {
@ -3101,7 +3173,7 @@
"description": "Composer plugin for Symfony",
"support": {
"issues": "https://github.com/symfony/flex/issues",
"source": "https://github.com/symfony/flex/tree/v2.7.1"
"source": "https://github.com/symfony/flex/tree/v2.7.0"
},
"funding": [
{
@ -3117,7 +3189,7 @@
"type": "tidelift"
}
],
"time": "2025-05-28T14:22:54+00:00"
"time": "2025-05-23T11:41:40+00:00"
},
{
"name": "symfony/form",
@ -3873,6 +3945,92 @@
],
"time": "2023-01-10T18:53:01+00:00"
},
{
"name": "symfony/messenger",
"version": "v6.1.11",
"source": {
"type": "git",
"url": "https://github.com/symfony/messenger.git",
"reference": "e3b7323b1d59fc77f5870735809bf39ce1596aee"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/messenger/zipball/e3b7323b1d59fc77f5870735809bf39ce1596aee",
"reference": "e3b7323b1d59fc77f5870735809bf39ce1596aee",
"shasum": ""
},
"require": {
"php": ">=8.1",
"psr/log": "^1|^2|^3"
},
"conflict": {
"symfony/event-dispatcher": "<5.4",
"symfony/event-dispatcher-contracts": "<2",
"symfony/framework-bundle": "<5.4",
"symfony/http-kernel": "<5.4",
"symfony/serializer": "<5.4"
},
"require-dev": {
"psr/cache": "^1.0|^2.0|^3.0",
"symfony/console": "^5.4|^6.0",
"symfony/dependency-injection": "^5.4|^6.0",
"symfony/event-dispatcher": "^5.4|^6.0",
"symfony/http-kernel": "^5.4|^6.0",
"symfony/process": "^5.4|^6.0",
"symfony/property-access": "^5.4|^6.0",
"symfony/routing": "^5.4|^6.0",
"symfony/serializer": "^5.4|^6.0",
"symfony/service-contracts": "^1.1|^2|^3",
"symfony/stopwatch": "^5.4|^6.0",
"symfony/validator": "^5.4|^6.0"
},
"suggest": {
"enqueue/messenger-adapter": "For using the php-enqueue library as a transport."
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Messenger\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Samuel Roze",
"email": "samuel.roze@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Helps applications send and receive messages to/from other applications or via message queues",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/messenger/tree/v6.1.11"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2023-01-20T17:44:30+00:00"
},
{
"name": "symfony/mime",
"version": "v6.1.11",
@ -5135,16 +5293,16 @@
},
{
"name": "symfony/runtime",
"version": "v6.4.22",
"version": "v6.1.11",
"source": {
"type": "git",
"url": "https://github.com/symfony/runtime.git",
"reference": "832c3ce3b810509815050434ccb7ead68d06395b"
"reference": "717cb91d66893a27a4607f227e28eb74ff007fde"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/runtime/zipball/832c3ce3b810509815050434ccb7ead68d06395b",
"reference": "832c3ce3b810509815050434ccb7ead68d06395b",
"url": "https://api.github.com/repos/symfony/runtime/zipball/717cb91d66893a27a4607f227e28eb74ff007fde",
"reference": "717cb91d66893a27a4607f227e28eb74ff007fde",
"shasum": ""
},
"require": {
@ -5156,10 +5314,10 @@
},
"require-dev": {
"composer/composer": "^1.0.2|^2.0",
"symfony/console": "^5.4.9|^6.0.9|^7.0",
"symfony/dotenv": "^5.4|^6.0|^7.0",
"symfony/http-foundation": "^5.4|^6.0|^7.0",
"symfony/http-kernel": "^5.4|^6.0|^7.0"
"symfony/console": "^5.4|^6.0",
"symfony/dotenv": "^5.4|^6.0",
"symfony/http-foundation": "^5.4|^6.0",
"symfony/http-kernel": "^5.4|^6.0"
},
"type": "composer-plugin",
"extra": {
@ -5190,11 +5348,8 @@
],
"description": "Enables decoupling PHP applications from global state",
"homepage": "https://symfony.com",
"keywords": [
"runtime"
],
"support": {
"source": "https://github.com/symfony/runtime/tree/v6.4.22"
"source": "https://github.com/symfony/runtime/tree/v6.1.11"
},
"funding": [
{
@ -5210,7 +5365,7 @@
"type": "tidelift"
}
],
"time": "2025-05-07T21:15:03+00:00"
"time": "2023-01-20T17:44:30+00:00"
},
{
"name": "symfony/security-bundle",
@ -6468,16 +6623,16 @@
},
{
"name": "symfony/var-exporter",
"version": "v6.4.22",
"version": "v6.4.21",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-exporter.git",
"reference": "f28cf841f5654955c9f88ceaf4b9dc29571988a9"
"reference": "717e7544aa99752c54ecba5c0e17459c48317472"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/f28cf841f5654955c9f88ceaf4b9dc29571988a9",
"reference": "f28cf841f5654955c9f88ceaf4b9dc29571988a9",
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/717e7544aa99752c54ecba5c0e17459c48317472",
"reference": "717e7544aa99752c54ecba5c0e17459c48317472",
"shasum": ""
},
"require": {
@ -6525,7 +6680,7 @@
"serialize"
],
"support": {
"source": "https://github.com/symfony/var-exporter/tree/v6.4.22"
"source": "https://github.com/symfony/var-exporter/tree/v6.4.21"
},
"funding": [
{
@ -6541,7 +6696,7 @@
"type": "tidelift"
}
],
"time": "2025-05-14T13:00:13+00:00"
"time": "2025-04-27T21:06:26+00:00"
},
{
"name": "symfony/web-link",
@ -8990,16 +9145,16 @@
},
{
"name": "symfony/phpunit-bridge",
"version": "v7.3.0",
"version": "v7.2.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/phpunit-bridge.git",
"reference": "2eabda563921f21cbce1d1e3247b3c36568905e6"
"reference": "6106ae85a0e3ed509d339b7f924788c9cc4e7cfb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/2eabda563921f21cbce1d1e3247b3c36568905e6",
"reference": "2eabda563921f21cbce1d1e3247b3c36568905e6",
"url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/6106ae85a0e3ed509d339b7f924788c9cc4e7cfb",
"reference": "6106ae85a0e3ed509d339b7f924788c9cc4e7cfb",
"shasum": ""
},
"require": {
@ -9052,7 +9207,7 @@
"description": "Provides utilities for PHPUnit, especially user deprecation notices management",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/phpunit-bridge/tree/v7.3.0"
"source": "https://github.com/symfony/phpunit-bridge/tree/v7.2.6"
},
"funding": [
{
@ -9068,7 +9223,7 @@
"type": "tidelift"
}
],
"time": "2025-05-23T07:26:30+00:00"
"time": "2025-04-09T08:35:42+00:00"
},
{
"name": "symfony/web-profiler-bundle",
@ -9201,7 +9356,7 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"stability-flags": {},
"prefer-stable": true,
"prefer-lowest": false,
"platform": {
@ -9209,6 +9364,6 @@
"ext-ctype": "*",
"ext-iconv": "*"
},
"platform-dev": [],
"platform-dev": {},
"plugin-api-version": "2.6.0"
}

@ -0,0 +1,29 @@
framework:
messenger:
failure_transport: failed
transports:
# https://symfony.com/doc/current/messenger.html#transport-configuration
async:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
options:
use_notify: true
check_delayed_interval: 60000
retry_strategy:
max_retries: 3
multiplier: 2
failed: 'doctrine://default?queue_name=failed'
# sync: 'sync://'
default_bus: messenger.bus.default
buses:
messenger.bus.default: []
routing:
Symfony\Component\Mailer\Messenger\SendEmailMessage: async
Symfony\Component\Notifier\Message\ChatMessage: async
Symfony\Component\Notifier\Message\SmsMessage: async
# Route your messages to the transports
# 'App\Message\YourMessage': async

@ -4,30 +4,14 @@ security:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\User
property: username
users_in_memory: { memory: null }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
lazy: true
provider: app_user_provider
custom_authenticator: App\Security\LoginFormAuthenticator
logout:
path: app_logout
target: app_login
entry_point: App\Security\LoginFormAuthenticator
remember_me:
secret: '%kernel.secret%'
lifetime: 604800
path: /
always_remember_me: true
provider: users_in_memory
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#the-firewall
@ -38,13 +22,8 @@ security:
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/login, roles: PUBLIC_ACCESS }
- { path: ^/register, roles: PUBLIC_ACCESS }
- { path: ^/, roles: ROLE_USER }
# - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }
when@test:
security:

@ -6,10 +6,6 @@
parameters:
services:
App\Repository\RarityRepository:
public: true
App\Repository\EmojiRepository:
public: true
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.

@ -1,48 +0,0 @@
création entity :
php bin/console make:entity Emoji
Réinstaller orm + doctrine_bundle :
composer require doctrine/orm doctrine/doctrine-bundle
Recompiler l'autoload (utile si ça persiste)
- composer dump-autoload
Vérifie ensuite que lentité est bien reconnue :
- php bin/console doctrine:mapping:info
'''Found 1 mapped entity:
[OK] App\Entity\Emoji'''
php bin/console make:migration
php bin/console doctrine:migrations:migrate
-------
Récréation BDD quand pb migrations :
php bin/console doctrine:database:drop --force
php bin/console doctrine:database:create
php bin/console doctrine:migrations:migrate
-------
Solution : Forcer la mise à jour de phpstan/phpdoc-parser
- composer require phpstan/phpdoc-parser:^1.26
Cree la BDD :
''' Modification .env (pour retirer URL_BDD)
Ajout .env.local (pour ajouter URL_BDD)'''
- symfony console doctrine:database:create
ok j'ai creer ma BDD avec symfony console doctrine:database:create comment j'applique mon entity dans la BDD que j'avais créer précedement avec symfony console

@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20250528130332 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql(<<<'SQL'
CREATE TABLE emoji (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, nom VARCHAR(255) NOT NULL, code VARCHAR(255) NOT NULL, force DOUBLE PRECISION NOT NULL, robustesse DOUBLE PRECISION NOT NULL, intelligence DOUBLE PRECISION NOT NULL, vitesse DOUBLE PRECISION NOT NULL, nb_combat_gagne INTEGER NOT NULL, rarete INTEGER NOT NULL)
SQL);
$this->addSql(<<<'SQL'
CREATE TABLE messenger_messages (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, body CLOB NOT NULL, headers CLOB NOT NULL, queue_name VARCHAR(190) NOT NULL, created_at DATETIME NOT NULL, available_at DATETIME NOT NULL, delivered_at DATETIME DEFAULT NULL)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_75EA56E0FB7336F0 ON messenger_messages (queue_name)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_75EA56E0E3BD61CE ON messenger_messages (available_at)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_75EA56E016BA31DB ON messenger_messages (delivered_at)
SQL);
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql(<<<'SQL'
DROP TABLE emoji
SQL);
$this->addSql(<<<'SQL'
DROP TABLE messenger_messages
SQL);
}
}

@ -1,314 +0,0 @@
body {
background-color: #314e57;
font-family: 'Georgia', serif;
text-align: center;
}
h1 {
font-size: 3rem;
margin-bottom: 30px;
text-shadow: 2px 2px 4px #000;
color: #f8b435;
}
.section-divider {
border: none;
border-top: 3px solid #f8b435;
margin: 50px auto 20px;
width: 80%;
}
.section-break-icon {
text-align: center;
font-size: 2rem;
margin: -20px 0;
color: #f8b435;
}
.section-title {
margin: 40px 10px;
font-size: 1.8rem;
color: #f8f5e0;
}
.emoji-container:not(.base) {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 30px;
width: fit-content;
margin: 0 auto;
}
.emoji-container.base {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 30px;
border: 3px dashed #f8b435;
padding: 20px;
margin: 0 auto 30px auto;
border-radius: 15px;
width: fit-content;
}
.emoji-card {
background: #f2e6c9;
width: 180px;
height: 220px;
border: 4px solid #000;
border-radius: 12px;
box-shadow: 0 0 20px rgba(0,0,0,0.5);
padding: 20px;
text-align: center;
font-family: 'Georgia', serif;
position: relative;
transition: transform 0.2s ease, box-shadow 0.2s ease;
cursor: pointer;
}
.emoji-card:hover {
transform: translateY(-8px) scale(1.03);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5);
z-index: 2;
}
/* Animation brillance pour rareté légendaire */
@keyframes shine {
0% { background-position: 0px; }
100% { background-position: 168px; }
}
.emoji-card.gold {
position: relative;
z-index: 1;
}
.emoji-card.gold::before {
content: '';
position: absolute;
top: 1%;
left: 1%;
width: 97%;
height: 99%;
background: linear-gradient(
120deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.8) 50%,
rgba(255, 255, 255, 0) 100%
);
animation: shine 3.5s infinite;
pointer-events: none;
z-index: -1;
}
.emoji-card > * {
position: relative;
z-index: 1;
}
/*---------------------------*/
.emoji-card.gray { border-color: gray; }
@keyframes auraGlow {
0% { box-shadow: 0 0 10px 0 rgba(0,0,0,0.3); }
50% { box-shadow: 0 0 25px 8px currentColor; }
100% { box-shadow: 0 0 10px 0 rgba(0,0,0,0.3); }
}
/* Applique une animation d'aura par rareté */
.emoji-card.green {
color: darkgreen;
animation: auraGlow 3s infinite ease-in-out;
}
.emoji-card.purple {
color: purple;
animation: auraGlow 3s infinite ease-in-out;
}
.emoji-card.red {
color: darkred;
animation: auraGlow 3s infinite ease-in-out;
}
.emoji-card.gold {
color: goldenrod;
animation: auraGlow 3s infinite ease-in-out;
}
/* Couleurs des noms selon rareté */
.emoji-name.green { color: darkgreen; }
.emoji-name.purple { color: purple; }
.emoji-name.red { color: darkred; }
.emoji-name.gold { color: goldenrod; }
.emoji-name.gray { color: gray; }
.emoji-card .emoji {
font-size: 50px;
margin-bottom: 10px;
}
.emoji-name {
font-weight: bold;
font-size: 1.2rem;
margin-top: 5px;
color: purple;
}
.emoji-level {
font-size: 1rem;
font-weight: bold;
color: #3c2f2f;
margin-bottom: 10px;
}
.action-buttons {
display: flex;
justify-content: center;
gap: 60px;
margin-top: 20px;
}
.btn {
background: #f2e6c9;
border: 3px solid #000;
padding: 10px 25px;
font-size: 1.2rem;
font-weight: bold;
border-radius: 8px;
cursor: pointer;
transition: transform 0.2s ease;
box-shadow: 3px 3px 0 #000;
}
.btn:hover {
transform: scale(1.05);
background-color: #e5d6b8;
}
.detail-icon {
position: absolute;
top: 8px;
right: 10px;
cursor: pointer;
font-size: 18px;
color: #555;
}
.popup {
position: absolute;
top: 110%;
left: 50%;
transform: translateX(-50%);
background: #fff8e1;
color: #000;
padding: 10px;
border: 2px solid #000;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
display: none;
z-index: 10;
width: 180px;
font-size: 14px;
}
/* Filtre et Tri*/
.filter-bar {
margin-bottom: 30px;
color: #f8f5e0;
font-size: 1rem;
}
.filter-bar select {
margin: 0 10px;
padding: 5px 10px;
border-radius: 6px;
border: 2px solid #000;
background: #f2e6c9;
font-family: 'Georgia', serif;
}
/* Champs de Recherche */
.filter-bar input[type="text"] {
padding: 5px 10px;
border-radius: 6px;
border: 2px solid #000;
background: #f2e6c9;
font-family: 'Georgia', serif;
margin-right: 10px;
}
/* Style séléction créature pour combat / accouplement */
/* Etat séléctionné */
.emoji-card.selected {
outline: 4px solid #f8b435;
outline-offset: -4px;
box-shadow: 0 0 30px 10px rgba(248, 180, 53, 0.6);
}
#selection-status {
font-size: 1.1rem;
margin-top: 4rem;
font-weight: bold;
color: #f9e8c0;
}
.selection-visual {
display: flex;
justify-content: center;
align-items: center;
gap: 15px;
margin-bottom: 30px;
}
.creature-tag {
padding: 8px 15px;
background: #f2e6c9;
border: 2px solid #000;
border-radius: 8px;
font-weight: bold;
font-size: 1.1rem;
box-shadow: 2px 2px 0 #000;
}
.vs-text {
font-size: 1.5rem;
font-weight: bold;
}
/* Pagination */
.pagination-bar {
margin: 4rem 4rem;
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
color: #f8f5e0;
font-family: 'Georgia', serif;
}
.pagination-bar button {
background: none;
border: none;
font-size: 1.1rem;
color: #f8f5e0;
cursor: pointer;
padding: 5px 10px;
border-radius: 50%;
transition: background 0.2s ease;
}
.pagination-bar button.active {
background: #333;
color: white;
font-weight: bold;
}
.pagination-bar button:hover:not(.active) {
color: #f8b435;
}

@ -1,235 +0,0 @@
document.addEventListener('DOMContentLoaded', () => {
// Copie tout ici
let selectedCards = [];
function toggleSelection(card) {
const id = card.dataset.id;
if (card.classList.contains('selected')) {
card.classList.remove('selected');
selectedCards = selectedCards.filter(c => c.dataset.id !== id);
} else {
if (selectedCards.length < 2) {
card.classList.add('selected');
selectedCards.push(card);
} else {
alert("Tu ne peux sélectionner que 2 créatures à la fois.");
}
}
updateSelectionDisplay();
}
function updateSelectionDisplay() {
const status = document.getElementById("selection-status");
const visual = document.getElementById("selection-visual");
if (selectedCards.length === 0) {
status.textContent = "Sélectionnez 2 créatures...";
visual.innerHTML = "";
} else if (selectedCards.length === 1) {
status.textContent = `1ère sélection : ${selectedCards[0].dataset.name}`;
visual.innerHTML = "";
} else {
status.textContent = "2 créatures sélectionnées !";
visual.innerHTML = `
<div class="creature-tag">${selectedCards[0].dataset.name}</div>
<span class="vs-text"> et </span>
<div class="creature-tag">${selectedCards[1].dataset.name}</div>
`;
}
}
function handleAction(type) {
if (selectedCards.length !== 2) {
alert("Tu dois sélectionner 2 créatures.");
return;
}
const name1 = selectedCards[0].dataset.name;
const name2 = selectedCards[1].dataset.name;
if (type === 'combat') {
const id1 = selectedCards[0].dataset.id;
const id2 = selectedCards[1].dataset.id;
const t1 = 4;
const t2 = 6;
console.log(`Combat : ${name1} contre ${name2}`);
//window.location.href = `/emoji/fight/${t2}/${t1}`;
//window.location.href = `/emoji/fight/${id1}/${id2}`;
fetch(`/emoji/fight/${t2}/${t1}`, { method: 'GET',
headers: {
'Accept': 'application/json'
}}).then(response => {
if (!response.ok) {
throw new Error(`Erreur HTTP ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('Données reçues :', data);
})
.catch(error => {
console.error('Erreur lors de la requête :', error);
});
} else if (type === 'reproduction') {
console.log(`Accouplement : ${name1} et ${name2}`);
}
// Réinitialiser après l'action (seulement si pas redirigé)
selectedCards.forEach(card => card.classList.remove('selected'));
selectedCards = [];
updateSelectionDisplay();
}
// Ouvre / Ferme la popup d'information
function togglePopup(id) {
const popup = document.getElementById('popup-' + id);
if (popup) {
popup.style.display = (popup.style.display === 'block') ? 'none' : 'block';
}
}
// Fermer les autres popups en cliquant ailleurs
document.addEventListener('click', function(e) {
document.querySelectorAll('.popup').forEach(p => {
if (!p.contains(e.target) && !p.previousElementSibling.contains(e.target)) {
p.style.display = 'none';
}
});
});
// Fonction pour appliquer la recherche, les filtres et le tri
function applyFilters() {
const selectedColor = document.getElementById('rarete-filter').value;
const sortBy = document.getElementById('sort-select').value;
const searchTerm = document.getElementById('search-input').value.toLowerCase();
const cards = Array.from(document.querySelectorAll('#collection-container .emoji-card'));
cards.forEach(card => {
const color = card.dataset.color;
const name = card.querySelector('.emoji-name')?.textContent.toLowerCase() ?? "";
const matchColor = !selectedColor || color === selectedColor;
const matchName = !searchTerm || name.includes(searchTerm);
if (matchColor && matchName) {
card.style.display = 'block';
} else {
card.style.display = 'none';
}
});
if (sortBy !== 'none') {
const container = document.getElementById('collection-container'); // ✅ corrigé
const visibleCards = cards.filter(c => c.style.display !== 'none');
visibleCards.sort((a, b) => {
const aVal = parseFloat(a.dataset[sortBy]);
const bVal = parseFloat(b.dataset[sortBy]);
return bVal - aVal;
});
visibleCards.forEach(card => container.appendChild(card));
paginateCards();
}
}
// Appel Fonctionnalité de popup d'information
document.querySelectorAll('.detail-icon').forEach(icon => {
icon.addEventListener('click', (e) => {
const card = icon.closest('.emoji-card');
const id = card?.dataset.id;
if (id) {
togglePopup(id);
}
e.stopPropagation(); // évite que ça sélectionne la carte
});
});
// Appliquer l'écouteur à toutes les cartes (base + collection)
document.querySelectorAll('.emoji-container').forEach(container => {
container.addEventListener('click', (e) => {
const card = e.target.closest('.emoji-card');
if (!card || e.target.closest('.detail-icon')) return; // Ignore si clic sur icône
toggleSelection(card);
});
});
// Appel Fonctionnalité de combat et reproduction
document.querySelectorAll('.btn').forEach(button => {
button.addEventListener('click', () => {
const type = button.textContent.includes('Combattre') ? 'combat' : 'reproduction';
handleAction(type);
});
});
// Appel Fonctionnalité de recherche et filtres
document.getElementById('search-input').addEventListener('input', applyFilters);
document.getElementById('rarete-filter').addEventListener('change', applyFilters);
document.getElementById('sort-select').addEventListener('change', applyFilters);
/***** Partie Pagination ******/
const ITEMS_PER_PAGE = 10;
let currentPage = 1;
function paginateCards() {
const allCards = Array.from(document.querySelectorAll('#collection-container .emoji-card'));
const container = document.getElementById('collection-container');
const totalPages = Math.ceil(allCards.length / ITEMS_PER_PAGE);
// Masquer toutes les cartes
allCards.forEach(card => card.style.display = 'none');
// Afficher uniquement les cartes de la page courante
const start = (currentPage - 1) * ITEMS_PER_PAGE;
const end = start + ITEMS_PER_PAGE;
allCards.slice(start, end).forEach(card => card.style.display = 'block');
renderPagination(totalPages);
}
function renderPagination(totalPages) {
const pagination = document.getElementById('pagination');
pagination.innerHTML = '';
const addBtn = (text, page = null, isActive = false, disabled = false) => {
const btn = document.createElement('button');
btn.textContent = text;
if (isActive) btn.classList.add('active');
if (disabled) btn.disabled = true;
if (page !== null) {
btn.addEventListener('click', () => {
currentPage = page;
paginateCards();
});
}
pagination.appendChild(btn);
};
addBtn('Précédent', currentPage - 1, false, currentPage === 1);
for (let i = 1; i <= totalPages; i++) {
if (
i === 1 ||
i === totalPages ||
(i >= currentPage - 1 && i <= currentPage + 1)
) {
addBtn(i, i, i === currentPage);
} else if (
i === 2 && currentPage > 3 ||
i === totalPages - 1 && currentPage < totalPages - 2
) {
addBtn('...');
}
}
addBtn('Suivant', currentPage + 1, false, currentPage === totalPages);
}
paginateCards();
});

@ -1,62 +0,0 @@
<?php
namespace App\Command;
use Doctrine\DBAL\Connection;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class PopulateDBCommand extends Command
{
protected static $defaultName = 'app:populateDB';
private Connection $connection;
public function __construct(Connection $connection)
{
parent::__construct();
$this->connection = $connection;
}
protected function configure()
{
$this
->setDescription('Populate the database.');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
try {
// On supprime la table si elle existe déjà
$this->connection->executeStatement('DROP TABLE IF EXISTS rarity');
// On crée la table
$this->connection->executeStatement('
CREATE TABLE rarity (
id INT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
drop_rate FLOAT NOT NULL
)
');
// On peuple la table
$this->connection->executeStatement("INSERT INTO rarity (id, name, drop_rate) VALUES
(1, 'Common', 0.6),
(2, 'Rare', 0.2),
(3, 'Epic', 0.1),
(4, 'Mythical', 0.085),
(5, 'Legendary', 0.015)
");
$output->writeln('Base de données peuplée.');
} catch (\Exception $e) {
$output->writeln('<error>Erreur : ' . $e->getMessage() . '</error>');
return Command::FAILURE;
}
return Command::SUCCESS;
}
}

@ -1,76 +0,0 @@
<?php
namespace App\Command;
use Doctrine\DBAL\Connection;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class PopulateDBEmojiAvailableCommand extends Command
{
protected static $defaultName = 'app:popDBEmojiAvai';
private Connection $connection;
public function __construct(Connection $connection)
{
parent::__construct();
$this->connection = $connection;
}
protected function configure()
{
$this
->setDescription('Populate the database.');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
try {
// On supprime la table si elle existe déjà
$this->connection->executeStatement('DROP TABLE IF EXISTS stock_emoji');
// On crée la table
$this->connection->executeStatement('
CREATE TABLE stock_emoji (
id INT PRIMARY KEY,
code VARCHAR(255) NOT NULL
)
');
// On peuple la table
$this->connection->executeStatement("INSERT INTO stock_emoji (id, code) VALUES
(1,'🤖'),
(2,'😺'),
(3,'🧠'),
(4,'👻'),
(5,'🧟'),
(6,'🐶'),
(7,'👽'),
(8,'🧛'),
(9,'🎃'),
(10,'🐸'),
(11,'⚡'),
(12,'💀'),
(13,'🔥'),
(14,'🧙'),
(15,'🌪️'),
(16,'😎'),
(17,'😁'),
(18,'🌟'),
(19,'😈')
");
$output->writeln('Base de données peuplée.');
} catch (\Exception $e) {
$output->writeln('<error>Erreur : ' . $e->getMessage() . '</error>');
return Command::FAILURE;
}
return Command::SUCCESS;
}
}

@ -2,239 +2,21 @@
namespace App\Controller;
use App\Entity\Emoji;
use App\Entity\Rarity;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use App\Repository\RarityRepository;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use App\Repository\EmojiRepository;
#[Route('/emojis', name: 'app_emoji_')]
class EmojiController extends AbstractController
{
private RarityRepository $rarityRepository;
private HttpClientInterface $httpClient;
public function __construct(RarityRepository $rarityRepository, HttpClientInterface $httpClient)
{
$this->rarityRepository = $rarityRepository;
$this->httpClient = $httpClient;
}
#[Route('/', name: 'emojis')]
#[Route('/emoji', name: 'app_emoji')]
public function index(): Response
{
$this->testToMove();
return $this->render('emoji/index.html.twig', [
'controller_name' => 'EmojiController',
]);
}
#[Route('/count', name: 'count')]
public function count(EmojiRepository $emojiRepository): Response
{
$count = count($emojiRepository->findAll());
return new Response(['count' => $count]);
}
#[Route('/addRarity', name: 'add_rarity')]
public function addRarityDebug(EntityManagerInterface $entityManager) {
$rarity = new Rarity();
$rarity->setName('Bip');
$rarity->setDropRate(42);
$entityManager->persist($rarity);
$entityManager->flush();
return new Response();
}
#[Route('/add/{code}', name: 'add_code')]
public function addEmojiDebug(string $code, EntityManagerInterface $entityManager) {
$emoji = new Emoji();
$emoji->setCode($code);
$emoji->setName('Default Name');
$emoji->setStrength(1.0);
$emoji->setToughness(1.0);
$emoji->setIntelligence(1.0);
$emoji->setSpeed(1.0);
$emoji->setFightsWon(0);
// On récupère une instance de Rarity existante (par exemple, la première)
$rarity = $this->getRarity();
if (!$rarity) {
throw new \RuntimeException('Aucun objet Rarity trouvé en base.');
}
$emoji->setRarity($rarity);
// Optionnel : définir parent1 et parent2 si tu veux tester avec des relations
// $emoji->setParent1(null);
// $emoji->setParent2(null);
$entityManager->persist($emoji);
$entityManager->flush();
return new Response();
}
private function getRarity(): Rarity {
$rarity = $this->rarityRepository->findAll();
$rand = mt_rand() / mt_getrandmax();
$sum = 0.0;
foreach($rarity as $r) {
$sum += $r->getDropRate();
if($sum > $rand) {
return $r;
}
}
return $rarity[0];
}
// Renvoi l'url de l'image issue de la fusion de deux emojis. Fonctionne avec un code et un emoji directement
private function getFusionUrl(string $emoji1, string $emoji2): string
{
$baseUrl = 'https://emojik.vercel.app/s/';
$size = 256;
// On encode les string pour l'url
$encodedEmoji1 = urlencode($emoji1);
$encodedEmoji2 = urlencode($emoji2);
return sprintf('%s%s_%s?size=%d', $baseUrl, $encodedEmoji1, $encodedEmoji2, $size);
}
#[Route('/fusion/{emoji1_id}/{emoji2_id}', name: 'fusion')]
public function reproduceEmoji(int $emoji1_id, int $emoji2_id, EntityManagerInterface $entityManager, EmojiRepository $emojiRepository): JsonResponse {
$emoji1 = $emojiRepository->find($emoji1_id);
$emoji2 = $emojiRepository->find($emoji2_id);
if (!$emoji1 || !$emoji2) {
return new JsonResponse(['error' => 'One or both emojis not found'], 404);
}
$child = new Emoji();
$child->setName("Testenfant");
// Chance de fusion
$fusionRand = mt_rand() / mt_getrandmax();
if($fusionRand > 0.8) {
// Si les emoji parents sont des fusions on remonte l'arbre généalogique jusqu'à trouver un smiley normal
// On ne peut pas fusionner un smiley déjà fusionné
$temp1 = $emoji1;
while(str_contains($temp1->getCode(), "vercel")) {
$temp1 = $temp1->getParent1();
}
$temp2 = $emoji2;
while(str_contains($temp2->getCode(), "vercel")) {
$temp2 = $temp2->getParent2();
}
$child->setCode($this->getFusionUrl($temp1->getCode(), $temp2->getCode()));
} else {
$rand = mt_rand() / mt_getrandmax();
if($rand > 0.5) {
$child->setCode($emoji1->getCode());
} else {
$child->setCode($emoji2->getCode());
}
}
// Lors d'une fusion un emoji récupère chaque stat d'un de ces deux parents de manière aléatoire
$rand = mt_rand() / mt_getrandmax();
if($rand > 0.5) {
$child->setStrength($emoji1->getStrength());
} else {
$child->setStrength($emoji2->getStrength());
}
$rand = mt_rand() / mt_getrandmax();
if($rand > 0.5) {
$child->setToughness($emoji1->getToughness());
} else {
$child->setToughness($emoji2->getToughness());
}
$rand = mt_rand() / mt_getrandmax();
if($rand > 0.5) {
$child->setIntelligence($emoji1->getIntelligence());
} else {
$child->setIntelligence($emoji2->getIntelligence());
}
$rand = mt_rand() / mt_getrandmax();
if($rand > 0.5) {
$child->setSpeed($emoji1->getSpeed());
} else {
$child->setSpeed($emoji2->getSpeed());
}
$child->setFightsWon(0);
$child->setRarity($this->getRarity());
$child->setParent1($emoji1);
$child->setParent2($emoji2);
$entityManager->persist($child);
$entityManager->flush();
return new JsonResponse([
'message' => 'Child created',
'childId' => $child->getId()
]);
}
#[Route('/fight/{idEmoji1}/{idEmoji2}', name: 'fight_emoji')]
public function fightEmoji(int $idEmoji1, int $idEmoji2, EntityManagerInterface $entityManager, EmojiRepository $emojiRepository): JsonResponse {
$emoji1 = $emojiRepository->find($idEmoji1);
$emoji2 = $emojiRepository->find($idEmoji2);
if (!$emoji1 || !$emoji2) {
return new JsonResponse(['error' => 'Emoji not found'], 404);
}
$aleatoire = random_int(0,3);
$valEmoji1 = [$emoji1->getStrength(),$emoji1->getToughness(),$emoji1->getIntelligence(),$emoji1->getSpeed()];
$valEmoji2 = [$emoji2->getStrength(),$emoji2->getToughness(),$emoji2->getIntelligence(),$emoji2->getSpeed()];
$difference = $valEmoji1[$aleatoire] - $valEmoji2[$aleatoire];
if($difference > 0){
$emoji1->wonFight();
$entityManager->persist($emoji1);
$entityManager->remove($emoji2);
} else {
$emoji2->wonFight();
$entityManager->persist($emoji2);
$entityManager->remove($emoji1);
}
$entityManager->flush();
return new JsonResponse([
'message' => 'End of the fight',
]);
}
public function testToMove(){
$e = new Emoji();
$e->setName("ROBERT");
$e->setStrength(5);
$e->setIntelligence(2);
$e->setToughness(3);
$e->setSpeed(4);
$e2 = new Emoji();
$e2->setName("BIBOP");
$e2->setStrength(42);
$e2->setIntelligence(1);
$e2->setToughness(1);
$e2->setSpeed(1);
$vic = $this->fightEmoji($e,$e2);
echo $vic->getName();
public function reproduceEmoji(Emoji $emoji1, Emoji $emoji2): Emoji {
return new Emoji();
}
}

@ -1,211 +0,0 @@
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class HomeController extends AbstractController
{
#[Route('/', name: 'app_home')]
public function index(): Response
{
$emojisDeBase = [
[
'id' => 1,
'nom' => 'Bob',
'code' => '😊',
'force' => 12.5,
'robustesse' => 9.3,
'intelligence' => 7.8,
'vitesse' => 10.0,
'nbCombatGagne' => 3,
'rarete' => 3, // épique
],
[
'id' => 2,
'nom' => 'John',
'code' => '😭',
'force' => 5.1,
'robustesse' => 4.2,
'intelligence' => 3.3,
'vitesse' => 6.0,
'nbCombatGagne' => 1,
'rarete' => 1, // commun
],
[
'id' => 3,
'nom' => 'Rodolph',
'code' => '😁',
'force' => 20.0,
'robustesse' => 15.0,
'intelligence' => 18.0,
'vitesse' => 17.0,
'nbCombatGagne' => 10,
'rarete' => 5, // légendaire
],
[
'id' => 4,
'nom' => 'Sophie',
'code' => '😎',
'force' => 8.0,
'robustesse' => 7.5,
'intelligence' => 9.0,
'vitesse' => 8.5,
'nbCombatGagne' => 2,
'rarete' => 4, // mythique
]
];
$emojisCrees = [
[
'id' => 6,
'nom' => 'Benoit',
'code' => '🤖',
'force' => 15.0,
'robustesse' => 12.0,
'intelligence' => 14.0,
'vitesse' => 13.0,
'nbCombatGagne' => 5,
'rarete' => 3, // épique
],
[
'id' => 7,
'nom' => 'Eric',
'code' => '🌟',
'force' => 18.0,
'robustesse' => 16.0,
'intelligence' => 17.0,
'vitesse' => 19.0,
'nbCombatGagne' => 8,
'rarete' => 5, // légendaire
],
[
'id' => 5,
'nom' => 'Alice',
'code' => '🥳',
'force' => 6.0,
'robustesse' => 5.0,
'intelligence' => 4.5,
'vitesse' => 6.5,
'nbCombatGagne' => 0,
'rarete' => 2, // rare
],
[
'id' => 8,
'nom' => 'Bobette',
'code' => '🤩',
'force' => 10.0,
'robustesse' => 9.0,
'intelligence' => 11.0,
'vitesse' => 12.0,
'nbCombatGagne' => 4,
'rarete' => 4, // mythique
],
[
'id' => 9,
'nom' => 'Charlie',
'code' => '😇',
'force' => 7.0,
'robustesse' => 6.0,
'intelligence' => 8.0,
'vitesse' => 7.5,
'nbCombatGagne' => 2,
'rarete' => 1, // commun
],
[
'id' => 10,
'nom' => 'Diana',
'code' => '😈',
'force' => 14.0,
'robustesse' => 13.0,
'intelligence' => 15.0,
'vitesse' => 16.0,
'nbCombatGagne' => 6,
'rarete' => 4, // mythique
],
[
'id' => 11,
'nom' => 'Ethan',
'code' => '🤯',
'force' => 9.0,
'robustesse' => 8.0,
'intelligence' => 10.0,
'vitesse' => 11.0,
'nbCombatGagne' => 3,
'rarete' => 3, // épique
],
[
'id' => 12,
'nom' => 'Fiona',
'code' => '🥺',
'force' => 4.0,
'robustesse' => 3.5,
'intelligence' => 5.0,
'vitesse' => 4.5,
'nbCombatGagne' => 1,
'rarete' => 1, // commun
],
[
'id' => 13,
'nom' => 'George',
'code' => '😜',
'force' => 11.0,
'robustesse' => 10.0,
'intelligence' => 12.0,
'vitesse' => 13.5,
'nbCombatGagne' => 2,
'rarete' => 3, // épique
],
[
'id' => 14,
'nom' => 'Hannah',
'code' => '😏',
'force' => 3.0,
'robustesse' => 2.5,
'intelligence' => 4.0,
'vitesse' => 3.5,
'nbCombatGagne' => 0,
'rarete' => 2, // rare
],
[
'id' => 15,
'nom' => 'Ian',
'code' => '😬',
'force' => 17.0,
'robustesse' => 14.0,
'intelligence' => 16.0,
'vitesse' => 18.0,
'nbCombatGagne' => 7,
'rarete' => 5, // légendaire
],
];
foreach ($emojisDeBase as &$emoji) {
$emoji['color'] = match ($emoji['rarete']) {
2 => 'green',
3 => 'purple',
4 => 'red',
5 => 'gold',
default => 'gray',
};
}
foreach ($emojisCrees as &$emoji) {
$emoji['color'] = match ($emoji['rarete']) {
2 => 'green',
3 => 'purple',
4 => 'red',
5 => 'gold',
default => 'gray',
};
}
return $this->render('home/index.html.twig', [
'emojisDeBase' => $emojisDeBase,
'emojisCrees' => $emojisCrees,
]);
}
}

@ -1,29 +0,0 @@
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class LoginController extends AbstractController
{
#[Route(path: '/login', name: 'app_login')]
public function login(AuthenticationUtils $authenticationUtils): Response
{
if ($this->getUser()) {
return $this->redirectToRoute('app_home');
}
$error = $authenticationUtils->getLastAuthenticationError();
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('auth/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]);
}
#[Route(path: '/logout', name: 'app_logout')]
public function logout(): void
{
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
}
}

@ -1,48 +0,0 @@
<?php
namespace App\Controller;
use App\Entity\User;
use App\Form\RegistrationFormType;
use App\Security\LoginFormAuthenticator;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\UserAuthenticatorInterface;
class RegistrationController extends AbstractController
{
#[Route('/register', name: 'app_register')]
public function register(Request $request, UserPasswordHasherInterface $userPasswordHasher, UserAuthenticatorInterface $userAuthenticator, LoginFormAuthenticator $authenticator, EntityManagerInterface $entityManager): Response
{
$user = new User();
$form = $this->createForm(RegistrationFormType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$user->setPassword(
$userPasswordHasher->hashPassword(
$user,
$form->get('plainPassword')->getData()
)
);
$user->setRoles(['ROLE_USER']);
$entityManager->persist($user);
$entityManager->flush();
return $userAuthenticator->authenticateUser(
$user,
$authenticator,
$request
);
}
return $this->render('auth/register.html.twig', [
'registrationForm' => $form->createView(),
]);
}
}

@ -14,73 +14,42 @@ class Emoji
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $name = null;
private ?string $nom = null;
#[ORM\Column(length: 255)]
private ?string $code = null;
#[ORM\Column]
private ?float $strength = null;
private ?float $force = null;
#[ORM\Column]
private ?float $toughness = null;
private ?float $robustesse = null;
#[ORM\Column]
private ?float $intelligence = null;
#[ORM\Column]
private ?float $speed = null;
private ?float $vitesse = null;
#[ORM\Column]
private ?int $fightsWon = null;
private ?int $nbCombatGagne = null;
#[ORM\ManyToOne(targetEntity: Rarity::class)]
#[ORM\JoinColumn(nullable: false)]
private ?Rarity $rarity = null;
#[ORM\ManyToOne(targetEntity: self::class)]
#[ORM\JoinColumn(nullable: true)]
private ?Emoji $parent1 = null;
#[ORM\ManyToOne(targetEntity: self::class)]
#[ORM\JoinColumn(nullable: true)]
private ?Emoji $parent2 = null;
public function getParent1(): ?self
{
return $this->parent1;
}
public function setParent1(?self $parent1): self
{
$this->parent1 = $parent1;
return $this;
}
public function getParent2(): ?self
{
return $this->parent2;
}
public function setParent2(?self $parent2): self
{
$this->parent2 = $parent2;
return $this;
}
#[ORM\Column]
private ?int $rarete = null;
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
public function getNom(): ?string
{
return $this->name;
return $this->nom;
}
public function setName(string $name): self
public function setNom(string $nom): self
{
$this->name = $name;
$this->nom = $nom;
return $this;
}
@ -95,25 +64,25 @@ class Emoji
return $this;
}
public function getStrength(): ?float
public function getForce(): ?float
{
return $this->strength;
return $this->force;
}
public function setStrength(float $strength): self
public function setForce(float $force): self
{
$this->strength = $strength;
$this->force = $force;
return $this;
}
public function getToughness(): ?float
public function getRobustesse(): ?float
{
return $this->toughness;
return $this->robustesse;
}
public function setToughness(float $toughness): self
public function setRobustesse(float $robustesse): self
{
$this->toughness = $toughness;
$this->robustesse = $robustesse;
return $this;
}
@ -128,45 +97,36 @@ class Emoji
return $this;
}
public function getSpeed(): ?float
public function getVitesse(): ?float
{
return $this->speed;
return $this->vitesse;
}
public function setSpeed(float $speed): self
public function setVitesse(float $vitesse): self
{
$this->speed = $speed;
$this->vitesse = $vitesse;
return $this;
}
public function getFightsWon(): ?int
public function getNbCombatGagne(): ?int
{
return $this->fightsWon;
return $this->nbCombatGagne;
}
public function setFightsWon(int $fightsWon): self
public function setNbCombatGagne(int $nbCombatGagne): self
{
$this->fightsWon = $fightsWon;
$this->nbCombatGagne = $nbCombatGagne;
return $this;
}
public function getRarity(): ?Rarity
public function getRarete(): ?int
{
return $this->rarity;
return $this->rarete;
}
public function setRarity(Rarity $rarity): self
public function setRarete(int $rarete): self
{
$this->rarity = $rarity;
return $this;
}
public function wonFight() : self{
$this->fightsWon = $this->fightsWon + 1;
$this->strength += 1;
$this->toughness += 1;
$this->intelligence += 1;
$this->speed += 1;
$this->rarete = $rarete;
return $this;
}
}

@ -1,48 +0,0 @@
<?php
namespace App\Entity;
use App\Repository\RarityRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: RarityRepository::class)]
class Rarity
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 50, unique: true)]
private string $name;
#[ORM\Column(type: 'float')]
private float $dropRate;
public function getId(): ?int
{
return $this->id;
}
public function getName(): string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getDropRate(): float
{
return $this->dropRate;
}
public function setDropRate(float $dropRate): self
{
$this->dropRate = $dropRate;
return $this;
}
}

@ -1,35 +0,0 @@
<?php
namespace App\Entity;
use App\Repository\StockEmojiRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: StockEmojiRepository::class)]
class StockEmoji
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $code = null;
public function getId(): ?int
{
return $this->id;
}
public function getCode(): ?string
{
return $this->code;
}
public function setCode(string $code): self
{
$this->code = $code;
return $this;
}
}

@ -1,116 +0,0 @@
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
#[ORM\Entity(repositoryClass: UserRepository::class)]
#[UniqueEntity(fields: ['username'], message: 'There is already an account with this username')]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 180, unique: true)]
private ?string $username = null;
#[ORM\Column]
private array $roles = [];
/**
* @var string The hashed password
*/
#[ORM\Column]
private ?string $password = null;
#[ORM\ManyToMany(targetEntity: Emoji::class, inversedBy: 'users')]
#[ORM\JoinTable(name: 'posseder')]
private Collection $emojis;
public function __construct() {
$this->emojis = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getUsername(): ?string
{
return $this->username;
}
public function setUsername(string $username): static
{
$this->username = $username;
return $this;
}
/**
* A visual identifier that represents this user.
*
* @see UserInterface
*/
public function getUserIdentifier(): string
{
return (string) $this->username;
}
/**
* @see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): static
{
$this->roles = $roles;
return $this;
}
/**
* @see PasswordAuthenticatedUserInterface
*/
public function getPassword(): string
{
return $this->password;
}
public function setPassword(string $password): static
{
$this->password = $password;
return $this;
}
public function getEmojis(): Collection
{
return $this->emojis;
}
/**
* @see UserInterface
*/
public function eraseCredentials(): void
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
}

@ -1,44 +0,0 @@
<?php
namespace App\Form;
use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\IsTrue;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
class RegistrationFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('username')
->add('plainPassword', PasswordType::class, [
'mapped' => false,
'attr' => ['autocomplete' => 'new-password'],
'constraints' => [
new NotBlank([
'message' => 'Please enter a password',
]),
new Length([
'min' => 6,
'minMessage' => 'Your password should be at least {{ limit }} characters',
'max' => 4096,
]),
],
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => User::class,
]);
}
}

@ -1,48 +0,0 @@
<?php
namespace App\Repository;
use App\Entity\Rarity;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<Rarity>
*
* @method Rarity|null find($id, $lockMode = null, $lockVersion = null)
* @method Rarity|null findOneBy(array $criteria, array $orderBy = null)
* @method Rarity[] findAll()
* @method Rarity[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class RarityRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Rarity::class);
}
// /**
// * @return Rarity[] Returns an array of Rarity objects
// */
// public function findByExampleField($value): array
// {
// return $this->createQueryBuilder('r')
// ->andWhere('r.exampleField = :val')
// ->setParameter('val', $value)
// ->orderBy('r.id', 'ASC')
// ->setMaxResults(10)
// ->getQuery()
// ->getResult()
// ;
// }
// public function findOneBySomeField($value): ?Rarity
// {
// return $this->createQueryBuilder('r')
// ->andWhere('r.exampleField = :val')
// ->setParameter('val', $value)
// ->getQuery()
// ->getOneOrNullResult()
// ;
// }
}

@ -1,48 +0,0 @@
<?php
namespace App\Repository;
use App\Entity\StockEmoji;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<StockEmoji>
*
* @method StockEmoji|null find($id, $lockMode = null, $lockVersion = null)
* @method StockEmoji|null findOneBy(array $criteria, array $orderBy = null)
* @method StockEmoji[] findAll()
* @method StockEmoji[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class StockEmojiRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, StockEmoji::class);
}
// /**
// * @return StockEmoji[] Returns an array of StockEmoji objects
// */
// public function findByExampleField($value): array
// {
// return $this->createQueryBuilder('s')
// ->andWhere('s.exampleField = :val')
// ->setParameter('val', $value)
// ->orderBy('s.id', 'ASC')
// ->setMaxResults(10)
// ->getQuery()
// ->getResult()
// ;
// }
// public function findOneBySomeField($value): ?StockEmoji
// {
// return $this->createQueryBuilder('s')
// ->andWhere('s.exampleField = :val')
// ->setParameter('val', $value)
// ->getQuery()
// ->getOneOrNullResult()
// ;
// }
}

@ -1,67 +0,0 @@
<?php
namespace App\Repository;
use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
/**
* @extends ServiceEntityRepository<User>
*
* @implements PasswordUpgraderInterface<User>
*
* @method User|null find($id, $lockMode = null, $lockVersion = null)
* @method User|null findOneBy(array $criteria, array $orderBy = null)
* @method User[] findAll()
* @method User[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class UserRepository extends ServiceEntityRepository implements PasswordUpgraderInterface
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, User::class);
}
/**
* Used to upgrade (rehash) the user's password automatically over time.
*/
public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void
{
if (!$user instanceof User) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', $user::class));
}
$user->setPassword($newHashedPassword);
$this->getEntityManager()->persist($user);
$this->getEntityManager()->flush();
}
// /**
// * @return User[] Returns an array of User objects
// */
// public function findByExampleField($value): array
// {
// return $this->createQueryBuilder('u')
// ->andWhere('u.exampleField = :val')
// ->setParameter('val', $value)
// ->orderBy('u.id', 'ASC')
// ->setMaxResults(10)
// ->getQuery()
// ->getResult()
// ;
// }
// public function findOneBySomeField($value): ?User
// {
// return $this->createQueryBuilder('u')
// ->andWhere('u.exampleField = :val')
// ->setParameter('val', $value)
// ->getQuery()
// ->getOneOrNullResult()
// ;
// }
}

@ -1,59 +0,0 @@
<?php
namespace App\Security;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
class LoginFormAuthenticator extends AbstractLoginFormAuthenticator
{
use TargetPathTrait;
public const LOGIN_ROUTE = 'app_login';
public function __construct(private UrlGeneratorInterface $urlGenerator)
{
}
public function authenticate(Request $request): Passport
{
$username = $request->request->get('username', '');
$request->getSession()->set(Security::LAST_USERNAME, $username);
return new Passport(
new UserBadge($username),
new PasswordCredentials($request->request->get('password', '')),
[
new CsrfTokenBadge('authenticate', $request->request->get('_csrf_token')),
new RememberMeBadge(),
]
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) {
return new RedirectResponse($targetPath);
}
return new RedirectResponse($this->urlGenerator->generate('app_home'));
}
protected function getLoginUrl(Request $request): string
{
return $this->urlGenerator->generate(self::LOGIN_ROUTE);
}
}

@ -1,13 +1,4 @@
{
"doctrine/deprecations": {
"version": "1.1",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "1.0",
"ref": "87424683adc81d7dc305eefec1fced883084aab9"
}
},
"doctrine/doctrine-bundle": {
"version": "2.13",
"recipe": {
@ -126,6 +117,18 @@
"ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
}
},
"symfony/messenger": {
"version": "6.1",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "6.0",
"ref": "ba1ac4e919baba5644d31b57a3284d6ba12d52ee"
},
"files": [
"./config/packages/messenger.yaml"
]
},
"symfony/monolog-bundle": {
"version": "3.10",
"recipe": {

@ -1,43 +0,0 @@
{% extends 'base.html.twig' %}
{% block title %}Connexion{% endblock %}
{% block stylesheets %}
{{ parent() }}
<link rel="stylesheet" href="{{ asset('css/login.css') }}">
{% endblock %}
{% block body %}
<div class="login-container">
<h1 class="login-title">🔐 Connexion</h1>
{% if error %}
<div class="login-error">Mauvais nom d'utilisateur ou mot de passe.</div>
{% endif %}
{% if app.user %}
<div class="already-logged">
Connecté en tant que {{ app.user.userIdentifier }},
<a href="{{ path('app_logout') }}" class="logout-link">Se déconnecter</a>
</div>
{% endif %}
<form method="post" class="login-form">
<label for="inputUsername">Nom d'utilisateur</label>
<input type="text" id="inputUsername" name="username" value="{{ last_username }}" required autofocus>
<label for="inputPassword">Mot de passe</label>
<input type="password" id="inputPassword" name="password" required>
<input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">
<button class="btn-login" type="submit">🚪 Se connecter</button>
</form>
</div>
<div class="no-account">
<p>Pas encore de compte ?</p>
<a href="{{ path('app_register') }}" class="btn-register">Créer un compte</a>
</div>
{% endblock %}

@ -1,30 +0,0 @@
{% extends 'base.html.twig' %}
{% block title %}Inscription{% endblock %}
{% block stylesheets %}
{{ parent() }}
<link rel="stylesheet" href="{{ asset('css/login.css') }}">
{% endblock %}
{% block body %}
<div class="register-container">
<h1>📝 Inscription</h1>
{{ form_start(registrationForm) }}
{{ form_errors(registrationForm) }}
{{ form_row(registrationForm.username) }}
{{ form_row(registrationForm.plainPassword, {
label: 'Mot de passe'
}) }}
<button type="submit" class="btn">Créer mon compte</button>
{{ form_end(registrationForm) }}
<div class="no-account">
<p>Déjà inscrit ?</p>
<a href="{{ path('app_login') }}" class="btn-register">Se connecter</a>
</div>
</div>
{% endblock %}

@ -3,7 +3,6 @@
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text></svg>">
{# Run `composer require symfony/webpack-encore-bundle` to start using Symfony UX #}
{% block stylesheets %}

@ -1,115 +0,0 @@
{% extends 'base.html.twig' %}
{% block title %}Accueil - Ma collection de créatures{% endblock %}
{% block stylesheets %}
<link rel="stylesheet" href="{{ asset('css/home.css') }}">
{% endblock %}
{% block body %}
{% if app.user %}
<div class="logout-container">
Connecté en tant que <strong>{{ app.user.username }}</strong>
<form action="{{ path('app_logout') }}" method="post" style="display: inline;">
<button type="submit" class="btn btn-logout">🚪 Se déconnecter</button>
</form>
</div>
{% endif %}
<h1>🧬 Ma collection de créatures 🐾</h1>
<h2 class="section-title">🌱 Vos créatures de base</h2>
<div class="emoji-container base">
{% for emoji in emojisDeBase %}
<div class="emoji-card {{ emoji.color }}"
data-id="{{ emoji.id }}"
data-name="{{ emoji.nom }}"
data-color="{{ emoji.color }}"
data-level="{{ emoji.nbCombatGagne }}"
data-force="{{ emoji.force }}"
data-vitesse="{{ emoji.vitesse }}">
<div class="emoji-level">Level {{ emoji.nbCombatGagne }}</div>
<div class="emoji">{{ emoji.code }}</div>
<div class="emoji-name {{ emoji.color }}">{{ emoji.nom }}</div>
<div class="detail-icon" data-id="{{ emoji.id }}"></div>
<div class="popup" id="popup-{{ emoji.id }}">
<strong>Stats :</strong><br>
Force: {{ emoji.force }}<br>
Robustesse: {{ emoji.robustesse }}<br>
Intelligence: {{ emoji.intelligence }}<br>
Vitesse: {{ emoji.vitesse }}
</div>
</div>
{% endfor %}
</div>
<div class="section-break-icon">🔻</div>
<h2 class="section-title">📦 Votre collection personnelle</h2>
<div class="filter-bar">
<label for="search-input">🔍 Rechercher un nom :</label>
<input type="text" id="search-input" placeholder="ex: Bob..." />
<label for="rarete-filter">Filtrer par rareté :</label>
<select id="rarete-filter">
<option value="">Toutes</option>
<option value="green">Communes</option>
<option value="purple">Épiques</option>
<option value="red">Mythiques</option>
<option value="gold">Légendaires</option>
</select>
<label for="sort-select">Trier par :</label>
<select id="sort-select">
<option value="none">--</option>
<option value="level">Level</option>
<option value="force">Force</option>
<option value="vitesse">Vitesse</option>
</select>
</div>
<div class="emoji-container" id="collection-container">
{% for emoji in emojisCrees %}
<div class="emoji-card {{ emoji.color }}"
data-id="{{ emoji.id }}"
data-name="{{ emoji.nom }}"
data-color="{{ emoji.color }}"
data-level="{{ emoji.nbCombatGagne }}"
data-force="{{ emoji.force }}"
data-vitesse="{{ emoji.vitesse }}">
<div class="emoji-level">Level {{ emoji.nbCombatGagne }}</div>
<div class="emoji">{{ emoji.code }}</div>
<div class="emoji-name {{ emoji.color }}">{{ emoji.nom }}</div>
<div class="detail-icon" data-id="{{ emoji.id }}"></div>
<div class="popup" id="popup-{{ emoji.id }}">
<strong>Stats :</strong><br>
Force: {{ emoji.force }}<br>
Robustesse: {{ emoji.robustesse }}<br>
Intelligence: {{ emoji.intelligence }}<br>
Vitesse: {{ emoji.vitesse }}
</div>
</div>
{% endfor %}
</div>
<div id="pagination" class="pagination-bar"></div>
<div id="selection-status">Sélectionnez 2 créatures de votre choix pour commencer ...</div>
<div id="selection-visual" class="selection-visual"></div>
<div class="action-buttons">
<button class="btn">⚔️ Combattre</button>
<button class="btn">💞 Reproduire</button>
</div>
{% endblock %}
{% block javascripts %}
<script src="{{ asset('js/home.js') }}"></script>
{% endblock %}

@ -1,89 +0,0 @@
<?php
namespace App\Tests\Controller;
use App\Entity\Emoji;
use App\Entity\Rarity;
use App\Repository\EmojiRepository;
use App\Repository\RarityRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\Response;
class EmojiControllerTest extends WebTestCase
{
private $client;
private EntityManagerInterface $em;
protected function setUp(): void
{
$this->client = static::createClient();
$this->em = static::getContainer()->get(EntityManagerInterface::class);
// Démarre une transaction pour pouvoir annuler les modifications des tests
$this->em->getConnection()->beginTransaction();
}
protected function tearDown(): void
{
// Rollback la transaction pour annuler les changements des tests
$this->em->getConnection()->rollBack();
parent::tearDown();
}
public function testReproduceEmoji(): void
{
$emoji1 = (new Emoji())
->setCode('😀')
->setName('Parent1')
->setStrength(1.0)
->setToughness(1.0)
->setIntelligence(1.0)
->setSpeed(1.0)
->setFightsWon(5);
$emoji2 = (new Emoji())
->setCode('😎')
->setName('Parent2')
->setStrength(2.0)
->setToughness(2.0)
->setIntelligence(2.0)
->setSpeed(2.0)
->setFightsWon(3);
$rarity = (new Rarity())
->setName('Rare')
->setDropRate(1.0);
$this->em->persist($rarity);
$this->em->persist($emoji1);
$this->em->persist($emoji2);
$this->em->flush();
$id1 = $emoji1->getId();
$id2 = $emoji2->getId();
$this->client->request('GET', "/emoji/fusion/$id1/$id2");
$response = $this->client->getResponse();
$this->assertEquals(Response::HTTP_OK, $response->getStatusCode());
$data = json_decode($response->getContent(), true);
$this->assertArrayHasKey('childId', $data);
$this->assertEquals('Child created', $data['message']);
}
public function testFusionEmojiNotFound(): void
{
$emojiRepo = $this->createMock(EmojiRepository::class);
$emojiRepo->method('find')->willReturn(null);
$this->client->request('GET', '/emoji/fusion/999/998');
$response = $this->client->getResponse();
$this->assertEquals(Response::HTTP_NOT_FOUND, $response->getStatusCode());
$data = json_decode($response->getContent(), true);
$this->assertEquals('One or both emojis not found', $data['error']);
}
}
Loading…
Cancel
Save