diff --git a/.devcontainer/.devcontainer.json b/.devcontainer/.devcontainer.json deleted file mode 100644 index 06e5ae9..0000000 --- a/.devcontainer/.devcontainer.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "image": "mcr.microsoft.com/devcontainers/php:8.3", - "features": { - } - } \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..addd6de --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,11 @@ +{ + "image": "mcr.microsoft.com/devcontainers/php:8.3", + "customizations": { + "vscode": { + "extensions": [ + "gruntfuggly.todo-tree", + "vscode-icons-team.vscode-icons" + ] + } + } +} diff --git a/Dockerfile b/Dockerfile index 7ed42ef..62e159e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,4 +32,4 @@ COPY config/etc/php/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer # Working directory. -WORKDIR /opt/app \ No newline at end of file +WORKDIR /opt/app diff --git a/README.md b/README.md index d3f5231..06b67dc 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Language](https://img.shields.io/github/languages/top/xsga/filmaffinity-api)](https://php.net/) [![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%208.3-8892BF?style=flat)](https://php.net/) -[![Latest version](https://img.shields.io/github/v/release/xsga/filmaffinity-api)](https://github.com/xsga/filmaffinity-api/releases/tag/v7.0.0) +[![Latest version](https://img.shields.io/github/v/release/xsga/filmaffinity-api)](https://github.com/xsga/filmaffinity-api/releases/tag/v7.1.0) [![License](https://img.shields.io/github/license/xsga/filmaffinity-api)](https://opensource.org/licenses/MIT) FilmAffinity-API is a public and non offical API wich allow you to get information about films from [FilmAffinity](http://filmaffinity.com "FilmAffinity Home") website. You can search films and get their complet information, including cast, synopsis and cover. @@ -74,9 +74,10 @@ The API has the following public methods: |Method name|HTTP method|API endpoint|Body| |-----------|-----------|------------|----| -|Simple films search|POST|searches/simple|Y| -|Advanced films search|POST|searches/advanced|Y| -|Get film information|GET|films/:id|URL parameter (:id)| +|Get user token|POST|users/token|[JSON Schema](https://github.com/xsga/filmaffinity-api/blob/master/config/schemas/input/get.token.schema.json)| +|Simple films search|POST|searches/simple|[JSON Schema](https://github.com/xsga/filmaffinity-api/blob/master/config/schemas/input/simple.search.schema.json)| +|Advanced films search|POST|searches/advanced|[JSON Schema](https://github.com/xsga/filmaffinity-api/blob/master/config/schemas/input/advanced.search.schema.json)| +|Get film information|GET|films/:id|-| |Get genres|GET|genres|-| |Get countries|GET|countries|-| @@ -145,6 +146,59 @@ docker exec -it filmaffinityapi-web-server php .bin/console ``` ### Advanced films search + +**EXAMPLE 1:** An example of an advanced search searching only for a text in the title of the film. + +**URL** +``` +[POST] http:///searches/advanced +``` + +**INPUT** +```json +{ + "text": "pulp fiction", + "search_in_title": true, + "search_in_director": false, + "search_in_cast": false, + "search_in_screenplay": false, + "search_in_photography": false, + "search_in_soundtrack": false, + "search_in_producer": false, + "country": "", + "genre": "", + "year_from": 0, + "year_to": 0 +} +``` + +**OUTPUT** +```json +{ + "status": "OK", + "statusCode": 200, + "response": { + "total": 1, + "results": [ + { + "id": 160882, + "title": "Pulp Fiction", + "year": 1994, + "directors": ["Quentin Taratino"] + }, + { + "id": 991349, + "title": "8 Bit Cinema: Pulp Fiction", + "year": 2014, + "directors": ["David Dutton"] + } + ] + } +} +``` + +**EXAMPLE 2:** another example of an advanced search searching for a text in the title of the film and between two years. + **URL** ``` [POST] http:///searches/advanced @@ -154,6 +208,15 @@ docker exec -it filmaffinityapi-web-server php .bin/console ```json { "text": "pulp fiction", + "search_in_title": true, + "search_in_director": false, + "search_in_cast": false, + "search_in_screenplay": false, + "search_in_photography": false, + "search_in_soundtrack": false, + "search_in_producer": false, + "country": "", + "genre": "", "year_from": 1992, "year_to": 2000 } diff --git a/composer.json b/composer.json index a69edc7..37c1100 100644 --- a/composer.json +++ b/composer.json @@ -10,6 +10,7 @@ } ], "require": { + "php": "^8.3", "slim/slim": "4.*", "slim/psr7": "^1.6", "vlucas/phpdotenv": "^5.5", diff --git a/config/.env.example b/config/.env.example index fdcf7cd..cfb33c5 100644 --- a/config/.env.example +++ b/config/.env.example @@ -23,6 +23,15 @@ URL_PATH="/app" # ERROR_DETAIL=true +# LOG_SQL - Log SQL queries. +# +# Accepted values: +# +# true (log SQL queries) +# false (don't log SQL queries) +# +LOG_SQL=true + # LANGUAGE - FilmAffinity's API language. # # Available languages: diff --git a/config/container/Container.php b/config/container/Container.php index c1489ec..c760720 100644 --- a/config/container/Container.php +++ b/config/container/Container.php @@ -3,6 +3,7 @@ declare(strict_types=1); use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Logging\Middleware; use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\ORMSetup; @@ -14,6 +15,8 @@ use Xsga\FilmAffinityApi\Modules\Errors\Domain\Repositories\ErrorsRepository; use Xsga\FilmAffinityApi\Modules\Errors\Infrastructure\Mappers\JsonErrorToError; use Xsga\FilmAffinityApi\Modules\Errors\Infrastructure\Repositories\JsonErrorsRepository; +use Xsga\FilmAffinityApi\Modules\Films\Application\Mappers\CountryToCountryDto; +use Xsga\FilmAffinityApi\Modules\Films\Application\Mappers\GenreToGenreDto; use Xsga\FilmAffinityApi\Modules\Films\Application\Services\BackupCountriesService; use Xsga\FilmAffinityApi\Modules\Films\Application\Services\BackupGenresService; use Xsga\FilmAffinityApi\Modules\Films\Domain\Parsers\AdvancedSearchFormParser; @@ -23,7 +26,10 @@ use Xsga\FilmAffinityApi\Modules\Films\Domain\Repositories\FilmsRepository; use Xsga\FilmAffinityApi\Modules\Films\Domain\Repositories\GenresRepository; use Xsga\FilmAffinityApi\Modules\Films\Domain\Repositories\SearchRepository; +use Xsga\FilmAffinityApi\Modules\Films\Domain\Services\GetAdvancedSearchResultsService; +use Xsga\FilmAffinityApi\Modules\Films\Domain\Services\GetCountriesService; use Xsga\FilmAffinityApi\Modules\Films\Domain\Services\GetFilmService; +use Xsga\FilmAffinityApi\Modules\Films\Domain\Services\GetGenresService; use Xsga\FilmAffinityApi\Modules\Films\Domain\Services\GetSimpleSearchResultsService; use Xsga\FilmAffinityApi\Modules\Films\Domain\Services\UrlService; use Xsga\FilmAffinityApi\Modules\Films\Infrastructure\Repositories\FilmAffinityAdvancedSearchRepository; @@ -69,6 +75,7 @@ // ENVIRONMENT. 'getLanguage' => $_ENV['LANGUAGE'], 'getErrorDetail' => filter_var($_ENV['ERROR_DETAIL'], FILTER_VALIDATE_BOOLEAN), + 'getLogSQL' => filter_var($_ENV['LOG_SQL'], FILTER_VALIDATE_BOOLEAN), 'getUrlPath' => $_ENV['URL_PATH'], 'getJwtSecretKey' => $_ENV['JWT_SECRET_KEY'], 'getJwtLifetime' => (int)$_ENV['JWT_LIFETIME'], @@ -109,11 +116,16 @@ $isDevMode = true; $entityPaths = $container->get('entity.folders'); $proxyPath = $container->get('entities.proxy.folder'); - $connection = DriverManager::getConnection($container->get('database.info')); - + $config = ORMSetup::createAttributeMetadataConfiguration($entityPaths, $isDevMode, $proxyPath, null); $config->setAutoGenerateProxyClasses($isDevMode); + if ($container->get('getLogSQL')) { + $config->setMiddlewares([$container->get(Middleware::class)]); + } + + $connection = DriverManager::getConnection($container->get('database.info'), $config); + return new EntityManager($connection, $config); }, @@ -126,8 +138,17 @@ } return Logger::getRootLogger(); }, + 'logger-cli' => function (ContainerInterface $container) { + if (!Logger::isInitialized()) { + Logger::configure($container->get('logger.config.folder') . 'log4php-cli.xml'); + } + return Logger::getRootLogger(); + }, LoggerInterface::class => function (ContainerInterface $container): LoggerInterface { - $logger = $container->get(Logger::class); + $logger = match (php_sapi_name()) { + 'cli' => $container->get('logger-cli'), + default => $container->get(Logger::class) + }; return new LoggerWrapper($logger); }, @@ -149,6 +170,7 @@ // USERS MODULE. // -------------------------------------------------------------------------------------------- + // Application mappers. UserToUserDto::class => DI\create(UserToUserDto::class)->constructor( DI\get('getDateTimeMask') ), @@ -167,13 +189,17 @@ // Application services. BackupGenresService::class => DI\create(BackupGenresService::class)->constructor( + DI\get(LoggerInterface::class), DI\get(FilmAffinityGenresRepository::class), + DI\get(GenreToGenreDto::class), DI\get('getLanguage'), DI\get('backup.folder') ), BackupCountriesService::class => DI\create(BackupCountriesService::class)->constructor( + DI\get(LoggerInterface::class), DI\get(FilmAffinityCountriesRepository::class), + DI\get(CountryToCountryDto::class), DI\get('getLanguage'), DI\get('backup.folder') ), @@ -197,18 +223,18 @@ DI\get(LoggerInterface::class), DI\get(UrlService::class), DI\get(HttpClientService::class), - DI\get(AdvancedSearchFormParser::class) + DI\get(GetGenresService::class) ), CountriesRepository::class => DI\create(FilmAffinityCountriesRepository::class)->constructor( DI\get(LoggerInterface::class), DI\get(UrlService::class), DI\get(HttpClientService::class), - DI\get(AdvancedSearchFormParser::class) + DI\get(GetCountriesService::class) ), AdvancedSearchRepository::class => DI\create(FilmAffinityAdvancedSearchRepository::class)->constructor( DI\get(UrlService::class), DI\get(HttpClientService::class), - DI\get(AdvancedSearchParser::class) + DI\get(GetAdvancedSearchResultsService::class) ), SearchRepository::class => DI\create(FilmAffinitySearchRepository::class)->constructor( DI\get(UrlService::class), diff --git a/config/env/Settings.php b/config/env/Settings.php index dc4433a..d693696 100644 --- a/config/env/Settings.php +++ b/config/env/Settings.php @@ -11,6 +11,7 @@ function loadEnvironmentSettings(): void $settings->required('URL_PATH'); $settings->required('ERROR_DETAIL')->isBoolean(); + $settings->required('LOG_SQL')->isBoolean(); $settings->required('LANGUAGE')->allowedValues(['spa', 'en']); $settings->required('BASE_URL'); $settings->required('SEARCH_URL'); diff --git a/config/errors/errors.json b/config/errors/errors.json index 12350ad..5eb510c 100644 --- a/config/errors/errors.json +++ b/config/errors/errors.json @@ -19,16 +19,32 @@ "code": 2002, "http": 500, "description": { - "en": "Genre code not valid", - "spa": "El codigo de género no es válido" + "en": "Genre with code '{1}' not valid", + "spa": "El codigo de género '{1}' no es válido" } }, { "code": 2003, "http": 500, "description": { - "en": "Country code not valid", - "spa": "El codigo de país no es válido" + "en": "Country with code '{1}' not valid", + "spa": "El codigo de país '{1}' no es válido" + } + }, + { + "code": 2004, + "http": 404, + "description": { + "en": "Page not found", + "spa": "Página no encontrada" + } + }, + { + "code": 2005, + "http": 404, + "description": { + "en": "Film with ID '{1}' not found", + "spa": "Film con ID '{1}' no encontrado" } }, { diff --git a/config/etc/php/xdebug.ini b/config/etc/php/xdebug.ini index fd98ab8..763a6f2 100644 --- a/config/etc/php/xdebug.ini +++ b/config/etc/php/xdebug.ini @@ -1,2 +1,3 @@ +[xdebug] zend_extension=xdebug.so xdebug.mode=coverage diff --git a/config/logger/log4php-cli.xml b/config/logger/log4php-cli.xml new file mode 100644 index 0000000..dd3f17e --- /dev/null +++ b/config/logger/log4php-cli.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/config/schemas/input/advanced.search.schema.json b/config/schemas/input/advanced.search.schema.json index 0140d2c..6420895 100644 --- a/config/schemas/input/advanced.search.schema.json +++ b/config/schemas/input/advanced.search.schema.json @@ -7,31 +7,31 @@ "type": "string", "title": "Text to search" }, - "title": { + "search_in_title": { "type": "boolean", "title": "Title flag" }, - "director": { + "search_in_director": { "type": "boolean", "title": "Director flag" }, - "cast": { + "search_in_cast": { "type": "boolean", "title": "Cast flag" }, - "screenplay": { + "search_in_screenplay": { "type": "boolean", "title": "Screenplay flag" }, - "photography": { + "search_in_photography": { "type": "boolean", "title": "Photography flag" }, - "soundtrack": { + "search_in_soundtrack": { "type": "boolean", "title": "Soundtrack flag" }, - "producer": { + "search_in_producer": { "type": "boolean", "title": "Producer flag" }, @@ -52,5 +52,5 @@ "title": "Film release year to" } }, - "required": [ "text" ] + "required": [ "text", "search_in_title", "search_in_director", "search_in_cast", "search_in_screenplay", "search_in_photography", "search_in_soundtrack", "search_in_producer", "country", "genre", "year_from", "year_to"] } \ No newline at end of file diff --git a/psalm.xml b/psalm.xml index 3e26efb..6af7b54 100644 --- a/psalm.xml +++ b/psalm.xml @@ -3,6 +3,8 @@ errorLevel="4" resolveFromConfigFile="true" allowStringToStandInForClass="true" + findUnusedBaselineEntry="false" + findUnusedCode="false" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://getpsalm.org/schema/config" xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"> @@ -11,4 +13,7 @@ + + + diff --git a/src/Log4Php/Appenders/LoggerAppenderSyslog.php b/src/Log4Php/Appenders/LoggerAppenderSyslog.php index 4783f7a..7186709 100644 --- a/src/Log4Php/Appenders/LoggerAppenderSyslog.php +++ b/src/Log4Php/Appenders/LoggerAppenderSyslog.php @@ -113,8 +113,7 @@ private function parseOption(): int if (defined($constant)) { $value |= constant($constant); } else { - $log = 'log4php: Invalid syslog option provided: $option. '; - $log .= 'Whole option string: ' . $this->option . '.'; + $log = "log4php: Invalid syslog option provided: $option. Whole option string: $this->option."; trigger_error($log, E_USER_WARNING); } } @@ -131,7 +130,7 @@ private function parseFacility(): string return constant($constant); } - trigger_error('log4php: Invalid syslog facility provided: ' . $this->facility . '.', E_USER_WARNING); + trigger_error("log4php: Invalid syslog facility provided: $this->facility.", E_USER_WARNING); } return ''; @@ -145,7 +144,7 @@ private function parsePriority(): mixed return constant($constant); } - trigger_error('log4php: Invalid syslog priority provided: ' . $this->priority . '.', E_USER_WARNING); + trigger_error("log4php: Invalid syslog priority provided: $this->priority.", E_USER_WARNING); } return null; diff --git a/src/Log4Php/Configurators/LoggerConfiguratorDefault.php b/src/Log4Php/Configurators/LoggerConfiguratorDefault.php index fa6fa5d..759f225 100644 --- a/src/Log4Php/Configurators/LoggerConfiguratorDefault.php +++ b/src/Log4Php/Configurators/LoggerConfiguratorDefault.php @@ -76,10 +76,8 @@ private function parseFile(string $url): array throw new LoggerException("File not found at [$url]."); } - $namespace = 'Log4Php\\Configurators\\'; - $type = $this->getConfigType($url); - $adapterClass = $namespace . $this->adapters[$type]; + $adapterClass = 'Log4Php\\Configurators\\' . $this->adapters[$type]; $adapter = new $adapterClass(); return $adapter->convert($url); @@ -198,15 +196,12 @@ private function configureDefaultRenderer(LoggerHierarchy $hierarchy, string $cl private function configureAppender(string $name, array $config): void { - $namespace = 'Log4Php\\Appenders\\'; - - $class = $config['class']; - if (empty($class)) { + if (empty($config['class'])) { $this->warn("No class given for appender [$name]. Skipping appender definition."); return; } - $class = $namespace . $class; + $class = 'Log4Php\\Appenders\\' . $config['class']; if (!class_exists($class)) { $log = "Invalid class [$class ] given for appender [$name]. "; @@ -242,7 +237,7 @@ private function parseAppenderThreshold(string $name, array $config, LoggerAppen return; } - $log = 'Invalid threshold value [' . $config['threshold'] . '] specified for appender [' . $name . ']. '; + $log = 'Invalid threshold value [' . $config['threshold'] . "] specified for appender [$name]. "; $log .= 'Ignoring threshold definition.'; $this->warn($log); } @@ -273,16 +268,14 @@ private function parseOptions(array $config, LoggerAppender $appender): void private function createAppenderLayout(LoggerAppender $appender, array $config): void { - $name = $appender->getName(); - $class = $config['class']; + $name = $appender->getName(); - if (empty($class)) { + if (empty($config['class'])) { $this->warn("Layout class not specified for appender [$name]. Reverting to default layout."); return; } - $namespace = 'Log4Php\\Layouts\\'; - $class = $namespace . $class; + $class = 'Log4Php\\Layouts\\' . $config['class']; if (!class_exists($class)) { $log = "Nonexistent layout class [$class] specified for appender [$name]. Reverting to default layout"; @@ -359,7 +352,7 @@ private function setLoggerLevel(Logger $logger, array $config, string $loggerNam return; } - $log = 'Invalid level value [' . $config['level'] . '] specified for logger [' . $loggerName . '].'; + $log = 'Invalid level value [' . $config['level'] . "] specified for logger [$loggerName]."; $log .= ' Ignoring level definition.'; $this->warn($log); } @@ -386,7 +379,7 @@ private function setLoggerAdditivity(Logger $logger, array $config, string $logg $logger->setAdditivity($additivity); } catch (\Exception $ex) { $log = 'Invalid additivity value [' . $config['additivity'] . '] specified for logger '; - $log .= '[' . $loggerName . ']. Ignoring additivity setting.'; + $log .= "[$loggerName]. Ignoring additivity setting."; $this->warn($log); } } diff --git a/src/Log4Php/Helpers/LoggerOptionConverter.php b/src/Log4Php/Helpers/LoggerOptionConverter.php index c953417..780824c 100644 --- a/src/Log4Php/Helpers/LoggerOptionConverter.php +++ b/src/Log4Php/Helpers/LoggerOptionConverter.php @@ -72,7 +72,6 @@ public static function toPositiveIntegerEx(mixed $value): int } $log = 'Given value [' . var_export($value, true) . '] cannot be converted to a positive integer.'; - throw new LoggerException($log); } @@ -124,7 +123,7 @@ public static function toFileSizeEx(mixed $value): int return (int)$size; } - throw new LoggerException('Given value [' . $value . '] cannot be converted to a file size.'); + throw new LoggerException("Given value [$value] cannot be converted to a file size."); } public static function toStringEx(mixed $value): string diff --git a/src/Log4Php/Helpers/LoggerPatternParser.php b/src/Log4Php/Helpers/LoggerPatternParser.php index ff0db13..b93d92f 100644 --- a/src/Log4Php/Helpers/LoggerPatternParser.php +++ b/src/Log4Php/Helpers/LoggerPatternParser.php @@ -101,19 +101,16 @@ private function getConverter(string $word, LoggerFormattingInfo $info, string $ throw new LoggerException('Invalid keyword "%$word" in converison pattern. Ignoring keyword.'); } - $converterClass = $this->converterMap[$word]; - - $namespace = 'Log4Php\\Pattern\\'; - $converterClass = $namespace . $converterClass; + $converterClass = 'Log4Php\\Pattern\\' . $this->converterMap[$word]; if (!class_exists($converterClass)) { - throw new LoggerException('Class "' . $converterClass . '" does not exist.'); + throw new LoggerException("Class '$converterClass' does not exist."); } $converter = new $converterClass($info, $option); if (!($converter instanceof LoggerPatternConverter)) { - throw new LoggerException('Class "' . $converterClass . '" is not an instance of LoggerPatternConverter.'); + throw new LoggerException("Class '$converterClass' is not an instance of LoggerPatternConverter."); } return $converter; @@ -150,8 +147,8 @@ private function parseModifiers(string $modifiers): LoggerFormattingInfo $parts = explode('.', $modifiers); if (!empty($parts[0])) { - $minPart = (int)$parts[0]; - $info->min = abs($minPart); + $minPart = (int)$parts[0]; + $info->min = abs($minPart); if ($minPart > 0) { $info->padLeft = true; @@ -161,8 +158,8 @@ private function parseModifiers(string $modifiers): LoggerFormattingInfo } if (!empty($parts[1])) { - $maxPart = (int)$parts[1]; - $info->max = abs($maxPart); + $maxPart = (int)$parts[1]; + $info->max = abs($maxPart); if ($maxPart < 0) { $info->trimLeft = true; diff --git a/src/Log4Php/Layouts/LoggerLayoutSimple.php b/src/Log4Php/Layouts/LoggerLayoutSimple.php index 8a30547..17a500b 100644 --- a/src/Log4Php/Layouts/LoggerLayoutSimple.php +++ b/src/Log4Php/Layouts/LoggerLayoutSimple.php @@ -12,6 +12,6 @@ public function format(LoggerLoggingEvent $event): string $level = $event->getLevel(); $message = $event->getRenderedMessage(); - return $level . ' - ' . $message . PHP_EOL; + return "$level - $message" . PHP_EOL; } } diff --git a/src/Log4Php/LoggerConfigurable.php b/src/Log4Php/LoggerConfigurable.php index e4277d5..1b3f5d5 100644 --- a/src/Log4Php/LoggerConfigurable.php +++ b/src/Log4Php/LoggerConfigurable.php @@ -13,10 +13,7 @@ protected function setBoolean(string $property, bool $value): void $this->$property = LoggerOptionConverter::toBooleanEx($value); } catch (Exception) { $value = var_export($value, true); - - $msg = "Invalid value given for '$property ' property: [$value]. "; - $msg .= "Expected a boolean value. Property not changed."; - + $msg = $this->getErrorMsg($property, $value) . "Expected a boolean value. Property not changed."; $this->warn($msg); } } @@ -27,10 +24,7 @@ protected function setInteger(string $property, string $value): void $this->$property = LoggerOptionConverter::toIntegerEx($value); } catch (Exception) { $value = var_export($value, true); - - $msg = "Invalid value given for '$property' property: [$value]. "; - $msg .= "Expected an integer. Property not changed."; - + $msg = $this->getErrorMsg($property, $value) . "Expected an integer. Property not changed."; $this->warn($msg); } } @@ -41,10 +35,7 @@ protected function setLevel(string $property, string $value): void $this->$property = LoggerOptionConverter::toLevelEx($value); } catch (Exception) { $value = var_export($value, true); - - $msg = "Invalid value given for '$property' property: [$value]. "; - $msg .= "Expected a level value. Property not changed."; - + $msg = $this->getErrorMsg($property, $value) . "Expected a level value. Property not changed."; $this->warn($msg); } } @@ -55,10 +46,7 @@ protected function setPositiveInteger(string $property, mixed $value): void $this->$property = LoggerOptionConverter::toPositiveIntegerEx($value); } catch (Exception) { $value = var_export($value, true); - - $msg = "Invalid value given for '$property' property: [$value]. "; - $msg .= "Expected a positive integer. Property not changed."; - + $msg = $this->getErrorMsg($property, $value) . "Expected a positive integer. Property not changed."; $this->warn($msg); } } @@ -69,10 +57,7 @@ protected function setFileSize(string $property, string $value): void $this->$property = LoggerOptionConverter::toFileSizeEx($value); } catch (Exception) { $value = var_export($value, true); - - $msg = "Invalid value given for '$property' property: [$value]. "; - $msg .= "Expected a file size value. Property not changed."; - + $msg = $this->getErrorMsg($property, $value) . "Expected a file size value. Property not changed."; $this->warn($msg); } } @@ -83,10 +68,7 @@ protected function setNumeric(string $property, string $value): void $this->$property = LoggerOptionConverter::toIntegerEx($value); } catch (Exception) { $value = var_export($value, true); - - $msg = "Invalid value given for '$property' property: [$value]. "; - $msg .= "Expected a number. Property not changed."; - + $msg = $this->getErrorMsg($property, $value) . "Expected a number. Property not changed."; $this->warn($msg); } } @@ -108,10 +90,7 @@ protected function setString(string $property, string $value, bool $nullable = f $this->$property = LoggerOptionConverter::substConstants($value); } catch (Exception) { $value = var_export($value, true); - - $msg = "Invalid value given for '$property' property: [$value]. "; - $msg .= "Expected a string. Property not changed."; - + $msg = $this->getErrorMsg($property, $value) . "Expected a string. Property not changed."; $this->warn($msg); } } @@ -121,4 +100,9 @@ protected function warn(string $message): void $class = get_class($this); trigger_error("log4php: $class : $message", E_USER_WARNING); } + + private function getErrorMsg(string $property, string $value): string + { + return "Invalid value given for '$property' property: [$value]. "; + } } diff --git a/src/Log4Php/LoggerLevel.php b/src/Log4Php/LoggerLevel.php index 5ec8546..c6a884f 100644 --- a/src/Log4Php/LoggerLevel.php +++ b/src/Log4Php/LoggerLevel.php @@ -4,14 +4,14 @@ class LoggerLevel { - public const int OFF = 2147483647; + public const int OFF = 2147483647; public const int FATAL = 50000; public const int ERROR = 40000; - public const int WARN = 30000; - public const int INFO = 20000; + public const int WARN = 30000; + public const int INFO = 20000; public const int DEBUG = 10000; public const int TRACE = 5000; - public const int ALL = -2147483647; + public const int ALL = -2147483647; private static array $levelMap = []; @@ -144,29 +144,29 @@ public static function toLevel(int|string $arg, LoggerLevel $defaultLevel = null private static function intLevel(int|string $arg, LoggerLevel $defaultLevel = null): ?LoggerLevel { return match ($arg) { - static::ALL => static::getLevelAll(), + static::ALL => static::getLevelAll(), static::TRACE => static::getLevelTrace(), static::DEBUG => static::getLevelDebug(), - static::INFO => static::getLevelInfo(), - static::WARN => static::getLevelWarn(), + static::INFO => static::getLevelInfo(), + static::WARN => static::getLevelWarn(), static::ERROR => static::getLevelError(), static::FATAL => static::getLevelFatal(), - static::OFF => static::getLevelOff(), - default => $defaultLevel + static::OFF => static::getLevelOff(), + default => $defaultLevel }; } private static function strLevel(int|string $arg, LoggerLevel $defaultLevel = null): ?LoggerLevel { return match (strtoupper((string)$arg)) { - 'ALL' => static::getLevelAll(), + 'ALL' => static::getLevelAll(), 'TRACE' => static::getLevelTrace(), 'DEBUG' => static::getLevelDebug(), - 'INFO' => static::getLevelInfo(), - 'WARN' => static::getLevelWarn(), + 'INFO' => static::getLevelInfo(), + 'WARN' => static::getLevelWarn(), 'ERROR' => static::getLevelError(), 'FATAL' => static::getLevelFatal(), - 'OFF' => static::getLevelOff(), + 'OFF' => static::getLevelOff(), default => $defaultLevel }; } diff --git a/src/Log4Php/LoggerLoggingEvent.php b/src/Log4Php/LoggerLoggingEvent.php index 8f5f2cd..91c9352 100644 --- a/src/Log4Php/LoggerLoggingEvent.php +++ b/src/Log4Php/LoggerLoggingEvent.php @@ -35,7 +35,7 @@ public function __construct( } if (($logger instanceof Logger)) { - $this->logger = $logger; + $this->logger = $logger; $this->categoryName = $logger->getName(); return; } @@ -120,7 +120,7 @@ public function getNDC(): ?string { if ($this->ndcLookupRequired) { $this->ndcLookupRequired = false; - $this->ndc = LoggerNDC::get(); + $this->ndc = LoggerNDC::get(); } return $this->ndc; diff --git a/src/Log4Php/LoggerReflectionUtils.php b/src/Log4Php/LoggerReflectionUtils.php index 71e7340..4dfd96f 100644 --- a/src/Log4Php/LoggerReflectionUtils.php +++ b/src/Log4Php/LoggerReflectionUtils.php @@ -10,11 +10,10 @@ public function __construct(private object $obj) { } - public static function setPropertiesByObject(object $obj, array $properties, string $prefix): mixed + public static function setPropertiesByObject(object $obj, array $properties, string $prefix): void { $pSetter = new LoggerReflectionUtils($obj); - - return $pSetter->setProperties($properties, $prefix); + $pSetter->setProperties($properties, $prefix); } public function setProperties(array $properties, string $prefix): void @@ -52,8 +51,8 @@ public function setProperty(string $name, string $value): mixed $method = 'set' . ucfirst($name); if (!method_exists($this->obj, $method)) { - $msg = 'Error setting log4php property ' . $name . ' to ' . $value . ': no method ' . $method; - $msg .= ' in class ' . get_class($this->obj); + $class = get_class($this->obj); + $msg = "Error setting log4php property $name to $value: no method $method in class $class"; throw new RuntimeException($msg); } diff --git a/src/Log4Php/LoggerThrowableInformation.php b/src/Log4Php/LoggerThrowableInformation.php index 0c7c784..76801b4 100644 --- a/src/Log4Php/LoggerThrowableInformation.php +++ b/src/Log4Php/LoggerThrowableInformation.php @@ -22,7 +22,6 @@ public function getStringRepresentation(): array { if (empty($this->throwableArray)) { $renderer = new LoggerRendererException(); - $this->throwableArray = explode("\n", $renderer->render($this->throwable)); } diff --git a/src/Log4Php/LoggerWrapper.php b/src/Log4Php/LoggerWrapper.php index d5a031c..d35a4cc 100644 --- a/src/Log4Php/LoggerWrapper.php +++ b/src/Log4Php/LoggerWrapper.php @@ -16,42 +16,42 @@ public function __construct(private Logger $logger) public function emergency(string|Stringable $message, array $context = []): void { - $this->logger->fatal($message); + $this->logger->fatal($this->interpolate($message, $context)); } public function alert(string|Stringable $message, array $context = []): void { - $this->logger->fatal($message); + $this->logger->fatal($this->interpolate($message, $context)); } public function critical(string|Stringable $message, array $context = []): void { - $this->logger->fatal($message); + $this->logger->fatal($this->interpolate($message, $context)); } public function error(string|Stringable $message, array $context = []): void { - $this->logger->error($message); + $this->logger->error($this->interpolate($message, $context)); } public function warning(string|Stringable $message, array $context = []): void { - $this->logger->warn($message); + $this->logger->warn($this->interpolate($message, $context)); } public function notice(string|Stringable $message, array $context = []): void { - $this->logger->info($message); + $this->logger->info($this->interpolate($message, $context)); } public function info(string|Stringable $message, array $context = []): void { - $this->logger->info($message); + $this->logger->info($this->interpolate($message, $context)); } public function debug(string|Stringable $message, array $context = []): void { - $this->logger->debug($message); + $this->logger->debug($this->interpolate($message, $context)); } public function log(mixed $level, string|Stringable $message, array $context = []): void @@ -73,6 +73,26 @@ public function log(mixed $level, string|Stringable $message, array $context = [ $level = LoggerLevel::toLevel($levels[$level]); - $this->logger->log($level, $message); + $this->logger->log($level, $this->interpolate($message, $context)); + } + + private function interpolate($message, array $context = []): string + { + $replace = []; + + foreach ($context as $key => $value) { + if (!is_array($value) && (!is_object($value) || method_exists($value, '__toString'))) { + $replace['{' . $key . '}'] = $value; + } + if (is_array($value)) { + $concatValue = ' '; + foreach ($value as $subKey => $subValue) { + $concatValue .= "$subKey => $subValue "; + } + $replace['{' . $key . '}'] = "[$concatValue]"; + } + } + + return strtr($message, $replace); } } diff --git a/src/Log4Php/Renderers/LoggerRendererMap.php b/src/Log4Php/Renderers/LoggerRendererMap.php index 9c71617..4e9302b 100644 --- a/src/Log4Php/Renderers/LoggerRendererMap.php +++ b/src/Log4Php/Renderers/LoggerRendererMap.php @@ -16,15 +16,13 @@ public function __construct() public function addRenderer(string $renderedClass, string $renderingClass): void { - $namespace = 'Log4Php\\Renderers\\'; + $class = 'Log4Php\\Renderers\\' . $renderingClass; - if (!class_exists($namespace . $renderingClass)) { + if (!class_exists($class)) { trigger_error('log4php: Failed adding renderer. Rendering class [' . $renderingClass . '] not found.'); return; } - $class = $namespace . $renderingClass; - $renderer = new $class(); if (!($renderer instanceof LoggerRenderer)) { diff --git a/src/Xsga/FilmAffinityApi/Modules/Errors/Application/Services/GetAllErrorsService.php b/src/Xsga/FilmAffinityApi/Modules/Errors/Application/Services/GetAllErrorsService.php index 6cc5b3b..be16c91 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Errors/Application/Services/GetAllErrorsService.php +++ b/src/Xsga/FilmAffinityApi/Modules/Errors/Application/Services/GetAllErrorsService.php @@ -21,8 +21,6 @@ public function __construct( */ public function get(): array { - $errors = $this->repository->getAllErrors(); - - return $this->mapper->convertArray($errors); + return $this->mapper->convertArray($this->repository->getAllErrors()); } } diff --git a/src/Xsga/FilmAffinityApi/Modules/Errors/Application/Services/GetErrorService.php b/src/Xsga/FilmAffinityApi/Modules/Errors/Application/Services/GetErrorService.php index 3ea6335..5cdd305 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Errors/Application/Services/GetErrorService.php +++ b/src/Xsga/FilmAffinityApi/Modules/Errors/Application/Services/GetErrorService.php @@ -18,8 +18,6 @@ public function __construct( public function get(int $code): ErrorDto { - $error = $this->getError->byCodeAndErrorNotFound($code); - - return $this->mapper->convert($error); + return $this->mapper->convert($this->getError->byCodeWithErrorWhenNotFound($code)); } } diff --git a/src/Xsga/FilmAffinityApi/Modules/Errors/Domain/Services/GetError.php b/src/Xsga/FilmAffinityApi/Modules/Errors/Domain/Services/GetError.php index 32a494d..98c9ee7 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Errors/Domain/Services/GetError.php +++ b/src/Xsga/FilmAffinityApi/Modules/Errors/Domain/Services/GetError.php @@ -22,12 +22,12 @@ public function byCode(int $code): ?Error return $this->repository->getError($code); } - public function byCodeAndErrorNotFound(int $code): Error + public function byCodeWithErrorWhenNotFound(int $code): Error { $error = $this->repository->getError($code); if ($error === null) { - $message = "Error '$code' not found"; + $message = "Error with code '$code' not found"; $this->logger->error($message); throw new ErrorNotFoundException($message, 1023, null, [1 => $code]); } diff --git a/src/Xsga/FilmAffinityApi/Modules/Errors/Infrastructure/Controllers/GetAllErrorsController.php b/src/Xsga/FilmAffinityApi/Modules/Errors/Infrastructure/Controllers/GetAllErrorsController.php index 0a96326..1396e9e 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Errors/Infrastructure/Controllers/GetAllErrorsController.php +++ b/src/Xsga/FilmAffinityApi/Modules/Errors/Infrastructure/Controllers/GetAllErrorsController.php @@ -17,8 +17,6 @@ public function __construct(private GetAllErrorsService $getAllErrorsService) public function __invoke(Request $request, Response $response): Response { - $errorsDto = $this->getAllErrorsService->get(); - - return $this->writeResponse($response, $errorsDto); + return $this->writeResponse($response, $this->getAllErrorsService->get()); } } diff --git a/src/Xsga/FilmAffinityApi/Modules/Errors/Infrastructure/Controllers/GetErrorController.php b/src/Xsga/FilmAffinityApi/Modules/Errors/Infrastructure/Controllers/GetErrorController.php index 9638321..780cc14 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Errors/Infrastructure/Controllers/GetErrorController.php +++ b/src/Xsga/FilmAffinityApi/Modules/Errors/Infrastructure/Controllers/GetErrorController.php @@ -17,8 +17,6 @@ public function __construct(private GetErrorService $getErrorService) public function __invoke(Request $request, Response $response, array $args): Response { - $errorDto = $this->getErrorService->get((int)$args['id']); - - return $this->writeResponse($response, $errorDto); + return $this->writeResponse($response, $this->getErrorService->get((int)$args['id'])); } } diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Application/Dto/AdvancedSearchDto.php b/src/Xsga/FilmAffinityApi/Modules/Films/Application/Dto/AdvancedSearchDto.php index 7099101..48e6929 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Application/Dto/AdvancedSearchDto.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Application/Dto/AdvancedSearchDto.php @@ -14,8 +14,8 @@ class AdvancedSearchDto public bool $searchTypePhotography = false; public bool $searchTypeSoundtrack = false; public bool $searchTypeProducer = false; - public string $searchCountry = ''; - public string $searchGenre = ''; + public string $searchCountryCode = ''; + public string $searchGenreCode = ''; public int $searchYearFrom = 0; public int $searchYearTo = 0; } diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Application/Dto/SingleSearchResultDto.php b/src/Xsga/FilmAffinityApi/Modules/Films/Application/Dto/SingleSearchResultDto.php index 0fb14fa..017276b 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Application/Dto/SingleSearchResultDto.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Application/Dto/SingleSearchResultDto.php @@ -9,5 +9,5 @@ class SingleSearchResultDto public int $id = 0; public string $title = ''; public int $year = 0; - public array $directors; + public array $directors = []; } diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Application/Mappers/AdvSearchDtoToAdvSearch.php b/src/Xsga/FilmAffinityApi/Modules/Films/Application/Mappers/AdvSearchDtoToAdvSearch.php index b38bbac..bfd07bd 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Application/Mappers/AdvSearchDtoToAdvSearch.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Application/Mappers/AdvSearchDtoToAdvSearch.php @@ -20,8 +20,8 @@ public function convert(AdvancedSearchDto $advSearchDto): AdvancedSearch $advSearchDto->searchTypePhotography, $advSearchDto->searchTypeSoundtrack, $advSearchDto->searchTypeProducer, - $advSearchDto->searchCountry === '' ? null : $advSearchDto->searchCountry, - $advSearchDto->searchGenre === '' ? null : $advSearchDto->searchGenre, + $advSearchDto->searchCountryCode === '' ? null : $advSearchDto->searchCountryCode, + $advSearchDto->searchGenreCode === '' ? null : $advSearchDto->searchGenreCode, $advSearchDto->searchYearFrom === 0 ? null : $advSearchDto->searchYearFrom, $advSearchDto->searchYearTo === 0 ? null : $advSearchDto->searchYearTo ); diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Application/Services/AdvancedSearchService.php b/src/Xsga/FilmAffinityApi/Modules/Films/Application/Services/AdvancedSearchService.php index f40650b..c9e8bc3 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Application/Services/AdvancedSearchService.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Application/Services/AdvancedSearchService.php @@ -18,11 +18,13 @@ final class AdvancedSearchService { + private const int SEARCH_TEXT_MIN_LENGTH = 2; + public function __construct( private LoggerInterface $logger, private GenresRepository $genresRepository, private CountriesRepository $countriesRepository, - private SearchResultsToSearchResultsDto $mapper, + private SearchResultsToSearchResultsDto $searchResultsMapper, private AdvSearchDtoToAdvSearch $advSearchMapper, private AdvancedSearchRepository $repository ) { @@ -31,50 +33,48 @@ public function __construct( public function search(AdvancedSearchDto $advancedSearchDto): SearchResultsDto { $this->validatesSearchTextLength($advancedSearchDto->searchText); - $this->validatesGenre($advancedSearchDto->searchGenre); - $this->validatesCountry($advancedSearchDto->searchCountry); + $this->validatesGenre($advancedSearchDto->searchGenreCode); + $this->validatesCountry($advancedSearchDto->searchCountryCode); $searchResults = $this->repository->get($this->advSearchMapper->convert($advancedSearchDto)); - return $this->mapper->convert($searchResults); + $this->logger->info('The advanced search returned ' . $searchResults->total() . ' results'); + + return $this->searchResultsMapper->convert($searchResults); } private function validatesSearchTextLength(string $searchText): void { - if (strlen($searchText) < 3) { - $errorMsg = 'Search text lenght not valid'; + if (strlen($searchText) < self::SEARCH_TEXT_MIN_LENGTH) { + $errorMsg = 'Search text length not valid'; $this->logger->error($errorMsg); throw new InvalidSearchLengthException($errorMsg, 2001); } } - private function validatesGenre(string $searchGenre): void + private function validatesGenre(string $searchGenreCode): void { - if (empty($searchGenre)) { + if (empty($searchGenreCode)) { return; } - $genre = $this->genresRepository->get($searchGenre); - - if (is_null($genre)) { - $errorMsg = 'Genre code not valid'; + if (is_null($this->genresRepository->get($searchGenreCode))) { + $errorMsg = "Genre with code '$searchGenreCode' not valid"; $this->logger->error($errorMsg); - throw new GenreNotFoundException($errorMsg, 2002); + throw new GenreNotFoundException($errorMsg, 2002, null, [1 => $searchGenreCode]); } } - private function validatesCountry(string $searchCountry): void + private function validatesCountry(string $searchCountryCode): void { - if (empty($searchCountry)) { + if (empty($searchCountryCode)) { return; } - $country = $this->countriesRepository->get($searchCountry); - - if (is_null($country)) { - $errorMsg = 'Country code not valid'; + if (is_null($this->countriesRepository->get($searchCountryCode))) { + $errorMsg = "Country with code '$searchCountryCode' not valid"; $this->logger->error($errorMsg); - throw new CountryNotFoundException($errorMsg, 2003); + throw new CountryNotFoundException($errorMsg, 2003, null, [1 => $searchCountryCode]); } } } diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Application/Services/BackupCountriesService.php b/src/Xsga/FilmAffinityApi/Modules/Films/Application/Services/BackupCountriesService.php index 45c0705..cc13538 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Application/Services/BackupCountriesService.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Application/Services/BackupCountriesService.php @@ -4,13 +4,17 @@ namespace Xsga\FilmAffinityApi\Modules\Films\Application\Services; +use Psr\Log\LoggerInterface; use Throwable; +use Xsga\FilmAffinityApi\Modules\Films\Application\Mappers\CountryToCountryDto; use Xsga\FilmAffinityApi\Modules\Films\Infrastructure\Repositories\FilmAffinityCountriesRepository; final class BackupCountriesService { public function __construct( + private LoggerInterface $logger, private FilmAffinityCountriesRepository $repository, + private CountryToCountryDto $mapper, private string $language, private string $destinationPath ) { @@ -20,14 +24,17 @@ public function get(): bool { try { $countries = $this->repository->getAll(); - $countriesJson = json_encode($countries); + $countriesJson = json_encode($this->mapper->convertArray($countries)); $fileName = "countries_$this->language.json"; file_put_contents($this->destinationPath . $fileName, $countriesJson); - } catch (Throwable $th) { + + return true; + } catch (Throwable $exception) { + $this->logger->error('Error storing countries backup'); + $this->logger->error($exception->__toString()); + return false; } - - return true; } } diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Application/Services/BackupGenresService.php b/src/Xsga/FilmAffinityApi/Modules/Films/Application/Services/BackupGenresService.php index 0a6a847..c36dfd9 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Application/Services/BackupGenresService.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Application/Services/BackupGenresService.php @@ -4,13 +4,17 @@ namespace Xsga\FilmAffinityApi\Modules\Films\Application\Services; +use Psr\Log\LoggerInterface; use Throwable; +use Xsga\FilmAffinityApi\Modules\Films\Application\Mappers\GenreToGenreDto; use Xsga\FilmAffinityApi\Modules\Films\Infrastructure\Repositories\FilmAffinityGenresRepository; final class BackupGenresService { public function __construct( + private LoggerInterface $logger, private FilmAffinityGenresRepository $repository, + private GenreToGenreDto $mapper, private string $language, private string $destinationPath ) { @@ -20,14 +24,17 @@ public function get(): bool { try { $genres = $this->repository->getAll(); - $genresJson = json_encode($genres); + $genresJson = json_encode($this->mapper->convertArray($genres)); $fileName = "genres_$this->language.json"; file_put_contents($this->destinationPath . $fileName, $genresJson); - } catch (Throwable $th) { + + return true; + } catch (Throwable $exception) { + $this->logger->error('Error storing genres backup'); + $this->logger->error($exception->__toString()); + return false; } - - return true; } } diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Application/Services/GetFilmByIdService.php b/src/Xsga/FilmAffinityApi/Modules/Films/Application/Services/GetFilmByIdService.php index 5a3a01c..1a96c0d 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Application/Services/GetFilmByIdService.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Application/Services/GetFilmByIdService.php @@ -6,7 +6,9 @@ use Xsga\FilmAffinityApi\Modules\Films\Application\Dto\FilmDto; use Xsga\FilmAffinityApi\Modules\Films\Application\Mappers\FilmToFilmDto; +use Xsga\FilmAffinityApi\Modules\Films\Domain\Exceptions\FilmNotFoundException; use Xsga\FilmAffinityApi\Modules\Films\Domain\Repositories\FilmsRepository; +use Xsga\FilmAffinityApi\Modules\Shared\HttpClient\Domain\Exceptions\PageNotFoundException; final class GetFilmByIdService { @@ -18,6 +20,10 @@ public function __construct( public function get(int $filmId): FilmDto { - return $this->mapper->convert($this->repository->get($filmId)); + try { + return $this->mapper->convert($this->repository->get($filmId)); + } catch (PageNotFoundException $exception) { + throw new FilmNotFoundException("Film with ID '$filmId' not found", 2005, null, [1 => $filmId]); + } } } diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Application/Services/SimpleSearchService.php b/src/Xsga/FilmAffinityApi/Modules/Films/Application/Services/SimpleSearchService.php index af780ff..fe0e90d 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Application/Services/SimpleSearchService.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Application/Services/SimpleSearchService.php @@ -4,15 +4,20 @@ namespace Xsga\FilmAffinityApi\Modules\Films\Application\Services; +use Psr\Log\LoggerInterface; use Xsga\FilmAffinityApi\Modules\Films\Application\Dto\SearchDto; use Xsga\FilmAffinityApi\Modules\Films\Application\Dto\SearchResultsDto; use Xsga\FilmAffinityApi\Modules\Films\Application\Mappers\SearchDtoToSearch; use Xsga\FilmAffinityApi\Modules\Films\Application\Mappers\SearchResultsToSearchResultsDto; +use Xsga\FilmAffinityApi\Modules\Films\Domain\Exceptions\InvalidSearchLengthException; use Xsga\FilmAffinityApi\Modules\Films\Domain\Repositories\SearchRepository; final class SimpleSearchService { + private const int SEARCH_TEXT_MIN_LENGTH = 2; + public function __construct( + private LoggerInterface $logger, private SearchResultsToSearchResultsDto $mapper, private SearchRepository $repository, private SearchDtoToSearch $searchMapper @@ -21,8 +26,21 @@ public function __construct( public function search(SearchDto $searchDto): SearchResultsDto { + $this->validatesSearchTextLength($searchDto->searchText); + $searchResults = $this->repository->get($this->searchMapper->convert($searchDto)); + $this->logger->info('The search returned ' . $searchResults->total() . ' results'); + return $this->mapper->convert($searchResults); } + + private function validatesSearchTextLength(string $searchText): void + { + if (strlen($searchText) < self::SEARCH_TEXT_MIN_LENGTH) { + $errorMsg = 'Search text length not valid'; + $this->logger->error($errorMsg); + throw new InvalidSearchLengthException($errorMsg, 2001); + } + } } diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Exceptions/FilmNotFoundException.php b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Exceptions/FilmNotFoundException.php new file mode 100644 index 0000000..921d628 --- /dev/null +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Exceptions/FilmNotFoundException.php @@ -0,0 +1,11 @@ +id = new FilmId($id); $this->title = new FilmTitle($title); $this->originalTitle = new FilmTitle($originalTitle); @@ -192,5 +191,4 @@ public function genreTopics(): array { return $this->genreTopics; } - } diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Model/Genre.php b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Model/Genre.php index ac70fc4..9fc5bd3 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Model/Genre.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Model/Genre.php @@ -7,7 +7,7 @@ use Xsga\FilmAffinityApi\Modules\Films\Domain\ValueObjects\GenreCode; use Xsga\FilmAffinityApi\Modules\Films\Domain\ValueObjects\GenreName; -class Genre +final class Genre { private GenreCode $code; private GenreName $name; diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Model/GenreTopic.php b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Model/GenreTopic.php index 0e1b664..45be5e0 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Model/GenreTopic.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Model/GenreTopic.php @@ -7,7 +7,7 @@ use Xsga\FilmAffinityApi\Modules\Films\Domain\ValueObjects\GenreTopicId; use Xsga\FilmAffinityApi\Modules\Films\Domain\ValueObjects\GenreTopicName; -class GenreTopic +final class GenreTopic { private GenreTopicId $id; private GenreTopicName $name; diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Model/Search.php b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Model/Search.php index 91dd0f1..4e69273 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Model/Search.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Model/Search.php @@ -6,7 +6,7 @@ use Xsga\FilmAffinityApi\Modules\Films\Domain\ValueObjects\SearchText; -class Search +final class Search { private SearchText $text; diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Model/SearchResults.php b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Model/SearchResults.php index c3c02e3..4075a67 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Model/SearchResults.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Model/SearchResults.php @@ -6,7 +6,7 @@ use Xsga\FilmAffinityApi\Modules\Films\Domain\ValueObjects\ResultsCount; -class SearchResults +final class SearchResults { private ResultsCount $total; diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Model/SingleSearchResult.php b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Model/SingleSearchResult.php index e22bc88..b169899 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Model/SingleSearchResult.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Model/SingleSearchResult.php @@ -8,7 +8,7 @@ use Xsga\FilmAffinityApi\Modules\Films\Domain\ValueObjects\FilmTitle; use Xsga\FilmAffinityApi\Modules\Films\Domain\ValueObjects\FilmYear; -class SingleSearchResult +final class SingleSearchResult { private FilmId $id; private FilmTitle $title; diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/AbstractParser.php b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/AbstractParser.php index 287a81e..8337229 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/AbstractParser.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/AbstractParser.php @@ -21,24 +21,26 @@ public function init(string $pageContent): void { libxml_use_internal_errors(true); - $this->content->loadHtml(htmlspecialchars_decode( - iconv('UTF-8', 'ISO-8859-1', htmlentities($pageContent, ENT_COMPAT, 'UTF-8')), - ENT_QUOTES - )); + $this->content->loadHtml(htmlspecialchars_decode(htmlentities($pageContent, ENT_COMPAT, 'UTF-8'), ENT_QUOTES)); libxml_use_internal_errors(false); } - protected function getData(string $query, bool $outArray = true): array|DOMNodeList + protected function getData(string $query): DOMNodeList { - $domXpath = new DOMXPath($this->content); - $data = $domXpath->query($query); + return $this->getDOMData($query); + } - if (!$outArray) { - return $data; - } + protected function getDataArray(string $query): array + { + return $this->convertToArray($this->getDOMData($query)); + } + + private function getDOMData(string $query): DOMNodeList + { + $domXpath = new DOMXPath($this->content); - return $this->convertToArray($data); + return $domXpath->query($query); } private function convertToArray(DOMNodeList $data): array diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/AdvancedSearchFormParser.php b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/AdvancedSearchFormParser.php index 1a3aa4c..418f1f2 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/AdvancedSearchFormParser.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/AdvancedSearchFormParser.php @@ -4,6 +4,7 @@ namespace Xsga\FilmAffinityApi\Modules\Films\Domain\Parsers; +use DOMElement; use Xsga\FilmAffinityApi\Modules\Films\Domain\Model\Country; use Xsga\FilmAffinityApi\Modules\Films\Domain\Model\Genre; @@ -17,19 +18,22 @@ final class AdvancedSearchFormParser extends AbstractParser */ public function getGenres(): array { - $xpathResults = $this->getData(self::QUERY_ADV_SEARCH_FORM_GET_GENRES, false); + $xpathResults = $this->getData(self::QUERY_ADV_SEARCH_FORM_GET_GENRES); $out = []; + /** + * @var DOMElement $element + */ foreach ($xpathResults as $element) { if ($element->getAttribute('value') === '') { continue; } - $out[] = new Genre($element->getAttribute('value'), trim($element->nodeValue)); + $out[] = new Genre($element->getAttribute('value'), trim($element->nodeValue ?? '')); } - $this->logger->info('FilmAffinity genres: ' . count($out) . ' results found'); + $this->logger->debug('FilmAffinity genres: ' . count($out) . ' results found'); return $out; } @@ -39,15 +43,18 @@ public function getGenres(): array */ public function getCountries(): array { - $xpathResults = $this->getData(self::QUERY_ADV_SEARCH_FORM_GET_COUNTRIES, false); + $xpathResults = $this->getData(self::QUERY_ADV_SEARCH_FORM_GET_COUNTRIES); $out = []; + /** + * @var DOMElement $element + */ foreach ($xpathResults as $element) { - $out[] = new Country($element->getAttribute('value'), trim($element->nodeValue)); + $out[] = new Country($element->getAttribute('value'), trim($element->nodeValue ?? '')); } - $this->logger->info('FilmAffinity countries: ' . count($out) . ' results found'); + $this->logger->debug('FilmAffinity countries: ' . count($out) . ' results found'); return $out; } diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/AdvancedSearchParser.php b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/AdvancedSearchParser.php index 03223ed..a3ddd9f 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/AdvancedSearchParser.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/AdvancedSearchParser.php @@ -24,10 +24,10 @@ final class AdvancedSearchParser extends AbstractParser public function getAdvSearchResults(): SearchResults { - $xpathResults = $this->getData(self::QUERY_ADV_SEARCH_DATA, false); + $xpathResults = $this->getData(self::QUERY_ADV_SEARCH_DATA); $totalResults = $xpathResults->length; - $this->logger->info("FilmAffinity search: $totalResults results found"); + $this->logger->debug("FilmAffinity search: $totalResults results found"); return new SearchResults($totalResults, $this->getResults($xpathResults)); } @@ -60,7 +60,7 @@ private function getResultData(DOMNodeList $node, int $itemNumber): SingleSearch private function getFilmId(DOMXPath $domXpath): int { $idResult = $domXpath->query(self::QUERY_ADV_SEARCH_GET_ID); - $id = $idResult->item(0)->attributes->getNamedItem('data-movie-id')->nodeValue; + $id = $idResult->item(0)->attributes->getNamedItem('data-movie-id')->nodeValue ?? ''; return (int)trim($id); } @@ -68,9 +68,9 @@ private function getFilmId(DOMXPath $domXpath): int private function getFilmTitle(DOMXPath $domXpath): string { $titleResult = $domXpath->query(self::QUERY_ADV_SEARCH_GET_TITLE); - $title = trim(str_replace(' ', ' ', trim(str_replace(' ', ' ', $titleResult->item(0)->nodeValue)))); + $title = str_replace(' ', ' ', trim(str_replace(' ', ' ', $titleResult->item(0)->nodeValue))); - return $title; + return trim($title ?? ''); } private function getFilmYear(DOMXPath $domXpath): int diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmCastParser.php b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmCastParser.php index 6a73d49..448cdaf 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmCastParser.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmCastParser.php @@ -18,7 +18,7 @@ final class FilmCastParser extends AbstractParser */ public function getCast(): array { - $data = $this->getData(self::QUERY_FILM_GET_ACTORS, false); + $data = $this->getData(self::QUERY_FILM_GET_ACTORS); $out = []; @@ -26,6 +26,8 @@ public function getCast(): array $out[] = $this->getActor($item); } + $this->logger->debug(count($out) . ' actors found'); + return $out; } @@ -34,7 +36,7 @@ private function getActor(DOMElement $item): Actor $url = trim($item->getAttribute('href')); $actorId = (int)substr($url, strpos($url, $this->urlPattern) + strlen($this->urlPattern), -1); - $actorName = trim($item->nodeValue); + $actorName = trim($item->nodeValue ?? ''); return new Actor($actorId, $actorName); } diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmCountryParser.php b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmCountryParser.php index 992846f..ec6969c 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmCountryParser.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmCountryParser.php @@ -12,12 +12,12 @@ final class FilmCountryParser extends AbstractParser public function getCountry(): Country { - $data = $this->getData(self::QUERY_FILM_GET_COUNTRY, false); + $data = $this->getData(self::QUERY_FILM_GET_COUNTRY); $countryCode = $this->getCountryCode($data->item(0)->attributes->getNamedItem('src')->nodeValue); $countryName = $data->item(0)->attributes->getNamedItem('alt')->nodeValue; - return new Country($countryCode, $countryName); + return new Country($countryCode, $countryName ?? ''); } private function getCountryCode(string $url): string diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmCoverParser.php b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmCoverParser.php index 5e31ee8..d89f6e1 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmCoverParser.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmCoverParser.php @@ -17,26 +17,26 @@ public function getCover(): Cover private function getCoverUrl(): string { - $data = $this->getData(self::QUERY_FILM_GET_COVER, false); + $data = $this->getData(self::QUERY_FILM_GET_COVER); if ($data->length === 0) { $this->logger->warning('Film cover URL not found'); return ''; } - return trim($data->item(0)?->attributes?->getNamedItem('href')?->nodeValue); + return trim($data->item(0)?->attributes?->getNamedItem('href')?->nodeValue ?? ''); } private function getCoverFileName(): string { - $data = $this->getData(self::QUERY_FILM_GET_COVER, false); + $data = $this->getData(self::QUERY_FILM_GET_COVER); if ($data->length === 0) { $this->logger->warning('Film cover file not found'); return ''; } - $coverUrl = trim($data->item(0)?->attributes?->getNamedItem('href')?->nodeValue); + $coverUrl = trim($data->item(0)?->attributes?->getNamedItem('href')?->nodeValue ?? ''); $coverUrlArray = explode('/', $coverUrl); $coverFile = end($coverUrlArray); diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmDirectorsParser.php b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmDirectorsParser.php index cdf1b04..3dbce3e 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmDirectorsParser.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmDirectorsParser.php @@ -18,7 +18,7 @@ final class FilmDirectorsParser extends AbstractParser */ public function getDirectors(): array { - $data = $this->getData(self::QUERY_FILM_GET_DIRECTORS, false); + $data = $this->getData(self::QUERY_FILM_GET_DIRECTORS); $out = []; @@ -26,6 +26,8 @@ public function getDirectors(): array $out[] = $this->getDirector($item); } + $this->logger->debug(count($out) . ' directors found'); + return $out; } @@ -34,7 +36,7 @@ private function getDirector(DOMElement $item): Director $url = trim($item->getAttribute('href')); $directorId = (int)substr($url, strpos($url, $this->urlPattern) + strlen($this->urlPattern), -1); - $directorName = trim($item->nodeValue); + $directorName = trim($item->nodeValue ?? ''); return new Director($directorId, $directorName); } diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmGenresParser.php b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmGenresParser.php index c549ba5..c79bf7f 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmGenresParser.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmGenresParser.php @@ -21,7 +21,7 @@ final class FilmGenresParser extends AbstractParser */ public function getGenres(): array { - $data = $this->getData(self::QUERY_FILM_GET_GENRES, false); + $data = $this->getData(self::QUERY_FILM_GET_GENRES); $out = []; @@ -29,6 +29,8 @@ public function getGenres(): array $out[] = $this->getGenre($item); } + $this->logger->debug(count($out) . ' genres found'); + return $out; } @@ -51,7 +53,7 @@ private function getGenre(DOMNode $item): Genre */ public function getGenreTopics(): array { - $data = $this->getData(self::QUERY_FILM_GET_GENRE_TOPICS, false); + $data = $this->getData(self::QUERY_FILM_GET_GENRE_TOPICS); $out = []; @@ -59,19 +61,21 @@ public function getGenreTopics(): array $out[] = $this->getGenreTopic($item); } + $this->logger->debug(count($out) . ' genre topics found'); + return $out; } private function getGenreTopic(DOMNode $item): GenreTopic { - $url = trim($item->attributes?->getNamedItem('href')?->nodeValue); + $url = trim($item->attributes?->getNamedItem('href')?->nodeValue ?? ''); $genreTopicId = (int)substr( $url, strpos($url, $this->urlTopic) + strlen($this->urlTopic), strpos($url, '&') - strpos($url, $this->urlTopic) - strlen($this->urlTopic) ); - $genreTopicName = trim($item->nodeValue); + $genreTopicName = trim($item->nodeValue ?? ''); return new GenreTopic($genreTopicId, $genreTopicName); } diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmParser.php b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmParser.php index 33358ae..28401ca 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmParser.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/FilmParser.php @@ -46,40 +46,40 @@ private function validateMultipleResult(array $results, string $element): bool public function getTitle(): string { - $data = $this->getData(self::QUERY_FILM_GET_TITLE); + $data = $this->getDataArray(self::QUERY_FILM_GET_TITLE); if (!$this->validateOneResult($data, 'film title')) { return ''; } - return trim($data[0]); + return trim($data[0] ?? ''); } public function getYear(): int { - $data = $this->getData(self::QUERY_FILM_GET_RELEASE_DATE); + $data = $this->getDataArray(self::QUERY_FILM_GET_RELEASE_DATE); if (!$this->validateOneResult($data, 'film release')) { return 0; } - return (int)trim($data[0]); + return (int)trim($data[0] ?? ''); } public function getDuration(): int { - $data = $this->getData(self::QUERY_FILM_GET_DURATION); + $data = $this->getDataArray(self::QUERY_FILM_GET_DURATION); if (!$this->validateOneResult($data, 'film duration')) { return 0; } - return (int)trim(str_replace('min.', '', $data[0])); + return (int)trim(str_replace('min.', '', $data[0] ?? '')); } public function getProducers(): string { - $data = $this->getData(self::QUERY_FILM_GET_PRODUCERS); + $data = $this->getDataArray(self::QUERY_FILM_GET_PRODUCERS); if (!$this->validateMultipleResult($data, 'film producers')) { return ''; @@ -90,63 +90,51 @@ public function getProducers(): string public function getOriginalTitle(): string { - $data = $this->getData(self::QUERY_FILM_GET_ORIGINAL_TITLE); + $data = $this->getDataArray(self::QUERY_FILM_GET_ORIGINAL_TITLE); - return match (isset($data[0])) { - true => trim(str_replace('aka', '', $data[0])), - false => '' - }; + return trim(str_replace('aka', '', $data[0] ?? '')); } public function getScreenplay(): string { - $data = $this->getData(self::QUERY_FILM_GET_VARIOUS); + $data = $this->getDataArray(self::QUERY_FILM_GET_VARIOUS); - return match (isset($data[0])) { - true => trim($data[0]), - false => '' - }; + return trim($data[0] ?? ''); } public function getSoundtrack(): string { - $data = $this->getData(self::QUERY_FILM_GET_VARIOUS); + $data = $this->getDataArray(self::QUERY_FILM_GET_VARIOUS); - return match (isset($data[1])) { - true => trim($data[1]), - false => '' - }; + return trim($data[1] ?? ''); } public function getPhotography(): string { - $data = $this->getData(self::QUERY_FILM_GET_VARIOUS); + $data = $this->getDataArray(self::QUERY_FILM_GET_VARIOUS); - return match (isset($data[2])) { - true => trim($data[2]), - false => '' - }; + return trim($data[2] ?? ''); } public function getRating(): string { - $data = $this->getData(self::QUERY_FILM_GET_RATING); + $data = $this->getDataArray(self::QUERY_FILM_GET_RATING); if (!$this->validateOneResult($data, 'film rating')) { return ''; } - return trim($data[0]); + return trim($data[0] ?? ''); } public function getSynopsis(): string { - $data = $this->getData(self::QUERY_FILM_GET_SYNOPSIS); + $data = $this->getDataArray(self::QUERY_FILM_GET_SYNOPSIS); if (!$this->validateOneResult($data, 'film synopsis')) { return ''; } - return trim(str_replace('(FILMAFFINITY)', '', $data[0])); + return trim(str_replace('(FILMAFFINITY)', '', $data[0] ?? '')); } } diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/SimpleSearchParser.php b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/SimpleSearchParser.php index 3715de3..d5af151 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/SimpleSearchParser.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Parsers/SimpleSearchParser.php @@ -25,9 +25,12 @@ final class SimpleSearchParser extends AbstractParser public function isSingleResult(): bool { - $queyResults = $this->getData(self::QUERY_RESULTS_TYPE, false); + $queyResults = $this->getData(self::QUERY_RESULTS_TYPE); - if (($queyResults->length > 0) && ($queyResults->item(0)?->attributes?->getNamedItem('content')?->nodeValue !== 'FilmAffinity')) { + if ( + ($queyResults->length > 0) && + ($queyResults->item(0)?->attributes?->getNamedItem('content')?->nodeValue !== 'FilmAffinity') + ) { return true; } @@ -36,7 +39,7 @@ public function isSingleResult(): bool public function getSingleResultId(): int { - $idSearch = $this->getData(self::QUERY_SINGLE_RESULT_GET_ID, false); + $idSearch = $this->getData(self::QUERY_SINGLE_RESULT_GET_ID); $idArray = explode('/', $idSearch->item(0)?->attributes?->getNamedItem('content')?->nodeValue); return (int)trim(str_replace('film', '', str_replace('.html', '', end($idArray)))); @@ -44,7 +47,7 @@ public function getSingleResultId(): int public function getMultiplesResultsTotal(): int { - $searchResults = $this->getData(self::QUERY_MULTIPLE_RESULTS_DATA, false); + $searchResults = $this->getData(self::QUERY_MULTIPLE_RESULTS_DATA); return $searchResults->length; } @@ -54,7 +57,7 @@ public function getMultiplesResultsTotal(): int */ public function getMultiplesResults(): array { - $searchResults = $this->getData(self::QUERY_MULTIPLE_RESULTS_DATA, false); + $searchResults = $this->getData(self::QUERY_MULTIPLE_RESULTS_DATA); $out = []; @@ -84,25 +87,22 @@ private function getId(DOMXPath $domXpath): int { $idResult = $domXpath->query(self::QUERY_MULTIPLE_RESULTS_GET_ID); - return (int)trim($idResult->item(0)?->attributes?->getNamedItem('data-movie-id')?->nodeValue); + return (int)trim($idResult->item(0)?->attributes?->getNamedItem('data-movie-id')?->nodeValue ?? ''); } private function getTitle(DOMXPath $domXpath): string { $titleResult = $domXpath->query(self::QUERY_MULTIPLE_RESULTS_GET_TITLE); + $titleText = is_null($titleResult->item(0)->nodeValue) ? '' : $titleResult->item(0)->nodeValue; - $title = trim(str_replace(' ', ' ', str_replace(' ', ' ', $titleResult->item(0)->nodeValue))); - - return $title; + return trim(str_replace(' ', ' ', str_replace(' ', ' ', $titleText))); } private function getYear(DOMXPath $domXpath): int { $yearResult = $domXpath->query(self::QUERY_MULTIPLE_RESULTS_GET_YEAR); - $year = $yearResult->item(0)->nodeValue ?? ''; - - return (int)trim($year); + return (int)trim($yearResult->item(0)->nodeValue ?? ''); } /** @@ -126,7 +126,7 @@ private function getDirector(DOMElement $item): Director $url = trim($item->getAttribute('href')); $directorId = (int)substr($url, strpos($url, $this->urlPattern) + strlen($this->urlPattern), -1); - $directorName = trim($item->nodeValue); + $directorName = trim($item->nodeValue ?? ''); return new Director($directorId, $directorName); } diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Services/GetAdvancedSearchResultsService.php b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Services/GetAdvancedSearchResultsService.php new file mode 100644 index 0000000..6b46185 --- /dev/null +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Services/GetAdvancedSearchResultsService.php @@ -0,0 +1,22 @@ +parser->init($pageContent); + + return $this->parser->getAdvSearchResults(); + } +} diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Services/GetCountriesService.php b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Services/GetCountriesService.php new file mode 100644 index 0000000..f6bc531 --- /dev/null +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Services/GetCountriesService.php @@ -0,0 +1,25 @@ +parser->init($pageContent); + + return $this->parser->getCountries(); + } +} diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Services/GetFilmService.php b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Services/GetFilmService.php index 43c743f..ada83cc 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Services/GetFilmService.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Services/GetFilmService.php @@ -26,18 +26,8 @@ public function __construct( public function get(int $filmId, string $pageContent): Film { - $this->filmParser->init($pageContent); - $this->castParser->init($pageContent); - $this->coverParser->init($pageContent); - $this->directorsParser->init($pageContent); - $this->genresParser->init($pageContent); - $this->countryParser->init($pageContent); - - return $this->getFilm($filmId); - } + $this->initParsers($pageContent); - private function getFilm(int $filmId): Film - { return new Film( $filmId, $this->filmParser->getTitle(), @@ -58,4 +48,14 @@ private function getFilm(int $filmId): Film $this->genresParser->getGenreTopics() ); } + + private function initParsers(string $pageContent): void + { + $this->filmParser->init($pageContent); + $this->castParser->init($pageContent); + $this->coverParser->init($pageContent); + $this->directorsParser->init($pageContent); + $this->genresParser->init($pageContent); + $this->countryParser->init($pageContent); + } } diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Services/GetGenresService.php b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Services/GetGenresService.php new file mode 100644 index 0000000..0cbba6a --- /dev/null +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Services/GetGenresService.php @@ -0,0 +1,25 @@ +parser->init($pageContent); + + return $this->parser->getGenres(); + } +} diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Services/GetSimpleSearchResultsService.php b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Services/GetSimpleSearchResultsService.php index 52876e7..e47f705 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Services/GetSimpleSearchResultsService.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Services/GetSimpleSearchResultsService.php @@ -4,7 +4,6 @@ namespace Xsga\FilmAffinityApi\Modules\Films\Domain\Services; -use Psr\Log\LoggerInterface; use Xsga\FilmAffinityApi\Modules\Films\Domain\Model\SearchResults; use Xsga\FilmAffinityApi\Modules\Films\Domain\Model\SingleSearchResult; use Xsga\FilmAffinityApi\Modules\Films\Domain\Parsers\FilmDirectorsParser; @@ -14,7 +13,6 @@ final class GetSimpleSearchResultsService { public function __construct( - private LoggerInterface $logger, private SimpleSearchParser $simpleSearchParser, private FilmParser $filmParser, private FilmDirectorsParser $filmDirectorsParser @@ -25,14 +23,10 @@ public function get(string $pageContent): SearchResults { $this->simpleSearchParser->init($pageContent); - $searchResults = match ($this->simpleSearchParser->isSingleResult()) { + return match ($this->simpleSearchParser->isSingleResult()) { true => $this->getSingleResult($pageContent), false => $this->getMultiplesResults() }; - - $this->logger->info('FilmAffinity search: ' . $searchResults->total() . 'results found'); - - return $searchResults; } private function getSingleResult(string $pageContent): SearchResults diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Services/UrlService.php b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Services/UrlService.php index 5907e6e..8e8af02 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Services/UrlService.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Domain/Services/UrlService.php @@ -21,30 +21,19 @@ public function __construct( public function getFilmUrl(int $filmId): string { - $url = str_replace('{1}', (string)$filmId, $this->filmUrl); - - $this->logger->debug("Film URL: $url"); - - return $this->baseUrl . $url; + return $this->baseUrl . str_replace('{1}', (string)$filmId, $this->filmUrl); } public function getSearchUrl(Search $search): string { - $url = str_replace('{1}', $this->prepareSearchText($search->text()), $this->searchUrl); - - $this->logger->debug("Search URL: $url"); - - return $this->baseUrl . $url; + return $this->baseUrl . str_replace('{1}', $this->prepareSearchText($search->text()), $this->searchUrl); } public function getAdvancedSearchFormUrl(): string { $urlArray = explode('?', $this->advancedSearchUrl); - $url = $urlArray[0]; - - $this->logger->debug("Advanced search form URL: $url"); - return $this->baseUrl . $url; + return $this->baseUrl . $urlArray[0]; } public function getAdvancedSearchUrl(AdvancedSearch $advancedSearch): string @@ -57,8 +46,6 @@ public function getAdvancedSearchUrl(AdvancedSearch $advancedSearch): string $url = str_replace('{5}', $advancedSearch->yearFrom() === 0 ? '' : (string)$advancedSearch->yearFrom(), $url); $url = str_replace('{6}', $advancedSearch->yearTo() === 0 ? '' : (string)$advancedSearch->yearTo(), $url); - $this->logger->debug("Advanced Search URL: $url"); - return $this->baseUrl . $url; } @@ -83,9 +70,6 @@ private function getAdvancedSearchType(AdvancedSearch $advancedSearch): string private function prepareSearchText(string $searchText): string { - $searchText = trim($searchText); - $searchText = str_replace(' ', '+', $searchText); - - return $searchText; + return str_replace(' ', '+', trim($searchText)); } } diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Infrastructure/Controllers/GetFilmByIdController.php b/src/Xsga/FilmAffinityApi/Modules/Films/Infrastructure/Controllers/GetFilmByIdController.php index 0864e32..ee1be5a 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Infrastructure/Controllers/GetFilmByIdController.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Infrastructure/Controllers/GetFilmByIdController.php @@ -17,8 +17,6 @@ public function __construct(private GetFilmByIdService $getFilmByIdService) public function __invoke(Request $request, Response $response, array $args): Response { - $filmId = (int)$args['id']; - - return $this->writeResponse($response, $this->getFilmByIdService->get($filmId)); + return $this->writeResponse($response, $this->getFilmByIdService->get((int)$args['id'])); } } diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Infrastructure/Mappers/JsonAdvancedSearchToAdvancedSearchDto.php b/src/Xsga/FilmAffinityApi/Modules/Films/Infrastructure/Mappers/JsonAdvancedSearchToAdvancedSearchDto.php index ce2a308..fe3f972 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Infrastructure/Mappers/JsonAdvancedSearchToAdvancedSearchDto.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Infrastructure/Mappers/JsonAdvancedSearchToAdvancedSearchDto.php @@ -13,15 +13,15 @@ public function convert(array $jsonData): AdvancedSearchDto $advancedSearchDto = new AdvancedSearchDto(); $advancedSearchDto->searchText = $jsonData['text']; - $advancedSearchDto->searchTypeTitle = $jsonData['title'] ?? false; - $advancedSearchDto->searchTypeDirector = $jsonData['director'] ?? false; - $advancedSearchDto->searchTypeCast = $jsonData['cast'] ?? false; - $advancedSearchDto->searchTypeScreenplay = $jsonData['screenplay'] ?? false; - $advancedSearchDto->searchTypePhotography = $jsonData['photography'] ?? false; - $advancedSearchDto->searchTypeSoundtrack = $jsonData['soundtrack'] ?? false; - $advancedSearchDto->searchTypeProducer = $jsonData['producer'] ?? false; - $advancedSearchDto->searchGenre = isset($jsonData['genre']) ? strtoupper($jsonData['genre']) : ''; - $advancedSearchDto->searchCountry = isset($jsonData['country']) ? strtoupper($jsonData['country']) : ''; + $advancedSearchDto->searchTypeTitle = $jsonData['search_in_title']; + $advancedSearchDto->searchTypeDirector = $jsonData['search_in_director']; + $advancedSearchDto->searchTypeCast = $jsonData['search_in_cast']; + $advancedSearchDto->searchTypeScreenplay = $jsonData['search_in_screenplay']; + $advancedSearchDto->searchTypePhotography = $jsonData['search_in_photography']; + $advancedSearchDto->searchTypeSoundtrack = $jsonData['search_in_soundtrack']; + $advancedSearchDto->searchTypeProducer = $jsonData['search_in_producer']; + $advancedSearchDto->searchGenreCode = strtoupper($jsonData['genre']); + $advancedSearchDto->searchCountryCode = strtoupper($jsonData['country']); $advancedSearchDto->searchYearFrom = $jsonData['year_from'] ?? 0; $advancedSearchDto->searchYearTo = $jsonData['year_to'] ?? 0; diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Infrastructure/Repositories/FilmAffinityAdvancedSearchRepository.php b/src/Xsga/FilmAffinityApi/Modules/Films/Infrastructure/Repositories/FilmAffinityAdvancedSearchRepository.php index f5c9359..1b92fbb 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Infrastructure/Repositories/FilmAffinityAdvancedSearchRepository.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Infrastructure/Repositories/FilmAffinityAdvancedSearchRepository.php @@ -6,8 +6,8 @@ use Xsga\FilmAffinityApi\Modules\Films\Domain\Model\AdvancedSearch; use Xsga\FilmAffinityApi\Modules\Films\Domain\Model\SearchResults; -use Xsga\FilmAffinityApi\Modules\Films\Domain\Parsers\AdvancedSearchParser; use Xsga\FilmAffinityApi\Modules\Films\Domain\Repositories\AdvancedSearchRepository; +use Xsga\FilmAffinityApi\Modules\Films\Domain\Services\GetAdvancedSearchResultsService; use Xsga\FilmAffinityApi\Modules\Films\Domain\Services\UrlService; use Xsga\FilmAffinityApi\Modules\Shared\HttpClient\Application\Services\HttpClientService; @@ -16,7 +16,7 @@ final class FilmAffinityAdvancedSearchRepository implements AdvancedSearchReposi public function __construct( private UrlService $urlService, private HttpClientService $httpClientService, - private AdvancedSearchParser $parser + private GetAdvancedSearchResultsService $getAdvSearchResultsService ) { } @@ -25,8 +25,6 @@ public function get(AdvancedSearch $advancedSearch): SearchResults $advancedSearchUrl = $this->urlService->getAdvancedSearchUrl($advancedSearch); $pageContent = $this->httpClientService->getPageContent($advancedSearchUrl); - $this->parser->init($pageContent); - - return $this->parser->getAdvSearchResults(); + return $this->getAdvSearchResultsService->get($pageContent); } } diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Infrastructure/Repositories/FilmAffinityCountriesRepository.php b/src/Xsga/FilmAffinityApi/Modules/Films/Infrastructure/Repositories/FilmAffinityCountriesRepository.php index be01b73..d306ef2 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Infrastructure/Repositories/FilmAffinityCountriesRepository.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Infrastructure/Repositories/FilmAffinityCountriesRepository.php @@ -6,8 +6,8 @@ use Psr\Log\LoggerInterface; use Xsga\FilmAffinityApi\Modules\Films\Domain\Model\Country; -use Xsga\FilmAffinityApi\Modules\Films\Domain\Parsers\AdvancedSearchFormParser; use Xsga\FilmAffinityApi\Modules\Films\Domain\Repositories\CountriesRepository; +use Xsga\FilmAffinityApi\Modules\Films\Domain\Services\GetCountriesService; use Xsga\FilmAffinityApi\Modules\Films\Domain\Services\UrlService; use Xsga\FilmAffinityApi\Modules\Shared\HttpClient\Application\Services\HttpClientService; @@ -17,7 +17,7 @@ public function __construct( private LoggerInterface $logger, private UrlService $urlService, private HttpClientService $httpClientService, - private AdvancedSearchFormParser $parser + private GetCountriesService $getCountriesService ) { } @@ -29,12 +29,10 @@ public function getAll(): array $advSearchFormUrl = $this->urlService->getAdvancedSearchFormUrl(); $pageContent = $this->httpClientService->getPageContent($advSearchFormUrl); - $this->parser->init($pageContent); - - $countries = $this->parser->getCountries(); + $countries = $this->getCountriesService->get($pageContent); if (empty($countries)) { - $this->logger->error('Error loading countries from FilmAffinity'); + $this->logger->error('Error getting countries from FilmAffinity'); return []; } diff --git a/src/Xsga/FilmAffinityApi/Modules/Films/Infrastructure/Repositories/FilmAffinityGenresRepository.php b/src/Xsga/FilmAffinityApi/Modules/Films/Infrastructure/Repositories/FilmAffinityGenresRepository.php index 6c4a002..eb9a158 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Films/Infrastructure/Repositories/FilmAffinityGenresRepository.php +++ b/src/Xsga/FilmAffinityApi/Modules/Films/Infrastructure/Repositories/FilmAffinityGenresRepository.php @@ -6,8 +6,8 @@ use Psr\Log\LoggerInterface; use Xsga\FilmAffinityApi\Modules\Films\Domain\Model\Genre; -use Xsga\FilmAffinityApi\Modules\Films\Domain\Parsers\AdvancedSearchFormParser; use Xsga\FilmAffinityApi\Modules\Films\Domain\Repositories\GenresRepository; +use Xsga\FilmAffinityApi\Modules\Films\Domain\Services\GetGenresService; use Xsga\FilmAffinityApi\Modules\Films\Domain\Services\UrlService; use Xsga\FilmAffinityApi\Modules\Shared\HttpClient\Application\Services\HttpClientService; @@ -17,7 +17,7 @@ public function __construct( private LoggerInterface $logger, private UrlService $urlService, private HttpClientService $httpClientService, - private AdvancedSearchFormParser $parser + private GetGenresService $getGenresService ) { } @@ -29,12 +29,10 @@ public function getAll(): array $advSearchFormUrl = $this->urlService->getAdvancedSearchFormUrl(); $pageContent = $this->httpClientService->getPageContent($advSearchFormUrl); - $this->parser->init($pageContent); - - $genres = $this->parser->getGenres(); + $genres = $this->getGenresService->get($pageContent); if (empty($genres)) { - $this->logger->error('Error loading genres from FilmAffinity'); + $this->logger->error('Error getting genres from FilmAffinity'); return []; } diff --git a/src/Xsga/FilmAffinityApi/Modules/Shared/HttpClient/Domain/Exceptions/PageNotFoundException.php b/src/Xsga/FilmAffinityApi/Modules/Shared/HttpClient/Domain/Exceptions/PageNotFoundException.php new file mode 100644 index 0000000..a825a68 --- /dev/null +++ b/src/Xsga/FilmAffinityApi/Modules/Shared/HttpClient/Domain/Exceptions/PageNotFoundException.php @@ -0,0 +1,11 @@ +logger->debug("GET $url"); + $response = $this->client->get($url); $statusCode = $response->getStatusCode(); + + $this->logger->debug("HTTP status code: $statusCode"); } catch (Throwable $exception) { $statusCode = $exception->getCode(); + $this->logger->error("HTTP status code: $statusCode"); + } + + if ($statusCode === 404) { + $errorMsg = "Page not found: $url"; + $this->logger->error($errorMsg); + throw new PageNotFoundException($errorMsg, 2004); } if ($statusCode !== 200) { diff --git a/src/Xsga/FilmAffinityApi/Modules/Shared/JWT/Application/Services/JWTService.php b/src/Xsga/FilmAffinityApi/Modules/Shared/JWT/Application/Services/JWTService.php index 6a82863..cb968ca 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Shared/JWT/Application/Services/JWTService.php +++ b/src/Xsga/FilmAffinityApi/Modules/Shared/JWT/Application/Services/JWTService.php @@ -5,10 +5,10 @@ namespace Xsga\FilmAffinityApi\Modules\Shared\JWT\Application\Services; use Xsga\FilmAffinityApi\Modules\Shared\Security\Application\Dto\UserDataTokenDto; -use Xsga\FilmAffinityApi\Modules\Users\Application\Dto\UserDto; +use Xsga\FilmAffinityApi\Modules\Users\Domain\Model\User; interface JWTService { - public function get(UserDto $userDto): string; + public function get(User $user): string; public function decode(string $token): ?UserDataTokenDto; } diff --git a/src/Xsga/FilmAffinityApi/Modules/Shared/JWT/Infrastructure/Services/FirebaseJwtService.php b/src/Xsga/FilmAffinityApi/Modules/Shared/JWT/Infrastructure/Services/FirebaseJwtService.php index 80ce8a9..94c6c86 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Shared/JWT/Infrastructure/Services/FirebaseJwtService.php +++ b/src/Xsga/FilmAffinityApi/Modules/Shared/JWT/Infrastructure/Services/FirebaseJwtService.php @@ -15,7 +15,7 @@ use UnexpectedValueException; use Xsga\FilmAffinityApi\Modules\Shared\JWT\Application\Services\JWTService; use Xsga\FilmAffinityApi\Modules\Shared\Security\Application\Dto\UserDataTokenDto; -use Xsga\FilmAffinityApi\Modules\Users\Application\Dto\UserDto; +use Xsga\FilmAffinityApi\Modules\Users\Domain\Model\User; final class FirebaseJwtService implements JWTService { @@ -28,7 +28,7 @@ public function __construct( ) { } - public function get(UserDto $userDto): string + public function get(User $user): string { $timestamp = time(); @@ -36,9 +36,9 @@ public function get(UserDto $userDto): string 'iat' => $timestamp, 'exp' => $timestamp + $this->tokenLifetime, 'userData' => [ - 'id' => $userDto->userId, - 'email' => $userDto->email, - 'password' => $userDto->hashedPass + 'id' => $user->userId(), + 'email' => $user->email(), + 'password' => $user->password() ] ]; diff --git a/src/Xsga/FilmAffinityApi/Modules/Shared/JsonUtils/Infrastructure/Services/GetSchemaServiceImpl.php b/src/Xsga/FilmAffinityApi/Modules/Shared/JsonUtils/Infrastructure/Services/GetSchemaServiceImpl.php index 054bd04..3a9bdd3 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Shared/JsonUtils/Infrastructure/Services/GetSchemaServiceImpl.php +++ b/src/Xsga/FilmAffinityApi/Modules/Shared/JsonUtils/Infrastructure/Services/GetSchemaServiceImpl.php @@ -38,7 +38,7 @@ private function getContent(string $fileLocation): string|false $schemaContent = file_get_contents($fileLocation); - if (empty($schemaContent)) { + if ($schemaContent === '') { $this->logger->error("Schema file empty"); return false; } diff --git a/src/Xsga/FilmAffinityApi/Modules/Shared/Security/Application/Services/BasicSecurityService.php b/src/Xsga/FilmAffinityApi/Modules/Shared/Security/Application/Services/BasicSecurityService.php index 6071003..e95ac67 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Shared/Security/Application/Services/BasicSecurityService.php +++ b/src/Xsga/FilmAffinityApi/Modules/Shared/Security/Application/Services/BasicSecurityService.php @@ -22,8 +22,6 @@ public function __construct( public function apply(string $authHeader): ?UserDataTokenDto { - $this->logger->debug('Applying BASIC security'); - $basicAuthToken = $this->authHeaderValidator->validate($authHeader, SecurityTypes::BASIC); if (empty($basicAuthToken)) { @@ -57,7 +55,8 @@ private function getUserAndPassFromBasicToken(string $authorization): array private function getUserDataToken(User $user): UserDataTokenDto { - $userDataToken = new UserDataTokenDto(); + $userDataToken = new UserDataTokenDto(); + $userDataToken->userId = $user->userId(); $userDataToken->email = $user->email(); $userDataToken->password = $user->password(); diff --git a/src/Xsga/FilmAffinityApi/Modules/Shared/Security/Application/Services/Helpers/AuthHeaderValidator.php b/src/Xsga/FilmAffinityApi/Modules/Shared/Security/Application/Services/Helpers/AuthHeaderValidator.php index 0d83a0f..d1b1517 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Shared/Security/Application/Services/Helpers/AuthHeaderValidator.php +++ b/src/Xsga/FilmAffinityApi/Modules/Shared/Security/Application/Services/Helpers/AuthHeaderValidator.php @@ -5,15 +5,10 @@ namespace Xsga\FilmAffinityApi\Modules\Shared\Security\Application\Services\Helpers; use InvalidArgumentException; -use Psr\Log\LoggerInterface; use Xsga\FilmAffinityApi\Modules\Shared\Security\Domain\SecurityTypes; final class AuthHeaderValidator { - public function __construct(private LoggerInterface $logger) - { - } - public function validate(string $authHeader, SecurityTypes $type): string { $authArray = explode(' ', $authHeader); diff --git a/src/Xsga/FilmAffinityApi/Modules/Shared/Security/Application/Services/TokenSecurityService.php b/src/Xsga/FilmAffinityApi/Modules/Shared/Security/Application/Services/TokenSecurityService.php index f44889c..f7b514c 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Shared/Security/Application/Services/TokenSecurityService.php +++ b/src/Xsga/FilmAffinityApi/Modules/Shared/Security/Application/Services/TokenSecurityService.php @@ -25,8 +25,6 @@ public function __construct( public function apply(string $authHeader): ?UserDataTokenDto { - $this->logger->debug('Applying TOKEN security'); - try { $authToken = $this->authHeaderValidator->validate($authHeader, SecurityTypes::TOKEN); $userDataToken = $this->getUserDataToken($authToken); diff --git a/src/Xsga/FilmAffinityApi/Modules/Shared/Slim/ErrorHandler/ErrorHandler.php b/src/Xsga/FilmAffinityApi/Modules/Shared/Slim/ErrorHandler/ErrorHandler.php index ccd9fbf..82f2c04 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Shared/Slim/ErrorHandler/ErrorHandler.php +++ b/src/Xsga/FilmAffinityApi/Modules/Shared/Slim/ErrorHandler/ErrorHandler.php @@ -35,8 +35,6 @@ public function __invoke( bool $logErrors, bool $logErrorDetails ): Response { - $this->logger->debug('Init ERROR HANDLER'); - $error = $this->getError->byCode($this->getErrorCode($exception)); $response = new Psr7Response($error->httpCode()); $responseDto = $this->getResponseDto($error, $response->getReasonPhrase(), $exception, $displayErrorDetails); @@ -45,7 +43,6 @@ public function __invoke( $this->logger->error('ERROR ' . $error->code() . ': ' . $exception->getMessage()); $this->logger->error($exception->__toString()); - $this->logger->debug('End ERROR HANDLER'); return $response; } diff --git a/src/Xsga/FilmAffinityApi/Modules/Shared/Slim/Middleware/SecurityMiddleware.php b/src/Xsga/FilmAffinityApi/Modules/Shared/Slim/Middleware/SecurityMiddleware.php index 4b22113..6b6917c 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Shared/Slim/Middleware/SecurityMiddleware.php +++ b/src/Xsga/FilmAffinityApi/Modules/Shared/Slim/Middleware/SecurityMiddleware.php @@ -21,7 +21,7 @@ final class SecurityMiddleware implements MiddlewareInterface { - private string $getTokenRoute = 'get_token'; + private const string GET_TOKEN_ROUTE = 'get_token'; public function __construct( private LoggerInterface $logger, @@ -33,18 +33,15 @@ public function __construct( public function process(Request $request, Handler $handler): Response { - $this->logger->debug('Init middleware SECURITY'); - $routeName = $this->getRouteName($request); $request = $request->withAttribute('routeName', $routeName); if ($this->isNonSecuredRequest($routeName)) { - if ($routeName === $this->getTokenRoute) { + if ($routeName === self::GET_TOKEN_ROUTE) { $this->validateSecurityType(); } $this->logger->debug('Non secured request, not applying security'); - $this->logger->debug('End middleware SECURITY'); return $handler->handle($request); } @@ -60,8 +57,6 @@ public function process(Request $request, Handler $handler): Response $request = $request->withAttribute('userDataToken', $userDataToken); - $this->logger->debug('End middleware SECURITY'); - return $handler->handle($request); } @@ -70,7 +65,7 @@ private function getRouteName(Request $request): string $route = RouteContext::fromRequest($request)->getRoute(); if (is_null($route)) { - $errorMsg = "Error getting Slim route context"; + $errorMsg = "Error getting Slim route from context"; $this->logger->error($errorMsg); throw new RouteContextException($errorMsg, 1021); } @@ -81,7 +76,7 @@ private function getRouteName(Request $request): string private function isNonSecuredRequest(string $routeName): bool { return match ($routeName) { - $this->getTokenRoute => true, + self::GET_TOKEN_ROUTE => true, default => false }; } diff --git a/src/Xsga/FilmAffinityApi/Modules/Users/Application/Dto/UserDto.php b/src/Xsga/FilmAffinityApi/Modules/Users/Application/Dto/UserDto.php index b932907..15c1d24 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Users/Application/Dto/UserDto.php +++ b/src/Xsga/FilmAffinityApi/Modules/Users/Application/Dto/UserDto.php @@ -8,7 +8,6 @@ class UserDto { public int $userId = 0; public string $email = ''; - public string $hashedPass = ''; public bool $status = true; public string $createDate = ''; public string $updateDate = ''; diff --git a/src/Xsga/FilmAffinityApi/Modules/Users/Application/Mappers/UserToUserDto.php b/src/Xsga/FilmAffinityApi/Modules/Users/Application/Mappers/UserToUserDto.php index c13c963..fb88625 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Users/Application/Mappers/UserToUserDto.php +++ b/src/Xsga/FilmAffinityApi/Modules/Users/Application/Mappers/UserToUserDto.php @@ -19,7 +19,6 @@ public function convert(User $user): UserDto $userDto->userId = $user->userId(); $userDto->email = $user->email(); - $userDto->hashedPass = $user->password(); $userDto->status = $user->status(); $userDto->createDate = $user->createDate()->format($this->dateTimeMask); $userDto->updateDate = $user->updateDate()->format($this->dateTimeMask); diff --git a/src/Xsga/FilmAffinityApi/Modules/Users/Application/Services/DeleteUserByEmailService.php b/src/Xsga/FilmAffinityApi/Modules/Users/Application/Services/DeleteUserByEmailService.php index c8fc325..fe5a499 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Users/Application/Services/DeleteUserByEmailService.php +++ b/src/Xsga/FilmAffinityApi/Modules/Users/Application/Services/DeleteUserByEmailService.php @@ -19,20 +19,19 @@ public function __construct( public function delete(string $userEmail): bool { $userDeleteStatus = $this->usersRepository->deleteUserByEmail($userEmail); - - $this->validateUserDelete($userDeleteStatus); - - $this->logger->info("User deleted successfully"); + $this->validateUserDelete($userDeleteStatus, $userEmail); return true; } - private function validateUserDelete(bool $userDeleteStatus): void + private function validateUserDelete(bool $userDeleteStatus, string $userEmail): void { if (!$userDeleteStatus) { $message = "Error deleting user"; $this->logger->error($message); throw new DeleteUserException($message, 1042); } + + $this->logger->info("User '$userEmail' deleted successfully"); } } diff --git a/src/Xsga/FilmAffinityApi/Modules/Users/Application/Services/GetTokenService.php b/src/Xsga/FilmAffinityApi/Modules/Users/Application/Services/GetTokenService.php index 37eea80..5c39c69 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Users/Application/Services/GetTokenService.php +++ b/src/Xsga/FilmAffinityApi/Modules/Users/Application/Services/GetTokenService.php @@ -6,7 +6,6 @@ use Psr\Log\LoggerInterface; use Xsga\FilmAffinityApi\Modules\Shared\JWT\Application\Services\JWTService; -use Xsga\FilmAffinityApi\Modules\Users\Application\Mappers\UserToUserDto; use Xsga\FilmAffinityApi\Modules\Users\Domain\Services\UserLogin; final class GetTokenService @@ -14,16 +13,14 @@ final class GetTokenService public function __construct( private LoggerInterface $logger, private UserLogin $userLogin, - private JWTService $jwt, - private UserToUserDto $mapper + private JWTService $jwt ) { } public function get(string $email, string $password): string { - $user = $this->userLogin->login($email, $password); - $userDto = $this->mapper->convert($user); - $token = $this->jwt->get($userDto); + $user = $this->userLogin->login($email, $password); + $token = $this->jwt->get($user); $this->logger->info("Token for user '$email' generated successfully"); diff --git a/src/Xsga/FilmAffinityApi/Modules/Users/Application/Services/GetUserByEmailService.php b/src/Xsga/FilmAffinityApi/Modules/Users/Application/Services/GetUserByEmailService.php new file mode 100644 index 0000000..90dc8d5 --- /dev/null +++ b/src/Xsga/FilmAffinityApi/Modules/Users/Application/Services/GetUserByEmailService.php @@ -0,0 +1,23 @@ +mapper->convert($this->getUser->byEmail($userEmail)); + } +} diff --git a/src/Xsga/FilmAffinityApi/Modules/Users/Application/Services/UpdatePasswordService.php b/src/Xsga/FilmAffinityApi/Modules/Users/Application/Services/UpdatePasswordService.php index bf7270c..f3288b9 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Users/Application/Services/UpdatePasswordService.php +++ b/src/Xsga/FilmAffinityApi/Modules/Users/Application/Services/UpdatePasswordService.php @@ -25,11 +25,8 @@ public function update(UpdatePasswordDto $userData): bool $user->updatePassword($userData->newPassword); $passwordUpdateStatus = $this->usersRepository->updatePassword($user); - $this->validateUserUpdate($passwordUpdateStatus, $user->email()); - $this->logger->info("User '" . $user->email() . "' updated successfully"); - return true; } @@ -40,5 +37,7 @@ private function validateUserUpdate(bool $userUpdateStatus, string $userEmail): $this->logger->error($errorMsg); throw new UpdateUserException($errorMsg, 1012, null, [1 => $userEmail]); } + + $this->logger->info("User '$userEmail' updated successfully"); } } diff --git a/src/Xsga/FilmAffinityApi/Modules/Users/Application/Services/UpdateUserStatusService.php b/src/Xsga/FilmAffinityApi/Modules/Users/Application/Services/UpdateUserStatusService.php index 8a1d357..10e0558 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Users/Application/Services/UpdateUserStatusService.php +++ b/src/Xsga/FilmAffinityApi/Modules/Users/Application/Services/UpdateUserStatusService.php @@ -19,27 +19,27 @@ public function __construct( ) { } - public function set(int|string $userId, bool $newStatus): bool + public function set(int $userId, bool $newStatus): bool { - $user = match (is_string($userId)) { - false => $this->getUser->byId($userId), - true => $this->getUser->byEmail($userId) - }; + $user = $this->getUser->byId($userId); $this->validateActualUserStatus($user, $newStatus); + $this->changeUserStatus($user, $newStatus); + + $userUpdateStatus = $this->usersRepository->updateUserStatus($user); + $this->validateUserUpdate($userUpdateStatus, $user->email()); + return true; + } + + private function changeUserStatus(User $user, bool $newStatus): User + { match ($newStatus) { true => $user->enable(), false => $user->disable() }; - $userUpdateStatus = $this->usersRepository->updateUserStatus($user); - - $this->validateUserUpdate($userUpdateStatus, $user->email()); - - $this->logger->info("User '" . $user->email() . "' status updated successfully"); - - return true; + return $user; } private function validateActualUserStatus(User $user, bool $newStatus): void @@ -66,5 +66,7 @@ private function validateUserUpdate(bool $userUpdateStatus, string $userEmail): $this->logger->error($errorMsg); throw new UpdateUserException($errorMsg, 1012, null, [1 => $userEmail]); } + + $this->logger->info("User '$userEmail' status updated successfully"); } } diff --git a/src/Xsga/FilmAffinityApi/Modules/Users/Infrastructure/Console/DisableUserCommand.php b/src/Xsga/FilmAffinityApi/Modules/Users/Infrastructure/Console/DisableUserCommand.php index d586631..a529036 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Users/Infrastructure/Console/DisableUserCommand.php +++ b/src/Xsga/FilmAffinityApi/Modules/Users/Infrastructure/Console/DisableUserCommand.php @@ -10,6 +10,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +use Xsga\FilmAffinityApi\Modules\Users\Application\Services\GetUserByEmailService; use Xsga\FilmAffinityApi\Modules\Users\Application\Services\UpdateUserStatusService; use Xsga\FilmAffinityApi\Modules\Users\Domain\ValueObjects\UserEmail; @@ -27,6 +28,9 @@ final class DisableUserCommand extends Command #[Inject] private UpdateUserStatusService $userStatusService; + #[Inject] + private GetUserByEmailService $getUserByEmailService; + protected function configure(): void { $this->setHelp('This command allows you to disable a user.'); @@ -58,7 +62,9 @@ function (?string $email) { protected function execute(InputInterface $input, OutputInterface $output): int { - $this->userStatusService->set($this->userEmail, false); + $user = $this->getUserByEmailService->get($this->userEmail); + + $this->userStatusService->set($user->userId, false); $this->display->success("User $this->userEmail disabled successfully"); diff --git a/src/Xsga/FilmAffinityApi/Modules/Users/Infrastructure/Console/EnableUserCommand.php b/src/Xsga/FilmAffinityApi/Modules/Users/Infrastructure/Console/EnableUserCommand.php index 20a5478..efa83de 100644 --- a/src/Xsga/FilmAffinityApi/Modules/Users/Infrastructure/Console/EnableUserCommand.php +++ b/src/Xsga/FilmAffinityApi/Modules/Users/Infrastructure/Console/EnableUserCommand.php @@ -10,6 +10,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +use Xsga\FilmAffinityApi\Modules\Users\Application\Services\GetUserByEmailService; use Xsga\FilmAffinityApi\Modules\Users\Application\Services\UpdateUserStatusService; use Xsga\FilmAffinityApi\Modules\Users\Domain\ValueObjects\UserEmail; @@ -27,6 +28,9 @@ final class EnableUserCommand extends Command #[Inject] private UpdateUserStatusService $userStatusService; + #[Inject] + private GetUserByEmailService $getUserByEmailService; + protected function configure(): void { $this->setHelp('This command allows you to enable a user.'); @@ -58,7 +62,9 @@ function (?string $email) { protected function execute(InputInterface $input, OutputInterface $output): int { - $this->userStatusService->set($this->userEmail, true); + $user = $this->getUserByEmailService->get($this->userEmail); + + $this->userStatusService->set($user->userId, true); $this->display->success("User $this->userEmail enabled successfully");