5 Commits
19 changed files with 624 additions and 102 deletions
+11 -3
View File
@@ -6,7 +6,9 @@ Powered by:
- PHP Symfony.
- Easyadmin.
## Commands:
## Get started:
### Dev commands
Build containers:
```bash
@@ -33,7 +35,7 @@ XDEBUG_LOG=/dev/stdout
```
### Prod:
### Production commands:
When deploying to a production environment a production build should be used.
Then, deployment can be done as containers or using docker-compose.
@@ -47,7 +49,7 @@ docker build -t somniarooms:prod --target frankenphp_prod .
**Deploy prod image**
You may use a env file to configure the SomniaRoom container:
You may use a env file to configure the SomniaRoom container (`.env.prod.local`):
```shell
SERVER_NAME=localhost
@@ -83,6 +85,12 @@ If you want your app and DB to comunicate using it's hostname (like on this exam
docker network create somniarooms-network
```
Check [deploy docs](docs/deploy.md) for complete examples on how to deploy SomniaRooms (backend+frontend) using Docker and Docker-compose.
<warning>
Examples shown on deploy docs may be out-of-date. Make sure all readme recommendations are applied before use on production.
</warning>
## Running the app!
+1
View File
@@ -16,6 +16,7 @@
"easycorp/easyadmin-bundle": "^4.10",
"nelmio/api-doc-bundle": "^4.30",
"nelmio/cors-bundle": "^2.4",
"php-ds/php-ds": "^1.5",
"phpdocumentor/reflection-docblock": "^5.4",
"phpstan/phpdoc-parser": "^1.28",
"runtime/frankenphp-symfony": "^0.2.0",
Generated
+56 -1
View File
@@ -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": "974cb22b15d6dc64bd6dac49da5d671d",
"content-hash": "03b299c4c2e514090c879c8f71219e56",
"packages": [
{
"name": "composer/semver",
@@ -1686,6 +1686,61 @@
},
"time": "2023-11-30T16:41:19+00:00"
},
{
"name": "php-ds/php-ds",
"version": "v1.5.0",
"source": {
"type": "git",
"url": "https://github.com/php-ds/polyfill.git",
"reference": "7b2c5f1843466d50769a0682ce6fa9ddaaa99cb4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-ds/polyfill/zipball/7b2c5f1843466d50769a0682ce6fa9ddaaa99cb4",
"reference": "7b2c5f1843466d50769a0682ce6fa9ddaaa99cb4",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": ">=7.0"
},
"provide": {
"ext-ds": "1.5.0"
},
"require-dev": {
"php-ds/tests": "^1.5"
},
"suggest": {
"ext-ds": "to improve performance and reduce memory usage"
},
"type": "library",
"autoload": {
"psr-4": {
"Ds\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Rudi Theunissen",
"email": "[email protected]"
}
],
"keywords": [
"data structures",
"ds",
"php",
"polyfill"
],
"support": {
"issues": "https://github.com/php-ds/polyfill/issues",
"source": "https://github.com/php-ds/polyfill/tree/v1.5.0"
},
"time": "2023-12-19T16:52:21+00:00"
},
{
"name": "phpdocumentor/reflection-common",
"version": "2.2.0",
+4
View File
@@ -24,5 +24,9 @@ services:
arguments:
$targetDirectory: 'images/'
App\Service\ExampleDataService:
arguments:
$sampleDirectory: '../samples/'
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
+182 -36
View File
@@ -1,54 +1,200 @@
<!-- omit in toc -->
# SomniaRooms - App Configuration Variables.
- [Config Sections](#config-sections)
- [Config implementation status](#config-implementation-status)
- [Company Information](#company-information)
- [Front-end Config](#front-end-config)
- [Var `NightAmountForContactDialog`](#var-nightamountforcontactdialog)
- [Var `GuestNumberForContactDialog`](#var-guestnumberforcontactdialog)
- [Var `ContactDialogShowBehaviour`](#var-contactdialogshowbehaviour)
- [Var `ShowNonAvailableRooms`](#var-shownonavailablerooms)
- [Var `SomniaAttributionNotice`](#var-somniaattributionnotice)
- [Var `PhotoAspectRatio`](#var-photoaspectratio)
- [Price Management](#price-management)
- [Var `TotalPriceMethod`.](#var-totalpricemethod)
- [Var `FareSelectionMethod`](#var-fareselectionmethod)
- [API Config.](#api-config)
- [Var `MaxAllowedNightAmount`](#var-maxallowednightamount)
- [Var `MaxAllowedGuestNumber`](#var-maxallowedguestnumber)
---
This document contains a list of all config variables of SomniaRooms. Variables are stored in DB (table _`config_variable`_) and can be configured from admin panel.
Variables are divided in sections. Some sections are ony accesible from SomniaRoom, but others can be retrieved by anyone using config endpoint (_`/config`_).
## List of sections:
## Config Sections
WIP
| Section | SectionName | Description | Availability |
| :------------------------------------------ | :-------------- | :--------------------------------------------- | :----------: |
| [Company Information](#company-information) | COMPANY | Configure company information | Public |
| [Front-end config](#front-end-config) | FRONTEND_CONFIG | Configure front-end behaviour | Public |
| [Price API Management](#price-management) | API_PRICES | Configure how prices are calculated for a stay | Private |
| [API Config](#api-config) | API_CONFIG | Configure API | Private |
TODO -> Add default table with basic information to all sections.
### **Company**
Variables in this section contains information about the Hotel.
### Config implementation status
- SectionName -> company
- Availability -> public
- Nº variables -> X
Not all variables are fully implemented, some are experimental or have options that are.
Current status for a variable and it's options:
- New!: :sparkles:
- Fully available: :heavy_check_mark:
- Partially available: :construction:
- Experimental: :bulb:
- Not yet: :x:
**Variable list:**
- Name - Name of hotel.
## Company Information
Variables in this section contains information about the Company. Those are used to identify the business and to customize frontend.
| Variable | Type | Description | Constraints | Default Value | Status |
| :------------- | :----- | :-------------------- | :----------------------------------------------------------- | :------------ | :------------: |
| CompanyName | string | Company name | - | SomniaRooms | :construction: |
| CompanyWebsite | string | Company website URL | Valid URL to company website. | somnia.dev | :construction: |
| CompanyLogo | string | Company Logo URL | Valid URL to company logo. Somnia Backend system can be used | "" | :construction: |
| CompanyAddress | string | Company Address | - | "" | :construction: |
| CompanyEmail | string | Company email address | - | "" | :construction: |
| CompanyPhone | string | Company phone number | - | "" | :construction: |
## Front-end Config
This variable set the behaviour of front-end app.
| Variable | Type | Description | Constraints | Default Value | Status |
| :-------------------------- | :------ | :------------------------------------------------- | :---------------------------------------------- | :------------ | :------------: |
| NightAmountForContactDialog | int | Set the amount of nights to show a contact dialog. | See [details](#var-nightamountforcontactdialog) | 10 | :construction: |
| GuestNumberForContactDialog | int | Set the guest number to show a contact dialog. | See [details](#var-guestnumberforcontactdialog) | 6 | :construction: |
| ContactDialogShowBehaviour | options | Set how the contact dialog is shown | See [details](#var-contactdialogshowbehaviour) | show | :construction: |
| ShowNonAvailableRooms | bool | Show rooms without availability on search | See [details](#var-shownonavailablerooms) | true | :construction: |
| SomniaAttributionNotice | options | How SomniaRooms notice is shown. | See [details](#var-somniaattributionnotice) | full | :construction: |
| PhotoAspectRatio | string | Set the aspect ratio for photos | See [details](#var-photoaspectratio) | 4:3 | :construction: |
### Var `NightAmountForContactDialog`
Set the amount of nights to show a contact dialog. This dialog contains company contact information and may be used to get a custom price for long stays.
The contact dialog will be shown when night amount is equal or greater than configured amount.
**Options:**
| Option | Value | Usage | Status |
| :------------ | :---- | :-------------------------------------------------------- | :----: |
| Disabled | -1 | Disable this funcionality. Contact dialog won't be shown. | :x: |
| Always | 0 | Contact dialog is always shown. | :x: |
| Custom Amount | - | Set the minimun number of nights to show the dialog. | :x: |
### Var `GuestNumberForContactDialog`
Set the guest number to show a contact dialog. This dialog contains company contact information and may be used to get a custom price for groups.
The contact dialog will be shown when guest number is equal or greater than configured amount.
**Options:**
| Option | Value | Usage | Status |
| :------------ | :---- | :-------------------------------------------------------- | :----: |
| Disabled | -1 | Disable this funcionality. Contact dialog won't be shown. | :x: |
| Always | 0 | Contact dialog is always shown. | :x: |
| Custom Amount | - | Set the minimun guest number to show the dialog. | :x: |
### Var `ContactDialogShowBehaviour`
How the contact dialog is shown to the user (when conditions are meet).
**Options:**
| Option | Value | Usage | Status |
| :----- | :---- | :------------------------------- | :----: |
| Show | SHOW | Show dialog on screen | :x: |
| Alert | ALERT | Show an alert to open the dialog | :x: |
### Var `ShowNonAvailableRooms`
Show rooms without availability on search.
Non-available rooms will be shown at the end of the list.
**Options:**
| Option | Value | Usage | Status |
| :----- | :---- | :-------------------------------------------------------- | :----: |
| Show | true | All rooms will appear on search. Even non-available ones. | :x: |
| Hide | false | Only show available rooms. | :x: |
### Var `SomniaAttributionNotice`
Configure how SomniaRooms attribution note is display.
**Options:**
| Option | Value | Usage | Status |
| :----- | :---- | :------------------------------------ | :----: |
| Full | FULL | Show the full message. Thanks :heart: | :x: |
| Small | SMALL | Show a small notice :+1: | :x: |
| Hide | HIDE | SomniaRooms notice won't be shown. | :x: |
### Var `PhotoAspectRatio`
Aspect ratio for front-end photos.
This setting is currently used for room photos. You may set any valid aspect-ratio you want.
It's recommended that all photos have the same aspect-ratio to avoid scaling.
## Price Management
How SomniaRooms API services calculates prices for bookings.
| Variable | Type | Description | Constraints | Default Value | Status |
| :------------------ | :----- | :---------------------------------------------------- | :-------------------------------------- | :------------ | :----: |
| TotalPriceMethod | option | How total price is calculated for a stay | See [details](#var-totalpricemethod) | SUM | :x: |
| FareSelectionMethod | option | What fare to select when multiple fares are available | See [details](#var-fareselectionmethod) | HIGHER | :x: |
### Var `TotalPriceMethod`.
Establish the method used to calculate the total price for a stay.
The total price for a stay will be calculated from the price of the days that make the stay. A operation must be selected to get total price from daily prices.
**Options:**
| Option | Value | Usage | Status |
| :------------ | :------ | :----------------------------------------------------------------- | :----: |
| Lower Price | LOWER | The lower price will be apply for each day. | :x: |
| High Price | HIGHER | The highest price will be apply for each day. | :x: |
| Total Sum | SUM | Total price will be the sum of the price for each day. | :x: |
| Average Price | AVERAGE | The average for all days will be calculated and apply to each day. | :x: |
### Var `FareSelectionMethod`
How to select the fare for a room when there are multiple fares applicable.
A faretable may contain multiple fares for a room and guest number given some conditions. If multiple conditions match for a date and therefore there are more than 1 applicable fare, one must be used. This method sets what to do in those cases.
Note: RoomPrices have more priority than fares. So if a RoomPrice is available, it will always be the prefered option.
**Options:**
| Option | Value | Usage | Status |
| :------------ | :------ | :---------------------------------------------------------------- | :----: |
| Lower Price | LOWER | Lower price available will be used. | :x: |
| High Price | HIGHER | Higher available price will be used. | :x: |
| Average price | AVERAGE | Selected price will be the aritmetic average of available prices. | :x: |
## API Config.
Configuration for some API endpoints.
| Variable | Type | Description | Constraints | Default Value | Status |
| :-------------------- | :--- | :----------------------------------- | :---------------------------------------- | :------------ | :------------: |
| MaxAllowedNightAmount | int | Max night amount that can be booked. | See [details](#var-maxallowednightamount) | 20 | :construction: |
| MaxAllowedGuestNumber | int | Max guest number that can be booked. | See [details](#var-maxallowedguestnumber) | 20 | :construction: |
### **Price Management**
Configure how backend services calculates prices for bookings.
### Var `MaxAllowedNightAmount`
Max number of nights that can be booked using the app. For longer stays the company should be contacted.
- SectionName -> price_mng
- Availability -> internal
- Nº variables -> X
**Variable list:**
WIP
#### FinalPriceCalculation.
Establece cómo se deben tratar los precios de cada día para generar el precio final de la estancia.
Algunos algoritmos para estos serán:
- Precio más alto.
- Precio más bajo.
- Suma de precios. --> Por defecto.
- Media de precios.
#### MultipleRoomFareValid
Estrategia de selección de tarifa en caso de que haya varias válidas (mientras no exista un sistema de prioridad de tarifas...)
Posibles algoritmos:
- Precio más alto.
- Precio más bajo.
- Media.
**Options:**
| Option | Value | Usage | Status |
| :------------ | :---- | :---------------------------------------------------------- | :----: |
| Disabled | 0 | Disable this funcionality. No limit on booking will be set. | :x: |
| Custom Amount | - | Set the max number of nights allowed to book. | :x: |
### Var `MaxAllowedGuestNumber`
Max guest number that can be booked using the app. For bigger groups the company should be contacted.
**Options:**
| Option | Value | Usage | Status |
| :------------ | :---- | :---------------------------------------------------------- | :----: |
| Disabled | 0 | Disable this funcionality. No limit on booking will be set. | :x: |
| Custom Amount | - | Set the max guest number allowed to book. | :x: |
+8
View File
@@ -1,5 +1,13 @@
<!-- omit in toc -->
# SomniaRooms - How to deploy.
- [Docker compose examples.](#docker-compose-examples)
- [Deploy back-end and front-end on different servers (different ip address).](#deploy-back-end-and-front-end-on-different-servers-different-ip-address)
- [Deploy on the same server.](#deploy-on-the-same-server)
- [What's next?](#whats-next)
---
SomniaRooms is designed to be easy to deploy and use.
Production images for Docker Containers are available from Gitea Registry.
+10
View File
@@ -0,0 +1,10 @@
[
{
"name": "Double Bed",
"iconUrl": "icon-bed-double"
},
{
"name": "Single Bed",
"iconUrl": "icon-bed-single"
}
]
+41
View File
@@ -0,0 +1,41 @@
[
{
"name": "Double room (double bed)",
"description": "Room with double bed and shared bathroom",
"maxGuestNum": 2,
"orderHomePage": null,
"roomCategoryPhotos": [
{
"path": "https://images.unsplash.com/photo-1559841644-08984562005a",
"altText": "Room",
"photoOrder": 1
}
]
},
{
"name": "Double w/ bath (double bed)",
"description": "Room with double bed and private bathroom",
"maxGuestNum": 2,
"orderHomePage": null,
"roomCategoryPhotos": [
{
"path": "https://images.unsplash.com/photo-1559841644-08984562005a",
"altText": "Room",
"photoOrder": 1
}
]
},
{
"name": "Double w/ bath (single bed)",
"description": "Room with 2 single beds and private bathroom",
"maxGuestNum": 2,
"orderHomePage": null,
"roomCategoryPhotos": [
{
"path": "https://images.unsplash.com/photo-1559841644-08984562005a",
"altText": "Room",
"photoOrder": 1
}
]
}
]
+12
View File
@@ -0,0 +1,12 @@
[
{
"amount": 1,
"roomCategory": 0,
"bedType": 0
},
{
"amount": 1,
"roomCategory": 2,
"bedType": 1
}
]
+61
View File
@@ -0,0 +1,61 @@
<?php
namespace App\Consts;
use App\Entity\ConfigVariable;
use App\Enum\ConfigVariableSectionEnum;
//Variable List Constants
// --> This constants contains all defined Config Variables. For each one a array is stored as [varKey => [varSection, varDefaultValue]].
class ConfigVariableConsts
{
public const CONFIG_COMPANY_NAME = ["CompanyName" => [ConfigVariableSectionEnum::Company, "SomniaRooms"]];
public const CONFIG_COMPANY_WEBSITE = ["CompanyWebsite" => [ConfigVariableSectionEnum::Company, "somnia.dev"]];
public const CONFIG_COMPANY_LOGO = ["CompanyLogo" => [ConfigVariableSectionEnum::Company, ""]];
public const CONFIG_COMPANY_ADDRESS = ["CompanyAddress" => [ConfigVariableSectionEnum::Company, ""]];
public const CONFIG_COMPANY_EMAIL = ["CompanyEmail" => [ConfigVariableSectionEnum::Company, ""]];
public const CONFIG_COMPANY_PHONE = ["CompanyPhone" => [ConfigVariableSectionEnum::Company, ""]];
public const CONFIG_FRONTEND_NIGHTSFORCONTACT = ["NightAmountForContactDialog" => [ConfigVariableSectionEnum::Frontend, "10"]];
public const CONFIG_FRONTEND_GUESTSFORCONTACT = ["GuestNumberForContactDialog" => [ConfigVariableSectionEnum::Frontend, "6"]];
public const CONFIG_FRONTEND_CONTACTDIALOGBEHAVIOUR = ["ContactDialogShowBehaviour" => [ConfigVariableSectionEnum::Frontend, "show"]];
public const CONFIG_FRONTEND_SHOWNONAVAILABLEROOMS = ["ShowNonAvailableRooms" => [ConfigVariableSectionEnum::Frontend, true]];
public const CONFIG_FRONTEND_SOMNIAATTRIBUTION = ["SomniaAttributionNotice" => [ConfigVariableSectionEnum::Frontend, "full"]];
public const CONFIG_FRONTEND_PHOTOASPECTRATIO = ["PhotoAspectRatio" => [ConfigVariableSectionEnum::Frontend, "4:3"]];
public const CONFIG_PRICES_PRICEMETHOD = ["TotalPriceMethod" => [ConfigVariableSectionEnum::API_PRICES, "SUM"]];
public const CONFIG_PRICES_FAREMETHOD = ["FareSelectionMethod" => [ConfigVariableSectionEnum::API_PRICES, "HIGHER"]];
public const CONFIG_API_NAXNIGHTS = ["MaxAllowedNightAmount" => [ConfigVariableSectionEnum::API_CONFIG, "20"]];
public const CONFIG_API_NAXGUESTS = ["MaxAllowedGuestNumber" => [ConfigVariableSectionEnum::API_CONFIG, "20"]];
public const CONFIG_INTERNAL_SETUP = ["SETUP_STATUS" => [ConfigVariableSectionEnum::Internal, "1"]];
public static function getKeyFromConstant(array $constant): string
{
return array_key_first($constant);
}
public static function GetAllDefinedConfigVariablesList(): array
{
return array_merge(
ConfigVariableConsts::CONFIG_COMPANY_NAME,
ConfigVariableConsts::CONFIG_COMPANY_WEBSITE,
ConfigVariableConsts::CONFIG_COMPANY_LOGO,
ConfigVariableConsts::CONFIG_COMPANY_ADDRESS,
ConfigVariableConsts::CONFIG_COMPANY_EMAIL,
ConfigVariableConsts::CONFIG_COMPANY_PHONE,
ConfigVariableConsts::CONFIG_FRONTEND_NIGHTSFORCONTACT,
ConfigVariableConsts::CONFIG_FRONTEND_GUESTSFORCONTACT,
ConfigVariableConsts::CONFIG_FRONTEND_CONTACTDIALOGBEHAVIOUR,
ConfigVariableConsts::CONFIG_FRONTEND_SHOWNONAVAILABLEROOMS,
ConfigVariableConsts::CONFIG_FRONTEND_SOMNIAATTRIBUTION,
ConfigVariableConsts::CONFIG_FRONTEND_PHOTOASPECTRATIO,
ConfigVariableConsts::CONFIG_PRICES_PRICEMETHOD,
ConfigVariableConsts::CONFIG_PRICES_FAREMETHOD,
ConfigVariableConsts::CONFIG_API_NAXNIGHTS,
ConfigVariableConsts::CONFIG_API_NAXGUESTS,
ConfigVariableConsts::CONFIG_INTERNAL_SETUP,
);
}
}
@@ -3,7 +3,8 @@
namespace App\Controller\Admin;
use App\Entity\ConfigVariable;
use App\Enum\ConfigSectionType;
use App\Enum\ConfigVariableSectionEnum;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Field\ChoiceField;
@@ -21,10 +22,15 @@ class ConfigVariableCrudController extends AbstractCrudController
return $crud;
}
public function configureActions(Actions $actions): Actions
{
return $actions->disable("new", "delete");;
}
public function configureFields(string $pageName): iterable
{
yield ChoiceField::new ('section')->setChoices(ConfigSectionType::cases());
yield TextField::new ('key');
yield TextField::new ('value');
yield ChoiceField::new('section')->setChoices(ConfigVariableSectionEnum::cases());
yield TextField::new('key');
yield TextField::new('value');
}
}
+6 -5
View File
@@ -2,6 +2,7 @@
namespace App\Controller;
use App\Consts\ConfigVariableConsts;
use App\DTO\AppSetupConfig;
use App\DTO\ExampleDataConfig;
use App\Entity\ConfigVariable;
@@ -9,9 +10,9 @@ use App\Form\AppSetupFormType;
use App\Form\ExampleDataFormType;
use App\Repository\RoomCategoryRepository;
use App\Service\AppSetupService;
use App\Service\ConfigVariableService;
use App\Service\ExampleDataService;
use Doctrine\ORM\EntityManagerInterface;
use Monolog\Logger;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
@@ -25,13 +26,13 @@ class AppSetupController extends AbstractController
LoggerInterface $logger,
AppSetupService $setupService,
Request $request,
EntityManagerInterface $entityManager
ConfigVariableService $configVars
): Response {
$configRepo = $entityManager->getRepository(ConfigVariable::class);
$setupStatus = $configRepo->findBy(["key" => "SETUP_STATUS"]);
$setupVar_key = ConfigVariableConsts::getKeyFromConstant(ConfigVariableConsts::CONFIG_INTERNAL_SETUP);
$setupStatus = $configVars->getVariable($setupVar_key);
if ($setupStatus != null && $setupStatus[0] != null && $setupStatus[0]->getValue() == 1) {
if ($setupStatus != null && $setupStatus->getValue()) {
$logger->warning("SomniaRooms was already setup");
return $this->GoToResultPage(
+6 -6
View File
@@ -3,22 +3,22 @@
namespace App\Entity;
use App\Repository\ConfigVariableRepository;
use App\Enum\ConfigSectionType;
use App\Enum\ConfigVariableSectionEnum;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: ConfigVariableRepository::class)]
class ConfigVariable
{
#[ORM\Id]
#[ORM\GeneratedValue("NONE")]
#[ORM\GeneratedValue(strategy: "NONE")]
#[ORM\Column(length: 32, unique: true, nullable: false)]
private string $key = "";
#[ORM\Column(length: 255, nullable: false)]
private ?string $value = null;
#[ORM\Column(type: 'string', enumType: ConfigSectionType::class, nullable: false)]
private ConfigSectionType $section = ConfigSectionType::General;
#[ORM\Column(type: 'string', enumType: ConfigVariableSectionEnum::class, nullable: false)]
private ConfigVariableSectionEnum $section = ConfigVariableSectionEnum::Internal;
public function getKey(): string
@@ -45,12 +45,12 @@ class ConfigVariable
return $this;
}
public function getSection(): ConfigSectionType
public function getSection(): ConfigVariableSectionEnum
{
return $this->section;
}
public function setSection(ConfigSectionType $section): static
public function setSection(ConfigVariableSectionEnum $section): static
{
$this->section = $section;
-12
View File
@@ -1,12 +0,0 @@
<?php
namespace App\Enum;
enum ConfigSectionType: string {
case General = 'GENERAL';
case Internal = 'INTERNAL';
case Company = 'COMPANY';
case Backend = 'BACK';
case Frontend = 'FRONT';
case Test = 'TEST';
}
+12
View File
@@ -0,0 +1,12 @@
<?php
namespace App\Enum;
enum ConfigVariableSectionEnum: string
{
case Company = 'COMPANY';
case Frontend = 'FRONTEND_CONFIG';
case API_PRICES = 'API_PRICES';
case API_CONFIG = 'API_CONFIG';
case Internal = 'INTERNAL';
}
+28 -3
View File
@@ -5,6 +5,7 @@ namespace App\Repository;
use App\Entity\ConfigVariable;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Ds\Set;
/**
* @extends ServiceEntityRepository<ConfigVariable>
@@ -51,14 +52,24 @@ class ConfigVariableRepository extends ServiceEntityRepository
* @param string Variable Key
* @return ConfigVariable|null Config Variable.
*/
public function findVariableByKey($varKey)
public function findVariableByKey($varKey): ConfigVariable|null
{
return $this->createQueryBuilder('c')
$rs = $this->createQueryBuilder('c')
->select('c.key, c.value, c.section')
->andWhere('c.key = :val')
->setParameter('val', $varKey)
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
->getResult();
// FIXME!!!
if (!$rs) {
return null;
}
$var = new ConfigVariable();
$var->setKey($rs[0]["key"]);
$var->setValue($rs[0]["value"]);
$var->setSection($rs[0]["section"]);
return $var;
}
/**
@@ -73,4 +84,18 @@ class ConfigVariableRepository extends ServiceEntityRepository
->getQuery()
->getResult();
}
/**
* Get all DB config variables names on a Set.
* @return Set Returns a Set with all variable names.
*/
public function getVariableSet(): Set
{
$varList = $this->findAll();
$keyList = array_map(function ($var) {
return $var->getKey();
}, $varList);
return new Set($keyList);
}
}
+14 -12
View File
@@ -2,10 +2,11 @@
namespace App\Service;
use App\Consts\ConfigVariableConsts;
use App\DTO\AppSetupConfig;
use App\Entity\ConfigVariable;
use App\Entity\User;
use App\Enum\ConfigSectionType;
use App\Enum\ConfigVariableSectionEnum;
use App\Enum\UserRoleEnum;
use App\Repository\ConfigVariableRepository;
@@ -23,7 +24,7 @@ class AppSetupService
public function __construct(
private LoggerInterface $logger,
private EntityManagerInterface $entityManager,
private ConfigVariableRepository $repoVariables,
private ConfigVariableService $configVars,
private UserPasswordHasherInterface $passwordHasher,
private Security $security
) {}
@@ -37,15 +38,18 @@ class AppSetupService
//Create system variables.
$this->logger->info("SomniaRooms Setup - Creating system variables...");
$var_setup = $this->repoVariables->find("SETUP_STATUS");
if (!$var_setup) {
$var_setup = new ConfigVariable();
$var_setup->setKey("SETUP_STATUS");
}
$var_setup->setValue("1");
$var_setup->setSection(ConfigSectionType::Internal);
$this->entityManager->persist($var_setup);
//Not neccesary anymore. CONFIG_INTERNAL_SETUP will be initialized with all vars.
// $var_setup_key = ConfigVariableConsts::getKeyFromConstant(ConfigVariableConsts::CONFIG_INTERNAL_SETUP);
// $var_setup = $this->configVars->getVariable($var_setup_key);
// if (!$var_setup) {
// $var_setup = new ConfigVariable();
// $var_setup->setKey($var_setup_key);
// $this->entityManager->persist($var_setup);
// }
// $var_setup->setValue("1");
// $var_setup->setSection(ConfigVariableSectionEnum::Internal);
$this->configVars->InitVariablesOnSetup();
//Create user Admin.
$this->logger->info("SomniaRooms Setup - Adding super-admin user...");
@@ -53,8 +57,6 @@ class AppSetupService
$user->setRoles([UserRoleEnum::ROLE_USER->value, UserRoleEnum::ROLE_ADMIN->value, UserRoleEnum::ROLE_SUPERADMIN->value]);
$plaintextPassword = $configData->getAdminPass();
// hash the password (based on the security.yaml config for the $user class)
$hashedPassword = $this->passwordHasher->hashPassword(
$user,
+35 -12
View File
@@ -2,10 +2,14 @@
namespace App\Service;
use App\Consts\ConfigVariableConsts;
use App\Entity\ConfigVariable;
use App\Enum\ConfigVariableSectionEnum;
use App\Repository\ConfigVariableRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
/**
* Service to manage config variables.
@@ -14,33 +18,52 @@ class ConfigVariableService
{
public function __construct(
private ConfigVariableRepository $repoVariables
private EntityManagerInterface $entityManager,
private ConfigVariableRepository $repoVariables,
private LoggerInterface $logger,
) {}
public function getVariable(string $key) {
$var = $this->repoVariables->findVariableByKey($key);
public function getVariable(string $key): ConfigVariable|null
{
return $this->repoVariables->findVariableByKey($key);
}
/**
* Create and init all variables.
* To be used during Setup process.
*/
public function InitVariablesOnSetup(): void {
public function InitVariablesOnSetup(): void
{
//Get a list of variables to check if those already exists.
//$this->entityManager->beginTransaction();
$varListDb = $this->repoVariables->getVariableSet();
$varListPHP = ConfigVariableConsts::GetAllDefinedConfigVariablesList();
$entityManager = $this->repoVariables->getEntityManager();
$entityManager->beginTransaction();
foreach ($varListPHP as $varKey => $varValue) {
if ($varListDb->contains($varKey)) {
$this->logger->info("Var $varKey already created. Skipped!", [$varKey, $varValue]);
continue;
}
$this->AddConfigVariable($varKey, $varValue[1], $varValue[0]);
}
try {
}
catch (\Throwable $th) {
$entityManager->rollback();
} catch (\Throwable $th) {
$this->entityManager->rollback();
throw $th;
}
}
private function AddConfigVariable(string $key, string $value, string $section): void {
public function AddConfigVariable(string $key, string $value, ConfigVariableSectionEnum $section): void
{
$var = new ConfigVariable();
$var->setKey($key);
$var->setValue($value);
$var->setSection($section);
$this->entityManager->persist($var);
$this->logger->info("Variable added to DB", ["key" => $key, "value" => $value, "section" => $section]);
}
}
+124 -5
View File
@@ -4,36 +4,67 @@ namespace App\Service;
use App\DTO\AppSetupConfig;
use App\DTO\ExampleDataConfig;
use App\Entity\BedType;
use App\Entity\ConfigVariable;
use App\Entity\FareTable;
use App\Entity\RoomCategory;
use App\Entity\RoomCategoryBedType;
use App\Entity\RoomFare;
use App\Entity\User;
use App\Enum\ConfigSectionType;
use App\Enum\UserRoleEnum;
use App\Repository\ConfigVariableRepository;
use App\Repository\FareTableRepository;
use App\Repository\RoomAvailabilityRepository;
use App\Repository\RoomCategoryRepository;
use App\Repository\RoomFareRepository;
use App\Serializer\Normalizer\EntityByIdDenormalizer;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use stdClass;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
/**
* Service for setup proccess.
*/
class ExampleDataService
{
private Serializer $sampleSerializer;
private FileLocator $fileLocator;
public function __construct(
private string $sampleDirectory,
private LoggerInterface $logger,
private EntityManagerInterface $entityManager,
private RoomCategoryRepository $roomCategoryRepository,
private RoomAvailabilityRepository $roomAvailabilityRepository,
private FareTableRepository $fareTableRepository,
private RoomFareRepository $roomFareRepository,
) {}
private RoomFareRepository $roomFareRepository
) {
$this->sampleSerializer = new Serializer(
array(
new ObjectNormalizer(
null,
null,
null,
new PhpDocExtractor()
),
new ArrayDenormalizer(),
),
array('json' => new JsonEncoder())
);
$this->fileLocator = new FileLocator($this->sampleDirectory);
}
public function AddExampleData(ExampleDataConfig $config): string
{
@@ -65,12 +96,53 @@ class ExampleDataService
return "OK";
}
private function GenerateRoomCategories(): bool
public function GenerateRoomCategories(): bool
{
//TODOME.
//Add Example BedTypes.
$bedTypeList = $this->DeserializeSampleFile("SampleBedTypes.json", BedType::class . '[]');
$this->PersistEntityArray($bedTypeList);
//Add Example RoomCategories
$roomCategoryList = $this->DeserializeSampleFile("SampleRoomCategories.json", RoomCategory::class . '[]');
$this->PersistEntityArray($roomCategoryList);
//Add Example RoomCategoryBedTypes
$sampleRcBtData = $this->GetFileData("SampleRoomCategoryBedTypes.json");
$sampleRcBtContent = json_decode($sampleRcBtData);
foreach ($sampleRcBtContent as $contentElement) {
$bedType = null;
if (count($bedTypeList) > $contentElement->bedType) {
$bedType = $bedTypeList[$contentElement->bedType];
}
$roomCategory = null;
if (count($roomCategoryList) > $contentElement->roomCategory) {
$roomCategory = $roomCategoryList[$contentElement->roomCategory];
}
if (!$roomCategory || !$bedType) {
$this->logger->error(
"Invalid RoomCategoryBedType: RoomCategory or BedType not found!",
["json" => $contentElement, "bedtype" => $bedType, "roomcategory" => $roomCategory]
);
continue;
}
$item = new RoomCategoryBedType();
$item->setAmount($contentElement->amount);
$item->setBedType($bedType);
$item->setRoomCategory($roomCategory);
$this->entityManager->persist($item);
}
$this->entityManager->flush();
return true;
}
#region Generate Ramdom Data
private function GenerateAvailability(ExampleDataConfig $config): bool
{
$roomCategoryList = $this->roomCategoryRepository->findAll();
@@ -126,4 +198,51 @@ class ExampleDataService
return true;
}
#endregion
#region Load Sample Data
//TODO
#endregion
#region Utils
private function DeserializeSampleFile(string $fileName, string $class)
{
$data = $this->GetFileData($fileName);
if (!$data) return;
$itemList = $this->sampleSerializer->deserialize($data, $class, "json");
return $itemList;
}
private function GetFileData(string $fileName): string|null
{
$file = $this->fileLocator->locate($fileName, null);
$data = file_get_contents($file);
if (!$data) {
$this->logger->error(
"ExampleDataService - DeserializeSampleFile - Unable to get file contents.",
["file" => $fileName, "fileData" => $data]
);
return null;
}
return $data;
}
private function PersistEntityArray(array $itemList): void
{
foreach ($itemList as $item) {
try {
$this->entityManager->persist($item);
} catch (\Throwable $th) {
$this->logger->error("Item can't be persisted", ["item" => $item, "exception" => $th]);
}
}
}
#endregion
}