Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe2bc7b78c | ||
|
|
850e8afaa1 | ||
|
|
bb688a724a | ||
|
|
74cf4621f4 | ||
|
|
bce27d5fa4 | ||
|
|
2960bf29d8 | ||
|
|
e4a68d2aa0 | ||
|
|
65605d6c5f | ||
|
|
da71f813cb | ||
|
|
ca13863c41 | ||
|
|
734a4a8fee | ||
|
|
ed6b8335c8 | ||
|
|
c9e1f95c81 | ||
|
|
f703727ccb | ||
|
|
77f8e9ec31 |
@@ -21,6 +21,9 @@ indent_size = 2
|
|||||||
[*.json]
|
[*.json]
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
|
[{compose.yaml,compose.*.yaml}]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
[*.md]
|
[*.md]
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
|||||||
@@ -16,19 +16,25 @@
|
|||||||
|
|
||||||
###> symfony/framework-bundle ###
|
###> symfony/framework-bundle ###
|
||||||
APP_ENV=dev
|
APP_ENV=dev
|
||||||
APP_SECRET=VerySecretKey
|
APP_SECRET=
|
||||||
###< symfony/framework-bundle ###
|
###< symfony/framework-bundle ###
|
||||||
|
|
||||||
###> doctrine/doctrine-bundle ###
|
###> doctrine/doctrine-bundle ###
|
||||||
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
|
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
|
||||||
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
|
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
|
||||||
#
|
#
|
||||||
# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
|
# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data_%kernel.environment%.db"
|
||||||
# DATABASE_URL="mysql://app:[email protected]:3306/app?serverVersion=8.0.32&charset=utf8mb4"
|
# DATABASE_URL="mysql://app:[email protected]:3306/app?serverVersion=8.0.32&charset=utf8mb4"
|
||||||
# DATABASE_URL="mysql://app:[email protected]:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4"
|
# DATABASE_URL="mysql://app:[email protected]:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4"
|
||||||
DATABASE_URL="postgresql://app:[email protected]:54195/app?serverVersion=16&charset=utf8"
|
DATABASE_URL="postgresql://app:[email protected]:5432/app?serverVersion=16&charset=utf8"
|
||||||
###< doctrine/doctrine-bundle ###
|
###< doctrine/doctrine-bundle ###
|
||||||
|
|
||||||
###> nelmio/cors-bundle ###
|
###> nelmio/cors-bundle ###
|
||||||
CORS_ALLOW_ORIGIN='^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$'
|
CORS_ALLOW_ORIGIN='^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$'
|
||||||
###< nelmio/cors-bundle ###
|
###< nelmio/cors-bundle ###
|
||||||
|
|
||||||
|
###> symfony/routing ###
|
||||||
|
# Configure how to generate URLs in non-HTTP contexts, such as CLI commands.
|
||||||
|
# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands
|
||||||
|
DEFAULT_URI=http://localhost
|
||||||
|
###< symfony/routing ###
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
###> symfony/framework-bundle ###
|
||||||
|
APP_SECRET=344d36e897253340ff9de6cfacce2da9
|
||||||
|
###< symfony/framework-bundle ###
|
||||||
@@ -12,6 +12,9 @@
|
|||||||
# Draw.io backup file.
|
# Draw.io backup file.
|
||||||
*bkp
|
*bkp
|
||||||
|
|
||||||
|
# Bolt db files
|
||||||
|
[Bb]olt.db
|
||||||
|
|
||||||
# JS files
|
# JS files
|
||||||
/node_modules/
|
/node_modules/
|
||||||
|
|
||||||
|
|||||||
@@ -35,5 +35,5 @@ services:
|
|||||||
###> doctrine/doctrine-bundle ###
|
###> doctrine/doctrine-bundle ###
|
||||||
database:
|
database:
|
||||||
ports:
|
ports:
|
||||||
- 54195:5432
|
- "5432"
|
||||||
###< doctrine/doctrine-bundle ###
|
###< doctrine/doctrine-bundle ###
|
||||||
|
|||||||
+1
-1
@@ -50,7 +50,7 @@ services:
|
|||||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-!ChangeMe!}
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-!ChangeMe!}
|
||||||
POSTGRES_USER: ${POSTGRES_USER:-app}
|
POSTGRES_USER: ${POSTGRES_USER:-app}
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "pg_isready", "-U", "${POSTGRES_USER:-app}"]
|
test: ["CMD", "pg_isready", "-d", "${POSTGRES_DB:-app}", "-U", "${POSTGRES_USER:-app}"]
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
start_period: 60s
|
start_period: 60s
|
||||||
|
|||||||
+5
-5
@@ -13,12 +13,12 @@
|
|||||||
"doctrine/doctrine-bundle": "^2.14",
|
"doctrine/doctrine-bundle": "^2.14",
|
||||||
"doctrine/doctrine-migrations-bundle": "^3.3",
|
"doctrine/doctrine-migrations-bundle": "^3.3",
|
||||||
"doctrine/orm": "^3.3",
|
"doctrine/orm": "^3.3",
|
||||||
"easycorp/easyadmin-bundle": "^4.10",
|
"easycorp/easyadmin-bundle": "^4.25",
|
||||||
"nelmio/api-doc-bundle": "^4.30",
|
"nelmio/api-doc-bundle": "^5.0",
|
||||||
"nelmio/cors-bundle": "^2.4",
|
"nelmio/cors-bundle": "^2.4",
|
||||||
"php-ds/php-ds": "^1.5",
|
"php-ds/php-ds": "^1.5",
|
||||||
"phpdocumentor/reflection-docblock": "^5.4",
|
"phpdocumentor/reflection-docblock": "^5.4",
|
||||||
"phpstan/phpdoc-parser": "^1.28",
|
"phpstan/phpdoc-parser": "^2.0",
|
||||||
"runtime/frankenphp-symfony": "^0.2.0",
|
"runtime/frankenphp-symfony": "^0.2.0",
|
||||||
"symfony/asset": "7.3.*",
|
"symfony/asset": "7.3.*",
|
||||||
"symfony/asset-mapper": "7.3.*",
|
"symfony/asset-mapper": "7.3.*",
|
||||||
@@ -41,8 +41,8 @@
|
|||||||
"symfony/validator": "7.3.*",
|
"symfony/validator": "7.3.*",
|
||||||
"symfony/var-exporter": "7.3.*",
|
"symfony/var-exporter": "7.3.*",
|
||||||
"symfony/yaml": "7.3.*",
|
"symfony/yaml": "7.3.*",
|
||||||
"twig/extra-bundle": "^2.12|^3.0",
|
"twig/extra-bundle": "^3.0",
|
||||||
"twig/twig": "^2.12|^3.0"
|
"twig/twig": "^3.0"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"allow-plugins": {
|
"allow-plugins": {
|
||||||
|
|||||||
Generated
+711
-514
File diff suppressed because it is too large
Load Diff
@@ -3,3 +3,9 @@ framework:
|
|||||||
# The paths to make available to the asset mapper.
|
# The paths to make available to the asset mapper.
|
||||||
paths:
|
paths:
|
||||||
- assets/
|
- assets/
|
||||||
|
missing_import_mode: strict
|
||||||
|
|
||||||
|
when@prod:
|
||||||
|
framework:
|
||||||
|
asset_mapper:
|
||||||
|
missing_import_mode: warn
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ doctrine:
|
|||||||
report_fields_where_declared: true
|
report_fields_where_declared: true
|
||||||
validate_xml_mapping: true
|
validate_xml_mapping: true
|
||||||
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
|
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
|
||||||
|
identity_generation_preferences:
|
||||||
|
Doctrine\DBAL\Platforms\PostgreSQLPlatform: identity
|
||||||
auto_mapping: true
|
auto_mapping: true
|
||||||
mappings:
|
mappings:
|
||||||
App:
|
App:
|
||||||
@@ -23,7 +25,7 @@ doctrine:
|
|||||||
prefix: 'App\Entity'
|
prefix: 'App\Entity'
|
||||||
alias: App
|
alias: App
|
||||||
controller_resolver:
|
controller_resolver:
|
||||||
auto_mapping: true
|
auto_mapping: false
|
||||||
|
|
||||||
when@test:
|
when@test:
|
||||||
doctrine:
|
doctrine:
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
# see https://symfony.com/doc/current/reference/configuration/framework.html
|
# see https://symfony.com/doc/current/reference/configuration/framework.html
|
||||||
framework:
|
framework:
|
||||||
secret: '%env(APP_SECRET)%'
|
secret: '%env(APP_SECRET)%'
|
||||||
#csrf_protection: true
|
|
||||||
|
|
||||||
# Note that the session will be started ONLY if you read or write from it.
|
# Note that the session will be started ONLY if you read or write from it.
|
||||||
session: true
|
session: true
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
nelmio_api_doc:
|
nelmio_api_doc:
|
||||||
|
type_info: true
|
||||||
documentation:
|
documentation:
|
||||||
info:
|
info:
|
||||||
title: SomniaRooms
|
title: SomniaRooms
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ framework:
|
|||||||
router:
|
router:
|
||||||
# Configure how to generate URLs in non-HTTP contexts, such as CLI commands.
|
# Configure how to generate URLs in non-HTTP contexts, such as CLI commands.
|
||||||
# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands
|
# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands
|
||||||
#default_uri: http://localhost
|
default_uri: '%env(DEFAULT_URI)%'
|
||||||
|
|
||||||
when@prod:
|
when@prod:
|
||||||
framework:
|
framework:
|
||||||
|
|||||||
@@ -2,6 +2,4 @@ framework:
|
|||||||
default_locale: en
|
default_locale: en
|
||||||
translator:
|
translator:
|
||||||
default_path: '%kernel.project_dir%/translations'
|
default_path: '%kernel.project_dir%/translations'
|
||||||
fallbacks:
|
|
||||||
- en
|
|
||||||
providers:
|
providers:
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
when@dev:
|
when@dev:
|
||||||
web_profiler:
|
web_profiler:
|
||||||
toolbar: true
|
toolbar: true
|
||||||
intercept_redirects: false
|
|
||||||
|
|
||||||
framework:
|
framework:
|
||||||
profiler:
|
profiler:
|
||||||
only_exceptions: false
|
|
||||||
collect_serializer_data: true
|
collect_serializer_data: true
|
||||||
|
|
||||||
when@test:
|
when@test:
|
||||||
web_profiler:
|
|
||||||
toolbar: false
|
|
||||||
intercept_redirects: false
|
|
||||||
|
|
||||||
framework:
|
framework:
|
||||||
profiler: { collect: false }
|
profiler:
|
||||||
|
collect: false
|
||||||
|
collect_serializer_data: true
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<?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 Version20251017201308 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 room_price (date VARCHAR(10) NOT NULL, guest_number SMALLINT NOT NULL, price INT NOT NULL, room_category_id INT NOT NULL, PRIMARY KEY(room_category_id, date, guest_number))
|
||||||
|
SQL);
|
||||||
|
$this->addSql(<<<'SQL'
|
||||||
|
CREATE INDEX IDX_8074837267333DD ON room_price (room_category_id)
|
||||||
|
SQL);
|
||||||
|
$this->addSql(<<<'SQL'
|
||||||
|
ALTER TABLE room_price ADD CONSTRAINT FK_8074837267333DD FOREIGN KEY (room_category_id) REFERENCES room_category (id) NOT DEFERRABLE INITIALLY IMMEDIATE
|
||||||
|
SQL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql(<<<'SQL'
|
||||||
|
ALTER TABLE room_price DROP CONSTRAINT FK_8074837267333DD
|
||||||
|
SQL);
|
||||||
|
$this->addSql(<<<'SQL'
|
||||||
|
DROP TABLE room_price
|
||||||
|
SQL);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,18 +28,18 @@ class UserCrudController extends AbstractCrudController
|
|||||||
|
|
||||||
public function configureFields(string $pageName): iterable
|
public function configureFields(string $pageName): iterable
|
||||||
{
|
{
|
||||||
yield TextField::new ('username');
|
yield TextField::new('username');
|
||||||
yield TextField::new ('username')->onlyWhenUpdating()->setDisabled(true);
|
yield TextField::new('username')->onlyWhenUpdating()->setDisabled(true);
|
||||||
|
|
||||||
$availableRoles = ['User' => 'ROLE_USER', 'Admin' => 'ROLE_ADMIN', 'Test' => 'ROLE_TEST', 'SuperAdmin' => 'ROLE_SUPERADMIN'];
|
$availableRoles = ['User' => 'ROLE_USER', 'Admin' => 'ROLE_ADMIN', 'Test' => 'ROLE_TEST', 'SuperAdmin' => 'ROLE_SUPERADMIN'];
|
||||||
yield ChoiceField::new ('roles')
|
yield ChoiceField::new('roles')
|
||||||
->setChoices($availableRoles)
|
->setChoices($availableRoles)
|
||||||
->allowMultipleChoices(true);
|
->allowMultipleChoices(true);
|
||||||
|
|
||||||
|
|
||||||
yield FormField::addPanel('Set password')->setIcon('fa fa-key');
|
yield FormField::addFieldset('Set password')->setIcon('fa fa-key');
|
||||||
|
|
||||||
$fieldPassword = Field::new ('plainPassword', 'New password')
|
$fieldPassword = Field::new('plainPassword', 'New password')
|
||||||
->hideOnIndex()
|
->hideOnIndex()
|
||||||
->setRequired(false)
|
->setRequired(false)
|
||||||
->setFormType(RepeatedType::class)
|
->setFormType(RepeatedType::class)
|
||||||
@@ -50,7 +50,7 @@ class UserCrudController extends AbstractCrudController
|
|||||||
'error_bubbling' => true,
|
'error_bubbling' => true,
|
||||||
'invalid_message' => 'The password fields do not match.',
|
'invalid_message' => 'The password fields do not match.',
|
||||||
]);
|
]);
|
||||||
if($pageName == Crud::PAGE_NEW) {
|
if ($pageName == Crud::PAGE_NEW) {
|
||||||
$fieldPassword->setRequired(true);
|
$fieldPassword->setRequired(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ class UserCrudController extends AbstractCrudController
|
|||||||
{
|
{
|
||||||
$plaintextPassword = $entityInstance->getPlainPassword();
|
$plaintextPassword = $entityInstance->getPlainPassword();
|
||||||
|
|
||||||
if($plaintextPassword && $plaintextPassword != "") {
|
if ($plaintextPassword && $plaintextPassword != "") {
|
||||||
//New password set. Hash password.
|
//New password set. Hash password.
|
||||||
$hashedPassword = $this->userPasswordHasher->hashPassword($entityInstance, $plaintextPassword);
|
$hashedPassword = $this->userPasswordHasher->hashPassword($entityInstance, $plaintextPassword);
|
||||||
$entityInstance->setPassword($hashedPassword);
|
$entityInstance->setPassword($hashedPassword);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use App\Request\BookingDetailsRequest;
|
|||||||
use App\Response\BookingCreateResponse;
|
use App\Response\BookingCreateResponse;
|
||||||
use App\Service\PricesService;
|
use App\Service\PricesService;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Nelmio\ApiDocBundle\Annotation\Model;
|
use Nelmio\ApiDocBundle\Attribute\Model;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
use Symfony\Component\Routing\Attribute\Route;
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Attribute\Route;
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
use App\Repository\ConfigVariableRepository;
|
use App\Repository\ConfigVariableRepository;
|
||||||
use Nelmio\ApiDocBundle\Annotation\Model;
|
use Nelmio\ApiDocBundle\Attribute\Model;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
|
|
||||||
class ConfigVariableController extends AbstractController
|
class ConfigVariableController extends AbstractController
|
||||||
|
|||||||
@@ -3,17 +3,12 @@
|
|||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
|
||||||
use App\Repository\RoomCategoryRepository;
|
use App\Repository\RoomCategoryRepository;
|
||||||
use App\Entity\RoomCategory;
|
use App\Entity\RoomCategory;
|
||||||
use App\Entity\RoomCategoryPhoto;
|
use Nelmio\ApiDocBundle\Attribute\Model;
|
||||||
use Nelmio\ApiDocBundle\Annotation\Model;
|
|
||||||
use OpenApi\Annotations\Property;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
|
||||||
use Symfony\Component\Routing\Attribute\Route;
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
|
||||||
|
|
||||||
class RoomCategoryController extends AbstractController
|
class RoomCategoryController extends AbstractController
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
use App\ApiErrorException;
|
use App\ApiErrorException;
|
||||||
|
use App\Consts\ConfigVariableConsts;
|
||||||
use App\DTO\ApiErrorMessage;
|
use App\DTO\ApiErrorMessage;
|
||||||
use App\Repository\RoomAvailabilityRepository;
|
use App\Repository\RoomAvailabilityRepository;
|
||||||
use App\Repository\RoomCategoryRepository;
|
use App\Repository\RoomCategoryRepository;
|
||||||
@@ -11,16 +12,28 @@ use App\Response\SearchAvailabilityResponse;
|
|||||||
use App\Request\SearchPricesForCategoryRequest;
|
use App\Request\SearchPricesForCategoryRequest;
|
||||||
use App\Response\SearchPricesResponse;
|
use App\Response\SearchPricesResponse;
|
||||||
use App\Response\SearchPricesResponsePriceItem;
|
use App\Response\SearchPricesResponsePriceItem;
|
||||||
|
use App\Service\ConfigVariableService;
|
||||||
use App\Service\PricesService;
|
use App\Service\PricesService;
|
||||||
use Nelmio\ApiDocBundle\Annotation\Model;
|
use DateTimeInterface;
|
||||||
|
use Nelmio\ApiDocBundle\Attribute\Model;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\Routing\Attribute\Route;
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
|
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
|
||||||
|
|
||||||
class SearchRoomsController extends AbstractController
|
class SearchRoomsController extends AbstractController
|
||||||
{
|
{
|
||||||
|
private string $configMaxAllowedNightAmount;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private ConfigVariableService $configVarService,
|
||||||
|
private LoggerInterface $logger
|
||||||
|
) {
|
||||||
|
$this->configMaxAllowedNightAmount = $configVarService->getVariableFromConst(ConfigVariableConsts::CONFIG_API_NAXNIGHTS)->getValue();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get availability for all rooms on period.
|
* Get availability for all rooms on period.
|
||||||
* For each room, the minimal availability will be returned.
|
* For each room, the minimal availability will be returned.
|
||||||
@@ -52,12 +65,11 @@ class SearchRoomsController extends AbstractController
|
|||||||
#[MapRequestPayload()] SearchPeriodRequest $searchRequest
|
#[MapRequestPayload()] SearchPeriodRequest $searchRequest
|
||||||
): JsonResponse {
|
): JsonResponse {
|
||||||
|
|
||||||
if ($searchRequest->startDate >= $searchRequest->endDate) {
|
$datesValid = $this->areRequestDatesValid($searchRequest->startDate, $searchRequest->endDate);
|
||||||
throw new ApiErrorException(
|
if ($datesValid != null) {
|
||||||
new ApiErrorMessage(422, ApiErrorMessage::TYPE_INVALID_DATE),
|
throw $datesValid;
|
||||||
'startDate cant be before endDate',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$rs = $repoAvailability->getAvailabilityForPeriod($searchRequest->startDate, $searchRequest->endDate);
|
$rs = $repoAvailability->getAvailabilityForPeriod($searchRequest->startDate, $searchRequest->endDate);
|
||||||
|
|
||||||
return $this->json(["results" => $rs]);
|
return $this->json(["results" => $rs]);
|
||||||
@@ -93,11 +105,10 @@ class SearchRoomsController extends AbstractController
|
|||||||
PricesService $priceService,
|
PricesService $priceService,
|
||||||
#[MapRequestPayload()] SearchPeriodRequest $searchRequest
|
#[MapRequestPayload()] SearchPeriodRequest $searchRequest
|
||||||
): JsonResponse {
|
): JsonResponse {
|
||||||
if ($searchRequest->startDate >= $searchRequest->endDate) {
|
|
||||||
throw new ApiErrorException(
|
$datesValid = $this->areRequestDatesValid($searchRequest->startDate, $searchRequest->endDate);
|
||||||
new ApiErrorMessage(422, ApiErrorMessage::TYPE_INVALID_DATE),
|
if ($datesValid != null) {
|
||||||
'startDate cant be before endDate',
|
throw $datesValid;
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$rs = $priceService->getRoomSearch($searchRequest->startDate, $searchRequest->endDate);
|
$rs = $priceService->getRoomSearch($searchRequest->startDate, $searchRequest->endDate);
|
||||||
@@ -138,6 +149,11 @@ class SearchRoomsController extends AbstractController
|
|||||||
#[MapRequestPayload()] SearchPricesForCategoryRequest $searchRequest
|
#[MapRequestPayload()] SearchPricesForCategoryRequest $searchRequest
|
||||||
): JsonResponse {
|
): JsonResponse {
|
||||||
|
|
||||||
|
$datesValid = $this->areRequestDatesValid($searchRequest->startDate, $searchRequest->endDate);
|
||||||
|
if ($datesValid != null) {
|
||||||
|
throw $datesValid;
|
||||||
|
}
|
||||||
|
|
||||||
$roomCategory = $repoCat->find($searchRequest->roomCategory);
|
$roomCategory = $repoCat->find($searchRequest->roomCategory);
|
||||||
if ($roomCategory === null) {
|
if ($roomCategory === null) {
|
||||||
throw new ApiErrorException(
|
throw new ApiErrorException(
|
||||||
@@ -146,19 +162,29 @@ class SearchRoomsController extends AbstractController
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($searchRequest->startDate >= $searchRequest->endDate) {
|
$rs = $priceService->getFinalPricesForSearch($roomCategory, $searchRequest->startDate, $searchRequest->endDate);
|
||||||
throw new ApiErrorException(
|
|
||||||
|
return $this->json(["results" => $rs]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function areRequestDatesValid(DateTimeInterface $dateStart, DateTimeInterface $dateEnd): ApiErrorException|null
|
||||||
|
{
|
||||||
|
if ($dateStart >= $dateEnd) {
|
||||||
|
return new ApiErrorException(
|
||||||
new ApiErrorMessage(422, ApiErrorMessage::TYPE_INVALID_DATE),
|
new ApiErrorMessage(422, ApiErrorMessage::TYPE_INVALID_DATE),
|
||||||
'startDate cant be before endDate',
|
'startDate cant be before endDate',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$fareList = $priceService->getFinalPricesForSearch($roomCategory, $searchRequest->startDate, $searchRequest->endDate);
|
$daysDiff = $dateStart->diff($dateEnd)->days;
|
||||||
$rs = [];
|
if ($daysDiff > $this->configMaxAllowedNightAmount) {
|
||||||
foreach ($fareList as $key => $value) {
|
return new ApiErrorException(
|
||||||
$rs[] = ["guestNumber" => $key, "price" => $value];
|
new ApiErrorMessage(400, ApiErrorMessage::TYPE_API_LIMIT),
|
||||||
|
'Query exceeded max allowed night amount',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->json(["results" => $rs]);
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ class ApiErrorMessage
|
|||||||
const TYPE_LOGICAL_ERROR = 'logical_error';
|
const TYPE_LOGICAL_ERROR = 'logical_error';
|
||||||
const TYPE_SERVER_ERROR = 'server_error';
|
const TYPE_SERVER_ERROR = 'server_error';
|
||||||
const TYPE_INVALID_DATE = 'invalid_date';
|
const TYPE_INVALID_DATE = 'invalid_date';
|
||||||
|
const TYPE_API_LIMIT = 'api_limit';
|
||||||
|
|
||||||
private static $titles = array(
|
private static $titles = array(
|
||||||
self::TYPE_VALIDATION_ERROR => 'There was a validation error',
|
self::TYPE_VALIDATION_ERROR => 'There was a validation error',
|
||||||
@@ -18,6 +19,7 @@ class ApiErrorMessage
|
|||||||
self::TYPE_LOGICAL_ERROR => 'Invalid request due to a logic error',
|
self::TYPE_LOGICAL_ERROR => 'Invalid request due to a logic error',
|
||||||
self::TYPE_SERVER_ERROR => 'Error on server side',
|
self::TYPE_SERVER_ERROR => 'Error on server side',
|
||||||
self::TYPE_INVALID_DATE => 'Invalid date on request',
|
self::TYPE_INVALID_DATE => 'Invalid date on request',
|
||||||
|
self::TYPE_API_LIMIT => 'Reached API Limit'
|
||||||
);
|
);
|
||||||
|
|
||||||
private int $statusCode;
|
private int $statusCode;
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Entity;
|
||||||
|
|
||||||
|
use App\Repository\RoomPriceRepository;
|
||||||
|
use Doctrine\DBAL\Types\Types;
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
|
#[ORM\Entity(repositoryClass: RoomPriceRepository::class)]
|
||||||
|
class RoomPrice
|
||||||
|
{
|
||||||
|
#[ORM\Id]
|
||||||
|
#[ORM\ManyToOne]
|
||||||
|
#[ORM\JoinColumn(nullable: false)]
|
||||||
|
private ?RoomCategory $roomCategory = null;
|
||||||
|
|
||||||
|
#[ORM\Id]
|
||||||
|
#[ORM\Column(length: 10, nullable: false)]
|
||||||
|
private ?string $date = null;
|
||||||
|
|
||||||
|
#[ORM\Id]
|
||||||
|
#[ORM\Column(type: Types::SMALLINT, nullable: false)]
|
||||||
|
private ?int $guestNumber = null;
|
||||||
|
|
||||||
|
#[ORM\Column(nullable: false)]
|
||||||
|
private ?int $price = null;
|
||||||
|
|
||||||
|
|
||||||
|
public function getRoomCategory(): ?RoomCategory
|
||||||
|
{
|
||||||
|
return $this->roomCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRoomCategory(?RoomCategory $roomCategory): static
|
||||||
|
{
|
||||||
|
$this->roomCategory = $roomCategory;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDate(): ?\DateTime
|
||||||
|
{
|
||||||
|
return new \DateTime($this->date);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDateString(): ?string
|
||||||
|
{
|
||||||
|
return $this->date;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDate(\DateTime $date): static
|
||||||
|
{
|
||||||
|
$this->date = $date->format('Y-m-d');
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGuestNumber(): ?int
|
||||||
|
{
|
||||||
|
return $this->guestNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setGuestNumber(int $guestNumber): static
|
||||||
|
{
|
||||||
|
$this->guestNumber = $guestNumber;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPrice(): ?int
|
||||||
|
{
|
||||||
|
return $this->price;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPrice(int $price): static
|
||||||
|
{
|
||||||
|
$this->price = $price;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ namespace App\Entity;
|
|||||||
|
|
||||||
use App\Enum\UserRoleEnum;
|
use App\Enum\UserRoleEnum;
|
||||||
use App\Repository\UserRepository;
|
use App\Repository\UserRepository;
|
||||||
|
use Deprecated;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
@@ -120,6 +121,7 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
|||||||
/**
|
/**
|
||||||
* @see UserInterface
|
* @see UserInterface
|
||||||
*/
|
*/
|
||||||
|
#[Deprecated("Not used after Symfony 7.3")]
|
||||||
public function eraseCredentials(): void
|
public function eraseCredentials(): void
|
||||||
{
|
{
|
||||||
// If you store any temporary, sensitive data on the user, clear it here
|
// If you store any temporary, sensitive data on the user, clear it here
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\Entity\RoomPrice;
|
||||||
|
use DateTimeInterface;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends ServiceEntityRepository<RoomPrice>
|
||||||
|
*/
|
||||||
|
class RoomPriceRepository extends ServiceEntityRepository
|
||||||
|
{
|
||||||
|
public function __construct(ManagerRegistry $registry)
|
||||||
|
{
|
||||||
|
parent::__construct($registry, RoomPrice::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all pricer for the given period.
|
||||||
|
* @param DateTimeInterface $startDate Period start date (included)
|
||||||
|
* @param DateTimeInterface $endDate Period end date (included)
|
||||||
|
* @return RoomPrice[] Array of RoomPrices
|
||||||
|
*/
|
||||||
|
public function getListForPeriod(DateTimeInterface $startDate, DateTimeInterface $endDate, ?int $roomCategoryId = null): array
|
||||||
|
{
|
||||||
|
$strStartDate = $startDate->format('Y-m-d');
|
||||||
|
$strEndDate = $endDate->format('Y-m-d');
|
||||||
|
$hasRoomCategory = $roomCategoryId != null;
|
||||||
|
|
||||||
|
$dql = 'SELECT p, rc FROM App\Entity\RoomPrice p
|
||||||
|
JOIN p.roomCategory rc
|
||||||
|
WHERE p.date >= :startDate
|
||||||
|
AND p.date <= :endDate';
|
||||||
|
|
||||||
|
if ($hasRoomCategory) {
|
||||||
|
$dql .= ' AND IDENTITY(p.roomCategory) = :roomCategoryId';
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = $this->getEntityManager()->createQuery($dql)
|
||||||
|
->setParameter('startDate', $strStartDate)
|
||||||
|
->setParameter('endDate', $strEndDate);
|
||||||
|
|
||||||
|
if ($hasRoomCategory) {
|
||||||
|
$query->setParameter('roomCategoryId', $roomCategoryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query->getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * @return RoomPrice[] Returns an array of RoomPrice 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): ?RoomPrice
|
||||||
|
// {
|
||||||
|
// return $this->createQueryBuilder('r')
|
||||||
|
// ->andWhere('r.exampleField = :val')
|
||||||
|
// ->setParameter('val', $value)
|
||||||
|
// ->getQuery()
|
||||||
|
// ->getOneOrNullResult()
|
||||||
|
// ;
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Response;
|
namespace App\Response;
|
||||||
|
|
||||||
use Nelmio\ApiDocBundle\Annotation\Model;
|
use Nelmio\ApiDocBundle\Attribute\Model;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
use OpenApi\Attributes\Items;
|
use OpenApi\Attributes\Items;
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,16 @@ class ConfigVariableService
|
|||||||
return $this->repoVariables->findVariableByKey($key);
|
return $this->repoVariables->findVariableByKey($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getVariableFromConst(array $varConst): ConfigVariable|null
|
||||||
|
{
|
||||||
|
$key = ConfigVariableConsts::getKeyFromConstant($varConst);
|
||||||
|
if (is_null($key)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->getVariable($key);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create and init all variables.
|
* Create and init all variables.
|
||||||
* To be used during Setup process.
|
* To be used during Setup process.
|
||||||
|
|||||||
+169
-81
@@ -2,13 +2,17 @@
|
|||||||
|
|
||||||
namespace App\Service;
|
namespace App\Service;
|
||||||
|
|
||||||
|
use App\Consts\ConfigVariableConsts;
|
||||||
use App\Entity\FareTable;
|
use App\Entity\FareTable;
|
||||||
use App\Entity\RoomCategory;
|
use App\Entity\RoomCategory;
|
||||||
|
use App\Entity\RoomFare;
|
||||||
use App\Repository\FareTableRepository;
|
use App\Repository\FareTableRepository;
|
||||||
use App\Repository\RoomCategoryRepository;
|
use App\Repository\RoomCategoryRepository;
|
||||||
use App\Repository\RoomFareRepository;
|
use App\Repository\RoomFareRepository;
|
||||||
|
use App\Repository\RoomPriceRepository;
|
||||||
use App\Response\SearchPricesResponse;
|
use App\Response\SearchPricesResponse;
|
||||||
use App\Response\SearchPricesResponsePriceItem;
|
use App\Response\SearchPricesResponsePriceItem;
|
||||||
|
use App\Util\PriceSelectionMethods;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Ds\Set;
|
use Ds\Set;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
@@ -19,13 +23,19 @@ use Psr\Log\LoggerInterface;
|
|||||||
*/
|
*/
|
||||||
class PricesService
|
class PricesService
|
||||||
{
|
{
|
||||||
|
private string $configFareMethod;
|
||||||
|
private string $configPriceMethod;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private RoomCategoryRepository $repoRoomCategory,
|
private RoomCategoryRepository $repoRoomCategory,
|
||||||
private FareTableRepository $repoFareTable,
|
private FareTableRepository $repoFareTable,
|
||||||
private RoomFareRepository $repoRoomFares,
|
private RoomPriceRepository $repoRoomPrices,
|
||||||
|
private ConfigVariableService $configVarService,
|
||||||
private LoggerInterface $logger
|
private LoggerInterface $logger
|
||||||
) {}
|
) {
|
||||||
|
$this->configFareMethod = $configVarService->getVariableFromConst(ConfigVariableConsts::CONFIG_PRICES_FAREMETHOD)->getValue();
|
||||||
|
$this->configPriceMethod = $configVarService->getVariableFromConst(ConfigVariableConsts::CONFIG_PRICES_PRICEMETHOD)->getValue();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calcula los precios de la estancia para la categoría y periodo indicados.
|
* Calcula los precios de la estancia para la categoría y periodo indicados.
|
||||||
@@ -44,43 +54,46 @@ class PricesService
|
|||||||
}
|
}
|
||||||
|
|
||||||
$fareTablesForPeriod = $this->repoFareTable->findFareTable($category->getId(), $startDate, $endDate);
|
$fareTablesForPeriod = $this->repoFareTable->findFareTable($category->getId(), $startDate, $endDate);
|
||||||
|
$roomPriceList = $this->repoRoomPrices->getListForPeriod($startDate, $endDate, $category->getId());
|
||||||
|
|
||||||
|
//"Organize" RoomPrices.
|
||||||
|
$filteredRoomPriceList = [];
|
||||||
|
foreach ($roomPriceList as $roomPrice) {
|
||||||
|
$priceDate = $roomPrice->getDateString();
|
||||||
|
$filteredRoomPriceList[$roomPrice->getGuestNumber()][$priceDate] = $roomPrice->getPrice();
|
||||||
|
}
|
||||||
|
|
||||||
//Obtenemos el número de personas permitido por todos los tarifarios activos.
|
//Obtenemos el número de personas permitido por todos los tarifarios activos.
|
||||||
$allowedGuestNumbers = $this->getAllowedGuestNumberFromFaretableList($fareTablesForPeriod);
|
$allowedGuestNumberList = $this->getAllowedGuestNumberFromFaretableList($fareTablesForPeriod);
|
||||||
|
|
||||||
$rs = [];
|
//Calculate stay price for each allowed GuestNumber.
|
||||||
|
$priceItems = [];
|
||||||
|
foreach ($allowedGuestNumberList as $currentGuestNumber) {
|
||||||
|
|
||||||
//Calculamos los precios para cada GuestNumber permitido.
|
//Get RoomPrices for current GuestNumber.
|
||||||
foreach ($allowedGuestNumbers as $currentGuestNumber) {
|
$roomPricesForGuestNum = [];
|
||||||
|
if (array_key_exists($currentGuestNumber, $filteredRoomPriceList)) {
|
||||||
|
$roomPricesForGuestNum = $filteredRoomPriceList[$currentGuestNumber];
|
||||||
|
}
|
||||||
|
|
||||||
//Calculamos el precio de cada día.
|
//Calculate the price for each day.
|
||||||
// e.g. --> [ "2024-04-30" => 20, "2024-05-01" => 28, "2024-05-02" => 20, ...].
|
$dailyPrices = $this->getDailyPricesForSearch($startDate, $endDate, $currentGuestNumber, $fareTablesForPeriod, $roomPricesForGuestNum);
|
||||||
$dailyPrices = $this->getDailyPricesForSearch($startDate, $endDate, $currentGuestNumber, $fareTablesForPeriod);
|
|
||||||
|
|
||||||
$rs[$currentGuestNumber] = $dailyPrices;
|
$stayPrice = $this->getTotalPriceForStay($dailyPrices);
|
||||||
|
|
||||||
|
$item = new SearchPricesResponsePriceItem();
|
||||||
|
$item->guestNumber = $currentGuestNumber;
|
||||||
|
$item->price = $stayPrice;
|
||||||
|
$priceItems[] = $item;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Aplicamos la estrategia de precios configurada para calcular el precio final de la estancia.
|
return $priceItems;
|
||||||
// TODOME: Crear y configurar diversas estrategias de precios -> Por el momento utilizamos "Suma del precio de cada día".
|
|
||||||
|
|
||||||
$finalResult = [];
|
|
||||||
foreach ($rs as $guestNum => $priceArray) {
|
|
||||||
$finalAmount = \array_reduce($priceArray, function ($acc, $element) {
|
|
||||||
return $acc + $element;
|
|
||||||
});
|
|
||||||
|
|
||||||
$finalResult[$guestNum] = $finalAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $finalResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate stay total price on each roomCategory for the given time period
|
* Calculate stay total price on each roomCategory for the given time period
|
||||||
* Returns a list with all roomCategories, containing the total price by guestnumber.
|
* Returns a list with all roomCategories, containing the total price by guestnumber.
|
||||||
*
|
*
|
||||||
* e. g. --> [ 2 => 120€, 3 => 180€ ]
|
|
||||||
*
|
|
||||||
* @return SearchPricesResponse[] Total prices for each guestnumber of each roomCategory.
|
* @return SearchPricesResponse[] Total prices for each guestnumber of each roomCategory.
|
||||||
*/
|
*/
|
||||||
public function getRoomSearch(\DateTimeInterface $startDate, \DateTimeInterface $endDate): array
|
public function getRoomSearch(\DateTimeInterface $startDate, \DateTimeInterface $endDate): array
|
||||||
@@ -100,41 +113,57 @@ class PricesService
|
|||||||
$faretablesByRoomCategory[$faretable->getRoomCategory()->getId()][] = $faretable;
|
$faretablesByRoomCategory[$faretable->getRoomCategory()->getId()][] = $faretable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Get and filter prices.
|
||||||
|
$priceList = $this->repoRoomPrices->getListForPeriod($startDate, $endDate);
|
||||||
|
$filteredPrices = [];
|
||||||
|
foreach ($priceList as $price) {
|
||||||
|
$priceDate = $price->getDateString();
|
||||||
|
//Store price in multi-level array by: RoomCategoryId, GuestNumber and Date.
|
||||||
|
$filteredPrices[$price->getRoomCategory()->getId()][$price->getGuestNumber()][$priceDate] = $price->getPrice();
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($roomCategoryList as $roomCat) {
|
foreach ($roomCategoryList as $roomCat) {
|
||||||
|
$currentRoomCategoryId = $roomCat->getId();
|
||||||
|
|
||||||
//Get Faretables for RoomCategory.
|
//Get Faretables for RoomCategory.
|
||||||
$roomFaretables = [];
|
$roomFaretables = [];
|
||||||
if (array_key_exists($roomCat->getId(), $faretablesByRoomCategory)) {
|
if (array_key_exists($currentRoomCategoryId, $faretablesByRoomCategory)) {
|
||||||
$roomFaretables = $faretablesByRoomCategory[$roomCat->getId()];
|
$roomFaretables = $faretablesByRoomCategory[$currentRoomCategoryId];
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get RoomPrices for current RoomCategory.
|
||||||
|
$roomPricesForCategory = [];
|
||||||
|
if (array_key_exists($currentRoomCategoryId, $filteredPrices)) {
|
||||||
|
$roomPricesForCategory = $filteredPrices[$currentRoomCategoryId];
|
||||||
}
|
}
|
||||||
|
|
||||||
//Calculate distinct GuestNumber for RoomCategory by it's faretables.
|
//Calculate distinct GuestNumber for RoomCategory by it's faretables.
|
||||||
$allowedGuestNumberList = $this->getAllowedGuestNumberFromFaretableList($roomFaretables);
|
$allowedGuestNumberList = $this->getAllowedGuestNumberFromFaretableList($roomFaretables);
|
||||||
|
|
||||||
//Calculate stay price for each allowed GuestNumber.
|
//Calculate stay price for each allowed GuestNumber.
|
||||||
$roomPricesByGuestNum = [];
|
$priceItems = [];
|
||||||
foreach ($allowedGuestNumberList as $currentGuestNumber) {
|
foreach ($allowedGuestNumberList as $currentGuestNumber) {
|
||||||
|
|
||||||
|
//Get RoomPrices for current GuestNumber.
|
||||||
|
$roomPricesForGuestNum = [];
|
||||||
|
if (array_key_exists($currentGuestNumber, $roomPricesForCategory)) {
|
||||||
|
$roomPricesForGuestNum = $roomPricesForCategory[$currentGuestNumber];
|
||||||
|
}
|
||||||
|
|
||||||
//Calculate the price for each day.
|
//Calculate the price for each day.
|
||||||
$dailyPrices = $this->getDailyPricesForSearch($startDate, $endDate, $currentGuestNumber, $roomFaretables);
|
$dailyPrices = $this->getDailyPricesForSearch($startDate, $endDate, $currentGuestNumber, $roomFaretables, $roomPricesForGuestNum);
|
||||||
|
|
||||||
$stayPrice = $this->getTotalPriceForStay($dailyPrices);
|
$stayPrice = $this->getTotalPriceForStay($dailyPrices);
|
||||||
|
|
||||||
$roomPricesByGuestNum[$currentGuestNumber] = $stayPrice;
|
|
||||||
}
|
|
||||||
|
|
||||||
$roomPrices = [];
|
|
||||||
foreach ($roomPricesByGuestNum as $guestNum => $price) {
|
|
||||||
$item = new SearchPricesResponsePriceItem();
|
$item = new SearchPricesResponsePriceItem();
|
||||||
$item->guestNumber = $guestNum;
|
$item->guestNumber = $currentGuestNumber;
|
||||||
$item->price = $price;
|
$item->price = $stayPrice;
|
||||||
|
$priceItems[] = $item;
|
||||||
$roomPrices[] = $item;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$roomItem = new SearchPricesResponse();
|
$roomItem = new SearchPricesResponse();
|
||||||
$roomItem->roomCategoryId = $roomCat->getId();
|
$roomItem->roomCategoryId = $roomCat->getId();
|
||||||
$roomItem->prices = $roomPrices;
|
$roomItem->prices = $priceItems;
|
||||||
|
|
||||||
$rs[] = $roomItem;
|
$rs[] = $roomItem;
|
||||||
}
|
}
|
||||||
@@ -150,7 +179,7 @@ class PricesService
|
|||||||
* @param array $fareTableList Array con los tarifarios que aplican en el periodo indicado.
|
* @param array $fareTableList Array con los tarifarios que aplican en el periodo indicado.
|
||||||
* @return array Devuelve un array con los precios: e.g. --> [ "2024-04-30" => 2500, "2024-05-01" => 3500, "2024-05-02" => 3000, ...].
|
* @return array Devuelve un array con los precios: e.g. --> [ "2024-04-30" => 2500, "2024-05-01" => 3500, "2024-05-02" => 3000, ...].
|
||||||
*/
|
*/
|
||||||
private function getDailyPricesForSearch(\DateTimeInterface $startDate, \DateTimeInterface $endDate, int $guestNumber, array $fareTableList): array
|
private function getDailyPricesForSearch(\DateTimeInterface $startDate, \DateTimeInterface $endDate, int $guestNumber, array $fareTableList, array $roomPricesByGuestNum): array
|
||||||
{
|
{
|
||||||
$daysDiff = $startDate->diff($endDate)->days;
|
$daysDiff = $startDate->diff($endDate)->days;
|
||||||
|
|
||||||
@@ -159,7 +188,8 @@ class PricesService
|
|||||||
for ($i = 0; $i < $daysDiff; $i++) {
|
for ($i = 0; $i < $daysDiff; $i++) {
|
||||||
$strCurrentDate = $currentDate->format("Y-m-d");
|
$strCurrentDate = $currentDate->format("Y-m-d");
|
||||||
|
|
||||||
//Obtenemos el tarifario que aplica --> TODOME: Buscar si se puede cachear esto, para no recarlcularlo para cada GuestNumber.
|
//Get Faretable for current day.
|
||||||
|
//TODO: Possible optimization - This data doesn't need to be recalculated so often.
|
||||||
$currentFareTableList = \array_filter($fareTableList, function (FareTable $element) use ($currentDate) {
|
$currentFareTableList = \array_filter($fareTableList, function (FareTable $element) use ($currentDate) {
|
||||||
return $element->isActiveOnDate($currentDate);
|
return $element->isActiveOnDate($currentDate);
|
||||||
});
|
});
|
||||||
@@ -177,33 +207,41 @@ class PricesService
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
$currentFareTable = array_values($currentFareTableList)[0];
|
|
||||||
|
|
||||||
//TODOME: -> Obtener precio específico (RoomPrices).
|
//Check for Prices.
|
||||||
|
$roomPrice = null;
|
||||||
//Si no hay precio, calcular mediante tarifarios.
|
if (array_key_exists($strCurrentDate, $roomPricesByGuestNum)) {
|
||||||
$roomFares = $currentFareTable->getRoomFaresByGuestNumber($guestNumber);
|
$roomPrice = $roomPricesByGuestNum[$strCurrentDate];
|
||||||
if (count($roomFares) < 1) {
|
|
||||||
$this->logger->error(
|
|
||||||
"PricesService.getDailyPricesForSearch: Can't calculate prices: Found faretable without fares",
|
|
||||||
[
|
|
||||||
"currentFareTable" => $currentFareTable,
|
|
||||||
"guestNumber" => $guestNumber,
|
|
||||||
"roomFaresForGuestNumber" => $roomFares
|
|
||||||
]
|
|
||||||
);
|
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Si sólo hay 1 tarifa, aplico esa, sino calculo la apropiada según condiciones.
|
//Si no hay precio, calcular mediante tarifarios.
|
||||||
$fareAmount = null;
|
$fareAmount = null;
|
||||||
if (count($roomFares) == 1) {
|
if ($roomPrice == null) {
|
||||||
$fareAmount = $roomFares->first()->getFareAmount();
|
//Get Faretables for guestNumber.
|
||||||
} else {
|
$currentFareTable = array_values($currentFareTableList)[0];
|
||||||
|
$roomFares = $currentFareTable->getRoomFaresByGuestNumber($guestNumber);
|
||||||
|
if (count($roomFares) < 1) {
|
||||||
|
$this->logger->error(
|
||||||
|
"PricesService.getDailyPricesForSearch: Can't calculate prices: Found faretable without fares",
|
||||||
|
[
|
||||||
|
"currentFareTable" => $currentFareTable,
|
||||||
|
"guestNumber" => $guestNumber,
|
||||||
|
"roomFaresForGuestNumber" => $roomFares
|
||||||
|
]
|
||||||
|
);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
//Calculate RoomFare.
|
||||||
$fareAmount = $this->calculateFareApplyingConditions($roomFares, $currentDate);
|
$fareAmount = $this->calculateFareApplyingConditions($roomFares, $currentDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
$dailyPrices[$strCurrentDate] = $fareAmount;
|
//If any price can't be calculated, NO price should be returned.
|
||||||
|
if ($roomPrice == null && $fareAmount == null) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$dailyPrices[$strCurrentDate] = $roomPrice ?? $fareAmount;
|
||||||
|
|
||||||
//Add 1 day to current date.
|
//Add 1 day to current date.
|
||||||
$currentDate->add(new \DateInterval("P1D"));
|
$currentDate->add(new \DateInterval("P1D"));
|
||||||
@@ -218,32 +256,76 @@ class PricesService
|
|||||||
* @param DateTimeInterface $fareCondDate Fecha utilizada para las condiciones.
|
* @param DateTimeInterface $fareCondDate Fecha utilizada para las condiciones.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private function calculateFareApplyingConditions(Collection $roomFareList, \DateTimeInterface $fareCondDate): int
|
private function calculateFareApplyingConditions(Collection $roomFareList, \DateTimeInterface $fareCondDate): int|null
|
||||||
{
|
{
|
||||||
$currentDayOfWeek = $fareCondDate->format("w");
|
|
||||||
|
|
||||||
if (count($roomFareList) == 1) {
|
if (count($roomFareList) == 1) {
|
||||||
return $roomFareList->first()->getFareAmount();
|
return $roomFareList->first()->getFareAmount();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Comprobamos si a alguna de las tarifas se la aplica la condición de "DayType" y coincide con el actual.
|
$currentDayOfWeek = $fareCondDate->format("w");
|
||||||
// Si es así, devolvemos su valor.
|
//Filter applicable fares.
|
||||||
foreach ($roomFareList as $roomFare) {
|
//TODO: Possible optimization - This data doesn't need to be recalculated so often; just once for each FareTable.
|
||||||
$dayType = $roomFare->getDayType();
|
/** @var RoomFare[] */
|
||||||
|
$applFaresByDay = [];
|
||||||
|
/** @var RoomFare[] */
|
||||||
|
$applFaresNonCondition = [];
|
||||||
|
foreach ($roomFareList as $fare) {
|
||||||
|
$dayType = $fare->getDayType();
|
||||||
if ($dayType == null || $dayType == "") {
|
if ($dayType == null || $dayType == "") {
|
||||||
|
$applFaresNonCondition[] = $fare;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ($dayType == $currentDayOfWeek) {
|
if ($dayType == $currentDayOfWeek) {
|
||||||
return $roomFare->getFareAmount();
|
$applFaresByDay[] = $fare;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODOME: Si existen múltiples tarifas posibles, debería haber una estrategia para elegir una u otra. Variable de configuración para esto.
|
//Return final fare value.
|
||||||
$faresWithoutConditions = $roomFareList->filter(function ($element) {
|
//Use fares for current weekday.
|
||||||
return $element->getDayType() == null;
|
$finalFare = $this->getPriceFromRoomFareList($applFaresByDay);
|
||||||
});
|
if ($finalFare != null) {
|
||||||
|
return $finalFare;
|
||||||
|
}
|
||||||
|
|
||||||
return $faresWithoutConditions->first()->getFareAmount();
|
//Use non-conditional fares.
|
||||||
|
$finalFare = $this->getPriceFromRoomFareList($applFaresNonCondition);
|
||||||
|
if ($finalFare != null) {
|
||||||
|
return $finalFare;
|
||||||
|
}
|
||||||
|
|
||||||
|
//No valid fare found!
|
||||||
|
$this->logger->error(
|
||||||
|
"PricesService.calculateFareApplyingConditions: No RoomFare found. Price can't be calculated.",
|
||||||
|
["RoomFareList" => $roomFareList, "Date" => $fareCondDate]
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the fare with bigger priority from a list of fares.
|
||||||
|
* @param RoomFare[] $roomFareList List of RoomFares.
|
||||||
|
*/
|
||||||
|
private function getPriceFromRoomFareList(array $roomFareList): int|null
|
||||||
|
{
|
||||||
|
$countFares = count($roomFareList);
|
||||||
|
if ($roomFareList == null || $countFares == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($countFares == 1) {
|
||||||
|
return $roomFareList[0]->getFareAmount();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($this->configFareMethod) {
|
||||||
|
case 'HIGHER':
|
||||||
|
return PriceSelectionMethods::selectFare_Highest($roomFareList);
|
||||||
|
case 'LOWER':
|
||||||
|
return PriceSelectionMethods::selectFare_Lowest($roomFareList);
|
||||||
|
case 'AVERAGE':
|
||||||
|
return PriceSelectionMethods::selectFare_Average($roomFareList);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -273,13 +355,19 @@ class PricesService
|
|||||||
* @param array $dailyPrices Array containing the price for each day.
|
* @param array $dailyPrices Array containing the price for each day.
|
||||||
* @return int Total stay price.
|
* @return int Total stay price.
|
||||||
*/
|
*/
|
||||||
private function getTotalPriceForStay(array $dailyPrices): int
|
private function getTotalPriceForStay(array $dailyPrices): int|null
|
||||||
{
|
{
|
||||||
// TODOME: Crear y configurar diversas estrategias de precios -> Por el momento utilizamos "Suma del precio de cada día".
|
switch ($this->configPriceMethod) {
|
||||||
|
case 'SUM':
|
||||||
$finalAmount = \array_reduce($dailyPrices, function ($acc, $element) {
|
return PriceSelectionMethods::priceMethod_Sum($dailyPrices);
|
||||||
return $acc + $element;
|
case 'AVERAGE':
|
||||||
});
|
return PriceSelectionMethods::priceMethod_Average($dailyPrices);
|
||||||
return $finalAmount;
|
case 'HIGHER':
|
||||||
|
return PriceSelectionMethods::priceMethod_Highest($dailyPrices);
|
||||||
|
case 'LOWER':
|
||||||
|
return PriceSelectionMethods::priceMethod_Lowest($dailyPrices);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Util;
|
||||||
|
|
||||||
|
use App\Entity\RoomFare;
|
||||||
|
|
||||||
|
//Utils - Functions to select prices and fares.
|
||||||
|
class PriceSelectionMethods
|
||||||
|
{
|
||||||
|
#region Fare Selection
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fare Selection - Get fare with highest price.
|
||||||
|
* @param RoomFare[] $fareList List of fares.
|
||||||
|
*/
|
||||||
|
public static function selectFare_Highest(array $fareList): int
|
||||||
|
{
|
||||||
|
$pricesArr = array_map(function (RoomFare $f) {
|
||||||
|
return $f->getFareAmount();
|
||||||
|
}, $fareList);
|
||||||
|
|
||||||
|
return max($pricesArr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fare Selection - Get fare with lowest price.
|
||||||
|
* @param RoomFare[] $fareList List of fares.
|
||||||
|
*/
|
||||||
|
public static function selectFare_Lowest(array $fareList): int
|
||||||
|
{
|
||||||
|
$pricesArr = array_map(function (RoomFare $f) {
|
||||||
|
return $f->getFareAmount();
|
||||||
|
}, $fareList);
|
||||||
|
|
||||||
|
return min($pricesArr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fare Selection - Get average price of given fares.
|
||||||
|
* @param RoomFare[] $fareList List of fares.
|
||||||
|
*/
|
||||||
|
public static function selectFare_Average(array $fareList): int|null
|
||||||
|
{
|
||||||
|
$totalAmount = 0;
|
||||||
|
$count = 0;
|
||||||
|
foreach ($fareList as $fare) {
|
||||||
|
$fareAmount = $fare->getFareAmount();
|
||||||
|
if ($fareAmount != null) {
|
||||||
|
$totalAmount += $fareAmount;
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $count > 0 ? ($totalAmount / $count) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Price Selection
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Price Method - Total price is the sum of daily prices.
|
||||||
|
* @param array $prices
|
||||||
|
* @return int Total price.
|
||||||
|
*/
|
||||||
|
public static function priceMethod_Sum(array $prices): int
|
||||||
|
{
|
||||||
|
$values = array_values($prices);
|
||||||
|
return array_sum($values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Price Method - Apply the hightest daily price to all days.
|
||||||
|
* @param array $prices
|
||||||
|
* @return int Total price.
|
||||||
|
*/
|
||||||
|
public static function priceMethod_Highest(array $prices): int
|
||||||
|
{
|
||||||
|
$highestPrice = max($prices);
|
||||||
|
$numDays = count($prices);
|
||||||
|
|
||||||
|
return $numDays * $highestPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Price Method - Apply the lowest daily price to all days.
|
||||||
|
* @param array $prices
|
||||||
|
* @return int Total price.
|
||||||
|
*/
|
||||||
|
public static function priceMethod_Lowest(array $prices): int
|
||||||
|
{
|
||||||
|
$highestPrice = min($prices);
|
||||||
|
$numDays = count($prices);
|
||||||
|
|
||||||
|
return $numDays * $highestPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Price Method - Calculate average daily price and apply it to all days.
|
||||||
|
* @param array $prices
|
||||||
|
* @return int Total price.
|
||||||
|
*/
|
||||||
|
public static function priceMethod_Average(array $prices): int|null
|
||||||
|
{
|
||||||
|
$sum = array_sum($prices);
|
||||||
|
$numDays = count($prices);
|
||||||
|
$averagePrice = $numDays > 0 ? $sum / $numDays : null;
|
||||||
|
|
||||||
|
return $averagePrice != null ? $averagePrice * $numDays : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
+42
-37
@@ -9,17 +9,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"doctrine/doctrine-bundle": {
|
"doctrine/doctrine-bundle": {
|
||||||
"version": "2.12",
|
"version": "2.18",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
"repo": "github.com/symfony/recipes",
|
"repo": "github.com/symfony/recipes",
|
||||||
"branch": "main",
|
"branch": "main",
|
||||||
"version": "2.12",
|
"version": "2.13",
|
||||||
"ref": "32f890075233ac4a1176e848a0f8e932ac6536d6"
|
"ref": "620b57f496f2e599a6015a9fa222c2ee0a32adcb"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"./config/packages/doctrine.yaml",
|
"config/packages/doctrine.yaml",
|
||||||
"./src/Entity/.gitignore",
|
"src/Entity/.gitignore",
|
||||||
"./src/Repository/.gitignore"
|
"src/Repository/.gitignore"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"doctrine/doctrine-migrations-bundle": {
|
"doctrine/doctrine-migrations-bundle": {
|
||||||
@@ -36,13 +36,16 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"easycorp/easyadmin-bundle": {
|
"easycorp/easyadmin-bundle": {
|
||||||
"version": "4.10",
|
"version": "4.26",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
"repo": "github.com/symfony/recipes",
|
"repo": "github.com/symfony/recipes",
|
||||||
"branch": "main",
|
"branch": "main",
|
||||||
"version": "3.0",
|
"version": "4.14",
|
||||||
"ref": "b131e6cbfe1b898a508987851963fff485986285"
|
"ref": "13b3e524d1038a6861ad6c7fc64e31b644ba0c54"
|
||||||
}
|
},
|
||||||
|
"files": [
|
||||||
|
"config/routes/easyadmin.yaml"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"nelmio/api-doc-bundle": {
|
"nelmio/api-doc-bundle": {
|
||||||
"version": "4.30",
|
"version": "4.30",
|
||||||
@@ -70,18 +73,18 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"symfony/asset-mapper": {
|
"symfony/asset-mapper": {
|
||||||
"version": "7.0",
|
"version": "7.3",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
"repo": "github.com/symfony/recipes",
|
"repo": "github.com/symfony/recipes",
|
||||||
"branch": "main",
|
"branch": "main",
|
||||||
"version": "6.4",
|
"version": "6.4",
|
||||||
"ref": "6c28c471640cc2c6e60812ebcb961c526ef8997f"
|
"ref": "5ad1308aa756d58f999ffbe1540d1189f5d7d14a"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"./assets/app.js",
|
"assets/app.js",
|
||||||
"./assets/styles/app.css",
|
"assets/styles/app.css",
|
||||||
"./config/packages/asset_mapper.yaml",
|
"config/packages/asset_mapper.yaml",
|
||||||
"./importmap.php"
|
"importmap.php"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"symfony/console": {
|
"symfony/console": {
|
||||||
@@ -97,15 +100,16 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"symfony/flex": {
|
"symfony/flex": {
|
||||||
"version": "2.4",
|
"version": "2.8",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
"repo": "github.com/symfony/recipes",
|
"repo": "github.com/symfony/recipes",
|
||||||
"branch": "main",
|
"branch": "main",
|
||||||
"version": "1.0",
|
"version": "2.4",
|
||||||
"ref": "146251ae39e06a95be0fe3d13c807bcf3938b172"
|
"ref": "52e9754527a15e2b79d9a610f98185a1fe46622a"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
".env"
|
".env",
|
||||||
|
".env.dev"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"symfony/form": {
|
"symfony/form": {
|
||||||
@@ -121,14 +125,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"symfony/framework-bundle": {
|
"symfony/framework-bundle": {
|
||||||
"version": "7.0",
|
"version": "7.3",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
"repo": "github.com/symfony/recipes",
|
"repo": "github.com/symfony/recipes",
|
||||||
"branch": "main",
|
"branch": "main",
|
||||||
"version": "7.0",
|
"version": "7.3",
|
||||||
"ref": "6356c19b9ae08e7763e4ba2d9ae63043efc75db5"
|
"ref": "5a1497d539f691b96afd45ae397ce5fe30beb4b9"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
|
".editorconfig",
|
||||||
"config/packages/cache.yaml",
|
"config/packages/cache.yaml",
|
||||||
"config/packages/framework.yaml",
|
"config/packages/framework.yaml",
|
||||||
"config/preload.php",
|
"config/preload.php",
|
||||||
@@ -173,12 +178,12 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"symfony/routing": {
|
"symfony/routing": {
|
||||||
"version": "7.0",
|
"version": "7.3",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
"repo": "github.com/symfony/recipes",
|
"repo": "github.com/symfony/recipes",
|
||||||
"branch": "main",
|
"branch": "main",
|
||||||
"version": "7.0",
|
"version": "7.0",
|
||||||
"ref": "21b72649d5622d8f7da329ffb5afb232a023619d"
|
"ref": "ab1e60e2afd5c6f4a6795908f646e235f2564eb2"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"config/packages/routing.yaml",
|
"config/packages/routing.yaml",
|
||||||
@@ -199,16 +204,16 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"symfony/translation": {
|
"symfony/translation": {
|
||||||
"version": "7.0",
|
"version": "7.3",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
"repo": "github.com/symfony/recipes",
|
"repo": "github.com/symfony/recipes",
|
||||||
"branch": "main",
|
"branch": "main",
|
||||||
"version": "6.3",
|
"version": "6.3",
|
||||||
"ref": "e28e27f53663cc34f0be2837aba18e3a1bef8e7b"
|
"ref": "620a1b84865ceb2ba304c8f8bf2a185fbf32a843"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"./config/packages/translation.yaml",
|
"config/packages/translation.yaml",
|
||||||
"./translations/.gitignore"
|
"translations/.gitignore"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"symfony/twig-bundle": {
|
"symfony/twig-bundle": {
|
||||||
@@ -246,15 +251,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"symfony/ux-twig-component": {
|
"symfony/ux-twig-component": {
|
||||||
"version": "2.25",
|
"version": "2.30",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
"repo": "github.com/symfony/recipes",
|
"repo": "github.com/symfony/recipes",
|
||||||
"branch": "main",
|
"branch": "main",
|
||||||
"version": "2.13",
|
"version": "2.13",
|
||||||
"ref": "9b627037a510e20eddb559e06050a34854ce51d6"
|
"ref": "f367ae2a1faf01c503de2171f1ec22567febeead"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"./config/packages/twig_component.yaml"
|
"config/packages/twig_component.yaml"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"symfony/validator": {
|
"symfony/validator": {
|
||||||
@@ -270,16 +275,16 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"symfony/web-profiler-bundle": {
|
"symfony/web-profiler-bundle": {
|
||||||
"version": "7.0",
|
"version": "7.3",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
"repo": "github.com/symfony/recipes",
|
"repo": "github.com/symfony/recipes",
|
||||||
"branch": "main",
|
"branch": "main",
|
||||||
"version": "6.1",
|
"version": "7.3",
|
||||||
"ref": "e42b3f0177df239add25373083a564e5ead4e13a"
|
"ref": "a363460c1b0b4a4d0242f2ce1a843ca0f6ac9026"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"./config/packages/web_profiler.yaml",
|
"config/packages/web_profiler.yaml",
|
||||||
"./config/routes/web_profiler.yaml"
|
"config/routes/web_profiler.yaml"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"twig/extra-bundle": {
|
"twig/extra-bundle": {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
|
{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
|
||||||
{# @var field \EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto #}
|
{# @var field \EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto #}
|
||||||
{# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #}
|
{# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #}
|
||||||
{% if ea.crud.currentAction == 'detail' %}
|
{% if ea().crud.currentAction == 'detail' %}
|
||||||
<div class="field-icon-div d-flex gap-3">
|
<div class="field-icon-div d-flex gap-3">
|
||||||
<span title="{{ field.value }}">{{ field.formattedValue|raw }}</span>
|
<span title="{{ field.value }}">{{ field.formattedValue|raw }}</span>
|
||||||
<div class="badge text-bg-secondary">
|
<div class="badge text-bg-secondary">
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
{% block stylesheets %}
|
{% block stylesheets %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block importmap %}{{ importmap('app') }}{% endblock %}
|
|
||||||
{% block javascripts %}
|
{% block javascripts %}
|
||||||
|
{% block importmap %}{{ importmap('app') }}{% endblock %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user