From f277dd89881b08a3280283b3ab11f60fa2d7d762 Mon Sep 17 00:00:00 2001 From: Maxim Harder Date: Fri, 25 Jul 2025 09:40:47 +0200 Subject: [PATCH 1/8] =?UTF-8?q?=F0=9F=93=9D=20DOCS=20=D0=94=D0=BE=D0=B1?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D1=8F=D0=B5=D1=82=20=D0=B4=D0=BE=D0=BA=D1=83?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=86=D0=B8=D1=8E=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB=D0=B5=D0=B9=20=D0=B8=20?= =?UTF-8?q?=D0=B8=D1=81=D0=BA=D0=BB=D1=8E=D1=87=D0=B5=D0=BD=D0=B8=D0=B9=20?= =?UTF-8?q?API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлена подробная документация для исключения InvalidApiKeyException с описанием, примерами использования, обработкой и рекомендациями - Создана новая страница с обзором моделей данных Kinopoisk API с навигацией по категориям моделей, связанным сервисам и перечислениям - Включены примеры создания и работы с моделями Film, Person, Staff - Обновлены рекомендации по обработке ошибок и валидации API ключа - Документация структурирована для удобства использования и быстрого старта --- docs/dev/notkinopoiskphp/README.md | 124 ++ docs/dev/notkinopoiskphp/abstract-classes.md | 127 ++ docs/dev/notkinopoiskphp/client.md | 244 ++++ docs/dev/notkinopoiskphp/enums/api-version.md | 433 +++++++ .../notkinopoiskphp/enums/box-office-type.md | 483 ++++++++ .../notkinopoiskphp/enums/collection-type.md | 436 +++++++ .../dev/notkinopoiskphp/enums/content-type.md | 693 +++++++++++ .../enums/distribution-type.md | 582 +++++++++ docs/dev/notkinopoiskphp/enums/fact-type.md | 449 +++++++ docs/dev/notkinopoiskphp/enums/film-order.md | 416 +++++++ docs/dev/notkinopoiskphp/enums/image-type.md | 250 ++++ docs/dev/notkinopoiskphp/enums/index.md | 326 +++++ docs/dev/notkinopoiskphp/enums/month.md | 705 +++++++++++ .../notkinopoiskphp/enums/profession-key.md | 620 ++++++++++ .../notkinopoiskphp/enums/relation-type.md | 422 +++++++ .../dev/notkinopoiskphp/enums/review-order.md | 686 +++++++++++ docs/dev/notkinopoiskphp/enums/review-type.md | 276 +++++ docs/dev/notkinopoiskphp/enums/sex.md | 461 ++++++++ docs/dev/notkinopoiskphp/enums/video-site.md | 410 +++++++ .../exceptions/api-exception.md | 403 +++++++ docs/dev/notkinopoiskphp/exceptions/index.md | 422 +++++++ .../exceptions/invalid-api-key-exception.md | 226 ++++ .../exceptions/rate-limit-exception.md | 323 +++++ .../resource-not-found-exception.md | 472 ++++++++ docs/dev/notkinopoiskphp/index.md | 269 +++++ docs/dev/notkinopoiskphp/interfaces.md | 192 +++ docs/dev/notkinopoiskphp/interfaces/index.md | 435 +++++++ docs/dev/notkinopoiskphp/models/award.md | 499 ++++++++ docs/dev/notkinopoiskphp/models/box-office.md | 554 +++++++++ docs/dev/notkinopoiskphp/models/country.md | 434 +++++++ .../notkinopoiskphp/models/distribution.md | 678 +++++++++++ docs/dev/notkinopoiskphp/models/episode.md | 540 +++++++++ .../notkinopoiskphp/models/external-source.md | 596 ++++++++++ docs/dev/notkinopoiskphp/models/fact.md | 384 ++++++ .../notkinopoiskphp/models/film-collection.md | 735 ++++++++++++ .../models/film-search-result.md | 777 ++++++++++++ docs/dev/notkinopoiskphp/models/film.md | 554 +++++++++ docs/dev/notkinopoiskphp/models/filters.md | 527 +++++++++ docs/dev/notkinopoiskphp/models/genre.md | 500 ++++++++ docs/dev/notkinopoiskphp/models/image.md | 386 ++++++ docs/dev/notkinopoiskphp/models/index.md | 308 +++++ .../dev/notkinopoiskphp/models/person-film.md | 1044 +++++++++++++++++ .../notkinopoiskphp/models/person-spouse.md | 722 ++++++++++++ docs/dev/notkinopoiskphp/models/person.md | 292 +++++ docs/dev/notkinopoiskphp/models/premiere.md | 655 +++++++++++ .../notkinopoiskphp/models/related-film.md | 450 +++++++ docs/dev/notkinopoiskphp/models/review.md | 323 +++++ docs/dev/notkinopoiskphp/models/season.md | 508 ++++++++ docs/dev/notkinopoiskphp/models/staff.md | 340 ++++++ docs/dev/notkinopoiskphp/models/user-vote.md | 819 +++++++++++++ docs/dev/notkinopoiskphp/models/video.md | 460 ++++++++ docs/dev/notkinopoiskphp/navigation-map.md | 325 +++++ .../responses/default-response.md | 749 ++++++++++++ docs/dev/notkinopoiskphp/responses/index.md | 369 ++++++ .../responses/keyword-search-response.md | 669 +++++++++++ .../responses/paginated-response.md | 839 +++++++++++++ .../notkinopoiskphp/services/film-service.md | 418 +++++++ docs/dev/notkinopoiskphp/services/index.md | 220 ++++ .../notkinopoiskphp/services/media-service.md | 189 +++ .../services/person-service.md | 192 +++ .../notkinopoiskphp/services/user-service.md | 190 +++ freeze.sh | 1 + requirements.txt | 75 +- 63 files changed, 28149 insertions(+), 57 deletions(-) create mode 100644 docs/dev/notkinopoiskphp/README.md create mode 100644 docs/dev/notkinopoiskphp/abstract-classes.md create mode 100644 docs/dev/notkinopoiskphp/client.md create mode 100644 docs/dev/notkinopoiskphp/enums/api-version.md create mode 100644 docs/dev/notkinopoiskphp/enums/box-office-type.md create mode 100644 docs/dev/notkinopoiskphp/enums/collection-type.md create mode 100644 docs/dev/notkinopoiskphp/enums/content-type.md create mode 100644 docs/dev/notkinopoiskphp/enums/distribution-type.md create mode 100644 docs/dev/notkinopoiskphp/enums/fact-type.md create mode 100644 docs/dev/notkinopoiskphp/enums/film-order.md create mode 100644 docs/dev/notkinopoiskphp/enums/image-type.md create mode 100644 docs/dev/notkinopoiskphp/enums/index.md create mode 100644 docs/dev/notkinopoiskphp/enums/month.md create mode 100644 docs/dev/notkinopoiskphp/enums/profession-key.md create mode 100644 docs/dev/notkinopoiskphp/enums/relation-type.md create mode 100644 docs/dev/notkinopoiskphp/enums/review-order.md create mode 100644 docs/dev/notkinopoiskphp/enums/review-type.md create mode 100644 docs/dev/notkinopoiskphp/enums/sex.md create mode 100644 docs/dev/notkinopoiskphp/enums/video-site.md create mode 100644 docs/dev/notkinopoiskphp/exceptions/api-exception.md create mode 100644 docs/dev/notkinopoiskphp/exceptions/index.md create mode 100644 docs/dev/notkinopoiskphp/exceptions/invalid-api-key-exception.md create mode 100644 docs/dev/notkinopoiskphp/exceptions/rate-limit-exception.md create mode 100644 docs/dev/notkinopoiskphp/exceptions/resource-not-found-exception.md create mode 100644 docs/dev/notkinopoiskphp/index.md create mode 100644 docs/dev/notkinopoiskphp/interfaces.md create mode 100644 docs/dev/notkinopoiskphp/interfaces/index.md create mode 100644 docs/dev/notkinopoiskphp/models/award.md create mode 100644 docs/dev/notkinopoiskphp/models/box-office.md create mode 100644 docs/dev/notkinopoiskphp/models/country.md create mode 100644 docs/dev/notkinopoiskphp/models/distribution.md create mode 100644 docs/dev/notkinopoiskphp/models/episode.md create mode 100644 docs/dev/notkinopoiskphp/models/external-source.md create mode 100644 docs/dev/notkinopoiskphp/models/fact.md create mode 100644 docs/dev/notkinopoiskphp/models/film-collection.md create mode 100644 docs/dev/notkinopoiskphp/models/film-search-result.md create mode 100644 docs/dev/notkinopoiskphp/models/film.md create mode 100644 docs/dev/notkinopoiskphp/models/filters.md create mode 100644 docs/dev/notkinopoiskphp/models/genre.md create mode 100644 docs/dev/notkinopoiskphp/models/image.md create mode 100644 docs/dev/notkinopoiskphp/models/index.md create mode 100644 docs/dev/notkinopoiskphp/models/person-film.md create mode 100644 docs/dev/notkinopoiskphp/models/person-spouse.md create mode 100644 docs/dev/notkinopoiskphp/models/person.md create mode 100644 docs/dev/notkinopoiskphp/models/premiere.md create mode 100644 docs/dev/notkinopoiskphp/models/related-film.md create mode 100644 docs/dev/notkinopoiskphp/models/review.md create mode 100644 docs/dev/notkinopoiskphp/models/season.md create mode 100644 docs/dev/notkinopoiskphp/models/staff.md create mode 100644 docs/dev/notkinopoiskphp/models/user-vote.md create mode 100644 docs/dev/notkinopoiskphp/models/video.md create mode 100644 docs/dev/notkinopoiskphp/navigation-map.md create mode 100644 docs/dev/notkinopoiskphp/responses/default-response.md create mode 100644 docs/dev/notkinopoiskphp/responses/index.md create mode 100644 docs/dev/notkinopoiskphp/responses/keyword-search-response.md create mode 100644 docs/dev/notkinopoiskphp/responses/paginated-response.md create mode 100644 docs/dev/notkinopoiskphp/services/film-service.md create mode 100644 docs/dev/notkinopoiskphp/services/index.md create mode 100644 docs/dev/notkinopoiskphp/services/media-service.md create mode 100644 docs/dev/notkinopoiskphp/services/person-service.md create mode 100644 docs/dev/notkinopoiskphp/services/user-service.md create mode 100755 freeze.sh diff --git a/docs/dev/notkinopoiskphp/README.md b/docs/dev/notkinopoiskphp/README.md new file mode 100644 index 0000000..5a417d2 --- /dev/null +++ b/docs/dev/notkinopoiskphp/README.md @@ -0,0 +1,124 @@ +# NotKinopoisk PHP Wrapper - Документация + +Полная документация библиотеки NotKinopoisk PHP Wrapper для работы с Kinopoisk API. + +## 📚 Содержание + +### 🏗️ Архитектура + +- [Клиент (Client)](client.md) - Основной класс для работы с API +- [Интерфейсы (Interfaces)](interfaces.md) - Интерфейсы библиотеки +- [Абстрактные классы](abstract-classes.md) - Базовые классы + +### 🎬 Сервисы + +- [FilmService](services/film-service.md) - Работа с фильмами и сериалами +- [PersonService](services/person-service.md) - Работа с персонами +- [MediaService](services/media-service.md) - Работа с медиа-контентом +- [UserService](services/user-service.md) - Работа с пользователем + +### 📊 Модели данных + +- [Film](models/film.md) - Модель фильма/сериала +- [Person](models/person.md) - Модель персоны +- [Staff](models/staff.md) - Модель персонала фильма +- [Review](models/review.md) - Модель отзыва +- [Fact](models/fact.md) - Модель факта +- [Image](models/image.md) - Модель изображения +- [Video](models/video.md) - Модель видео +- [BoxOffice](models/box-office.md) - Модель кассовых сборов +- [Distribution](models/distribution.md) - Модель проката +- [Award](models/award.md) - Модель награды +- [Genre](models/genre.md) - Модель жанра +- [Country](models/country.md) - Модель страны +- [Season](models/season.md) - Модель сезона +- [Episode](models/episode.md) - Модель эпизода +- [PersonFilm](models/person-film.md) - Модель фильма персоны +- [PersonSpouse](models/person-spouse.md) - Модель супруга персоны +- [PersonByNameResult](models/person-by-name-result.md) - Результат поиска персоны +- [FilmSearchResult](models/film-search-result.md) - Результат поиска фильма +- [FilmCollection](models/film-collection.md) - Коллекция фильмов +- [RelatedFilm](models/related-film.md) - Связанный фильм +- [Premiere](models/premiere.md) - Модель премьеры +- [UserVote](models/user-vote.md) - Модель голоса пользователя +- [ExternalSource](models/external-source.md) - Модель внешнего источника +- [MediaPost](models/media-post.md) - Модель медиа-поста +- [ApiKeyInfo](models/api-key-info.md) - Информация об API ключе +- [ApiKeyQuota](models/api-key-quota.md) - Квоты API ключа +- [Filters](models/filters.md) - Фильтры для поиска + +### 🔧 Перечисления (Enums) + +- [ContentType](enums/content-type.md) - Типы контента +- [ImageType](enums/image-type.md) - Типы изображений +- [VideoSite](enums/video-site.md) - Сайты видео +- [BoxOfficeType](enums/box-office-type.md) - Типы кассовых сборов +- [DistributionType](enums/distribution-type.md) - Типы проката +- [DistributionSubType](enums/distribution-sub-type.md) - Подтипы проката +- [FactType](enums/fact-type.md) - Типы фактов +- [ReviewType](enums/review-type.md) - Типы отзывов +- [ReviewOrder](enums/review-order.md) - Порядок сортировки отзывов +- [FilmOrder](enums/film-order.md) - Порядок сортировки фильмов +- [CollectionType](enums/collection-type.md) - Типы коллекций +- [RelationType](enums/relation-type.md) - Типы связей +- [ProductionStatus](enums/production-status.md) - Статусы производства +- [ProfessionKey](enums/profession-key.md) - Ключи профессий +- [AccountType](enums/account-type.md) - Типы аккаунтов +- [Sex](enums/sex.md) - Пол +- [Month](enums/month.md) - Месяцы +- [ApiVersion](enums/api-version.md) - Версии API + +### 📤 Ответы API + +- [DefaultResponse](responses/default-response.md) - Базовый ответ +- [PaginatedResponse](responses/paginated-response.md) - Пагинированный ответ +- [SimpleResponse](responses/simple-response.md) - Простой ответ +- [BudgetResponse](responses/budget-response.md) - Ответ с бюджетом +- [KeywordSearchResponse](responses/keyword-search-response.md) - Ответ поиска по ключевым словам +- [MovieStaffResponse](responses/movie-staff-response.md) - Ответ с персоналом фильма +- [SequelPrequelResponse](responses/sequel-prequel-response.md) - Ответ с сиквелами и приквелами + +### ⚠️ Исключения + +- [ApiException](exceptions/api-exception.md) - Базовое исключение API +- [InvalidApiKeyException](exceptions/invalid-api-key-exception.md) - Неверный API ключ +- [RateLimitException](exceptions/rate-limit-exception.md) - Превышен лимит запросов +- [ResourceNotFoundException](exceptions/resource-not-found-exception.md) - Ресурс не найден +- [KpValidationException](exceptions/kp-validation-exception.md) - Ошибка валидации + +## 🚀 Быстрый старт + +```php +films->getById(301); +echo $film->getDisplayName(); +``` + +## 📋 Требования + +- PHP 8.3+ +- Composer +- API ключ от Kinopoisk Unofficial API + +## 🔗 Полезные ссылки + +- [GitHub репозиторий](https://github.com/DevCraftClub/NotKinopoiskPHP) +- [Issues](https://github.com/DevCraftClub/NotKinopoiskPHP/issues) +- [Примеры использования](../examples/) +- [Kinopoisk Unofficial API](https://kinopoiskapiunofficial.tech) + +## ⚠️ Важные предостережения + +- **Структура API** в последний раз обновлялась **16.10.2023** +- **НЕ путать** с kinopoisk.dev - это другой API +- Работает с **kinopoiskapiunofficial.tech** +- Некоторые методы могут не возвращать актуальные данные diff --git a/docs/dev/notkinopoiskphp/abstract-classes.md b/docs/dev/notkinopoiskphp/abstract-classes.md new file mode 100644 index 0000000..c377687 --- /dev/null +++ b/docs/dev/notkinopoiskphp/abstract-classes.md @@ -0,0 +1,127 @@ +# Абстрактные классы + +Абстрактные базовые классы библиотеки NotKinopoisk PHP Wrapper. + +## AbstractService + +Абстрактный базовый класс для сервисов Kinopoisk API. + +### Описание + +Предоставляет общую функциональность для всех сервисов, работающих с Kinopoisk API. Содержит базовые методы для выполнения запросов и обработки ответов. + +### Основные возможности + +- Общие методы для работы с API +- Обработка ошибок и исключений +- Логирование запросов и ответов +- Базовая валидация данных + +### Свойства + +#### `$client` + +**Тип:** `\NotKinopoisk\Client` + +Основной клиент для работы с API. + +#### `$apiVersion` + +**Тип:** `ApiVersion` + +Версия API для использования. + +### Методы + +#### `__construct(Client $client, ApiVersion $apiVersion = ApiVersion::V1)` + +Конструктор абстрактного сервиса для работы с Kinopoisk API. + +**Параметры:** + +- `$client` (\NotKinopoisk\Client) - Основной клиент для работы с API +- `$apiVersion` (ApiVersion) - Версия API для использования (по умолчанию 'v1') + +**Пример:** + +```php +// Создание сервиса с версией API по умолчанию +$client = new Client('your-api-key'); +$service = new FilmService($client); + +// Создание сервиса с указанной версией API +$service = new FilmService($client, ApiVersion::V2_2); +``` + +#### `setApiVersion(ApiVersion $apiVersion): void` + +Устанавливает версию API для работы сервиса. + +**Параметры:** + +- `$apiVersion` (ApiVersion) - Версия API из перечисления доступных версий + +#### `get(string $uri, array $query = []): array` + +Выполняет GET запрос к API. + +**Параметры:** + +- `$uri` (string) - URI для запроса +- `$query` (array) - Параметры запроса + +**Возвращает:** + +- `array` - Ответ от API + +#### `buildUri(string $endpoint, ?ApiVersion $api_version = null): string` + +Строит URI для запроса с учетом версии API. + +**Параметры:** + +- `$endpoint` (string) - Конечная точка API +- `$api_version` (ApiVersion|null) - Версия API (если не указана, используется текущая) + +**Возвращает:** + +- `string` - Полный URI для запроса + +### Пример реализации + +```php +class MyService extends AbstractService +{ + public function getData(): array + { + $response = $this->client->get('/api/v1/endpoint'); + return $response->getData(); + } + + public function getCustomData(string $id): array + { + $uri = $this->buildUri("/films/{$id}"); + return $this->get($uri); + } +} +``` + +### Наследующие классы + +- `\NotKinopoisk\Services\FilmService` - Сервис для работы с фильмами +- `\NotKinopoisk\Services\PersonService` - Сервис для работы с персонами +- `\NotKinopoisk\Services\MediaService` - Сервис для работы с медиа +- `\NotKinopoisk\Services\UserService` - Сервис для работы с пользователем + +### Связанные классы + +- `\NotKinopoisk\Client` - Основной клиент API +- `\NotKinopoisk\Enums\ApiVersion` - Перечисление версий API + +## Информация о пакете + +- **Пакет:** NotKinopoisk\Services +- **API:** https://kinopoiskapiunofficial.tech +- **Документация API:** https://kinopoiskapiunofficial.tech/documentation/api +- **Автор:** Maxim Harder +- **Версия:** 1.0.0 diff --git a/docs/dev/notkinopoiskphp/client.md b/docs/dev/notkinopoiskphp/client.md new file mode 100644 index 0000000..fed0f6f --- /dev/null +++ b/docs/dev/notkinopoiskphp/client.md @@ -0,0 +1,244 @@ +# Client - Основной клиент + +Основной клиент для работы с Kinopoisk API. + +--- + +**📚 Навигация:** [Главная](index.md) → Client + +**🔗 Связанные классы:** + +- [FilmService](services/film-service.md) - Сервис для работы с фильмами +- [PersonService](services/person-service.md) - Сервис для работы с персонами +- [MediaService](services/media-service.md) - Сервис для работы с медиа +- [UserService](services/user-service.md) - Сервис для работы с пользователями +- [ApiVersion](enums/api-version.md) - Версии API +- [ApiException](exceptions/api-exception.md) - Базовое исключение API +- [InvalidApiKeyException](exceptions/invalid-api-key-exception.md) - Неверный API ключ +- [RateLimitException](exceptions/rate-limit-exception.md) - Превышение лимита +- [ResourceNotFoundException](exceptions/resource-not-found-exception.md) - Ресурс не найден + +--- + +## Описание + +Предоставляет единую точку входа для работы с Kinopoisk API. Содержит все сервисы для работы с различными типами данных: фильмы, персоны, сериалы и другие. + +## Основные возможности + +- Инициализация с API ключом +- Доступ к различным сервисам API +- Централизованная обработка ошибок +- Управление HTTP клиентом + +## Свойства + +### `$films` + +**Тип:** `\NotKinopoisk\Services\FilmService` + +Сервис для работы с фильмами. + +### `$persons` + +**Тип:** `\NotKinopoisk\Services\PersonService` + +Сервис для работы с персонами (актеры, режиссеры и т.д.). + +### `$users` + +**Тип:** `\NotKinopoisk\Services\UserService` + +Сервис для работы с пользовательскими данными. + +### `$media` + +**Тип:** `\NotKinopoisk\Services\MediaService` + +Сервис для работы с медиа контентом. + +## Методы + +### `__construct(?string $apiKey = null, array $config = [])` + +Конструктор клиента. + +**Параметры:** + +- `$apiKey` (string|null) - API ключ для доступа к Kinopoisk API. Если не указан, будет попытка загрузить из переменной окружения `KINOPOISK_API_KEY` +- `$config` (array) - Дополнительная конфигурация для HTTP клиента + +**Исключения:** + +- `InvalidArgumentException` - Если API ключ не указан и не найден в переменных окружения + +**Пример:** + +```php +// С API ключом +$client = new Client('your-api-key'); + +// Без API ключа (будет загружен из переменной окружения) +$client = new Client(); + +// С дополнительной конфигурацией +$client = new Client('your-api-key', [ + 'timeout' => 30, + 'headers' => ['User-Agent' => 'MyApp/1.0'] +]); +``` + +### `get(string $uri, array $query = []): array` + +Выполняет GET запрос к API. + +**Параметры:** + +- `$uri` (string) - URI для запроса +- `$query` (array) - Параметры запроса + +**Возвращает:** + +- `array` - Ответ от API + +**Исключения:** + +- `ApiException` - При ошибке API +- `InvalidApiKeyException` - При неверном API ключе +- `RateLimitException` - При превышении лимита запросов +- `ResourceNotFoundException` - При отсутствии ресурса + +### `request(string $method, string $uri, array $options = []): array` + +Выполняет HTTP запрос к API. + +**Параметры:** + +- `$method` (string) - HTTP метод (GET, POST, PUT, DELETE) +- `$uri` (string) - URI для запроса +- `$options` (array) - Дополнительные опции запроса + +**Возвращает:** + +- `array` - Ответ от API + +**Исключения:** + +- `ApiException` - При ошибке API +- `InvalidApiKeyException` - При неверном API ключе +- `RateLimitException` - При превышении лимита запросов +- `ResourceNotFoundException` - При отсутствии ресурса + +### `getApiKey(): string|null` + +Получает текущий API ключ. + +**Возвращает:** + +- `string|null` - API ключ или null, если не установлен + +### `getBaseUrl(): string` + +Получает базовый URL для API запросов. + +**Возвращает:** + +- `string` - Базовый URL (по умолчанию: https://kinopoiskapiunofficial.tech) + +### `setBaseUrl(string $url): void` + +Устанавливает базовый URL для API запросов. + +**Параметры:** + +- `$url` (string) - Новый базовый URL + +## Примеры использования + +### Базовое использование + +```php +films->getById(301); +echo $film->getDisplayName(); + +// Работа с персонами +$person = $client->persons->getById(12345); +echo $person->getDisplayName(); + +// Поиск персон +$searchResult = $client->persons->search('Том Круз'); +``` + +### Использование с переменными окружения + +```php +media->getImages(301, ImageType::POSTER); +``` + +### Использование с дополнительной конфигурацией + +```php + 60, + 'headers' => [ + 'User-Agent' => 'MyApp/1.0', + 'Accept' => 'application/json' + ], + 'verify' => true +]; + +$client = new Client('your-api-key', $config); +``` + +## Обработка ошибок + +```php +films->getById(301); +} catch (InvalidApiKeyException $e) { + echo "Неверный API ключ: " . $e->getMessage(); +} catch (RateLimitException $e) { + echo "Превышен лимит запросов: " . $e->getMessage(); +} catch (ResourceNotFoundException $e) { + echo "Ресурс не найден: " . $e->getMessage(); +} catch (ApiException $e) { + echo "Ошибка API: " . $e->getMessage(); +} +``` + +## Информация о пакете + +- **Пакет:** NotKinopoisk +- **API:** https://kinopoiskapiunofficial.tech +- **Документация API:** https://kinopoiskapiunofficial.tech/documentation/api +- **Автор:** Maxim Harder +- **Версия:** 1.0.0 diff --git a/docs/dev/notkinopoiskphp/enums/api-version.md b/docs/dev/notkinopoiskphp/enums/api-version.md new file mode 100644 index 0000000..864e191 --- /dev/null +++ b/docs/dev/notkinopoiskphp/enums/api-version.md @@ -0,0 +1,433 @@ +# ApiVersion + +Перечисление версий API Kinopoisk Unofficial. + +## Описание + +`ApiVersion` определяет доступные версии API для работы с Kinopoisk Unofficial. Каждая версия API может иметь различные эндпоинты и возможности. + +### Основные возможности + +- Определение поддерживаемых версий API +- Получение всех доступных версий в виде массива +- Статическое кэширование для повышения производительности + +## Значения enum + +### Версии API + +- `V1` - Версия 1.0 API +- `V2_1` - Версия 2.1 API +- `V2_2` - Версия 2.2 API + +## Методы + +### getApiVersions() + +Получает массив всех доступных версий API. + +```php +public static function getApiVersions(): array +``` + +#### Возвращаемое значение + +- `ApiVersion[]` - Массив всех доступных версий API + +#### Описание + +Возвращает статически кэшированный массив всех версий API, определенных в данном перечислении. Использует ленивую инициализацию для оптимизации производительности при повторных вызовах. + +#### Пример использования + +```php +$versions = ApiVersion::getApiVersions(); + +// Вывод всех версий +foreach ($versions as $version) { + echo "API версия: {$version->value}\n"; +} + +// Получение количества версий +$count = count(ApiVersion::getApiVersions()); +echo "Доступно версий: {$count}"; + +// Проверка поддержки версии +$supportedVersions = ApiVersion::getApiVersions(); +$isSupported = in_array(ApiVersion::V22, $supportedVersions, true); +``` + +## Полный пример использования + +```php +value} - {$description}\n"; +} + +echo "\n"; + +// Сравнение версий +$v1 = ApiVersion::V1; +$v2_1 = ApiVersion::V2_1; +$v2_2 = ApiVersion::V2_2; + +echo "Сравнение версий:\n"; +echo "V1: {$v1->value}\n"; +echo "V2.1: {$v2_1->value}\n"; +echo "V2.2: {$v2_2->value}\n"; + +// Проверка поддержки версий +$supportedVersions = ApiVersion::getApiVersions(); +echo "\nПоддерживаемые версии:\n"; +foreach ($supportedVersions as $version) { + $isLatest = isLatestVersion($version); + $status = $isLatest ? ' (последняя)' : ''; + echo "• {$version->value}{$status}\n"; +} +``` + +## Работа с версиями API + +```php +// Функция для получения иконки версии API +function getApiVersionIcon(ApiVersion $version): string { + return match ($version) { + ApiVersion::V1 => '🔴', + ApiVersion::V2_1 => '🟡', + ApiVersion::V2_2 => '🟢' + }; +} + +// Функция для получения описания версии API +function getApiVersionDescription(ApiVersion $version): string { + return match ($version) { + ApiVersion::V1 => 'Базовая версия API с основными функциями', + ApiVersion::V2_1 => 'Улучшенная версия с дополнительными возможностями', + ApiVersion::V2_2 => 'Последняя версия с полным функционалом' + }; +} + +// Функция для получения цвета версии API +function getApiVersionColor(ApiVersion $version): string { + return match ($version) { + ApiVersion::V1 => '#dc3545', // Красный + ApiVersion::V2_1 => '#ffc107', // Желтый + ApiVersion::V2_2 => '#28a745' // Зеленый + }; +} + +// Функция для проверки, является ли версия последней +function isLatestVersion(ApiVersion $version): bool { + return $version === ApiVersion::V2_2; +} + +// Функция для получения рекомендуемой версии +function getRecommendedVersion(): ApiVersion { + return ApiVersion::V2_2; +} + +// Функция для получения устаревших версий +function getDeprecatedVersions(): array { + return [ApiVersion::V1]; +} + +// Функция для проверки, является ли версия устаревшей +function isDeprecatedVersion(ApiVersion $version): bool { + return in_array($version, getDeprecatedVersions()); +} + +// Функция для получения стабильных версий +function getStableVersions(): array { + return [ApiVersion::V2_1, ApiVersion::V2_2]; +} + +// Функция для проверки, является ли версия стабильной +function isStableVersion(ApiVersion $version): bool { + return in_array($version, getStableVersions()); +} + +// Использование +$allVersions = ApiVersion::getApiVersions(); + +echo "Анализ версий API:\n"; +foreach ($allVersions as $version) { + $icon = getApiVersionIcon($version); + $description = getApiVersionDescription($version); + $color = getApiVersionColor($version); + $isLatest = isLatestVersion($version) ? 'Да' : 'Нет'; + $isDeprecated = isDeprecatedVersion($version) ? 'Да' : 'Нет'; + $isStable = isStableVersion($version) ? 'Да' : 'Нет'; + + echo "{$icon} {$version->value}\n"; + echo " Описание: {$description}\n"; + echo " Цвет: {$color}\n"; + echo " Последняя: {$isLatest}\n"; + echo " Устаревшая: {$isDeprecated}\n"; + echo " Стабильная: {$isStable}\n\n"; +} + +// Рекомендации +$recommended = getRecommendedVersion(); +echo "Рекомендуемая версия: {$recommended->value}\n"; + +$deprecated = getDeprecatedVersions(); +if (!empty($deprecated)) { + echo "Устаревшие версии:\n"; + foreach ($deprecated as $version) { + echo "- {$version->value}\n"; + } +} +``` + +## Создание отчета по версиям API + +```php +class ApiVersionReport { + private array $versions; + + public function __construct() { + $this->versions = ApiVersion::getApiVersions(); + } + + public function getVersions(): array { + return $this->versions; + } + + public function getLatestVersion(): ApiVersion { + return ApiVersion::V2_2; + } + + public function getDeprecatedVersions(): array { + return getDeprecatedVersions(); + } + + public function getStableVersions(): array { + return getStableVersions(); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО ВЕРСИЯМ API ===\n\n"; + + $latestVersion = $this->getLatestVersion(); + $deprecatedVersions = $this->getDeprecatedVersions(); + $stableVersions = $this->getStableVersions(); + + // Общая информация + $report .= "📊 ОБЩАЯ ИНФОРМАЦИЯ:\n"; + $report .= "Всего версий: " . count($this->versions) . "\n"; + $report .= "Последняя версия: {$latestVersion->value}\n"; + $report .= "Устаревших версий: " . count($deprecatedVersions) . "\n"; + $report .= "Стабильных версий: " . count($stableVersions) . "\n\n"; + + // Детали по версиям + $report .= "📋 ДЕТАЛИ ПО ВЕРСИЯМ:\n"; + foreach ($this->versions as $version) { + $icon = getApiVersionIcon($version); + $description = getApiVersionDescription($version); + $isLatest = isLatestVersion($version) ? ' (последняя)' : ''; + $isDeprecated = isDeprecatedVersion($version) ? ' (устаревшая)' : ''; + $isStable = isStableVersion($version) ? ' (стабильная)' : ''; + + $report .= "{$icon} {$version->value}{$isLatest}{$isDeprecated}{$isStable}\n"; + $report .= " Описание: {$description}\n"; + $report .= " Цвет: " . getApiVersionColor($version) . "\n\n"; + } + + // Рекомендации + $report .= "💡 РЕКОМЕНДАЦИИ:\n"; + $report .= "• Рекомендуется использовать: {$latestVersion->value}\n"; + + if (!empty($deprecatedVersions)) { + $report .= "• Избегайте использования устаревших версий:\n"; + foreach ($deprecatedVersions as $version) { + $report .= " - {$version->value}\n"; + } + } + + $report .= "• Стабильные версии для продакшена:\n"; + foreach ($stableVersions as $version) { + $report .= " - {$version->value}\n"; + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $latestVersion = $this->getLatestVersion(); + $deprecatedVersions = $this->getDeprecatedVersions(); + $stableVersions = $this->getStableVersions(); + + // Статистика + $html .= "
\n"; + $html .= "

Общая информация

\n"; + $html .= "

Всего версий: " . count($this->versions) . "

\n"; + $html .= "

Последняя версия: {$latestVersion->value}

\n"; + $html .= "

Устаревших версий: " . count($deprecatedVersions) . "

\n"; + $html .= "

Стабильных версий: " . count($stableVersions) . "

\n"; + $html .= "
\n"; + + // Версии API + $html .= "
\n"; + $html .= "
📋 Версии API
\n"; + $html .= "
\n"; + + foreach ($this->versions as $version) { + $icon = getApiVersionIcon($version); + $description = getApiVersionDescription($version); + $color = getApiVersionColor($version); + $isLatest = isLatestVersion($version); + $isDeprecated = isDeprecatedVersion($version); + $isStable = isStableVersion($version); + + $cssClass = 'v' . str_replace('.', '_', $version->value); + if ($isDeprecated) $cssClass .= ' deprecated'; + if ($isLatest) $cssClass .= ' latest'; + + $html .= "
\n"; + $html .= "
{$icon} {$version->value}
\n"; + $html .= "
{$description}
\n"; + $html .= "
Цвет: {$color}
\n"; + + if ($isLatest) { + $html .= "
Последняя версия
\n"; + } + if ($isDeprecated) { + $html .= "
Устаревшая версия
\n"; + } + if ($isStable) { + $html .= "
Стабильная версия
\n"; + } + + $html .= "
\n"; + } + + $html .= "
\n
\n"; + + // Рекомендации + $html .= "
\n"; + $html .= "
💡 Рекомендации
\n"; + $html .= "

Рекомендуется использовать: {$latestVersion->value}

\n"; + + if (!empty($deprecatedVersions)) { + $html .= "

Избегайте использования устаревших версий:

\n"; + $html .= "
    \n"; + foreach ($deprecatedVersions as $version) { + $html .= "
  • {$version->value}
  • \n"; + } + $html .= "
\n"; + } + + $html .= "

Стабильные версии для продакшена:

\n"; + $html .= "
    \n"; + foreach ($stableVersions as $version) { + $html .= "
  • {$version->value}
  • \n"; + } + $html .= "
\n"; + $html .= "
\n"; + + $html .= "
\n\n"; + + return $html; + } +} + +// Использование +$report = new ApiVersionReport(); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Отчет по версиям API'); +file_put_contents('api_versions_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в api_versions_report.html\n"; +``` + +## Анализ версий API + +```php +function analyzeApiVersions(): array { + $allVersions = ApiVersion::getApiVersions(); + + $analysis = [ + 'totalVersions' => count($allVersions), + 'latestVersion' => ApiVersion::V2_2, + 'deprecatedVersions' => getDeprecatedVersions(), + 'stableVersions' => getStableVersions(), + 'versionDetails' => [] + ]; + + foreach ($allVersions as $version) { + $analysis['versionDetails'][$version->value] = [ + 'isLatest' => isLatestVersion($version), + 'isDeprecated' => isDeprecatedVersion($version), + 'isStable' => isStableVersion($version), + 'icon' => getApiVersionIcon($version), + 'color' => getApiVersionColor($version), + 'description' => getApiVersionDescription($version) + ]; + } + + return $analysis; +} + +// Использование +$analysis = analyzeApiVersions(); + +echo "=== Анализ версий API ===\n"; +echo "Всего версий: {$analysis['totalVersions']}\n"; +echo "Последняя версия: {$analysis['latestVersion']->value}\n"; +echo "Устаревших версий: " . count($analysis['deprecatedVersions']) . "\n"; +echo "Стабильных версий: " . count($analysis['stableVersions']) . "\n"; + +echo "\nДетали по версиям:\n"; +foreach ($analysis['versionDetails'] as $versionValue => $details) { + echo "• {$versionValue}:\n"; + echo " - Последняя: " . ($details['isLatest'] ? 'Да' : 'Нет') . "\n"; + echo " - Устаревшая: " . ($details['isDeprecated'] ? 'Да' : 'Нет') . "\n"; + echo " - Стабильная: " . ($details['isStable'] ? 'Да' : 'Нет') . "\n"; + echo " - Описание: {$details['description']}\n"; +} +``` + +## Связанные классы + +- [`Client`](../client.md) - Основной клиент для работы с API +- [`AbstractService`](../services/abstract-service.md) - Базовый класс для сервисов diff --git a/docs/dev/notkinopoiskphp/enums/box-office-type.md b/docs/dev/notkinopoiskphp/enums/box-office-type.md new file mode 100644 index 0000000..0ef713d --- /dev/null +++ b/docs/dev/notkinopoiskphp/enums/box-office-type.md @@ -0,0 +1,483 @@ +# BoxOfficeType + +Типы кассовых сборов в Kinopoisk API. + +## Описание + +`BoxOfficeType` определяет различные типы финансовых данных, связанных с фильмом: бюджет, сборы по странам и маркетинговые расходы. + +## Значения enum + +### Типы финансовых данных + +- `BUDGET` - Бюджет фильма +- `RUS` - Сборы в России +- `USA` - Сборы в США +- `WORLD` - Мировые сборы +- `MARKETING` - Средства спущенные на маркетинг + +## Методы + +### isBudget() + +Проверяет, является ли тип бюджетом. + +```php +public function isBudget(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если это бюджет, `false` в противном случае + +#### Пример использования + +```php +if (BoxOfficeType::BUDGET->isBudget()) { + echo "Это бюджет фильма"; +} +``` + +### isRevenue() + +Проверяет, является ли тип сборами. + +```php +public function isRevenue(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если это сборы, `false` в противном случае + +#### Описание + +Возвращает `true` для типов `RUS`, `USA` и `WORLD`. + +#### Пример использования + +```php +if (BoxOfficeType::RUS->isRevenue()) { + echo "Это сборы"; +} +``` + +### getDisplayName() + +Получает человекочитаемое название типа кассовых сборов. + +```php +public function getDisplayName(): string +``` + +#### Возвращаемое значение + +- `string` - Название типа на русском языке + +#### Пример использования + +```php +echo BoxOfficeType::BUDGET->getDisplayName(); // "Бюджет" +echo BoxOfficeType::RUS->getDisplayName(); // "Сборы в России" +``` + +## Полный пример использования + +```php +films; +$boxOffice = $filmService->getBoxOffice(301); // Матрица + +echo "=== Анализ типов кассовых сборов ===\n"; + +// Группировка по типам +$budgets = []; +$revenues = []; +$marketing = []; + +foreach ($boxOffice as $item) { + if ($item->type->isBudget()) { + $budgets[] = $item; + } elseif ($item->type->isRevenue()) { + $revenues[] = $item; + } elseif ($item->type === BoxOfficeType::MARKETING) { + $marketing[] = $item; + } +} + +// Вывод бюджетов +if (!empty($budgets)) { + echo "\n💰 Бюджеты:\n"; + foreach ($budgets as $budget) { + echo "• {$budget->type->getDisplayName()}: {$budget->getFormattedAmount()}\n"; + } +} + +// Вывод сборов +if (!empty($revenues)) { + echo "\n💵 Сборы:\n"; + foreach ($revenues as $revenue) { + echo "• {$revenue->type->getDisplayName()}: {$revenue->getFormattedAmount()}\n"; + } +} + +// Вывод маркетинговых расходов +if (!empty($marketing)) { + echo "\n📢 Маркетинг:\n"; + foreach ($marketing as $item) { + echo "• {$item->type->getDisplayName()}: {$item->getFormattedAmount()}\n"; + } +} +``` + +## Работа с типами кассовых сборов + +```php +// Функция для получения всех типов +function getAllBoxOfficeTypes(): array { + return [ + BoxOfficeType::BUDGET, + BoxOfficeType::RUS, + BoxOfficeType::USA, + BoxOfficeType::WORLD, + BoxOfficeType::MARKETING + ]; +} + +// Функция для получения только типов сборов +function getRevenueTypes(): array { + return array_filter(getAllBoxOfficeTypes(), fn($type) => $type->isRevenue()); +} + +// Функция для получения только бюджетных типов +function getBudgetTypes(): array { + return array_filter(getAllBoxOfficeTypes(), fn($type) => $type->isBudget()); +} + +// Функция для группировки по категориям +function groupBoxOfficeByCategory(array $boxOffice): array { + $categories = [ + 'budgets' => [], + 'revenues' => [], + 'marketing' => [] + ]; + + foreach ($boxOffice as $item) { + if ($item->type->isBudget()) { + $categories['budgets'][] = $item; + } elseif ($item->type->isRevenue()) { + $categories['revenues'][] = $item; + } elseif ($item->type === BoxOfficeType::MARKETING) { + $categories['marketing'][] = $item; + } + } + + return $categories; +} + +// Функция для получения статистики по типам +function getBoxOfficeTypeStats(array $boxOffice): array { + $stats = []; + + foreach (getAllBoxOfficeTypes() as $type) { + $stats[$type->value] = [ + 'type' => $type, + 'displayName' => $type->getDisplayName(), + 'count' => 0, + 'total' => 0, + 'isBudget' => $type->isBudget(), + 'isRevenue' => $type->isRevenue() + ]; + } + + foreach ($boxOffice as $item) { + $typeKey = $item->type->value; + $stats[$typeKey]['count']++; + $stats[$typeKey]['total'] += $item->amount; + } + + return $stats; +} + +// Использование +$boxOffice = $filmService->getBoxOffice(301); + +// Получение типов +$allTypes = getAllBoxOfficeTypes(); +$revenueTypes = getRevenueTypes(); +$budgetTypes = getBudgetTypes(); + +echo "Всего типов: " . count($allTypes) . "\n"; +echo "Типов сборов: " . count($revenueTypes) . "\n"; +echo "Типов бюджетов: " . count($budgetTypes) . "\n"; + +// Группировка по категориям +$categories = groupBoxOfficeByCategory($boxOffice); +echo "Бюджетов: " . count($categories['budgets']) . "\n"; +echo "Типов сборов: " . count($categories['revenues']) . "\n"; +echo "Маркетинговых расходов: " . count($categories['marketing']) . "\n"; + +// Статистика по типам +$stats = getBoxOfficeTypeStats($boxOffice); +echo "\nСтатистика по типам:\n"; +foreach ($stats as $typeKey => $data) { + if ($data['count'] > 0) { + echo "- {$data['displayName']}: {$data['count']} записей, " . number_format($data['total']) . "\n"; + } +} +``` + +## Создание отчета по типам + +```php +class BoxOfficeTypeReport { + private array $boxOffice; + + public function __construct(array $boxOffice) { + $this->boxOffice = $boxOffice; + } + + public function getTypeBreakdown(): array { + return groupBoxOfficeByCategory($this->boxOffice); + } + + public function getTypeStats(): array { + return getBoxOfficeTypeStats($this->boxOffice); + } + + public function getTotalByType(BoxOfficeType $type): int { + $total = 0; + foreach ($this->boxOffice as $item) { + if ($item->type === $type) { + $total += $item->amount; + } + } + return $total; + } + + public function getRevenueBreakdown(): array { + $breakdown = []; + + foreach ($this->boxOffice as $item) { + if ($item->type->isRevenue()) { + $typeName = $item->type->getDisplayName(); + if (!isset($breakdown[$typeName])) { + $breakdown[$typeName] = 0; + } + $breakdown[$typeName] += $item->amount; + } + } + + arsort($breakdown); + return $breakdown; + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО ТИПАМ КАССОВЫХ СБОРОВ ===\n\n"; + + $categories = $this->getTypeBreakdown(); + $stats = $this->getTypeStats(); + + // Общая статистика + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего записей: " . count($this->boxOffice) . "\n"; + $report .= "Бюджетов: " . count($categories['budgets']) . "\n"; + $report .= "Типов сборов: " . count($categories['revenues']) . "\n"; + $report .= "Маркетинговых расходов: " . count($categories['marketing']) . "\n\n"; + + // Детали по типам + $report .= "📋 ДЕТАЛИ ПО ТИПАМ:\n"; + foreach ($stats as $typeKey => $data) { + if ($data['count'] > 0) { + $report .= "• {$data['displayName']}:\n"; + $report .= " - Количество записей: {$data['count']}\n"; + $report .= " - Общая сумма: " . number_format($data['total']) . "\n"; + $report .= " - Тип: " . ($data['isBudget'] ? 'Бюджет' : ($data['isRevenue'] ? 'Сборы' : 'Маркетинг')) . "\n\n"; + } + } + + // Разбивка сборов + $revenueBreakdown = $this->getRevenueBreakdown(); + if (!empty($revenueBreakdown)) { + $report .= "💵 РАЗБИВКА СБОРОВ:\n"; + foreach ($revenueBreakdown as $typeName => $total) { + $report .= "• {$typeName}: " . number_format($total) . "\n"; + } + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $categories = $this->getTypeBreakdown(); + $stats = $this->getTypeStats(); + + // Общая статистика + $html .= "
\n"; + $html .= "

Общая статистика

\n"; + $html .= "

Всего записей: " . count($this->boxOffice) . "

\n"; + $html .= "

Бюджетов: " . count($categories['budgets']) . "

\n"; + $html .= "

Типов сборов: " . count($categories['revenues']) . "

\n"; + $html .= "

Маркетинговых расходов: " . count($categories['marketing']) . "

\n"; + $html .= "
\n"; + + // Детали по типам + $html .= "
\n"; + $html .= "
Детали по типам
\n"; + + foreach ($stats as $typeKey => $data) { + if ($data['count'] > 0) { + $cssClass = $data['isBudget'] ? 'budget' : ($data['isRevenue'] ? 'revenue' : 'marketing'); + $html .= "
\n"; + $html .= "
{$data['displayName']}
\n"; + $html .= "
\n"; + $html .= "
Количество записей: {$data['count']}
\n"; + $html .= "
Общая сумма: " . number_format($data['total']) . "
\n"; + $html .= "
Тип: " . ($data['isBudget'] ? 'Бюджет' : ($data['isRevenue'] ? 'Сборы' : 'Маркетинг')) . "
\n"; + $html .= "
\n
\n"; + } + } + + $html .= "
\n"; + + // Разбивка сборов + $revenueBreakdown = $this->getRevenueBreakdown(); + if (!empty($revenueBreakdown)) { + $html .= "
\n"; + $html .= "
Разбивка сборов
\n"; + + foreach ($revenueBreakdown as $typeName => $total) { + $html .= "
\n"; + $html .= "
{$typeName}
\n"; + $html .= "
Общая сумма: " . number_format($total) . "
\n"; + $html .= "
\n"; + } + + $html .= "
\n"; + } + + $html .= "
\n\n"; + + return $html; + } +} + +// Использование +$boxOffice = $filmService->getBoxOffice(301); +$report = new BoxOfficeTypeReport($boxOffice); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Отчет по типам кассовых сборов'); +file_put_contents('box_office_types_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в box_office_types_report.html\n"; +``` + +## Анализ эффективности типов + +```php +function analyzeTypeEffectiveness(array $boxOffice): array { + $analysis = [ + 'totalRecords' => count($boxOffice), + 'typeDistribution' => [], + 'currencyDistribution' => [], + 'averageAmounts' => [], + 'totalAmounts' => [] + ]; + + $typeStats = []; + $currencyStats = []; + + foreach ($boxOffice as $item) { + $typeKey = $item->type->value; + $currency = $item->currency ?? 'Unknown'; + + // Статистика по типам + if (!isset($typeStats[$typeKey])) { + $typeStats[$typeKey] = [ + 'count' => 0, + 'total' => 0, + 'displayName' => $item->type->getDisplayName() + ]; + } + $typeStats[$typeKey]['count']++; + $typeStats[$typeKey]['total'] += $item->amount; + + // Статистика по валютам + if (!isset($currencyStats[$currency])) { + $currencyStats[$currency] = [ + 'count' => 0, + 'total' => 0 + ]; + } + $currencyStats[$currency]['count']++; + $currencyStats[$currency]['total'] += $item->amount; + } + + // Вычисление средних значений + foreach ($typeStats as $typeKey => $data) { + $analysis['averageAmounts'][$typeKey] = round($data['total'] / $data['count']); + $analysis['totalAmounts'][$typeKey] = $data['total']; + } + + $analysis['typeDistribution'] = $typeStats; + $analysis['currencyDistribution'] = $currencyStats; + + return $analysis; +} + +// Использование +$boxOffice = $filmService->getBoxOffice(301); +$analysis = analyzeTypeEffectiveness($boxOffice); + +echo "=== Анализ эффективности типов ===\n"; +echo "Всего записей: {$analysis['totalRecords']}\n"; + +echo "\nРаспределение по типам:\n"; +foreach ($analysis['typeDistribution'] as $typeKey => $data) { + echo "- {$data['displayName']}: {$data['count']} записей\n"; + echo " Средняя сумма: " . number_format($analysis['averageAmounts'][$typeKey]) . "\n"; + echo " Общая сумма: " . number_format($analysis['totalAmounts'][$typeKey]) . "\n"; +} + +echo "\nРаспределение по валютам:\n"; +foreach ($analysis['currencyDistribution'] as $currency => $data) { + echo "- {$currency}: {$data['count']} записей, " . number_format($data['total']) . "\n"; +} +``` + +## Связанные классы + +- [`BoxOffice`](../models/box-office.md) - Модель кассовых сборов +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/enums/collection-type.md b/docs/dev/notkinopoiskphp/enums/collection-type.md new file mode 100644 index 0000000..f7894da --- /dev/null +++ b/docs/dev/notkinopoiskphp/enums/collection-type.md @@ -0,0 +1,436 @@ +# CollectionType + +Типы коллекций фильмов в Kinopoisk API. + +## Описание + +`CollectionType` определяет различные типы коллекций фильмов, которые можно получить через API: топ-250, популярные и т.д. + +## Значения enum + +### Типы коллекций + +- `TOP_POPULAR_ALL` - Топ популярных фильмов и сериалов +- `TOP_POPULAR_MOVIES` - Топ популярных фильмов +- `TOP_POPULAR_SERIES` - Топ популярных сериалов +- `TOP_250_MOVIES` - Топ-250 фильмов +- `TOP_250_TV_SHOWS` - Топ-250 сериалов +- `VAMPIRE_THEME` - Тематическая коллекция: вампиры +- `COMICS_THEME` - Тематическая коллекция: комиксы +- `CLOSES_RELEASES` - Скоро выходящие фильмы +- `FAMILY` - Семейные фильмы +- `OSKAR_WINNERS_2021` - Победители Оскара 2021 +- `LOVE_THEME` - Тематическая коллекция: любовь +- `ZOMBIE_THEME` - Тематическая коллекция: зомби +- `CATASTROPHE_THEME` - Тематическая коллекция: катастрофы +- `KIDS_ANIMATION_THEME` - Тематическая коллекция: детская анимация +- `POPULAR_SERIES` - Популярные сериалы + +## Методы + +### getDisplayName() + +Получает человекочитаемое название типа коллекции. + +```php +public function getDisplayName(): string +``` + +#### Возвращаемое значение + +- `string` - Название типа коллекции на русском языке + +#### Пример использования + +```php +echo CollectionType::TOP_250_MOVIES->getDisplayName(); // "Топ-250 фильмов" +``` + +## Полный пример использования + +```php +films; + +echo "=== Работа с коллекциями фильмов ===\n"; + +// Получение топ-250 фильмов +$top250Movies = $filmService->getCollections(CollectionType::TOP_250_MOVIES); +echo "Топ-250 фильмов: " . count($top250Movies) . " элементов\n"; + +// Получение популярных сериалов +$popularSeries = $filmService->getCollections(CollectionType::TOP_POPULAR_SERIES); +echo "Популярные сериалы: " . count($popularSeries) . " элементов\n"; + +// Получение тематической коллекции +$vampireCollection = $filmService->getCollections(CollectionType::VAMPIRE_THEME); +echo "Вампиры: " . count($vampireCollection) . " элементов\n"; + +// Вывод информации о коллекциях +foreach ($vampireCollection as $index => $item) { + echo ($index + 1) . ". {$item->getDisplayName()}\n"; + if ($item->year) { + echo " Год: {$item->year}\n"; + } + $rating = $item->getMainRating(); + if ($rating !== null) { + echo " Рейтинг: {$rating}\n"; + } +} +``` + +## Работа с типами коллекций + +```php +// Функция для получения всех типов коллекций +function getAllCollectionTypes(): array { + return [ + CollectionType::TOP_POPULAR_ALL, + CollectionType::TOP_POPULAR_MOVIES, + CollectionType::TOP_POPULAR_SERIES, + CollectionType::TOP_250_MOVIES, + CollectionType::TOP_250_TV_SHOWS, + CollectionType::VAMPIRE_THEME, + CollectionType::COMICS_THEME, + CollectionType::CLOSES_RELEASES, + CollectionType::FAMILY, + CollectionType::OSKAR_WINNERS_2021, + CollectionType::LOVE_THEME, + CollectionType::ZOMBIE_THEME, + CollectionType::CATASTROPHE_THEME, + CollectionType::KIDS_ANIMATION_THEME, + CollectionType::POPULAR_SERIES + ]; +} + +// Функция для получения рейтинговых коллекций +function getRatingCollections(): array { + return [ + CollectionType::TOP_250_MOVIES, + CollectionType::TOP_250_TV_SHOWS + ]; +} + +// Функция для получения популярных коллекций +function getPopularCollections(): array { + return [ + CollectionType::TOP_POPULAR_ALL, + CollectionType::TOP_POPULAR_MOVIES, + CollectionType::TOP_POPULAR_SERIES, + CollectionType::POPULAR_SERIES + ]; +} + +// Функция для получения тематических коллекций +function getThematicCollections(): array { + return [ + CollectionType::VAMPIRE_THEME, + CollectionType::COMICS_THEME, + CollectionType::LOVE_THEME, + CollectionType::ZOMBIE_THEME, + CollectionType::CATASTROPHE_THEME, + CollectionType::KIDS_ANIMATION_THEME + ]; +} + +// Функция для получения специальных коллекций +function getSpecialCollections(): array { + return [ + CollectionType::CLOSES_RELEASES, + CollectionType::FAMILY, + CollectionType::OSKAR_WINNERS_2021 + ]; +} + +// Функция для группировки коллекций по категориям +function groupCollectionsByCategory(): array { + return [ + 'rating' => getRatingCollections(), + 'popular' => getPopularCollections(), + 'thematic' => getThematicCollections(), + 'special' => getSpecialCollections() + ]; +} + +// Использование +$allTypes = getAllCollectionTypes(); +$ratingCollections = getRatingCollections(); +$popularCollections = getPopularCollections(); +$thematicCollections = getThematicCollections(); +$specialCollections = getSpecialCollections(); + +echo "Всего типов коллекций: " . count($allTypes) . "\n"; +echo "Рейтинговых коллекций: " . count($ratingCollections) . "\n"; +echo "Популярных коллекций: " . count($popularCollections) . "\n"; +echo "Тематических коллекций: " . count($thematicCollections) . "\n"; +echo "Специальных коллекций: " . count($specialCollections) . "\n"; + +// Группировка по категориям +$categories = groupCollectionsByCategory(); +foreach ($categories as $category => $collections) { + echo "\n{$category}:\n"; + foreach ($collections as $collection) { + echo "- {$collection->getDisplayName()}\n"; + } +} +``` + +## Создание отчета по коллекциям + +```php +class CollectionTypeReport { + private array $collections; + + public function __construct(array $collections) { + $this->collections = $collections; + } + + public function getCollectionsByType(CollectionType $type): array { + return array_filter($this->collections, fn($item) => $item->type === $type); + } + + public function getRatingCollections(): array { + return getRatingCollections(); + } + + public function getPopularCollections(): array { + return getPopularCollections(); + } + + public function getThematicCollections(): array { + return getThematicCollections(); + } + + public function getSpecialCollections(): array { + return getSpecialCollections(); + } + + public function getCategoryBreakdown(): array { + return groupCollectionsByCategory(); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО ТИПАМ КОЛЛЕКЦИЙ ===\n\n"; + + $categories = $this->getCategoryBreakdown(); + + // Общая статистика + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего типов коллекций: " . count(getAllCollectionTypes()) . "\n"; + $report .= "Рейтинговых коллекций: " . count($categories['rating']) . "\n"; + $report .= "Популярных коллекций: " . count($categories['popular']) . "\n"; + $report .= "Тематических коллекций: " . count($categories['thematic']) . "\n"; + $report .= "Специальных коллекций: " . count($categories['special']) . "\n\n"; + + // Рейтинговые коллекции + $report .= "🏆 РЕЙТИНГОВЫЕ КОЛЛЕКЦИИ:\n"; + foreach ($categories['rating'] as $collection) { + $report .= "• {$collection->getDisplayName()}\n"; + } + + $report .= "\n"; + + // Популярные коллекции + $report .= "🔥 ПОПУЛЯРНЫЕ КОЛЛЕКЦИИ:\n"; + foreach ($categories['popular'] as $collection) { + $report .= "• {$collection->getDisplayName()}\n"; + } + + $report .= "\n"; + + // Тематические коллекции + $report .= "🎭 ТЕМАТИЧЕСКИЕ КОЛЛЕКЦИИ:\n"; + foreach ($categories['thematic'] as $collection) { + $report .= "• {$collection->getDisplayName()}\n"; + } + + $report .= "\n"; + + // Специальные коллекции + $report .= "⭐ СПЕЦИАЛЬНЫЕ КОЛЛЕКЦИИ:\n"; + foreach ($categories['special'] as $collection) { + $report .= "• {$collection->getDisplayName()}\n"; + } + + $report .= "\n"; + + // Детали по всем типам + $report .= "📋 ВСЕ ТИПЫ КОЛЛЕКЦИЙ:\n"; + foreach (getAllCollectionTypes() as $collection) { + $report .= "• {$collection->getDisplayName()}\n"; + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $categories = $this->getCategoryBreakdown(); + + // Общая статистика + $html .= "
\n"; + $html .= "

Общая статистика

\n"; + $html .= "

Всего типов коллекций: " . count(getAllCollectionTypes()) . "

\n"; + $html .= "

Рейтинговых коллекций: " . count($categories['rating']) . "

\n"; + $html .= "

Популярных коллекций: " . count($categories['popular']) . "

\n"; + $html .= "

Тематических коллекций: " . count($categories['thematic']) . "

\n"; + $html .= "

Специальных коллекций: " . count($categories['special']) . "

\n"; + $html .= "
\n"; + + // Рейтинговые коллекции + $html .= "
\n"; + $html .= "
🏆 Рейтинговые коллекции
\n"; + + foreach ($categories['rating'] as $collection) { + $html .= "
\n"; + $html .= "
{$collection->getDisplayName()}
\n"; + $html .= "
\n"; + } + + $html .= "
\n"; + + // Популярные коллекции + $html .= "
\n"; + $html .= "
🔥 Популярные коллекции
\n"; + + foreach ($categories['popular'] as $collection) { + $html .= "\n"; + } + + $html .= "
\n"; + + // Тематические коллекции + $html .= "
\n"; + $html .= "
🎭 Тематические коллекции
\n"; + + foreach ($categories['thematic'] as $collection) { + $html .= "
\n"; + $html .= "
{$collection->getDisplayName()}
\n"; + $html .= "
\n"; + } + + $html .= "
\n"; + + // Специальные коллекции + $html .= "
\n"; + $html .= "
⭐ Специальные коллекции
\n"; + + foreach ($categories['special'] as $collection) { + $html .= "
\n"; + $html .= "
{$collection->getDisplayName()}
\n"; + $html .= "
\n"; + } + + $html .= "
\n
\n\n"; + + return $html; + } +} + +// Использование +$collections = getAllCollectionTypes(); +$report = new CollectionTypeReport($collections); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Отчет по типам коллекций'); +file_put_contents('collection_types_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в collection_types_report.html\n"; +``` + +## Анализ коллекций + +```php +function analyzeCollectionTypes(): array { + $analysis = [ + 'totalTypes' => count(getAllCollectionTypes()), + 'categoryDistribution' => [], + 'typeDetails' => [] + ]; + + $categories = groupCollectionsByCategory(); + + foreach ($categories as $category => $collections) { + $analysis['categoryDistribution'][$category] = [ + 'count' => count($collections), + 'collections' => $collections + ]; + } + + foreach (getAllCollectionTypes() as $collection) { + $analysis['typeDetails'][$collection->value] = [ + 'type' => $collection, + 'displayName' => $collection->getDisplayName(), + 'category' => getCollectionCategory($collection) + ]; + } + + return $analysis; +} + +function getCollectionCategory(CollectionType $collection): string { + if (in_array($collection, getRatingCollections())) { + return 'rating'; + } elseif (in_array($collection, getPopularCollections())) { + return 'popular'; + } elseif (in_array($collection, getThematicCollections())) { + return 'thematic'; + } elseif (in_array($collection, getSpecialCollections())) { + return 'special'; + } + return 'unknown'; +} + +// Использование +$analysis = analyzeCollectionTypes(); + +echo "=== Анализ типов коллекций ===\n"; +echo "Всего типов коллекций: {$analysis['totalTypes']}\n"; + +echo "\nРаспределение по категориям:\n"; +foreach ($analysis['categoryDistribution'] as $category => $data) { + $percentage = round(($data['count'] / $analysis['totalTypes']) * 100, 1); + echo "- {$category}: {$data['count']} коллекций ({$percentage}%)\n"; +} + +echo "\nДетали по типам:\n"; +foreach ($analysis['typeDetails'] as $typeKey => $data) { + echo "- {$data['displayName']} ({$data['category']})\n"; +} +``` + +## Связанные классы + +- [`FilmCollection`](../models/film-collection.md) - Модель элемента коллекции +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/enums/content-type.md b/docs/dev/notkinopoiskphp/enums/content-type.md new file mode 100644 index 0000000..4e8fca2 --- /dev/null +++ b/docs/dev/notkinopoiskphp/enums/content-type.md @@ -0,0 +1,693 @@ +# ContentType + +Типы контента в Kinopoisk API. + +## Описание + +`ContentType` определяет различные типы контента, которые могут быть возвращены API: фильмы, сериалы, мини-сериалы и т.д. + +## Значения enum + +### Типы контента + +- `FILM` - Фильм +- `SERIES` - Сериал +- `MINI_SERIES` - Мини-сериал +- `TV_SHOW` - Телешоу +- `TV_MOVIE` - ТВ-фильм +- `VIDEO` - Видео +- `SHORT` - Короткометражка +- `DOCUMENTARY` - Документальный фильм +- `TV_SERIES` - ТВ-сериал (альтернативное название) +- `UNKNOWN` - Неизвестный тип +- `ALL` - Все типы (для фильтрации) + +## Методы + +### isFilm() + +Проверяет, является ли контент фильмом. + +```php +public function isFilm(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если это фильм, `false` в противном случае + +#### Описание + +Возвращает `true` для следующих типов: FILM, TV_MOVIE, VIDEO, SHORT, DOCUMENTARY. + +#### Пример использования + +```php +if (ContentType::FILM->isFilm()) { + echo "Это фильм"; +} +``` + +### isSeries() + +Проверяет, является ли контент сериалом. + +```php +public function isSeries(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если это сериал, `false` в противном случае + +#### Описание + +Возвращает `true` для следующих типов: SERIES, MINI_SERIES, TV_SHOW, TV_SERIES. + +#### Пример использования + +```php +if (ContentType::SERIES->isSeries()) { + echo "Это сериал"; +} +``` + +### getDisplayName() + +Получает человекочитаемое название типа контента. + +```php +public function getDisplayName(): string +``` + +#### Возвращаемое значение + +- `string` - Название типа контента на русском языке + +#### Пример использования + +```php +echo ContentType::FILM->getDisplayName(); // "Фильм" +echo ContentType::SERIES->getDisplayName(); // "Сериал" +``` + +## Полный пример использования + +```php +films; +$films = $filmService->getTopFilms(ContentType::FILM); + +echo "=== Работа с типами контента ===\n"; + +// Вывод всех типов контента +echo "Доступные типы контента:\n"; +foreach ([ + ContentType::FILM, + ContentType::SERIES, + ContentType::MINI_SERIES, + ContentType::TV_SHOW, + ContentType::TV_MOVIE, + ContentType::VIDEO, + ContentType::SHORT, + ContentType::DOCUMENTARY, + ContentType::TV_SERIES +] as $type) { + $icon = getContentTypeIcon($type); + $displayName = $type->getDisplayName(); + echo "{$icon} {$displayName} ({$type->value})\n"; +} + +echo "\n"; + +// Группировка контента по типам +$groupedContent = []; +foreach ($films as $film) { + $type = $film->type; + if (!isset($groupedContent[$type->value])) { + $groupedContent[$type->value] = []; + } + $groupedContent[$type->value][] = $film; +} + +// Вывод контента по типам +foreach ($groupedContent as $typeValue => $typeContent) { + $type = ContentType::from($typeValue); + $icon = getContentTypeIcon($type); + $displayName = $type->getDisplayName(); + + echo "{$icon} {$displayName} (" . count($typeContent) . " элементов):\n"; + foreach (array_slice($typeContent, 0, 3) as $content) { + echo " • {$content->getDisplayName()}\n"; + } + if (count($typeContent) > 3) { + echo " ... и еще " . (count($typeContent) - 3) . " элементов\n"; + } + echo "\n"; +} +``` + +## Работа с типами контента + +```php +// Функция для получения иконки типа контента +function getContentTypeIcon(ContentType $type): string { + return match ($type) { + ContentType::FILM => '🎬', + ContentType::SERIES => '📺', + ContentType::MINI_SERIES => '📺', + ContentType::TV_SHOW => '🎭', + ContentType::TV_MOVIE => '📺', + ContentType::VIDEO => '🎥', + ContentType::SHORT => '⏱️', + ContentType::DOCUMENTARY => '📹', + ContentType::TV_SERIES => '📺', + ContentType::UNKNOWN => '❓', + ContentType::ALL => '📚' + }; +} + +// Функция для получения цвета типа контента +function getContentTypeColor(ContentType $type): string { + return match ($type) { + ContentType::FILM => '#007bff', // Синий + ContentType::SERIES => '#28a745', // Зеленый + ContentType::MINI_SERIES => '#20c997', // Бирюзовый + ContentType::TV_SHOW => '#ffc107', // Желтый + ContentType::TV_MOVIE => '#fd7e14', // Оранжевый + ContentType::VIDEO => '#6f42c1', // Фиолетовый + ContentType::SHORT => '#e83e8c', // Розовый + ContentType::DOCUMENTARY => '#6c757d', // Серый + ContentType::TV_SERIES => '#17a2b8', // Голубой + ContentType::UNKNOWN => '#6c757d', // Серый + ContentType::ALL => '#343a40' // Темно-серый + }; +} + +// Функция для получения описания типа контента +function getContentTypeDescription(ContentType $type): string { + return match ($type) { + ContentType::FILM => 'Полнометражный художественный фильм', + ContentType::SERIES => 'Многосерийный телевизионный сериал', + ContentType::MINI_SERIES => 'Короткий сериал с ограниченным количеством серий', + ContentType::TV_SHOW => 'Телевизионное шоу или программа', + ContentType::TV_MOVIE => 'Фильм, созданный специально для телевидения', + ContentType::VIDEO => 'Короткое видео или клип', + ContentType::SHORT => 'Короткометражный фильм', + ContentType::DOCUMENTARY => 'Документальный фильм', + ContentType::TV_SERIES => 'Телевизионный сериал (альтернативное название)', + ContentType::UNKNOWN => 'Тип контента не определен', + ContentType::ALL => 'Все типы контента' + }; +} + +// Функция для фильтрации контента по типу +function filterContentByType(array $content, ContentType $type): array { + return array_filter($content, fn($item) => $item->type === $type); +} + +// Функция для получения статистики по типам контента +function getContentTypeStats(array $content): array { + $stats = [ + 'total' => count($content), + 'films' => 0, + 'series' => 0, + 'other' => 0, + 'byType' => [] + ]; + + foreach ($content as $item) { + $type = $item->type; + + if ($type->isFilm()) { + $stats['films']++; + } elseif ($type->isSeries()) { + $stats['series']++; + } else { + $stats['other']++; + } + + if (!isset($stats['byType'][$type->value])) { + $stats['byType'][$type->value] = 0; + } + $stats['byType'][$type->value]++; + } + + return $stats; +} + +// Функция для получения всех типов контента +function getAllContentTypes(): array { + return [ + ContentType::FILM, + ContentType::SERIES, + ContentType::MINI_SERIES, + ContentType::TV_SHOW, + ContentType::TV_MOVIE, + ContentType::VIDEO, + ContentType::SHORT, + ContentType::DOCUMENTARY, + ContentType::TV_SERIES + ]; +} + +// Функция для получения основных типов контента +function getMainContentTypes(): array { + return [ContentType::FILM, ContentType::SERIES]; +} + +// Функция для получения телевизионных типов контента +function getTVContentTypes(): array { + return [ + ContentType::TV_SHOW, + ContentType::TV_MOVIE, + ContentType::TV_SERIES, + ContentType::MINI_SERIES + ]; +} + +// Функция для получения коротких типов контента +function getShortContentTypes(): array { + return [ContentType::SHORT, ContentType::VIDEO]; +} + +// Использование +$allTypes = getAllContentTypes(); + +echo "Работа с типами контента:\n"; +foreach ($allTypes as $type) { + $icon = getContentTypeIcon($type); + $displayName = $type->getDisplayName(); + $color = getContentTypeColor($type); + $description = getContentTypeDescription($type); + $isFilm = $type->isFilm() ? 'Да' : 'Нет'; + $isSeries = $type->isSeries() ? 'Да' : 'Нет'; + + echo "{$icon} {$displayName}\n"; + echo " Цвет: {$color}\n"; + echo " Описание: {$description}\n"; + echo " Фильм: {$isFilm}\n"; + echo " Сериал: {$isSeries}\n\n"; +} + +// Группировка по категориям +$mainTypes = getMainContentTypes(); +$tvTypes = getTVContentTypes(); +$shortTypes = getShortContentTypes(); + +echo "Основные типы:\n"; +foreach ($mainTypes as $type) { + echo "- {$type->getDisplayName()}\n"; +} + +echo "\nТелевизионные типы:\n"; +foreach ($tvTypes as $type) { + echo "- {$type->getDisplayName()}\n"; +} + +echo "\nКороткие типы:\n"; +foreach ($shortTypes as $type) { + echo "- {$type->getDisplayName()}\n"; +} +``` + +## Создание отчета по типам контента + +```php +class ContentTypeReport { + private array $content; + + public function __construct(array $content) { + $this->content = $content; + } + + public function getContent(): array { + return $this->content; + } + + public function getContentByType(ContentType $type): array { + return filterContentByType($this->content, $type); + } + + public function getContentTypeStats(): array { + return getContentTypeStats($this->content); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО ТИПАМ КОНТЕНТА ===\n\n"; + + $stats = $this->getContentTypeStats(); + + // Общая статистика + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего элементов: {$stats['total']}\n"; + $report .= "Фильмов: {$stats['films']}\n"; + $report .= "Сериалов: {$stats['series']}\n"; + $report .= "Других типов: {$stats['other']}\n\n"; + + // Процентное распределение + if ($stats['total'] > 0) { + $filmsPercent = round(($stats['films'] / $stats['total']) * 100, 1); + $seriesPercent = round(($stats['series'] / $stats['total']) * 100, 1); + $otherPercent = round(($stats['other'] / $stats['total']) * 100, 1); + + $report .= "📈 ПРОЦЕНТНОЕ РАСПРЕДЕЛЕНИЕ:\n"; + $report .= "• Фильмы: {$filmsPercent}%\n"; + $report .= "• Сериалы: {$seriesPercent}%\n"; + $report .= "• Другие типы: {$otherPercent}%\n\n"; + } + + // Детали по типам + $report .= "📋 ДЕТАЛИ ПО ТИПАМ:\n"; + foreach ($stats['byType'] as $typeValue => $count) { + $type = ContentType::from($typeValue); + $icon = getContentTypeIcon($type); + $displayName = $type->getDisplayName(); + $percent = round(($count / $stats['total']) * 100, 1); + + $report .= "{$icon} {$displayName}: {$count} ({$percent}%)\n"; + } + $report .= "\n"; + + // Фильмы + $films = $this->getContentByType(ContentType::FILM); + if (!empty($films)) { + $report .= "🎬 ФИЛЬМЫ (" . count($films) . " элементов):\n"; + foreach (array_slice($films, 0, 5) as $film) { + $report .= "• {$film->getDisplayName()}\n"; + } + if (count($films) > 5) { + $report .= "... и еще " . (count($films) - 5) . " фильмов\n"; + } + $report .= "\n"; + } + + // Сериалы + $series = $this->getContentByType(ContentType::SERIES); + if (!empty($series)) { + $report .= "📺 СЕРИАЛЫ (" . count($series) . " элементов):\n"; + foreach (array_slice($series, 0, 5) as $seriesItem) { + $report .= "• {$seriesItem->getDisplayName()}\n"; + } + if (count($series) > 5) { + $report .= "... и еще " . (count($series) - 5) . " сериалов\n"; + } + $report .= "\n"; + } + + // Мини-сериалы + $miniSeries = $this->getContentByType(ContentType::MINI_SERIES); + if (!empty($miniSeries)) { + $report .= "📺 МИНИ-СЕРИАЛЫ (" . count($miniSeries) . " элементов):\n"; + foreach (array_slice($miniSeries, 0, 5) as $miniSeriesItem) { + $report .= "• {$miniSeriesItem->getDisplayName()}\n"; + } + if (count($miniSeries) > 5) { + $report .= "... и еще " . (count($miniSeries) - 5) . " мини-сериалов\n"; + } + $report .= "\n"; + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $stats = $this->getContentTypeStats(); + $films = $this->getContentByType(ContentType::FILM); + $series = $this->getContentByType(ContentType::SERIES); + $miniSeries = $this->getContentByType(ContentType::MINI_SERIES); + + // Статистика + $html .= "
\n"; + $html .= "

Общая статистика

\n"; + $html .= "

Всего элементов: {$stats['total']}

\n"; + $html .= "

Фильмов: {$stats['films']}

\n"; + $html .= "

Сериалов: {$stats['series']}

\n"; + $html .= "

Других типов: {$stats['other']}

\n"; + + // Прогресс-бары + if ($stats['total'] > 0) { + $filmsPercent = round(($stats['films'] / $stats['total']) * 100, 1); + $seriesPercent = round(($stats['series'] / $stats['total']) * 100, 1); + $otherPercent = round(($stats['other'] / $stats['total']) * 100, 1); + + $html .= "

Распределение по категориям

\n"; + $html .= "
\n"; + $html .= "
\n"; + $html .= "
\n"; + $html .= "

Фильмы: {$filmsPercent}%

\n"; + + $html .= "
\n"; + $html .= "
\n"; + $html .= "
\n"; + $html .= "

Сериалы: {$seriesPercent}%

\n"; + + $html .= "
\n"; + $html .= "
\n"; + $html .= "
\n"; + $html .= "

Другие типы: {$otherPercent}%

\n"; + } + + $html .= "
\n"; + + // Фильмы + if (!empty($films)) { + $html .= "
\n"; + $html .= "
🎬 Фильмы (" . count($films) . " элементов)
\n"; + $html .= "
\n"; + + foreach (array_slice($films, 0, 12) as $film) { + $html .= "
\n"; + $html .= "
{$film->getDisplayName()}
\n"; + if ($film->rating) { + $html .= "\n"; + } + if ($film->year) { + $html .= "\n"; + } + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + // Сериалы + if (!empty($series)) { + $html .= "
\n"; + $html .= "
📺 Сериалы (" . count($series) . " элементов)
\n"; + $html .= "
\n"; + + foreach (array_slice($series, 0, 12) as $seriesItem) { + $html .= "
\n"; + $html .= "
{$seriesItem->getDisplayName()}
\n"; + if ($seriesItem->rating) { + $html .= "\n"; + } + if ($seriesItem->year) { + $html .= "\n"; + } + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + // Мини-сериалы + if (!empty($miniSeries)) { + $html .= "
\n"; + $html .= "
📺 Мини-сериалы (" . count($miniSeries) . " элементов)
\n"; + $html .= "
\n"; + + foreach (array_slice($miniSeries, 0, 12) as $miniSeriesItem) { + $html .= "
\n"; + $html .= "
{$miniSeriesItem->getDisplayName()}
\n"; + if ($miniSeriesItem->rating) { + $html .= "\n"; + } + if ($miniSeriesItem->year) { + $html .= "\n"; + } + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + $html .= "
\n\n"; + + return $html; + } +} + +// Использование +$content = $filmService->getTopFilms(ContentType::ALL); +$report = new ContentTypeReport($content); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Отчет по типам контента'); +file_put_contents('content_types_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в content_types_report.html\n"; +``` + +## Анализ типов контента + +```php +function analyzeContentTypes(array $content): array { + $analysis = [ + 'totalContent' => count($content), + 'contentTypeDistribution' => [ + 'films' => 0, + 'series' => 0, + 'other' => 0 + ], + 'contentTypePercentages' => [ + 'films' => 0, + 'series' => 0, + 'other' => 0 + ], + 'detailedTypeDistribution' => [], + 'ratingAnalysis' => [ + 'averageRating' => 0, + 'highestRatedType' => '', + 'lowestRatedType' => '', + 'typeRatings' => [] + ] + ]; + + $totalRating = 0; + $ratedContent = 0; + $typeRatings = []; + + foreach ($content as $item) { + $type = $item->type; + + // Распределение по категориям + if ($type->isFilm()) { + $analysis['contentTypeDistribution']['films']++; + } elseif ($type->isSeries()) { + $analysis['contentTypeDistribution']['series']++; + } else { + $analysis['contentTypeDistribution']['other']++; + } + + // Детальное распределение по типам + if (!isset($analysis['detailedTypeDistribution'][$type->value])) { + $analysis['detailedTypeDistribution'][$type->value] = 0; + } + $analysis['detailedTypeDistribution'][$type->value]++; + + // Анализ рейтингов + if ($item->rating) { + $rating = (float)$item->rating; + $totalRating += $rating; + $ratedContent++; + + if (!isset($typeRatings[$type->value])) { + $typeRatings[$type->value] = []; + } + $typeRatings[$type->value][] = $rating; + } + } + + // Вычисление процентов + if ($analysis['totalContent'] > 0) { + $analysis['contentTypePercentages']['films'] = round(($analysis['contentTypeDistribution']['films'] / $analysis['totalContent']) * 100, 1); + $analysis['contentTypePercentages']['series'] = round(($analysis['contentTypeDistribution']['series'] / $analysis['totalContent']) * 100, 1); + $analysis['contentTypePercentages']['other'] = round(($analysis['contentTypeDistribution']['other'] / $analysis['totalContent']) * 100, 1); + } + + // Анализ рейтингов по типам + if (!empty($typeRatings)) { + $analysis['ratingAnalysis']['averageRating'] = round($totalRating / $ratedContent, 2); + + $typeAverages = []; + foreach ($typeRatings as $typeValue => $ratings) { + $typeAverages[$typeValue] = round(array_sum($ratings) / count($ratings), 2); + } + + if (!empty($typeAverages)) { + $analysis['ratingAnalysis']['highestRatedType'] = array_search(max($typeAverages), $typeAverages); + $analysis['ratingAnalysis']['lowestRatedType'] = array_search(min($typeAverages), $typeAverages); + $analysis['ratingAnalysis']['typeRatings'] = $typeAverages; + } + } + + return $analysis; +} + +// Использование +$content = $filmService->getTopFilms(ContentType::ALL); +$analysis = analyzeContentTypes($content); + +echo "=== Анализ типов контента ===\n"; +echo "Всего элементов: {$analysis['totalContent']}\n"; + +echo "\nРаспределение по категориям:\n"; +echo "- Фильмы: {$analysis['contentTypeDistribution']['films']} ({$analysis['contentTypePercentages']['films']}%)\n"; +echo "- Сериалы: {$analysis['contentTypeDistribution']['series']} ({$analysis['contentTypePercentages']['series']}%)\n"; +echo "- Другие типы: {$analysis['contentTypeDistribution']['other']} ({$analysis['contentTypePercentages']['other']}%)\n"; + +echo "\nДетальное распределение по типам:\n"; +foreach ($analysis['detailedTypeDistribution'] as $typeValue => $count) { + $type = ContentType::from($typeValue); + echo "- {$type->getDisplayName()}: {$count}\n"; +} + +echo "\nАнализ рейтингов:\n"; +echo "- Средний рейтинг: {$analysis['ratingAnalysis']['averageRating']}\n"; +if ($analysis['ratingAnalysis']['highestRatedType']) { + $highestType = ContentType::from($analysis['ratingAnalysis']['highestRatedType']); + echo "- Самый высокий рейтинг у: {$highestType->getDisplayName()}\n"; +} +if ($analysis['ratingAnalysis']['lowestRatedType']) { + $lowestType = ContentType::from($analysis['ratingAnalysis']['lowestRatedType']); + echo "- Самый низкий рейтинг у: {$lowestType->getDisplayName()}\n"; +} +``` + +## Связанные классы + +- [`Film`](film.md) - Модель фильма +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/enums/distribution-type.md b/docs/dev/notkinopoiskphp/enums/distribution-type.md new file mode 100644 index 0000000..473e18e --- /dev/null +++ b/docs/dev/notkinopoiskphp/enums/distribution-type.md @@ -0,0 +1,582 @@ +# DistributionType + +Типы проката в Kinopoisk API. + +## Описание + +`DistributionType` определяет различные типы проката фильмов: кинотеатральный прокат, DVD, Blu-ray и другие форматы. + +## Значения enum + +### Типы проката + +- `ALL` - Все типы проката +- `CINEMA` - Кинотеатральный прокат +- `DVD` - DVD прокат +- `BLURAY` - Blu-ray прокат +- `DIGITAL` - Цифровой прокат +- `TV` - Телевизионный прокат +- `STREAMING` - Стриминговый прокат +- `COUNTRY_SPECIFIC` - Страновой прокат +- `PREMIERE` - Премьерный прокат +- `WORLD_PREMIER` - Мировая премьера + +## Методы + +### isCinema() + +Проверяет, является ли прокат кинотеатральным. + +```php +public function isCinema(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если это кинотеатральный прокат, `false` в противном случае + +#### Пример использования + +```php +if (DistributionType::CINEMA->isCinema()) { + echo "Кинотеатральный прокат"; +} +``` + +### isHomeVideo() + +Проверяет, является ли прокат домашним видео. + +```php +public function isHomeVideo(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если это домашнее видео, `false` в противном случае + +#### Описание + +Возвращает `true` для типов `DVD` и `BLURAY`. + +#### Пример использования + +```php +if (DistributionType::DVD->isHomeVideo()) { + echo "Домашнее видео"; +} +``` + +### isDigital() + +Проверяет, является ли прокат цифровым. + +```php +public function isDigital(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если это цифровой прокат, `false` в противном случае + +#### Описание + +Возвращает `true` для типов `DIGITAL` и `STREAMING`. + +#### Пример использования + +```php +if (DistributionType::DIGITAL->isDigital()) { + echo "Цифровой прокат"; +} +``` + +### getDisplayName() + +Получает человекочитаемое название типа проката. + +```php +public function getDisplayName(): string +``` + +#### Возвращаемое значение + +- `string` - Название типа проката на русском языке + +#### Пример использования + +```php +echo DistributionType::CINEMA->getDisplayName(); // "Кинотеатральный прокат" +echo DistributionType::DVD->getDisplayName(); // "DVD прокат" +``` + +## Полный пример использования + +```php +films; +$distributions = $filmService->getDistributions(301); // Матрица + +echo "=== Анализ типов проката фильма 'Матрица' ===\n"; + +// Группировка по типам проката +$cinemaDistributions = []; +$homeVideoDistributions = []; +$digitalDistributions = []; +$otherDistributions = []; + +foreach ($distributions as $distribution) { + if ($distribution->type->isCinema()) { + $cinemaDistributions[] = $distribution; + } elseif ($distribution->type->isHomeVideo()) { + $homeVideoDistributions[] = $distribution; + } elseif ($distribution->type->isDigital()) { + $digitalDistributions[] = $distribution; + } else { + $otherDistributions[] = $distribution; + } +} + +// Вывод статистики +echo "\n🎬 Кинотеатральный прокат: " . count($cinemaDistributions) . " записей\n"; +echo "📀 Домашнее видео: " . count($homeVideoDistributions) . " записей\n"; +echo "💻 Цифровой прокат: " . count($digitalDistributions) . " записей\n"; +echo "📺 Прочие типы: " . count($otherDistributions) . " записей\n"; + +// Детали по типам +echo "\n📋 ДЕТАЛИ ПО ТИПАМ:\n"; +foreach ($distributions as $distribution) { + echo "• {$distribution->type->getDisplayName()}\n"; + if ($distribution->country) { + echo " Страна: {$distribution->country->country}\n"; + } + if ($distribution->date) { + echo " Дата: {$distribution->date}\n"; + } +} +``` + +## Работа с типами проката + +```php +// Функция для получения всех типов проката +function getAllDistributionTypes(): array { + return [ + DistributionType::ALL, + DistributionType::CINEMA, + DistributionType::DVD, + DistributionType::BLURAY, + DistributionType::DIGITAL, + DistributionType::TV, + DistributionType::STREAMING, + DistributionType::COUNTRY_SPECIFIC, + DistributionType::PREMIERE, + DistributionType::WORLD_PREMIER + ]; +} + +// Функция для получения типов домашнего видео +function getHomeVideoTypes(): array { + return array_filter(getAllDistributionTypes(), fn($type) => $type->isHomeVideo()); +} + +// Функция для получения цифровых типов +function getDigitalTypes(): array { + return array_filter(getAllDistributionTypes(), fn($type) => $type->isDigital()); +} + +// Функция для получения кинотеатральных типов +function getCinemaTypes(): array { + return array_filter(getAllDistributionTypes(), fn($type) => $type->isCinema()); +} + +// Функция для группировки прокатов по категориям +function groupDistributionsByCategory(array $distributions): array { + $categories = [ + 'cinema' => [], + 'homeVideo' => [], + 'digital' => [], + 'other' => [] + ]; + + foreach ($distributions as $distribution) { + if ($distribution->type->isCinema()) { + $categories['cinema'][] = $distribution; + } elseif ($distribution->type->isHomeVideo()) { + $categories['homeVideo'][] = $distribution; + } elseif ($distribution->type->isDigital()) { + $categories['digital'][] = $distribution; + } else { + $categories['other'][] = $distribution; + } + } + + return $categories; +} + +// Функция для получения статистики по типам +function getDistributionTypeStats(array $distributions): array { + $stats = []; + + foreach (getAllDistributionTypes() as $type) { + $stats[$type->value] = [ + 'type' => $type, + 'displayName' => $type->getDisplayName(), + 'count' => 0, + 'isCinema' => $type->isCinema(), + 'isHomeVideo' => $type->isHomeVideo(), + 'isDigital' => $type->isDigital() + ]; + } + + foreach ($distributions as $distribution) { + $typeKey = $distribution->type->value; + $stats[$typeKey]['count']++; + } + + return $stats; +} + +// Использование +$distributions = $filmService->getDistributions(301); + +// Получение типов +$allTypes = getAllDistributionTypes(); +$homeVideoTypes = getHomeVideoTypes(); +$digitalTypes = getDigitalTypes(); +$cinemaTypes = getCinemaTypes(); + +echo "Всего типов проката: " . count($allTypes) . "\n"; +echo "Типов домашнего видео: " . count($homeVideoTypes) . "\n"; +echo "Цифровых типов: " . count($digitalTypes) . "\n"; +echo "Кинотеатральных типов: " . count($cinemaTypes) . "\n"; + +// Группировка по категориям +$categories = groupDistributionsByCategory($distributions); +echo "Кинотеатральных прокатов: " . count($categories['cinema']) . "\n"; +echo "Домашнего видео: " . count($categories['homeVideo']) . "\n"; +echo "Цифровых прокатов: " . count($categories['digital']) . "\n"; +echo "Прочих типов: " . count($categories['other']) . "\n"; + +// Статистика по типам +$stats = getDistributionTypeStats($distributions); +echo "\nСтатистика по типам:\n"; +foreach ($stats as $typeKey => $data) { + if ($data['count'] > 0) { + echo "- {$data['displayName']}: {$data['count']} записей\n"; + } +} +``` + +## Создание отчета по типам проката + +```php +class DistributionTypeReport { + private array $distributions; + + public function __construct(array $distributions) { + $this->distributions = $distributions; + } + + public function getDistributionsByType(DistributionType $type): array { + return array_filter($this->distributions, fn($dist) => $dist->type === $type); + } + + public function getCategoryBreakdown(): array { + return groupDistributionsByCategory($this->distributions); + } + + public function getTypeStats(): array { + return getDistributionTypeStats($this->distributions); + } + + public function getCinemaDistributions(): array { + return array_filter($this->distributions, fn($dist) => $dist->type->isCinema()); + } + + public function getHomeVideoDistributions(): array { + return array_filter($this->distributions, fn($dist) => $dist->type->isHomeVideo()); + } + + public function getDigitalDistributions(): array { + return array_filter($this->distributions, fn($dist) => $dist->type->isDigital()); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО ТИПАМ ПРОКАТА ===\n\n"; + + $categories = $this->getCategoryBreakdown(); + $stats = $this->getTypeStats(); + + // Общая статистика + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего записей о прокате: " . count($this->distributions) . "\n"; + $report .= "Кинотеатральных прокатов: " . count($categories['cinema']) . "\n"; + $report .= "Домашнего видео: " . count($categories['homeVideo']) . "\n"; + $report .= "Цифровых прокатов: " . count($categories['digital']) . "\n"; + $report .= "Прочих типов: " . count($categories['other']) . "\n\n"; + + // Статистика по категориям + $report .= "🎬 СТАТИСТИКА ПО КАТЕГОРИЯМ:\n"; + $report .= "• Кинотеатральный прокат: " . count($categories['cinema']) . " записей\n"; + $report .= "• Домашнее видео: " . count($categories['homeVideo']) . " записей\n"; + $report .= "• Цифровой прокат: " . count($categories['digital']) . " записей\n"; + $report .= "• Прочие типы: " . count($categories['other']) . " записей\n\n"; + + // Детали по типам + $report .= "📋 ДЕТАЛИ ПО ТИПАМ:\n"; + foreach ($stats as $typeKey => $data) { + if ($data['count'] > 0) { + $report .= "• {$data['displayName']}: {$data['count']} записей\n"; + + // Дополнительная информация + if ($data['isCinema']) { + $report .= " 🎬 Кинотеатральный тип\n"; + } + if ($data['isHomeVideo']) { + $report .= " 📀 Домашнее видео\n"; + } + if ($data['isDigital']) { + $report .= " 💻 Цифровой тип\n"; + } + } + } + + // Примеры по категориям + $report .= "\n🎬 ПРИМЕРЫ КИНОТЕАТРАЛЬНЫХ ПРОКАТОВ:\n"; + foreach (array_slice($categories['cinema'], 0, 3) as $distribution) { + $report .= "• {$distribution->type->getDisplayName()}\n"; + if ($distribution->country) { + $report .= " Страна: {$distribution->country->country}\n"; + } + if ($distribution->date) { + $report .= " Дата: {$distribution->date}\n"; + } + } + + if (count($categories['cinema']) > 3) { + $report .= " ... и еще " . (count($categories['cinema']) - 3) . " записей\n"; + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $categories = $this->getCategoryBreakdown(); + $stats = $this->getTypeStats(); + + // Общая статистика + $html .= "
\n"; + $html .= "

Общая статистика

\n"; + $html .= "

Всего записей о прокате: " . count($this->distributions) . "

\n"; + $html .= "

Кинотеатральных прокатов: " . count($categories['cinema']) . "

\n"; + $html .= "

Домашнего видео: " . count($categories['homeVideo']) . "

\n"; + $html .= "

Цифровых прокатов: " . count($categories['digital']) . "

\n"; + $html .= "

Прочих типов: " . count($categories['other']) . "

\n"; + $html .= "
\n"; + + // Детали по типам + $html .= "
\n"; + $html .= "
Детали по типам
\n"; + + foreach ($stats as $typeKey => $data) { + if ($data['count'] > 0) { + $cssClass = ''; + if ($data['isCinema']) { + $cssClass = 'cinema'; + } elseif ($data['isHomeVideo']) { + $cssClass = 'home-video'; + } elseif ($data['isDigital']) { + $cssClass = 'digital'; + } else { + $cssClass = 'other'; + } + + $html .= "
\n"; + $html .= "
{$data['displayName']}
\n"; + $html .= "
\n"; + $html .= "
Количество записей: {$data['count']}
\n"; + + if ($data['isCinema']) { + $html .= "
🎬 Кинотеатральный тип
\n"; + } + if ($data['isHomeVideo']) { + $html .= "
📀 Домашнее видео
\n"; + } + if ($data['isDigital']) { + $html .= "
💻 Цифровой тип
\n"; + } + + $html .= "
\n
\n"; + } + } + + $html .= "
\n
\n\n"; + + return $html; + } +} + +// Использование +$distributions = $filmService->getDistributions(301); +$report = new DistributionTypeReport($distributions); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Отчет по типам проката'); +file_put_contents('distribution_types_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в distribution_types_report.html\n"; +``` + +## Анализ эффективности типов + +```php +function analyzeDistributionTypeEffectiveness(array $distributions): array { + $analysis = [ + 'totalDistributions' => count($distributions), + 'typeDistribution' => [], + 'categoryDistribution' => [], + 'countryByType' => [], + 'dateByType' => [], + 'mostPopularType' => null, + 'leastPopularType' => null + ]; + + $typeStats = []; + $categoryStats = [ + 'cinema' => 0, + 'homeVideo' => 0, + 'digital' => 0, + 'other' => 0 + ]; + + foreach ($distributions as $distribution) { + $typeKey = $distribution->type->value; + + // Статистика по типам + if (!isset($typeStats[$typeKey])) { + $typeStats[$typeKey] = [ + 'count' => 0, + 'countries' => [], + 'dates' => [] + ]; + } + $typeStats[$typeKey]['count']++; + + // Статистика по категориям + if ($distribution->type->isCinema()) { + $categoryStats['cinema']++; + } elseif ($distribution->type->isHomeVideo()) { + $categoryStats['homeVideo']++; + } elseif ($distribution->type->isDigital()) { + $categoryStats['digital']++; + } else { + $categoryStats['other']++; + } + + // Статистика по странам + if ($distribution->country) { + $countryName = $distribution->country->country; + if (!isset($typeStats[$typeKey]['countries'][$countryName])) { + $typeStats[$typeKey]['countries'][$countryName] = 0; + } + $typeStats[$typeKey]['countries'][$countryName]++; + } + + // Статистика по датам + if ($distribution->date) { + $typeStats[$typeKey]['dates'][] = $distribution->date; + } + } + + $analysis['typeDistribution'] = $typeStats; + $analysis['categoryDistribution'] = $categoryStats; + + // Самый популярный и непопулярный тип + if (!empty($typeStats)) { + $counts = array_column($typeStats, 'count'); + $maxCount = max($counts); + $minCount = min($counts); + + foreach ($typeStats as $typeKey => $data) { + if ($data['count'] === $maxCount) { + $analysis['mostPopularType'] = DistributionType::from($typeKey); + } + if ($data['count'] === $minCount) { + $analysis['leastPopularType'] = DistributionType::from($typeKey); + } + } + } + + return $analysis; +} + +// Использование +$distributions = $filmService->getDistributions(301); +$analysis = analyzeDistributionTypeEffectiveness($distributions); + +echo "=== Анализ эффективности типов проката ===\n"; +echo "Всего записей о прокате: {$analysis['totalDistributions']}\n"; + +echo "\nРаспределение по категориям:\n"; +foreach ($analysis['categoryDistribution'] as $category => $count) { + $percentage = round(($count / $analysis['totalDistributions']) * 100, 1); + echo "- {$category}: {$count} записей ({$percentage}%)\n"; +} + +echo "\nРаспределение по типам:\n"; +foreach ($analysis['typeDistribution'] as $typeKey => $data) { + $type = DistributionType::from($typeKey); + $percentage = round(($data['count'] / $analysis['totalDistributions']) * 100, 1); + echo "- {$type->getDisplayName()}: {$data['count']} записей ({$percentage}%)\n"; + + if (!empty($data['countries'])) { + echo " Топ стран: " . implode(', ', array_keys(array_slice($data['countries'], 0, 3, true))) . "\n"; + } +} + +if ($analysis['mostPopularType']) { + echo "\nСамый популярный тип: {$analysis['mostPopularType']->getDisplayName()}\n"; +} + +if ($analysis['leastPopularType']) { + echo "Самый непопулярный тип: {$analysis['leastPopularType']->getDisplayName()}\n"; +} +``` + +## Связанные классы + +- [`Distribution`](../models/distribution.md) - Модель проката +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/enums/fact-type.md b/docs/dev/notkinopoiskphp/enums/fact-type.md new file mode 100644 index 0000000..09f2a0b --- /dev/null +++ b/docs/dev/notkinopoiskphp/enums/fact-type.md @@ -0,0 +1,449 @@ +# FactType + +Типы фактов в Kinopoisk API. + +## Описание + +`FactType` определяет различные типы фактов, которые могут быть связаны с фильмом: интересные факты, ошибки (блуперы) и т.д. + +## Значения enum + +### Типы фактов + +- `FACT` - Интересный факт +- `BLOOPER` - Ошибка в фильме (блупер) + +## Методы + +### isBlooper() + +Проверяет, является ли факт ошибкой в фильме. + +```php +public function isBlooper(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если это ошибка в фильме, `false` в противном случае + +#### Пример использования + +```php +if (FactType::BLOOPER->isBlooper()) { + echo "Это ошибка в фильме"; +} +``` + +### isFact() + +Проверяет, является ли факт интересным фактом. + +```php +public function isFact(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если это интересный факт, `false` в противном случае + +#### Пример использования + +```php +if (FactType::FACT->isFact()) { + echo "Это интересный факт"; +} +``` + +### getDisplayName() + +Получает человекочитаемое название типа факта. + +```php +public function getDisplayName(): string +``` + +#### Возвращаемое значение + +- `string` - Название типа факта на русском языке + +#### Пример использования + +```php +echo FactType::FACT->getDisplayName(); // "Интересный факт" +echo FactType::BLOOPER->getDisplayName(); // "Ошибка в фильме" +``` + +## Полный пример использования + +```php +films; +$facts = $filmService->getFacts(301); // ID фильма "Матрица" + +echo "=== Работа с типами фактов ===\n"; + +// Вывод всех типов фактов +echo "Доступные типы фактов:\n"; +foreach ([FactType::FACT, FactType::BLOOPER] as $type) { + $icon = getFactTypeIcon($type); + $displayName = $type->getDisplayName(); + echo "{$icon} {$displayName} ({$type->value})\n"; +} + +echo "\n"; + +// Группировка фактов по типам +$groupedFacts = []; +foreach ($facts as $fact) { + $type = $fact->type; + if (!isset($groupedFacts[$type->value])) { + $groupedFacts[$type->value] = []; + } + $groupedFacts[$type->value][] = $fact; +} + +// Вывод фактов по типам +foreach ($groupedFacts as $typeValue => $typeFacts) { + $type = FactType::from($typeValue); + $icon = getFactTypeIcon($type); + $displayName = $type->getDisplayName(); + + echo "{$icon} {$displayName} (" . count($typeFacts) . " фактов):\n"; + foreach ($typeFacts as $fact) { + echo " • {$fact->text}\n"; + } + echo "\n"; +} +``` + +## Работа с типами фактов + +```php +// Функция для получения иконки типа факта +function getFactTypeIcon(FactType $type): string { + return match ($type) { + FactType::FACT => '💡', + FactType::BLOOPER => '🎬' + }; +} + +// Функция для получения цвета типа факта +function getFactTypeColor(FactType $type): string { + return match ($type) { + FactType::FACT => '#28a745', // Зеленый + FactType::BLOOPER => '#ffc107' // Желтый + }; +} + +// Функция для получения описания типа факта +function getFactTypeDescription(FactType $type): string { + return match ($type) { + FactType::FACT => 'Интересные факты о съемках, актерах, сюжете и других аспектах фильма', + FactType::BLOOPER => 'Ошибки, несоответствия и ляпы, замеченные в фильме' + }; +} + +// Функция для фильтрации фактов по типу +function filterFactsByType(array $facts, FactType $type): array { + return array_filter($facts, fn($fact) => $fact->type === $type); +} + +// Функция для получения статистики по типам фактов +function getFactTypeStats(array $facts): array { + $stats = [ + 'total' => count($facts), + 'facts' => 0, + 'bloopers' => 0 + ]; + + foreach ($facts as $fact) { + if ($fact->type->isFact()) { + $stats['facts']++; + } elseif ($fact->type->isBlooper()) { + $stats['bloopers']++; + } + } + + return $stats; +} + +// Функция для получения всех типов фактов +function getAllFactTypes(): array { + return [FactType::FACT, FactType::BLOOPER]; +} + +// Использование +$allTypes = getAllFactTypes(); + +echo "Работа с типами фактов:\n"; +foreach ($allTypes as $type) { + $icon = getFactTypeIcon($type); + $displayName = $type->getDisplayName(); + $color = getFactTypeColor($type); + $description = getFactTypeDescription($type); + + echo "{$icon} {$displayName}\n"; + echo " Цвет: {$color}\n"; + echo " Описание: {$description}\n\n"; +} +``` + +## Создание отчета по типам фактов + +```php +class FactTypeReport { + private array $facts; + + public function __construct(array $facts) { + $this->facts = $facts; + } + + public function getFacts(): array { + return $this->facts; + } + + public function getFactsByType(FactType $type): array { + return filterFactsByType($this->facts, $type); + } + + public function getFactTypeStats(): array { + return getFactTypeStats($this->facts); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО ТИПАМ ФАКТОВ ===\n\n"; + + $stats = $this->getFactTypeStats(); + + // Общая статистика + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего фактов: {$stats['total']}\n"; + $report .= "Интересных фактов: {$stats['facts']}\n"; + $report .= "Ошибок в фильме: {$stats['bloopers']}\n\n"; + + // Процентное распределение + if ($stats['total'] > 0) { + $factsPercent = round(($stats['facts'] / $stats['total']) * 100, 1); + $bloopersPercent = round(($stats['bloopers'] / $stats['total']) * 100, 1); + + $report .= "📈 ПРОЦЕНТНОЕ РАСПРЕДЕЛЕНИЕ:\n"; + $report .= "• Интересные факты: {$factsPercent}%\n"; + $report .= "• Ошибки в фильме: {$bloopersPercent}%\n\n"; + } + + // Интересные факты + $interestingFacts = $this->getFactsByType(FactType::FACT); + if (!empty($interestingFacts)) { + $report .= "💡 ИНТЕРЕСНЫЕ ФАКТЫ (" . count($interestingFacts) . " фактов):\n"; + foreach ($interestingFacts as $index => $fact) { + $report .= ($index + 1) . ". {$fact->text}\n"; + } + $report .= "\n"; + } + + // Ошибки в фильме + $bloopers = $this->getFactsByType(FactType::BLOOPER); + if (!empty($bloopers)) { + $report .= "🎬 ОШИБКИ В ФИЛЬМЕ (" . count($bloopers) . " ошибок):\n"; + foreach ($bloopers as $index => $fact) { + $report .= ($index + 1) . ". {$fact->text}\n"; + } + $report .= "\n"; + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $stats = $this->getFactTypeStats(); + $interestingFacts = $this->getFactsByType(FactType::FACT); + $bloopers = $this->getFactsByType(FactType::BLOOPER); + + // Статистика + $html .= "
\n"; + $html .= "

Общая статистика

\n"; + $html .= "

Всего фактов: {$stats['total']}

\n"; + $html .= "

Интересных фактов: {$stats['facts']}

\n"; + $html .= "

Ошибок в фильме: {$stats['bloopers']}

\n"; + + // Прогресс-бары + if ($stats['total'] > 0) { + $factsPercent = round(($stats['facts'] / $stats['total']) * 100, 1); + $bloopersPercent = round(($stats['bloopers'] / $stats['total']) * 100, 1); + + $html .= "

Распределение по типам

\n"; + $html .= "
\n"; + $html .= "
\n"; + $html .= "
\n"; + $html .= "

Интересные факты: {$factsPercent}%

\n"; + + $html .= "
\n"; + $html .= "
\n"; + $html .= "
\n"; + $html .= "

Ошибки в фильме: {$bloopersPercent}%

\n"; + } + + $html .= "
\n"; + + // Интересные факты + if (!empty($interestingFacts)) { + $html .= "
\n"; + $html .= "
💡 Интересные факты (" . count($interestingFacts) . " фактов)
\n"; + $html .= "
\n"; + + foreach ($interestingFacts as $fact) { + $html .= "
\n"; + $html .= "
{$fact->text}
\n"; + $html .= "
Тип: Интересный факт
\n"; + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + // Ошибки в фильме + if (!empty($bloopers)) { + $html .= "
\n"; + $html .= "
🎬 Ошибки в фильме (" . count($bloopers) . " ошибок)
\n"; + $html .= "
\n"; + + foreach ($bloopers as $fact) { + $html .= "
\n"; + $html .= "
{$fact->text}
\n"; + $html .= "
Тип: Ошибка в фильме
\n"; + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + $html .= "
\n\n"; + + return $html; + } +} + +// Использование +$facts = $filmService->getFacts(301); +$report = new FactTypeReport($facts); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Отчет по типам фактов'); +file_put_contents('fact_types_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в fact_types_report.html\n"; +``` + +## Анализ типов фактов + +```php +function analyzeFactTypes(array $facts): array { + $analysis = [ + 'totalFacts' => count($facts), + 'factTypeDistribution' => [ + 'facts' => 0, + 'bloopers' => 0 + ], + 'factTypePercentages' => [ + 'facts' => 0, + 'bloopers' => 0 + ], + 'textAnalysis' => [ + 'averageLength' => 0, + 'longestFact' => '', + 'shortestFact' => '', + 'factLengths' => [] + ] + ]; + + foreach ($facts as $fact) { + // Статистика по типам + if ($fact->type->isFact()) { + $analysis['factTypeDistribution']['facts']++; + } elseif ($fact->type->isBlooper()) { + $analysis['factTypeDistribution']['bloopers']++; + } + + // Анализ текста + $textLength = strlen($fact->text); + $analysis['textAnalysis']['factLengths'][] = $textLength; + + if (empty($analysis['textAnalysis']['longestFact']) || $textLength > strlen($analysis['textAnalysis']['longestFact'])) { + $analysis['textAnalysis']['longestFact'] = $fact->text; + } + + if (empty($analysis['textAnalysis']['shortestFact']) || $textLength < strlen($analysis['textAnalysis']['shortestFact'])) { + $analysis['textAnalysis']['shortestFact'] = $fact->text; + } + } + + // Вычисление процентов + if ($analysis['totalFacts'] > 0) { + $analysis['factTypePercentages']['facts'] = round(($analysis['factTypeDistribution']['facts'] / $analysis['totalFacts']) * 100, 1); + $analysis['factTypePercentages']['bloopers'] = round(($analysis['factTypeDistribution']['bloopers'] / $analysis['totalFacts']) * 100, 1); + } + + // Средняя длина текста + if (!empty($analysis['textAnalysis']['factLengths'])) { + $analysis['textAnalysis']['averageLength'] = round(array_sum($analysis['textAnalysis']['factLengths']) / count($analysis['textAnalysis']['factLengths'])); + } + + return $analysis; +} + +// Использование +$facts = $filmService->getFacts(301); +$analysis = analyzeFactTypes($facts); + +echo "=== Анализ типов фактов ===\n"; +echo "Всего фактов: {$analysis['totalFacts']}\n"; + +echo "\nРаспределение по типам:\n"; +echo "- Интересные факты: {$analysis['factTypeDistribution']['facts']} ({$analysis['factTypePercentages']['facts']}%)\n"; +echo "- Ошибки в фильме: {$analysis['factTypeDistribution']['bloopers']} ({$analysis['factTypePercentages']['bloopers']}%)\n"; + +echo "\nАнализ текста:\n"; +echo "- Средняя длина: {$analysis['textAnalysis']['averageLength']} символов\n"; +echo "- Самый длинный факт: " . substr($analysis['textAnalysis']['longestFact'], 0, 100) . "...\n"; +echo "- Самый короткий факт: {$analysis['textAnalysis']['shortestFact']}\n"; +``` + +## Связанные классы + +- [`Fact`](../models/fact.md) - Модель факта +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/enums/film-order.md b/docs/dev/notkinopoiskphp/enums/film-order.md new file mode 100644 index 0000000..a00579e --- /dev/null +++ b/docs/dev/notkinopoiskphp/enums/film-order.md @@ -0,0 +1,416 @@ +# FilmOrder + +Порядок сортировки фильмов в Kinopoisk API. + +## Описание + +`FilmOrder` определяет различные способы сортировки фильмов при получении списков и коллекций. + +## Значения enum + +### Типы сортировки + +- `RATING` - Сортировка по рейтингу +- `NUM_VOTE` - Сортировка по количеству голосов +- `YEAR` - Сортировка по году выпуска + +## Полный пример использования + +```php +films; + +echo "=== Работа с сортировкой фильмов ===\n"; + +// Получение топ фильмов по рейтингу +$topByRating = $filmService->getTopFilms(order: FilmOrder::RATING); +echo "Топ фильмов по рейтингу: " . count($topByRating) . " элементов\n"; + +// Получение фильмов по количеству голосов +$topByVotes = $filmService->getTopFilms(order: FilmOrder::NUM_VOTE); +echo "Топ фильмов по голосам: " . count($topByVotes) . " элементов\n"; + +// Получение фильмов по году +$topByYear = $filmService->getTopFilms(order: FilmOrder::YEAR); +echo "Топ фильмов по году: " . count($topByYear) . " элементов\n"; + +// Вывод первых 5 фильмов по рейтингу +echo "\n🏆 ТОП-5 ПО РЕЙТИНГУ:\n"; +foreach (array_slice($topByRating, 0, 5) as $index => $film) { + echo ($index + 1) . ". {$film->getDisplayName()}\n"; + if ($film->rating) { + echo " Рейтинг: {$film->rating}\n"; + } + if ($film->year) { + echo " Год: {$film->year}\n"; + } +} +``` + +## Работа с порядком сортировки + +```php +// Функция для получения всех типов сортировки +function getAllFilmOrders(): array { + return [ + FilmOrder::RATING, + FilmOrder::NUM_VOTE, + FilmOrder::YEAR + ]; +} + +// Функция для получения отображаемого названия сортировки +function getOrderDisplayName(FilmOrder $order): string { + return match ($order) { + FilmOrder::RATING => 'По рейтингу', + FilmOrder::NUM_VOTE => 'По количеству голосов', + FilmOrder::YEAR => 'По году выпуска' + }; +} + +// Функция для получения описания сортировки +function getOrderDescription(FilmOrder $order): string { + return match ($order) { + FilmOrder::RATING => 'Фильмы отсортированы по убыванию рейтинга', + FilmOrder::NUM_VOTE => 'Фильмы отсортированы по количеству голосов', + FilmOrder::YEAR => 'Фильмы отсортированы по году выпуска (новые первыми)' + }; +} + +// Функция для получения иконки сортировки +function getOrderIcon(FilmOrder $order): string { + return match ($order) { + FilmOrder::RATING => '🏆', + FilmOrder::NUM_VOTE => '👥', + FilmOrder::YEAR => '📅' + }; +} + +// Использование +$allOrders = getAllFilmOrders(); + +echo "Доступные типы сортировки:\n"; +foreach ($allOrders as $order) { + $icon = getOrderIcon($order); + $name = getOrderDisplayName($order); + $description = getOrderDescription($order); + echo "{$icon} {$name}: {$description}\n"; +} +``` + +## Создание отчета по сортировке + +```php +class FilmOrderReport { + private array $filmsByRating; + private array $filmsByVotes; + private array $filmsByYear; + + public function __construct(array $filmsByRating, array $filmsByVotes, array $filmsByYear) { + $this->filmsByRating = $filmsByRating; + $this->filmsByVotes = $filmsByVotes; + $this->filmsByYear = $filmsByYear; + } + + public function getFilmsByRating(): array { + return $this->filmsByRating; + } + + public function getFilmsByVotes(): array { + return $this->filmsByVotes; + } + + public function getFilmsByYear(): array { + return $this->filmsByYear; + } + + public function getTopByRating(int $limit = 10): array { + return array_slice($this->filmsByRating, 0, $limit); + } + + public function getTopByVotes(int $limit = 10): array { + return array_slice($this->filmsByVotes, 0, $limit); + } + + public function getTopByYear(int $limit = 10): array { + return array_slice($this->filmsByYear, 0, $limit); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО СОРТИРОВКЕ ФИЛЬМОВ ===\n\n"; + + // Статистика + $report .= "📊 СТАТИСТИКА:\n"; + $report .= "Фильмов по рейтингу: " . count($this->filmsByRating) . "\n"; + $report .= "Фильмов по голосам: " . count($this->filmsByVotes) . "\n"; + $report .= "Фильмов по году: " . count($this->filmsByYear) . "\n\n"; + + // Топ по рейтингу + $report .= "🏆 ТОП-10 ПО РЕЙТИНГУ:\n"; + foreach ($this->getTopByRating(10) as $index => $film) { + $report .= ($index + 1) . ". {$film->getDisplayName()}"; + if ($film->rating) { + $report .= " (★ {$film->rating})"; + } + if ($film->year) { + $report .= " ({$film->year})"; + } + $report .= "\n"; + } + + $report .= "\n"; + + // Топ по голосам + $report .= "👥 ТОП-10 ПО КОЛИЧЕСТВУ ГОЛОСОВ:\n"; + foreach ($this->getTopByVotes(10) as $index => $film) { + $report .= ($index + 1) . ". {$film->getDisplayName()}"; + if ($film->rating) { + $report .= " (★ {$film->rating})"; + } + if ($film->year) { + $report .= " ({$film->year})"; + } + $report .= "\n"; + } + + $report .= "\n"; + + // Топ по году + $report .= "📅 ТОП-10 ПО ГОДУ ВЫПУСКА:\n"; + foreach ($this->getTopByYear(10) as $index => $film) { + $report .= ($index + 1) . ". {$film->getDisplayName()}"; + if ($film->year) { + $report .= " ({$film->year})"; + } + if ($film->rating) { + $report .= " (★ {$film->rating})"; + } + $report .= "\n"; + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + // Статистика + $html .= "
\n"; + $html .= "

Статистика

\n"; + $html .= "

Фильмов по рейтингу: " . count($this->filmsByRating) . "

\n"; + $html .= "

Фильмов по голосам: " . count($this->filmsByVotes) . "

\n"; + $html .= "

Фильмов по году: " . count($this->filmsByYear) . "

\n"; + $html .= "
\n"; + + // Топ по рейтингу + $html .= "
\n"; + $html .= "
🏆 Топ-10 по рейтингу
\n"; + + foreach ($this->getTopByRating(10) as $index => $film) { + $html .= "
\n"; + $html .= "
" . ($index + 1) . ". {$film->getDisplayName()}
\n"; + $html .= "
\n"; + if ($film->rating) { + $html .= "Рейтинг: ★ {$film->rating} "; + } + if ($film->year) { + $html .= "Год: {$film->year}"; + } + $html .= "
\n
\n"; + } + + $html .= "
\n"; + + // Топ по голосам + $html .= "
\n"; + $html .= "
👥 Топ-10 по количеству голосов
\n"; + + foreach ($this->getTopByVotes(10) as $index => $film) { + $html .= "
\n"; + $html .= "
" . ($index + 1) . ". {$film->getDisplayName()}
\n"; + $html .= "
\n"; + if ($film->rating) { + $html .= "Рейтинг: ★ {$film->rating} "; + } + if ($film->year) { + $html .= "Год: {$film->year}"; + } + $html .= "
\n
\n"; + } + + $html .= "
\n"; + + // Топ по году + $html .= "
\n"; + $html .= "
📅 Топ-10 по году выпуска
\n"; + + foreach ($this->getTopByYear(10) as $index => $film) { + $html .= "
\n"; + $html .= "
" . ($index + 1) . ". {$film->getDisplayName()}
\n"; + $html .= "
\n"; + if ($film->year) { + $html .= "Год: {$film->year} "; + } + if ($film->rating) { + $html .= "Рейтинг: ★ {$film->rating}"; + } + $html .= "
\n
\n"; + } + + $html .= "
\n
\n\n"; + + return $html; + } +} + +// Использование +$filmsByRating = $filmService->getTopFilms(order: FilmOrder::RATING); +$filmsByVotes = $filmService->getTopFilms(order: FilmOrder::NUM_VOTE); +$filmsByYear = $filmService->getTopFilms(order: FilmOrder::YEAR); + +$report = new FilmOrderReport($filmsByRating, $filmsByVotes, $filmsByYear); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Отчет по сортировке фильмов'); +file_put_contents('film_order_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в film_order_report.html\n"; +``` + +## Анализ сортировки + +```php +function analyzeFilmOrders(array $filmsByRating, array $filmsByVotes, array $filmsByYear): array { + $analysis = [ + 'totalFilms' => [ + 'rating' => count($filmsByRating), + 'votes' => count($filmsByVotes), + 'year' => count($filmsByYear) + ], + 'averageRating' => 0, + 'averageYear' => 0, + 'ratingDistribution' => [ + 'excellent' => 0, // 9+ + 'good' => 0, // 7-8.9 + 'average' => 0, // 5-6.9 + 'poor' => 0 // <5 + ], + 'yearDistribution' => [ + 'recent' => 0, // 2020+ + 'modern' => 0, // 2010-2019 + 'classic' => 0, // 2000-2009 + 'old' => 0 // <2000 + ] + ]; + + $totalRating = 0; + $ratedCount = 0; + $totalYear = 0; + $yearCount = 0; + + // Анализ фильмов по рейтингу + foreach ($filmsByRating as $film) { + if ($film->rating) { + $rating = (float) $film->rating; + $totalRating += $rating; + $ratedCount++; + + if ($rating >= 9) { + $analysis['ratingDistribution']['excellent']++; + } elseif ($rating >= 7) { + $analysis['ratingDistribution']['good']++; + } elseif ($rating >= 5) { + $analysis['ratingDistribution']['average']++; + } else { + $analysis['ratingDistribution']['poor']++; + } + } + + if ($film->year) { + $year = (int) $film->year; + $totalYear += $year; + $yearCount++; + + if ($year >= 2020) { + $analysis['yearDistribution']['recent']++; + } elseif ($year >= 2010) { + $analysis['yearDistribution']['modern']++; + } elseif ($year >= 2000) { + $analysis['yearDistribution']['classic']++; + } else { + $analysis['yearDistribution']['old']++; + } + } + } + + // Средние значения + if ($ratedCount > 0) { + $analysis['averageRating'] = round($totalRating / $ratedCount, 2); + } + + if ($yearCount > 0) { + $analysis['averageYear'] = round($totalYear / $yearCount); + } + + return $analysis; +} + +// Использование +$filmsByRating = $filmService->getTopFilms(order: FilmOrder::RATING); +$filmsByVotes = $filmService->getTopFilms(order: FilmOrder::NUM_VOTE); +$filmsByYear = $filmService->getTopFilms(order: FilmOrder::YEAR); + +$analysis = analyzeFilmOrders($filmsByRating, $filmsByVotes, $filmsByYear); + +echo "=== Анализ сортировки фильмов ===\n"; +echo "Фильмов по рейтингу: {$analysis['totalFilms']['rating']}\n"; +echo "Фильмов по голосам: {$analysis['totalFilms']['votes']}\n"; +echo "Фильмов по году: {$analysis['totalFilms']['year']}\n"; +echo "Средний рейтинг: {$analysis['averageRating']}\n"; +echo "Средний год: {$analysis['averageYear']}\n"; + +echo "\nРаспределение по рейтингам:\n"; +echo "- Отличные (9+): {$analysis['ratingDistribution']['excellent']}\n"; +echo "- Хорошие (7-8.9): {$analysis['ratingDistribution']['good']}\n"; +echo "- Средние (5-6.9): {$analysis['ratingDistribution']['average']}\n"; +echo "- Плохие (<5): {$analysis['ratingDistribution']['poor']}\n"; + +echo "\nРаспределение по годам:\n"; +echo "- Современные (2020+): {$analysis['yearDistribution']['recent']}\n"; +echo "- Новые (2010-2019): {$analysis['yearDistribution']['modern']}\n"; +echo "- Классические (2000-2009): {$analysis['yearDistribution']['classic']}\n"; +echo "- Старые (<2000): {$analysis['yearDistribution']['old']}\n"; +``` + +## Связанные классы + +- [`FilmCollection`](../models/film-collection.md) - Модель элемента коллекции +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/enums/image-type.md b/docs/dev/notkinopoiskphp/enums/image-type.md new file mode 100644 index 0000000..ade8558 --- /dev/null +++ b/docs/dev/notkinopoiskphp/enums/image-type.md @@ -0,0 +1,250 @@ +# ImageType + +Типы изображений в Kinopoisk API. + +## Описание + +`ImageType` определяет различные типы изображений, которые могут быть связаны с фильмом: кадры из фильма, постеры, фоны и т.д. + +## Значения enum + +### Основные типы изображений + +- `STILL` - Кадр из фильма +- `SHOOTING` - Изображения со съемок +- `POSTER` - Постер +- `FAN_ART` - Фан-арты +- `PROMO` - Промо +- `CONCEPT` - Концепт-арты +- `WALLPAPER` - Обои +- `COVER` - Обложки +- `SCREENSHOT` - Скриншоты +- `BACKGROUND` - Фон +- `PREVIEW` - Превью + +## Методы + +### getDisplayName() + +Получает человекочитаемое название типа изображения. + +```php +public function getDisplayName(): string +``` + +#### Возвращаемое значение + +- `string` - Название типа изображения на русском языке + +#### Пример использования + +```php +echo ImageType::STILL->getDisplayName(); // "Кадр из фильма" +echo ImageType::POSTER->getDisplayName(); // "Постер" +echo ImageType::FAN_ART->getDisplayName(); // "Фан-арты" +``` + +### isMain() + +Проверяет, является ли тип изображения основным. + +```php +public function isMain(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если основной тип, `false` в противном случае + +#### Описание + +Основные типы изображений - это те, которые обычно используются для отображения в галереях и каталогах. + +#### Пример использования + +```php +if (ImageType::POSTER->isMain()) { + echo "Основной тип изображения"; +} + +// Основные типы: POSTER, STILL, FAN_ART, CONCEPT +``` + +### isPromo() + +Проверяет, является ли тип изображения промо-материалом. + +```php +public function isPromo(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если промо-материал, `false` в противном случае + +#### Описание + +Промо-материалы используются для рекламы и продвижения фильма. + +#### Пример использования + +```php +if (ImageType::PROMO->isPromo()) { + echo "Промо-материал"; +} + +// Промо-типы: PROMO, POSTER, COVER, WALLPAPER +``` + +## Полный пример использования + +```php +films->getImages($filmId, ImageType::POSTER); +echo "📰 Постеров: " . count($posters) . "\n"; + +// Кадры из фильма +$stills = $client->films->getImages($filmId, ImageType::STILL); +echo "🎬 Кадров из фильма: " . count($stills) . "\n"; + +// Фан-арты +$fanArts = $client->films->getImages($filmId, ImageType::FAN_ART); +echo "🎨 Фан-артов: " . count($fanArts) . "\n"; + +// Промо-материалы +$promos = $client->films->getImages($filmId, ImageType::PROMO); +echo "📢 Промо-материалов: " . count($promos) . "\n"; + +// Концепт-арты +$concepts = $client->films->getImages($filmId, ImageType::CONCEPT); +echo "🎭 Концепт-артов: " . count($concepts) . "\n"; + +// Обои +$wallpapers = $client->films->getImages($filmId, ImageType::WALLPAPER); +echo "🖼️ Обоев: " . count($wallpapers) . "\n"; +``` + +## Работа с типами изображений + +```php +// Получение всех типов изображений +$allImageTypes = [ + ImageType::STILL, + ImageType::SHOOTING, + ImageType::POSTER, + ImageType::FAN_ART, + ImageType::PROMO, + ImageType::CONCEPT, + ImageType::WALLPAPER, + ImageType::COVER, + ImageType::SCREENSHOT, + ImageType::BACKGROUND, + ImageType::PREVIEW +]; + +echo "=== Все типы изображений ===\n"; +foreach ($allImageTypes as $type) { + echo "- {$type->value}: {$type->getDisplayName()}\n"; +} + +// Фильтрация основных типов +$mainTypes = array_filter($allImageTypes, fn($type) => $type->isMain()); +echo "\n=== Основные типы ===\n"; +foreach ($mainTypes as $type) { + echo "- {$type->getDisplayName()}\n"; +} + +// Фильтрация промо-материалов +$promoTypes = array_filter($allImageTypes, fn($type) => $type->isPromo()); +echo "\n=== Промо-материалы ===\n"; +foreach ($promoTypes as $type) { + echo "- {$type->getDisplayName()}\n"; +} +``` + +## Получение изображений по типам + +```php +function getImagesByType(Client $client, int $filmId, ImageType $imageType): array { + try { + return $client->films->getImages($filmId, $imageType); + } catch (\Exception $e) { + echo "Ошибка при получении {$imageType->getDisplayName()}: {$e->getMessage()}\n"; + return []; + } +} + +// Получение всех изображений для фильма +$filmId = 301; +$imageTypes = [ImageType::POSTER, ImageType::STILL, ImageType::FAN_ART]; + +echo "=== Изображения фильма ===\n"; +foreach ($imageTypes as $type) { + $images = getImagesByType($client, $filmId, $type); + echo "{$type->getDisplayName()}: " . count($images) . " изображений\n"; + + // Вывод первых 3 изображений + foreach (array_slice($images, 0, 3) as $image) { + echo " - {$image->imageUrl}\n"; + } + echo "\n"; +} +``` + +## Создание галереи изображений + +```php +function createImageGallery(Client $client, int $filmId): array { + $gallery = []; + + // Получение основных типов изображений + $mainTypes = [ImageType::POSTER, ImageType::STILL, ImageType::FAN_ART, ImageType::CONCEPT]; + + foreach ($mainTypes as $type) { + $images = getImagesByType($client, $filmId, $type); + if (!empty($images)) { + $gallery[$type->value] = [ + 'type' => $type->getDisplayName(), + 'count' => count($images), + 'images' => $images + ]; + } + } + + return $gallery; +} + +// Использование +$gallery = createImageGallery($client, 301); + +echo "=== Галерея изображений ===\n"; +foreach ($gallery as $typeKey => $typeData) { + echo "📁 {$typeData['type']} ({$typeData['count']} изображений)\n"; + + foreach (array_slice($typeData['images'], 0, 2) as $image) { + echo " 🖼️ {$image->imageUrl}\n"; + } + echo "\n"; +} +``` + +## Связанные классы + +- [`Image`](../models/image.md) - Модель изображения +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами +- [`Film`](../models/film.md) - Модель фильма diff --git a/docs/dev/notkinopoiskphp/enums/index.md b/docs/dev/notkinopoiskphp/enums/index.md new file mode 100644 index 0000000..14379ff --- /dev/null +++ b/docs/dev/notkinopoiskphp/enums/index.md @@ -0,0 +1,326 @@ +# Перечисления (Enums) + +Перечисления для определения констант и типов в Kinopoisk API. + +--- + +**📚 Навигация:** [Главная](../index.md) → Перечисления + +--- + +## 📋 Категории перечислений + +### 🖼️ Медиа и контент + +- [ImageType](image-type.md) - Типы изображений +- [VideoSite](video-site.md) - Сайты видео +- [ContentType](content-type.md) - Типы контента +- [CollectionType](collection-type.md) - Типы коллекций + +### 📝 Отзывы и факты + +- [ReviewType](review-type.md) - Типы отзывов +- [ReviewOrder](review-order.md) - Порядок сортировки отзывов +- [FactType](fact-type.md) - Типы фактов + +### 👥 Персоны и профессии + +- [ProfessionKey](profession-key.md) - Ключи профессий +- [Sex](sex.md) - Пол +- [RelationType](relation-type.md) - Типы связей + +### 🎬 Фильмы и сериалы + +- [FilmOrder](film-order.md) - Порядок сортировки фильмов +- [DistributionType](distribution-type.md) - Типы дистрибуции +- [DistributionSubType](distribution-sub-type.md) - Подтипы дистрибуции + +### 📊 Статистика и награды + +- [BoxOfficeType](box-office-type.md) - Типы кассовых сборов +- [Month](month.md) - Месяцы + +### 🔧 API и система + +- [ApiVersion](api-version.md) - Версии API +- [AccountType](account-type.md) - Типы аккаунтов + +## 🔗 Связанные компоненты + +### Модели + +- [Film](../models/film.md) - Использует ContentType, FilmOrder +- [Image](../models/image.md) - Использует ImageType +- [Video](../models/video.md) - Использует VideoSite +- [Review](../models/review.md) - Использует ReviewType, ReviewOrder +- [Fact](../models/fact.md) - Использует FactType +- [Staff](../models/staff.md) - Использует ProfessionKey +- [Person](../models/person.md) - Использует Sex +- [PersonSpouse](../models/person-spouse.md) - Использует Sex, RelationType +- [BoxOffice](../models/box-office.md) - Использует BoxOfficeType +- [Distribution](../models/distribution.md) - Использует DistributionType + +### Сервисы + +- [FilmService](../services/film-service.md) - Использует все перечисления фильмов +- [PersonService](../services/person-service.md) - Использует ProfessionKey, Sex +- [MediaService](../services/media-service.md) - Использует ImageType, VideoSite +- [UserService](../services/user-service.md) - Использует ApiVersion, AccountType + +## 🚀 Быстрый старт + +### Использование перечислений + +```php +value}\n"; // "POSTER" +echo "Отображаемое имя: {$posterType->getDisplayName()}\n"; // "Постер" + +// Проверка типов +if ($posterType->isMain()) { + echo "Это основной тип изображения\n"; +} + +// Работа с типами отзывов +$positiveReview = ReviewType::POSITIVE; +$negativeReview = ReviewType::NEGATIVE; + +echo "Тип отзыва: {$positiveReview->getDisplayName()}\n"; // "Положительный" + +// Работа с профессиями +$director = ProfessionKey::DIRECTOR; +$actor = ProfessionKey::ACTOR; + +echo "Профессия: {$director->getDisplayName()}\n"; // "Режиссер" + +if ($director->isCreativeProfession()) { + echo "Это творческая профессия\n"; +} + +// Работа с типами контента +$film = ContentType::FILM; +$series = ContentType::SERIES; + +echo "Тип контента: {$film->getDisplayName()}\n"; // "Фильм" + +if ($film->isFilm()) { + echo "Это фильм\n"; +} + +// Работа с полом +$male = Sex::MALE; +$female = Sex::FEMALE; + +echo "Пол: {$male->getDisplayName()}\n"; // "Мужской" +``` + +## 📊 Статистика перечислений + +### Медиа и контент (4) + +- **ImageType** - 8 типов изображений +- **VideoSite** - 6 сайтов видео +- **ContentType** - 11 типов контента +- **CollectionType** - 4 типа коллекций + +### Отзывы и факты (3) + +- **ReviewType** - 3 типа отзывов +- **ReviewOrder** - 4 порядка сортировки +- **FactType** - 2 типа фактов + +### Персоны и профессии (3) + +- **ProfessionKey** - 15+ ключей профессий +- **Sex** - 3 значения пола +- **RelationType** - 4 типа связей + +### Фильмы и сериалы (3) + +- **FilmOrder** - 8 порядков сортировки +- **DistributionType** - 3 типа дистрибуции +- **DistributionSubType** - 6 подтипов дистрибуции + +### Статистика и награды (2) + +- **BoxOfficeType** - 3 типа кассовых сборов +- **Month** - 12 месяцев + +### API и система (2) + +- **ApiVersion** - 3 версии API +- **AccountType** - 2 типа аккаунтов + +## 🔧 Общие методы + +Большинство перечислений имеют общие методы: + +### getDisplayName() + +```php +public function getDisplayName(): string +``` + +Возвращает человекочитаемое название значения. + +### from() + +```php +public static function from(string|int $value): self +``` + +Создает экземпляр перечисления из значения. + +### values() + +```php +public static function values(): array +``` + +Возвращает все возможные значения перечисления. + +## 📖 Примеры использования + +### Работа с типами изображений + +```php +use NotKinopoisk\Enums\ImageType; + +// Получение всех типов изображений +$allTypes = ImageType::cases(); + +foreach ($allTypes as $type) { + echo "{$type->value}: {$type->getDisplayName()}\n"; +} + +// Фильтрация основных типов +$mainTypes = array_filter($allTypes, fn($type) => $type->isMain()); + +foreach ($mainTypes as $type) { + echo "Основной тип: {$type->getDisplayName()}\n"; +} + +// Создание из строки +$type = ImageType::from('POSTER'); +echo "Тип: {$type->getDisplayName()}\n"; +``` + +### Работа с профессиями + +```php +use NotKinopoisk\Enums\ProfessionKey; + +// Получение всех профессий +$allProfessions = ProfessionKey::cases(); + +// Группировка по категориям +$creativeProfessions = array_filter($allProfessions, fn($p) => $p->isCreativeProfession()); +$technicalProfessions = array_filter($allProfessions, fn($p) => $p->isTechnicalProfession()); + +echo "Творческие профессии:\n"; +foreach ($creativeProfessions as $profession) { + echo "- {$profession->getDisplayName()}\n"; +} + +echo "Технические профессии:\n"; +foreach ($technicalProfessions as $profession) { + echo "- {$profession->getDisplayName()}\n"; +} + +// Проверка конкретных профессий +$director = ProfessionKey::DIRECTOR; +$actor = ProfessionKey::ACTOR; + +if ($director->isCreativeProfession()) { + echo "Режиссер - творческая профессия\n"; +} + +if ($actor->isCreativeProfession()) { + echo "Актер - творческая профессия\n"; +} +``` + +### Работа с типами контента + +```php +use NotKinopoisk\Enums\ContentType; + +// Получение всех типов контента +$allTypes = ContentType::cases(); + +// Фильтрация фильмов +$films = array_filter($allTypes, fn($type) => $type->isFilm()); + +foreach ($films as $type) { + echo "Фильм: {$type->getDisplayName()}\n"; +} + +// Фильтрация сериалов +$series = array_filter($allTypes, fn($type) => $type->isSeries()); + +foreach ($series as $type) { + echo "Сериал: {$type->getDisplayName()}\n"; +} + +// Создание из строки +$contentType = ContentType::from('FILM'); +echo "Тип контента: {$contentType->getDisplayName()}\n"; +``` + +### Работа с порядками сортировки + +```php +use NotKinopoisk\Enums\FilmOrder; +use NotKinopoisk\Enums\ReviewOrder; + +// Получение всех порядков сортировки фильмов +$filmOrders = FilmOrder::cases(); + +foreach ($filmOrders as $order) { + echo "{$order->getDisplayName()}: {$order->value}\n"; +} + +// Получение всех порядков сортировки отзывов +$reviewOrders = ReviewOrder::cases(); + +foreach ($reviewOrders as $order) { + echo "{$order->getDisplayName()}: {$order->value}\n"; + + if ($order->isDateSort()) { + echo " - Сортировка по дате\n"; + } + + if ($order->isPositiveRatingSort()) { + echo " - Сортировка по положительным оценкам\n"; + } + + if ($order->isNegativeRatingSort()) { + echo " - Сортировка по отрицательным оценкам\n"; + } +} +``` + +## 🔗 Связанные разделы + +- [Модели](../models/index.md) - Используют перечисления +- [Сервисы](../services/index.md) - Работают с перечислениями +- [Ответы](../responses/index.md) - Могут содержать перечисления +- [Исключения](../exceptions/index.md) - Обработка ошибок +- [Интерфейсы](../interfaces/index.md) - Базовые интерфейсы + +--- + +**📚 Навигация:** [Главная](../index.md) → Перечисления diff --git a/docs/dev/notkinopoiskphp/enums/month.md b/docs/dev/notkinopoiskphp/enums/month.md new file mode 100644 index 0000000..82e432b --- /dev/null +++ b/docs/dev/notkinopoiskphp/enums/month.md @@ -0,0 +1,705 @@ +# Month + +Enum месяцев года для API запросов. + +## Описание + +`Month` представляет месяцы года, используемые в различных API запросах Kinopoisk API, например для получения премьер фильмов. + +**API Endpoint:** `/api/v2.2/films/premieres` + +## Значения enum + +### Месяцы года + +- `JANUARY` - Январь +- `FEBRUARY` - Февраль +- `MARCH` - Март +- `APRIL` - Апрель +- `MAY` - Май +- `JUNE` - Июнь +- `JULY` - Июль +- `AUGUST` - Август +- `SEPTEMBER` - Сентябрь +- `OCTOBER` - Октябрь +- `NOVEMBER` - Ноябрь +- `DECEMBER` - Декабрь + +## Методы + +### getDisplayName() + +Получает отображаемое название месяца на русском языке. + +```php +public function getDisplayName(): string +``` + +#### Возвращаемое значение + +- `string` - Отображаемое название месяца + +#### Пример использования + +```php +echo Month::JANUARY->getDisplayName(); // "Январь" +echo Month::DECEMBER->getDisplayName(); // "Декабрь" +``` + +### getShortName() + +Получает краткое название месяца (3 буквы). + +```php +public function getShortName(): string +``` + +#### Возвращаемое значение + +- `string` - Краткое название месяца + +#### Пример использования + +```php +echo Month::JANUARY->getShortName(); // "Янв" +echo Month::DECEMBER->getShortName(); // "Дек" +``` + +### getNumber() + +Получает номер месяца (1-12). + +```php +public function getNumber(): int +``` + +#### Возвращаемое значение + +- `int` - Номер месяца (1-12) + +#### Пример использования + +```php +echo Month::JANUARY->getNumber(); // 1 +echo Month::DECEMBER->getNumber(); // 12 +``` + +### isWinter() + +Проверяет, является ли месяц зимним. + +```php +public function isWinter(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если зимний месяц, `false` в противном случае + +#### Описание + +К зимним месяцам относятся декабрь, январь, февраль. + +#### Пример использования + +```php +if (Month::DECEMBER->isWinter()) { + echo "Зимний месяц"; +} +``` + +### isSpring() + +Проверяет, является ли месяц весенним. + +```php +public function isSpring(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если весенний месяц, `false` в противном случае + +#### Описание + +К весенним месяцам относятся март, апрель, май. + +#### Пример использования + +```php +if (Month::MARCH->isSpring()) { + echo "Весенний месяц"; +} +``` + +### isSummer() + +Проверяет, является ли месяц летним. + +```php +public function isSummer(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если летний месяц, `false` в противном случае + +#### Описание + +К летним месяцам относятся июнь, июль, август. + +#### Пример использования + +```php +if (Month::JULY->isSummer()) { + echo "Летний месяц"; +} +``` + +### isAutumn() + +Проверяет, является ли месяц осенним. + +```php +public function isAutumn(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если осенний месяц, `false` в противном случае + +#### Описание + +К осенним месяцам относятся сентябрь, октябрь, ноябрь. + +#### Пример использования + +```php +if (Month::SEPTEMBER->isAutumn()) { + echo "Осенний месяц"; +} +``` + +### getSeason() + +Получает сезон месяца. + +```php +public function getSeason(): string +``` + +#### Возвращаемое значение + +- `string` - Название сезона + +#### Пример использования + +```php +echo Month::JANUARY->getSeason(); // "Зима" +echo Month::JULY->getSeason(); // "Лето" +``` + +### isQuarterStart() + +Проверяет, является ли месяц первым в квартале. + +```php +public function isQuarterStart(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если первый месяц квартала, `false` в противном случае + +#### Пример использования + +```php +if (Month::JANUARY->isQuarterStart()) { + echo "Начало квартала"; +} +``` + +### getQuarter() + +Получает номер квартала (1-4). + +```php +public function getQuarter(): int +``` + +#### Возвращаемое значение + +- `int` - Номер квартала (1-4) + +#### Пример использования + +```php +echo Month::JANUARY->getQuarter(); // 1 +echo Month::OCTOBER->getQuarter(); // 4 +``` + +### isYearEnd() + +Проверяет, является ли месяц последним в году. + +```php +public function isYearEnd(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если декабрь, `false` в противном случае + +#### Пример использования + +```php +if (Month::DECEMBER->isYearEnd()) { + echo "Конец года"; +} +``` + +### isYearStart() + +Проверяет, является ли месяц первым в году. + +```php +public function isYearStart(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если январь, `false` в противном случае + +#### Пример использования + +```php +if (Month::JANUARY->isYearStart()) { + echo "Начало года"; +} +``` + +## Полный пример использования + +```php +films; +$premieres = $filmService->getPremieres(2024, Month::JANUARY); + +echo "=== Премьеры за {$premieres->month->getDisplayName()} 2024 ===\n"; + +// Анализ премьер по месяцам +foreach ($premieres->items as $premiere) { + $month = Month::from($premiere->month); + echo "Фильм: {$premiere->nameRu}\n"; + echo "Месяц: {$month->getDisplayName()} ({$month->getShortName()})\n"; + echo "Сезон: {$month->getSeason()}\n"; + echo "Квартал: {$month->getQuarter()}\n\n"; +} +``` + +## Работа с месяцами + +```php +// Функция для получения всех месяцев +function getAllMonths(): array { + return [ + Month::JANUARY, Month::FEBRUARY, Month::MARCH, Month::APRIL, + Month::MAY, Month::JUNE, Month::JULY, Month::AUGUST, + Month::SEPTEMBER, Month::OCTOBER, Month::NOVEMBER, Month::DECEMBER + ]; +} + +// Функция для получения месяцев по сезону +function getMonthsBySeason(string $season): array { + return array_filter(getAllMonths(), function($month) use ($season) { + return $month->getSeason() === $season; + }); +} + +// Функция для получения месяцев по кварталу +function getMonthsByQuarter(int $quarter): array { + return array_filter(getAllMonths(), function($month) use ($quarter) { + return $month->getQuarter() === $quarter; + }); +} + +// Функция для получения следующего месяца +function getNextMonth(Month $currentMonth): Month { + $months = getAllMonths(); + $currentIndex = array_search($currentMonth, $months); + $nextIndex = ($currentIndex + 1) % 12; + return $months[$nextIndex]; +} + +// Функция для получения предыдущего месяца +function getPreviousMonth(Month $currentMonth): Month { + $months = getAllMonths(); + $currentIndex = array_search($currentMonth, $months); + $previousIndex = ($currentIndex - 1 + 12) % 12; + return $months[$previousIndex]; +} + +// Функция для получения месяцев в диапазоне +function getMonthsInRange(Month $startMonth, Month $endMonth): array { + $months = getAllMonths(); + $startIndex = array_search($startMonth, $months); + $endIndex = array_search($endMonth, $months); + + if ($startIndex <= $endIndex) { + return array_slice($months, $startIndex, $endIndex - $startIndex + 1); + } else { + // Переход через год + $firstPart = array_slice($months, $startIndex); + $secondPart = array_slice($months, 0, $endIndex + 1); + return array_merge($firstPart, $secondPart); + } +} + +// Использование +$allMonths = getAllMonths(); +echo "Всего месяцев: " . count($allMonths) . "\n"; + +// Месяцы по сезонам +$winterMonths = getMonthsBySeason('Зима'); +echo "Зимние месяцы: " . implode(', ', array_map(fn($m) => $m->getDisplayName(), $winterMonths)) . "\n"; + +$summerMonths = getMonthsBySeason('Лето'); +echo "Летние месяцы: " . implode(', ', array_map(fn($m) => $m->getDisplayName(), $summerMonths)) . "\n"; + +// Месяцы по кварталам +$firstQuarter = getMonthsByQuarter(1); +echo "Первый квартал: " . implode(', ', array_map(fn($m) => $m->getDisplayName(), $firstQuarter)) . "\n"; + +// Следующий и предыдущий месяц +$currentMonth = Month::JUNE; +$nextMonth = getNextMonth($currentMonth); +$previousMonth = getPreviousMonth($currentMonth); + +echo "Текущий: {$currentMonth->getDisplayName()}\n"; +echo "Следующий: {$nextMonth->getDisplayName()}\n"; +echo "Предыдущий: {$previousMonth->getDisplayName()}\n"; + +// Диапазон месяцев +$rangeMonths = getMonthsInRange(Month::NOVEMBER, Month::FEBRUARY); +echo "Диапазон (ноябрь-февраль): " . implode(', ', array_map(fn($m) => $m->getDisplayName(), $rangeMonths)) . "\n"; +``` + +## Создание календаря премьер + +```php +class PremieresCalendar { + private array $premieres; + + public function __construct(array $premieres) { + $this->premieres = $premieres; + } + + public function getPremieresByMonth(Month $month): array { + return array_filter($this->premieres, function($premiere) use ($month) { + return Month::from($premiere->month) === $month; + }); + } + + public function getPremieresBySeason(string $season): array { + $seasonMonths = getMonthsBySeason($season); + $seasonPremieres = []; + + foreach ($seasonMonths as $month) { + $monthPremieres = $this->getPremieresByMonth($month); + $seasonPremieres = array_merge($seasonPremieres, $monthPremieres); + } + + return $seasonPremieres; + } + + public function getPremieresByQuarter(int $quarter): array { + $quarterMonths = getMonthsByQuarter($quarter); + $quarterPremieres = []; + + foreach ($quarterMonths as $month) { + $monthPremieres = $this->getPremieresByMonth($month); + $quarterPremieres = array_merge($quarterPremieres, $monthPremieres); + } + + return $quarterPremieres; + } + + public function getMonthlyStatistics(): array { + $stats = []; + + foreach (getAllMonths() as $month) { + $monthPremieres = $this->getPremieresByMonth($month); + $stats[$month->value] = [ + 'month' => $month, + 'count' => count($monthPremieres), + 'season' => $month->getSeason(), + 'quarter' => $month->getQuarter() + ]; + } + + return $stats; + } + + public function createDetailedReport(): string { + $report = "=== КАЛЕНДАРЬ ПРЕМЬЕР ===\n\n"; + + $stats = $this->getMonthlyStatistics(); + + // Общая статистика + $totalPremieres = array_sum(array_column($stats, 'count')); + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего премьер: {$totalPremieres}\n"; + $report .= "Среднее количество в месяц: " . round($totalPremieres / 12, 1) . "\n\n"; + + // Статистика по сезонам + $seasonStats = []; + foreach (['Зима', 'Весна', 'Лето', 'Осень'] as $season) { + $seasonPremieres = $this->getPremieresBySeason($season); + $seasonStats[$season] = count($seasonPremieres); + } + + $report .= "🌍 СТАТИСТИКА ПО СЕЗОНАМ:\n"; + foreach ($seasonStats as $season => $count) { + $percentage = round(($count / $totalPremieres) * 100, 1); + $report .= "• {$season}: {$count} премьер ({$percentage}%)\n"; + } + + $report .= "\n"; + + // Статистика по кварталам + $report .= "📅 СТАТИСТИКА ПО КВАРТАЛАМ:\n"; + for ($quarter = 1; $quarter <= 4; $quarter++) { + $quarterPremieres = $this->getPremieresByQuarter($quarter); + $percentage = round((count($quarterPremieres) / $totalPremieres) * 100, 1); + $report .= "• Квартал {$quarter}: " . count($quarterPremieres) . " премьер ({$percentage}%)\n"; + } + + $report .= "\n"; + + // Детали по месяцам + $report .= "📋 ДЕТАЛИ ПО МЕСЯЦАМ:\n"; + foreach ($stats as $monthKey => $monthStats) { + $month = $monthStats['month']; + $report .= "\n{$month->getDisplayName()}:\n"; + $report .= " Количество премьер: {$monthStats['count']}\n"; + $report .= " Сезон: {$monthStats['season']}\n"; + $report .= " Квартал: {$monthStats['quarter']}\n"; + + if ($month->isQuarterStart()) { + $report .= " 🎯 Начало квартала\n"; + } + + if ($month->isYearStart()) { + $report .= " 🎆 Начало года\n"; + } + + if ($month->isYearEnd()) { + $report .= " 🎄 Конец года\n"; + } + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $stats = $this->getMonthlyStatistics(); + $totalPremieres = array_sum(array_column($stats, 'count')); + + // Общая статистика + $html .= "
\n"; + $html .= "

Общая статистика

\n"; + $html .= "

Всего премьер: {$totalPremieres}

\n"; + $html .= "

Среднее количество в месяц: " . round($totalPremieres / 12, 1) . "

\n"; + $html .= "
\n"; + + // Детали по месяцам + $html .= "
\n"; + $html .= "
Детали по месяцам
\n"; + + foreach ($stats as $monthKey => $monthStats) { + $month = $monthStats['month']; + $season = strtolower($monthStats['season']); + $cssClass = "season-{$season}"; + + if ($month->isQuarterStart() || $month->isYearStart() || $month->isYearEnd()) { + $cssClass .= " highlight"; + } + + $html .= "
\n"; + $html .= "
{$month->getDisplayName()}
\n"; + $html .= "
\n"; + $html .= "
Количество премьер: {$monthStats['count']}
\n"; + $html .= "
Сезон: {$monthStats['season']}
\n"; + $html .= "
Квартал: {$monthStats['quarter']}
\n"; + + if ($month->isQuarterStart()) { + $html .= "
🎯 Начало квартала
\n"; + } + + if ($month->isYearStart()) { + $html .= "
🎆 Начало года
\n"; + } + + if ($month->isYearEnd()) { + $html .= "
🎄 Конец года
\n"; + } + + $html .= "
\n
\n"; + } + + $html .= "
\n
\n\n"; + + return $html; + } +} + +// Использование +$premieres = $filmService->getPremieres(2024, Month::JANUARY); +$calendar = new PremieresCalendar($premieres->items); + +// Создание текстового отчета +$textReport = $calendar->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $calendar->createHtmlReport('Календарь премьер 2024'); +file_put_contents('premieres_calendar.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в premieres_calendar.html\n"; +``` + +## Анализ сезонности + +```php +function analyzeSeasonality(array $premieres): array { + $analysis = [ + 'totalPremieres' => count($premieres), + 'seasonalDistribution' => [], + 'quarterlyDistribution' => [], + 'monthlyDistribution' => [], + 'peakMonth' => null, + 'lowestMonth' => null, + 'seasonalTrends' => [] + ]; + + $monthlyCounts = []; + $seasonalCounts = []; + $quarterlyCounts = []; + + foreach ($premieres as $premiere) { + $month = Month::from($premiere->month); + $season = $month->getSeason(); + $quarter = $month->getQuarter(); + + // Подсчет по месяцам + $monthKey = $month->value; + if (!isset($monthlyCounts[$monthKey])) { + $monthlyCounts[$monthKey] = 0; + } + $monthlyCounts[$monthKey]++; + + // Подсчет по сезонам + if (!isset($seasonalCounts[$season])) { + $seasonalCounts[$season] = 0; + } + $seasonalCounts[$season]++; + + // Подсчет по кварталам + if (!isset($quarterlyCounts[$quarter])) { + $quarterlyCounts[$quarter] = 0; + } + $quarterlyCounts[$quarter]++; + } + + // Анализ распределения + $analysis['monthlyDistribution'] = $monthlyCounts; + $analysis['seasonalDistribution'] = $seasonalCounts; + $analysis['quarterlyDistribution'] = $quarterlyCounts; + + // Пиковый и минимальный месяцы + if (!empty($monthlyCounts)) { + $maxCount = max($monthlyCounts); + $minCount = min($monthlyCounts); + + foreach ($monthlyCounts as $monthKey => $count) { + if ($count === $maxCount) { + $analysis['peakMonth'] = Month::from($monthKey); + } + if ($count === $minCount) { + $analysis['lowestMonth'] = Month::from($monthKey); + } + } + } + + // Сезонные тренды + $analysis['seasonalTrends'] = [ + 'winter' => $seasonalCounts['Зима'] ?? 0, + 'spring' => $seasonalCounts['Весна'] ?? 0, + 'summer' => $seasonalCounts['Лето'] ?? 0, + 'autumn' => $seasonalCounts['Осень'] ?? 0 + ]; + + return $analysis; +} + +// Использование +$premieres = $filmService->getPremieres(2024, Month::JANUARY); +$analysis = analyzeSeasonality($premieres->items); + +echo "=== Анализ сезонности премьер ===\n"; +echo "Всего премьер: {$analysis['totalPremieres']}\n"; + +echo "\nРаспределение по сезонам:\n"; +foreach ($analysis['seasonalDistribution'] as $season => $count) { + $percentage = round(($count / $analysis['totalPremieres']) * 100, 1); + echo "- {$season}: {$count} премьер ({$percentage}%)\n"; +} + +echo "\nРаспределение по кварталам:\n"; +for ($quarter = 1; $quarter <= 4; $quarter++) { + $count = $analysis['quarterlyDistribution'][$quarter] ?? 0; + $percentage = round(($count / $analysis['totalPremieres']) * 100, 1); + echo "- Квартал {$quarter}: {$count} премьер ({$percentage}%)\n"; +} + +if ($analysis['peakMonth']) { + echo "\nПиковый месяц: {$analysis['peakMonth']->getDisplayName()} (" . $analysis['monthlyDistribution'][$analysis['peakMonth']->value] . " премьер)\n"; +} + +if ($analysis['lowestMonth']) { + echo "Минимальный месяц: {$analysis['lowestMonth']->getDisplayName()} (" . $analysis['monthlyDistribution'][$analysis['lowestMonth']->value] . " премьер)\n"; +} +``` + +## Связанные классы + +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/enums/profession-key.md b/docs/dev/notkinopoiskphp/enums/profession-key.md new file mode 100644 index 0000000..faa8516 --- /dev/null +++ b/docs/dev/notkinopoiskphp/enums/profession-key.md @@ -0,0 +1,620 @@ +# ProfessionKey + +Enum профессий персоны в фильме. + +## Описание + +`ProfessionKey` представляет различные профессии, которые может иметь персона в фильме согласно Kinopoisk API. + +**API Endpoint:** `/api/v1/persons/{id}` + +## Значения enum + +### Основные профессии + +- `ACTOR` - Актер +- `DIRECTOR` - Режиссер +- `WRITER` - Сценарист +- `PRODUCER` - Продюсер +- `PRODUCER_USSR` - Продюсер (СССР) +- `COMPOSER` - Композитор +- `OPERATOR` - Оператор +- `EDITOR` - Монтажер +- `DESIGN` - Художник +- `TRANSLATOR` - Переводчик +- `VOICE_DIRECTOR` - Режиссер дубляжа + +### Специальные роли + +- `HIMSELF` - В роли самого себя +- `HERSELF` - В роли самой себя +- `HRONO_TITR_MALE` - За кадром (мужской голос) +- `HRONO_TITR_FEMALE` - За кадром (женский голос) +- `UNKNOWN` - Неизвестно + +## Методы + +### isActor() + +Проверяет, является ли профессия актерской. + +```php +public function isActor(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если актер, `false` в противном случае + +#### Пример использования + +```php +if (ProfessionKey::ACTOR->isActor()) { + echo "Актерская роль"; +} +``` + +### isDirector() + +Проверяет, является ли профессия режиссерской. + +```php +public function isDirector(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если режиссер, `false` в противном случае + +#### Пример использования + +```php +if (ProfessionKey::DIRECTOR->isDirector()) { + echo "Режиссерская работа"; +} +``` + +### isWriter() + +Проверяет, является ли профессия сценарной. + +```php +public function isWriter(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если сценарист, `false` в противном случае + +#### Пример использования + +```php +if (ProfessionKey::WRITER->isWriter()) { + echo "Сценарная работа"; +} +``` + +### isVoiceDirector() + +Проверяет, является ли роль режиссёром озвучивания. + +```php +public function isVoiceDirector(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если режиссёр озвучивания, `false` в противном случае + +#### Описание + +Определяет, соответствует ли текущий экземпляр enum константе VOICE_DIRECTOR. Используется для фильтрации персонала по специфической роли в озвучивании фильмов и сериалов. + +#### Пример использования + +```php +if ($role->isVoiceDirector()) { + echo 'Это режиссёр озвучивания'; +} +``` + +### isComposer() + +Проверяет, является ли роль композитором. + +```php +public function isComposer(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если композитор, `false` в противном случае + +#### Описание + +Определяет, соответствует ли текущий экземпляр enum константе COMPOSER. Используется для идентификации людей, ответственных за музыкальное сопровождение фильма или сериала. + +#### Пример использования + +```php +if ($role->isComposer()) { + echo 'Этот человек создал музыку к фильму'; +} +``` + +### isEditor() + +Проверяет, является ли роль редактором. + +```php +public function isEditor(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если редактор, `false` в противном случае + +#### Описание + +Определяет, соответствует ли текущий экземпляр enum константе EDITOR. Используется для определения людей, занимавшихся монтажом и редактированием материала фильма или сериала. + +#### Пример использования + +```php +if ($role->isEditor()) { + echo 'Этот человек занимался монтажом'; +} +``` + +### isDesigner() + +Проверяет, является ли роль дизайнером. + +```php +public function isDesigner(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если дизайнер, `false` в противном случае + +#### Описание + +Определяет, соответствует ли текущий экземпляр enum константе DESIGN. Используется для идентификации специалистов по визуальному оформлению, художников-постановщиков и дизайнеров костюмов. + +#### Пример использования + +```php +if ($role->isDesigner()) { + echo 'Этот человек работал над визуальным оформлением'; +} +``` + +### isTranslator() + +Проверяет, является ли роль переводчиком. + +```php +public function isTranslator(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если переводчик, `false` в противном случае + +#### Описание + +Определяет, соответствует ли текущий экземпляр enum константе TRANSLATOR. Используется для определения людей, ответственных за перевод диалогов и локализацию контента на другие языки. + +#### Пример использования + +```php +if ($role->isTranslator()) { + echo 'Этот человек занимался переводом'; +} +``` + +### isProducer() + +Проверяет, является ли профессия продюсерской. + +```php +public function isProducer(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если продюсер, `false` в противном случае + +#### Пример использования + +```php +if (ProfessionKey::PRODUCER->isProducer()) { + echo "Продюсерская работа"; +} +``` + +### getShortName() + +Получает краткое название профессии. + +```php +public function getShortName(): string +``` + +#### Возвращаемое значение + +- `string` - Краткое название профессии + +#### Описание + +Возвращает краткое название без дополнительных пояснений. + +#### Пример использования + +```php +echo ProfessionKey::HRONO_TITR_MALE->getShortName(); // "За кадром" +echo ProfessionKey::HIMSELF->getShortName(); // "В роли самого себя" +``` + +### getDisplayName() + +Получает отображаемое название профессии на русском языке. + +```php +public function getDisplayName(): string +``` + +#### Возвращаемое значение + +- `string` - Отображаемое название профессии + +#### Описание + +Возвращает человекочитаемое название профессии для отображения пользователю. + +#### Пример использования + +```php +echo ProfessionKey::ACTOR->getDisplayName(); // "Актер" +echo ProfessionKey::DIRECTOR->getDisplayName(); // "Режиссер" +``` + +### getCategory() + +Получает категорию профессии. + +```php +public function getCategory(): string +``` + +#### Возвращаемое значение + +- `string` - Категория профессии + +#### Описание + +Возвращает категорию, к которой относится профессия. + +#### Пример использования + +```php +echo ProfessionKey::ACTOR->getCategory(); // "Творческая" +echo ProfessionKey::PRODUCER->getCategory(); // "Управленческая" +``` + +### isCreative() + +Проверяет, является ли профессия творческой. + +```php +public function isCreative(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если творческая профессия, `false` в противном случае + +#### Описание + +К творческим профессиям относятся актер, режиссер, сценарист, композитор. + +#### Пример использования + +```php +if (ProfessionKey::ACTOR->isCreative()) { + echo "Творческая профессия"; +} +``` + +### isManagement() + +Проверяет, является ли профессия управленческой. + +```php +public function isManagement(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если управленческая профессия, `false` в противном случае + +#### Описание + +К управленческим профессиям относятся продюсеры. + +#### Пример использования + +```php +if (ProfessionKey::PRODUCER->isManagement()) { + echo "Управленческая профессия"; +} +``` + +### isTechnical() + +Проверяет, является ли профессия технической. + +```php +public function isTechnical(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если техническая профессия, `false` в противном случае + +#### Описание + +К техническим профессиям относятся оператор, монтажер, художник. + +#### Пример использования + +```php +if (ProfessionKey::OPERATOR->isTechnical()) { + echo "Техническая профессия"; +} +``` + +### isSpecial() + +Проверяет, является ли профессия специальной. + +```php +public function isSpecial(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если специальная профессия, `false` в противном случае + +#### Описание + +К специальным профессиям относятся роли самого себя, за кадром, переводчик. + +#### Пример использования + +```php +if (ProfessionKey::HIMSELF->isSpecial()) { + echo "Специальная роль"; +} +``` + +## Полный пример использования + +```php +persons; +$staff = $personService->getFilmStaff(301); // Матрица + +echo "=== Анализ персонала фильма 'Матрица' ===\n"; + +// Группировка по категориям +$categories = [ + 'Творческая' => [], + 'Управленческая' => [], + 'Техническая' => [], + 'Специальная' => [] +]; + +foreach ($staff as $member) { + $category = $member->professionKey->getCategory(); + if (isset($categories[$category])) { + $categories[$category][] = $member; + } +} + +// Вывод статистики по категориям +foreach ($categories as $category => $members) { + if (!empty($members)) { + echo "\n📁 {$category} (" . count($members) . " человек):\n"; + + // Группировка по профессиям внутри категории + $professions = []; + foreach ($members as $member) { + $profession = $member->professionKey->getDisplayName(); + if (!isset($professions[$profession])) { + $professions[$profession] = 0; + } + $professions[$profession]++; + } + + foreach ($professions as $profession => $count) { + echo " - {$profession}: {$count}\n"; + } + } +} + +// Поиск конкретных профессий +echo "\n🔍 Поиск конкретных профессий:\n"; + +$actors = array_filter($staff, fn($member) => $member->professionKey->isActor()); +$directors = array_filter($staff, fn($member) => $member->professionKey->isDirector()); +$writers = array_filter($staff, fn($member) => $member->professionKey->isWriter()); +$composers = array_filter($staff, fn($member) => $member->professionKey->isComposer()); + +echo "Актеров: " . count($actors) . "\n"; +echo "Режиссеров: " . count($directors) . "\n"; +echo "Сценаристов: " . count($writers) . "\n"; +echo "Композиторов: " . count($composers) . "\n"; +``` + +## Работа с профессиями + +```php +// Функция для получения персонала по профессии +function getStaffByProfession(array $staff, ProfessionKey $profession): array { + return array_filter($staff, fn($member) => $member->professionKey === $profession); +} + +// Функция для получения персонала по категории +function getStaffByCategory(array $staff, string $category): array { + return array_filter($staff, function($member) use ($category) { + return $member->professionKey->getCategory() === $category; + }); +} + +// Функция для анализа профессий +function analyzeProfessions(array $staff): array { + $analysis = [ + 'total' => count($staff), + 'categories' => [], + 'professions' => [], + 'creative' => 0, + 'management' => 0, + 'technical' => 0, + 'special' => 0 + ]; + + foreach ($staff as $member) { + $profession = $member->professionKey; + + // Подсчет по категориям + $category = $profession->getCategory(); + if (!isset($analysis['categories'][$category])) { + $analysis['categories'][$category] = 0; + } + $analysis['categories'][$category]++; + + // Подсчет по профессиям + $professionName = $profession->getDisplayName(); + if (!isset($analysis['professions'][$professionName])) { + $analysis['professions'][$professionName] = 0; + } + $analysis['professions'][$professionName]++; + + // Подсчет по типам + if ($profession->isCreative()) $analysis['creative']++; + if ($profession->isManagement()) $analysis['management']++; + if ($profession->isTechnical()) $analysis['technical']++; + if ($profession->isSpecial()) $analysis['special']++; + } + + return $analysis; +} + +// Использование +$staff = $personService->getFilmStaff(301); +$analysis = analyzeProfessions($staff); + +echo "=== Анализ профессий ===\n"; +echo "Всего участников: {$analysis['total']}\n"; +echo "Творческих: {$analysis['creative']}\n"; +echo "Управленческих: {$analysis['management']}\n"; +echo "Технических: {$analysis['technical']}\n"; +echo "Специальных: {$analysis['special']}\n"; + +echo "\nПо категориям:\n"; +foreach ($analysis['categories'] as $category => $count) { + echo "- {$category}: {$count}\n"; +} +``` + +## Создание отчета о персонале + +```php +function createStaffReport(array $staff): string { + $report = "=== ОТЧЕТ О ПЕРСОНАЛЕ ФИЛЬМА ===\n\n"; + + // Основные творческие роли + $mainRoles = [ + ProfessionKey::DIRECTOR => 'Режиссер', + ProfessionKey::WRITER => 'Сценарист', + ProfessionKey::COMPOSER => 'Композитор' + ]; + + foreach ($mainRoles as $profession => $title) { + $members = getStaffByProfession($staff, $profession); + if (!empty($members)) { + $report .= "🎬 {$title}:\n"; + foreach ($members as $member) { + $report .= " - {$member->getDisplayName()}\n"; + } + $report .= "\n"; + } + } + + // Актеры + $actors = getStaffByProfession($staff, ProfessionKey::ACTOR); + if (!empty($actors)) { + $report .= "🎭 Актеры (" . count($actors) . "):\n"; + foreach (array_slice($actors, 0, 10) as $actor) { + $report .= " - {$actor->getDisplayName()}"; + if ($actor->description) { + $report .= " ({$actor->description})"; + } + $report .= "\n"; + } + if (count($actors) > 10) { + $report .= " ... и еще " . (count($actors) - 10) . " актеров\n"; + } + $report .= "\n"; + } + + // Технический персонал + $technical = getStaffByCategory($staff, 'Техническая'); + if (!empty($technical)) { + $report .= "🔧 Технический персонал (" . count($technical) . "):\n"; + $techProfessions = []; + foreach ($technical as $member) { + $profession = $member->professionKey->getDisplayName(); + if (!isset($techProfessions[$profession])) { + $techProfessions[$profession] = []; + } + $techProfessions[$profession][] = $member; + } + + foreach ($techProfessions as $profession => $members) { + $report .= " {$profession} (" . count($members) . "):\n"; + foreach (array_slice($members, 0, 3) as $member) { + $report .= " - {$member->getDisplayName()}\n"; + } + if (count($members) > 3) { + $report .= " ... и еще " . (count($members) - 3) . "\n"; + } + } + } + + return $report; +} + +// Использование +$staff = $personService->getFilmStaff(301); +$report = createStaffReport($staff); +echo $report; +``` + +## Связанные классы + +- [`Staff`](../models/staff.md) - Модель персонала +- [`PersonFilm`](../models/person-film.md) - Фильм в фильмографии персоны +- [`PersonService`](../services/person-service.md) - Сервис для работы с персонами diff --git a/docs/dev/notkinopoiskphp/enums/relation-type.md b/docs/dev/notkinopoiskphp/enums/relation-type.md new file mode 100644 index 0000000..50e8774 --- /dev/null +++ b/docs/dev/notkinopoiskphp/enums/relation-type.md @@ -0,0 +1,422 @@ +# RelationType + +Типы связи между фильмами в Kinopoisk API. + +## Описание + +`RelationType` определяет различные типы связей между фильмами в API Кинопоиска. + +## Значения enum + +### Типы связи + +- `SIMILAR` - Похожий фильм (фильм, который похож по жанру, стилю или содержанию) +- `SEQUEL` - Сиквел (продолжение фильма, действие которого происходит после событий оригинала) +- `PREQUEL` - Приквел (фильм, действие которого происходит до событий оригинала) +- `REMAKE` - Ремейк (новая версия существующего фильма) +- `UNKNOWN` - Неизвестный тип связи (используется для случаев, когда тип связи не определен) + +## Методы + +### getDescription() + +Получает описание типа связи. + +```php +public function getDescription(): string +``` + +#### Возвращаемое значение + +- `string` - Описание типа связи на русском языке + +#### Пример использования + +```php +$description = RelationType::SIMILAR->getDescription(); +echo $description; // "Похожий фильм" +``` + +### isKnown() + +Проверяет, является ли тип связи известным. + +```php +public function isKnown(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true`, если тип связи известен + +#### Пример использования + +```php +if (RelationType::SIMILAR->isKnown()) { + echo "Это известный тип связи"; +} +``` + +## Полный пример использования + +```php +films; +$relatedFilms = $filmService->getSimilarFilms(301); // ID фильма "Матрица" + +echo "=== Работа с типами связи между фильмами ===\n"; + +// Вывод всех типов связи +echo "Доступные типы связи:\n"; +foreach ([RelationType::SIMILAR, RelationType::SEQUEL, RelationType::PREQUEL, RelationType::REMAKE, RelationType::UNKNOWN] as $type) { + $description = $type->getDescription(); + $isKnown = $type->isKnown() ? 'Да' : 'Нет'; + echo "• {$type->value}: {$description} (Известный: {$isKnown})\n"; +} + +echo "\n"; + +// Группировка связанных фильмов по типу связи +$groupedByType = []; +foreach ($relatedFilms as $film) { + $type = $film->relationType; + if (!isset($groupedByType[$type->value])) { + $groupedByType[$type->value] = []; + } + $groupedByType[$type->value][] = $film; +} + +// Вывод фильмов по типам связи +foreach ($groupedByType as $typeValue => $films) { + $type = RelationType::from($typeValue); + $description = $type->getDescription(); + echo "📋 {$description} (" . count($films) . " фильмов):\n"; + foreach ($films as $film) { + echo " • {$film->getDisplayName()}\n"; + } + echo "\n"; +} +``` + +## Работа с типами связи + +```php +// Функция для получения всех известных типов связи +function getKnownRelationTypes(): array { + return [ + RelationType::SIMILAR, + RelationType::SEQUEL, + RelationType::PREQUEL, + RelationType::REMAKE + ]; +} + +// Функция для получения отображаемого названия типа связи +function getRelationTypeDisplayName(RelationType $type): string { + return $type->getDescription(); +} + +// Функция для получения иконки типа связи +function getRelationTypeIcon(RelationType $type): string { + return match ($type) { + RelationType::SIMILAR => '🔍', + RelationType::SEQUEL => '➡️', + RelationType::PREQUEL => '⬅️', + RelationType::REMAKE => '🔄', + RelationType::UNKNOWN => '❓' + }; +} + +// Функция для получения цвета типа связи +function getRelationTypeColor(RelationType $type): string { + return match ($type) { + RelationType::SIMILAR => '#28a745', // Зеленый + RelationType::SEQUEL => '#007bff', // Синий + RelationType::PREQUEL => '#ffc107', // Желтый + RelationType::REMAKE => '#dc3545', // Красный + RelationType::UNKNOWN => '#6c757d' // Серый + }; +} + +// Функция для проверки, является ли тип связи последовательным +function isSequentialRelation(RelationType $type): bool { + return in_array($type, [RelationType::SEQUEL, RelationType::PREQUEL]); +} + +// Функция для проверки, является ли тип связи производным +function isDerivativeRelation(RelationType $type): bool { + return in_array($type, [RelationType::REMAKE, RelationType::SEQUEL, RelationType::PREQUEL]); +} + +// Использование +$knownTypes = getKnownRelationTypes(); + +echo "Известные типы связи:\n"; +foreach ($knownTypes as $type) { + $icon = getRelationTypeIcon($type); + $name = getRelationTypeDisplayName($type); + $color = getRelationTypeColor($type); + $isSequential = isSequentialRelation($type) ? 'Да' : 'Нет'; + $isDerivative = isDerivativeRelation($type) ? 'Да' : 'Нет'; + + echo "{$icon} {$name} (Цвет: {$color}, Последовательный: {$isSequential}, Производный: {$isDerivative})\n"; +} +``` + +## Создание отчета по типам связи + +```php +class RelationTypeReport { + private array $relatedFilms; + + public function __construct(array $relatedFilms) { + $this->relatedFilms = $relatedFilms; + } + + public function getRelatedFilms(): array { + return $this->relatedFilms; + } + + public function getFilmsByRelationType(RelationType $type): array { + return array_filter($this->relatedFilms, fn($film) => $film->relationType === $type); + } + + public function getRelationTypeStats(): array { + $stats = []; + foreach ($this->relatedFilms as $film) { + $type = $film->relationType->value; + if (!isset($stats[$type])) { + $stats[$type] = 0; + } + $stats[$type]++; + } + return $stats; + } + + public function getKnownRelationTypes(): array { + return getKnownRelationTypes(); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО ТИПАМ СВЯЗИ ===\n\n"; + + $stats = $this->getRelationTypeStats(); + $knownTypes = $this->getKnownRelationTypes(); + + // Общая статистика + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего связанных фильмов: " . count($this->relatedFilms) . "\n"; + $report .= "Известных типов связи: " . count($knownTypes) . "\n\n"; + + // Статистика по типам связи + $report .= "📋 СТАТИСТИКА ПО ТИПАМ СВЯЗИ:\n"; + foreach ($stats as $typeValue => $count) { + $type = RelationType::from($typeValue); + $description = $type->getDescription(); + $icon = getRelationTypeIcon($type); + $percentage = round(($count / count($this->relatedFilms)) * 100, 1); + $report .= "{$icon} {$description}: {$count} фильмов ({$percentage}%)\n"; + } + + $report .= "\n"; + + // Детали по каждому типу связи + foreach ($knownTypes as $type) { + $films = $this->getFilmsByRelationType($type); + if (empty($films)) continue; + + $icon = getRelationTypeIcon($type); + $description = $type->getDescription(); + + $report .= "🎬 {$icon} {$description} (" . count($films) . " фильмов):\n"; + foreach ($films as $film) { + $report .= " • {$film->getDisplayName()}\n"; + } + $report .= "\n"; + } + + // Анализ типов связи + $report .= "📈 АНАЛИЗ ТИПОВ СВЯЗИ:\n"; + $sequentialCount = 0; + $derivativeCount = 0; + + foreach ($this->relatedFilms as $film) { + if (isSequentialRelation($film->relationType)) { + $sequentialCount++; + } + if (isDerivativeRelation($film->relationType)) { + $derivativeCount++; + } + } + + $report .= "• Последовательные связи (сиквелы/приквелы): {$sequentialCount}\n"; + $report .= "• Производные связи (ремейки/сиквелы/приквелы): {$derivativeCount}\n"; + $report .= "• Похожие фильмы: " . ($stats[RelationType::SIMILAR->value] ?? 0) . "\n"; + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $stats = $this->getRelationTypeStats(); + $knownTypes = $this->getKnownRelationTypes(); + + // Статистика + $html .= "
\n"; + $html .= "

Общая статистика

\n"; + $html .= "

Всего связанных фильмов: " . count($this->relatedFilms) . "

\n"; + $html .= "

Известных типов связи: " . count($knownTypes) . "

\n"; + $html .= "
\n"; + + // Статистика по типам связи + $html .= "
\n"; + $html .= "
📋 Статистика по типам связи
\n"; + foreach ($stats as $typeValue => $count) { + $type = RelationType::from($typeValue); + $description = $type->getDescription(); + $icon = getRelationTypeIcon($type); + $percentage = round(($count / count($this->relatedFilms)) * 100, 1); + $html .= "

{$icon} {$description}: {$count} фильмов ({$percentage}%)

\n"; + } + $html .= "
\n"; + + // Фильмы по типам связи + foreach ($knownTypes as $type) { + $films = $this->getFilmsByRelationType($type); + if (empty($films)) continue; + + $icon = getRelationTypeIcon($type); + $description = $type->getDescription(); + $cssClass = strtolower($type->value); + + $html .= "
\n"; + $html .= "
{$icon} {$description} (" . count($films) . " фильмов)
\n"; + $html .= "
\n"; + + foreach ($films as $film) { + $html .= "
\n"; + $html .= "
{$film->getDisplayName()}
\n"; + $html .= "
ID: {$film->filmId}
\n"; + if ($film->nameOriginal && $film->nameOriginal !== $film->getDisplayName()) { + $html .= "
Оригинальное название: {$film->nameOriginal}
\n"; + } + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + $html .= "
\n\n"; + + return $html; + } +} + +// Использование +$relatedFilms = $filmService->getSimilarFilms(301); +$report = new RelationTypeReport($relatedFilms); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Отчет по типам связи'); +file_put_contents('relation_types_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в relation_types_report.html\n"; +``` + +## Анализ типов связи + +```php +function analyzeRelationTypes(array $relatedFilms): array { + $analysis = [ + 'totalFilms' => count($relatedFilms), + 'relationTypeDistribution' => [], + 'sequentialRelations' => 0, + 'derivativeRelations' => 0, + 'similarRelations' => 0, + 'unknownRelations' => 0 + ]; + + foreach ($relatedFilms as $film) { + $type = $film->relationType; + + // Статистика по типам связи + $typeValue = $type->value; + if (!isset($analysis['relationTypeDistribution'][$typeValue])) { + $analysis['relationTypeDistribution'][$typeValue] = 0; + } + $analysis['relationTypeDistribution'][$typeValue]++; + + // Категоризация связей + if (isSequentialRelation($type)) { + $analysis['sequentialRelations']++; + } + if (isDerivativeRelation($type)) { + $analysis['derivativeRelations']++; + } + if ($type === RelationType::SIMILAR) { + $analysis['similarRelations']++; + } + if ($type === RelationType::UNKNOWN) { + $analysis['unknownRelations']++; + } + } + + return $analysis; +} + +// Использование +$relatedFilms = $filmService->getSimilarFilms(301); +$analysis = analyzeRelationTypes($relatedFilms); + +echo "=== Анализ типов связи ===\n"; +echo "Всего фильмов: {$analysis['totalFilms']}\n"; + +echo "\nРаспределение по типам связи:\n"; +foreach ($analysis['relationTypeDistribution'] as $type => $count) { + $percentage = round(($count / $analysis['totalFilms']) * 100, 1); + echo "- {$type}: {$count} фильмов ({$percentage}%)\n"; +} + +echo "\nКатегоризация связей:\n"; +echo "- Последовательные связи: {$analysis['sequentialRelations']}\n"; +echo "- Производные связи: {$analysis['derivativeRelations']}\n"; +echo "- Похожие фильмы: {$analysis['similarRelations']}\n"; +echo "- Неизвестные связи: {$analysis['unknownRelations']}\n"; +``` + +## Связанные классы + +- [`RelatedFilm`](../models/related-film.md) - Модель связанного фильма +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/enums/review-order.md b/docs/dev/notkinopoiskphp/enums/review-order.md new file mode 100644 index 0000000..66e7c92 --- /dev/null +++ b/docs/dev/notkinopoiskphp/enums/review-order.md @@ -0,0 +1,686 @@ +# ReviewOrder + +Enum типов сортировки отзывов в Kinopoisk API. + +## Описание + +`ReviewOrder` представляет различные способы сортировки отзывов к фильмам в API запросах Kinopoisk. + +**API Endpoint:** `/api/v2.2/films/{id}/reviews` + +## Значения enum + +### Типы сортировки + +- `DATE_DESC` - По дате (новые сначала) +- `DATE_ASC` - По дате (старые сначала) +- `USER_POSITIVE_RATING_ASC` - По положительным оценкам (низкие сначала) +- `USER_POSITIVE_RATING_DESC` - По положительным оценкам (высокие сначала) +- `USER_NEGATIVE_RATING_ASC` - По отрицательным оценкам (низкие сначала) +- `USER_NEGATIVE_RATING_DESC` - По отрицательным оценкам (высокие сначала) + +## Методы + +### getDisplayName() + +Получает отображаемое название типа сортировки. + +```php +public function getDisplayName(): string +``` + +#### Возвращаемое значение + +- `string` - Отображаемое название типа сортировки + +#### Пример использования + +```php +echo ReviewOrder::DATE_DESC->getDisplayName(); // "По дате (новые сначала)" +echo ReviewOrder::USER_POSITIVE_RATING_DESC->getDisplayName(); // "По положительным оценкам (высокие сначала)" +``` + +### getShortName() + +Получает краткое название типа сортировки. + +```php +public function getShortName(): string +``` + +#### Возвращаемое значение + +- `string` - Краткое название типа сортировки + +#### Пример использования + +```php +echo ReviewOrder::DATE_DESC->getShortName(); // "Дата ↓" +echo ReviewOrder::USER_POSITIVE_RATING_DESC->getShortName(); // "Положительные ↓" +``` + +### isDateSort() + +Проверяет, является ли сортировка по дате. + +```php +public function isDateSort(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если сортировка по дате, `false` в противном случае + +#### Пример использования + +```php +if (ReviewOrder::DATE_DESC->isDateSort()) { + echo "Сортировка по дате"; +} +``` + +### isPositiveRatingSort() + +Проверяет, является ли сортировка по положительным оценкам. + +```php +public function isPositiveRatingSort(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если сортировка по положительным оценкам, `false` в противном случае + +#### Пример использования + +```php +if (ReviewOrder::USER_POSITIVE_RATING_DESC->isPositiveRatingSort()) { + echo "Сортировка по положительным оценкам"; +} +``` + +### isNegativeRatingSort() + +Проверяет, является ли сортировка по отрицательным оценкам. + +```php +public function isNegativeRatingSort(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если сортировка по отрицательным оценкам, `false` в противном случае + +#### Пример использования + +```php +if (ReviewOrder::USER_NEGATIVE_RATING_DESC->isNegativeRatingSort()) { + echo "Сортировка по отрицательным оценкам"; +} +``` + +### isAscending() + +Проверяет, является ли сортировка по возрастанию. + +```php +public function isAscending(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если сортировка по возрастанию, `false` в противном случае + +#### Пример использования + +```php +if (ReviewOrder::DATE_ASC->isAscending()) { + echo "Сортировка по возрастанию"; +} +``` + +### isDescending() + +Проверяет, является ли сортировка по убыванию. + +```php +public function isDescending(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если сортировка по убыванию, `false` в противном случае + +#### Пример использования + +```php +if (ReviewOrder::DATE_DESC->isDescending()) { + echo "Сортировка по убыванию"; +} +``` + +### getDirection() + +Получает направление сортировки. + +```php +public function getDirection(): string +``` + +#### Возвращаемое значение + +- `string` - Направление сортировки ("asc" или "desc") + +#### Пример использования + +```php +echo ReviewOrder::DATE_ASC->getDirection(); // "asc" +echo ReviewOrder::DATE_DESC->getDirection(); // "desc" +``` + +### getFieldType() + +Получает тип поля для сортировки. + +```php +public function getFieldType(): string +``` + +#### Возвращаемое значение + +- `string` - Тип поля для сортировки + +#### Пример использования + +```php +echo ReviewOrder::DATE_DESC->getFieldType(); // "date" +echo ReviewOrder::USER_POSITIVE_RATING_DESC->getFieldType(); // "positive_rating" +``` + +### getDefault() + +Получает значение по умолчанию. + +```php +public static function getDefault(): self +``` + +#### Возвращаемое значение + +- `self` - Значение по умолчанию (DATE_DESC) + +#### Пример использования + +```php +$defaultOrder = ReviewOrder::getDefault(); +echo $defaultOrder->value; // "DATE_DESC" +``` + +### getAll() + +Получает все доступные типы сортировки. + +```php +public static function getAll(): array +``` + +#### Возвращаемое значение + +- `self[]` - Массив всех доступных типов сортировки + +#### Пример использования + +```php +$allOrders = ReviewOrder::getAll(); +foreach ($allOrders as $order) { + echo $order->getDisplayName() . "\n"; +} +``` + +## Полный пример использования + +```php +films; + +echo "=== Работа с сортировкой отзывов ===\n"; + +// Получение отзывов, отсортированных по дате (новые сначала) +$reviewsByDate = $filmService->getReviews(301, 1, ReviewOrder::DATE_DESC); +echo "Отзывы по дате (новые): " . count($reviewsByDate) . " элементов\n"; + +// Получение отзывов, отсортированных по положительным оценкам +$reviewsByPositive = $filmService->getReviews(301, 1, ReviewOrder::USER_POSITIVE_RATING_DESC); +echo "Отзывы по положительным оценкам: " . count($reviewsByPositive) . " элементов\n"; + +// Получение отзывов, отсортированных по отрицательным оценкам +$reviewsByNegative = $filmService->getReviews(301, 1, ReviewOrder::USER_NEGATIVE_RATING_DESC); +echo "Отзывы по отрицательным оценкам: " . count($reviewsByNegative) . " элементов\n"; + +// Вывод всех доступных типов сортировки +echo "\nДоступные типы сортировки:\n"; +foreach (ReviewOrder::getAll() as $order) { + $icon = getReviewOrderIcon($order); + $displayName = $order->getDisplayName(); + $shortName = $order->getShortName(); + $direction = $order->getDirection(); + $fieldType = $order->getFieldType(); + + echo "{$icon} {$displayName}\n"; + echo " Краткое название: {$shortName}\n"; + echo " Направление: {$direction}\n"; + echo " Тип поля: {$fieldType}\n\n"; +} +``` + +## Работа с сортировкой отзывов + +```php +// Функция для получения иконки типа сортировки +function getReviewOrderIcon(ReviewOrder $order): string { + return match ($order) { + ReviewOrder::DATE_DESC => '📅', + ReviewOrder::DATE_ASC => '📅', + ReviewOrder::USER_POSITIVE_RATING_ASC => '👍', + ReviewOrder::USER_POSITIVE_RATING_DESC => '👍', + ReviewOrder::USER_NEGATIVE_RATING_ASC => '👎', + ReviewOrder::USER_NEGATIVE_RATING_DESC => '👎' + }; +} + +// Функция для получения цвета типа сортировки +function getReviewOrderColor(ReviewOrder $order): string { + return match ($order) { + ReviewOrder::DATE_DESC, ReviewOrder::DATE_ASC => '#6c757d', // Серый + ReviewOrder::USER_POSITIVE_RATING_ASC, ReviewOrder::USER_POSITIVE_RATING_DESC => '#28a745', // Зеленый + ReviewOrder::USER_NEGATIVE_RATING_ASC, ReviewOrder::USER_NEGATIVE_RATING_DESC => '#dc3545' // Красный + }; +} + +// Функция для получения описания типа сортировки +function getReviewOrderDescription(ReviewOrder $order): string { + return match ($order) { + ReviewOrder::DATE_DESC => 'Отзывы отсортированы по дате публикации (новые первыми)', + ReviewOrder::DATE_ASC => 'Отзывы отсортированы по дате публикации (старые первыми)', + ReviewOrder::USER_POSITIVE_RATING_ASC => 'Отзывы отсортированы по количеству положительных оценок (по возрастанию)', + ReviewOrder::USER_POSITIVE_RATING_DESC => 'Отзывы отсортированы по количеству положительных оценок (по убыванию)', + ReviewOrder::USER_NEGATIVE_RATING_ASC => 'Отзывы отсортированы по количеству отрицательных оценок (по возрастанию)', + ReviewOrder::USER_NEGATIVE_RATING_DESC => 'Отзывы отсортированы по количеству отрицательных оценок (по убыванию)' + }; +} + +// Функция для группировки типов сортировки +function groupReviewOrders(): array { + $groups = [ + 'date' => [], + 'positive_rating' => [], + 'negative_rating' => [] + ]; + + foreach (ReviewOrder::getAll() as $order) { + $fieldType = $order->getFieldType(); + $groups[$fieldType][] = $order; + } + + return $groups; +} + +// Функция для получения рекомендуемого типа сортировки +function getRecommendedReviewOrder(string $purpose): ReviewOrder { + return match ($purpose) { + 'latest' => ReviewOrder::DATE_DESC, + 'oldest' => ReviewOrder::DATE_ASC, + 'best' => ReviewOrder::USER_POSITIVE_RATING_DESC, + 'worst' => ReviewOrder::USER_NEGATIVE_RATING_DESC, + 'controversial' => ReviewOrder::USER_NEGATIVE_RATING_ASC, + default => ReviewOrder::getDefault() + }; +} + +// Использование +$allOrders = ReviewOrder::getAll(); + +echo "Все типы сортировки отзывов:\n"; +foreach ($allOrders as $order) { + $icon = getReviewOrderIcon($order); + $displayName = $order->getDisplayName(); + $shortName = $order->getShortName(); + $description = getReviewOrderDescription($order); + $color = getReviewOrderColor($order); + + echo "{$icon} {$displayName} ({$shortName})\n"; + echo " Цвет: {$color}\n"; + echo " Описание: {$description}\n\n"; +} + +// Группировка +$groups = groupReviewOrders(); +echo "Группировка по типам:\n"; +foreach ($groups as $type => $orders) { + echo "• {$type}: " . count($orders) . " типов\n"; + foreach ($orders as $order) { + echo " - {$order->getDisplayName()}\n"; + } +} + +// Рекомендации +echo "\nРекомендуемые типы сортировки:\n"; +$recommendations = [ + 'latest' => 'Последние отзывы', + 'best' => 'Лучшие отзывы', + 'worst' => 'Худшие отзывы', + 'controversial' => 'Спорные отзывы' +]; + +foreach ($recommendations as $purpose => $description) { + $order = getRecommendedReviewOrder($purpose); + echo "• {$description}: {$order->getDisplayName()}\n"; +} +``` + +## Создание отчета по сортировке отзывов + +```php +class ReviewOrderReport { + private array $reviewsByDate; + private array $reviewsByPositive; + private array $reviewsByNegative; + + public function __construct(array $reviewsByDate, array $reviewsByPositive, array $reviewsByNegative) { + $this->reviewsByDate = $reviewsByDate; + $this->reviewsByPositive = $reviewsByPositive; + $this->reviewsByNegative = $reviewsByNegative; + } + + public function getReviewsByDate(): array { + return $this->reviewsByDate; + } + + public function getReviewsByPositive(): array { + return $this->reviewsByPositive; + } + + public function getReviewsByNegative(): array { + return $this->reviewsByNegative; + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО СОРТИРОВКЕ ОТЗЫВОВ ===\n\n"; + + // Статистика + $report .= "📊 СТАТИСТИКА:\n"; + $report .= "Отзывов по дате: " . count($this->reviewsByDate) . "\n"; + $report .= "Отзывов по положительным оценкам: " . count($this->reviewsByPositive) . "\n"; + $report .= "Отзывов по отрицательным оценкам: " . count($this->reviewsByNegative) . "\n\n"; + + // Сравнение первых отзывов + $report .= "🔍 СРАВНЕНИЕ ПЕРВЫХ ОТЗЫВОВ:\n"; + + if (!empty($this->reviewsByDate)) { + $firstByDate = $this->reviewsByDate[0]; + $report .= "• По дате: {$firstByDate->getDisplayName()}\n"; + $report .= " Дата: {$firstByDate->date}\n"; + $report .= " Положительных оценок: {$firstByDate->positiveRating}\n"; + $report .= " Отрицательных оценок: {$firstByDate->negativeRating}\n\n"; + } + + if (!empty($this->reviewsByPositive)) { + $firstByPositive = $this->reviewsByPositive[0]; + $report .= "• По положительным оценкам: {$firstByPositive->getDisplayName()}\n"; + $report .= " Дата: {$firstByPositive->date}\n"; + $report .= " Положительных оценок: {$firstByPositive->positiveRating}\n"; + $report .= " Отрицательных оценок: {$firstByPositive->negativeRating}\n\n"; + } + + if (!empty($this->reviewsByNegative)) { + $firstByNegative = $this->reviewsByNegative[0]; + $report .= "• По отрицательным оценкам: {$firstByNegative->getDisplayName()}\n"; + $report .= " Дата: {$firstByNegative->date}\n"; + $report .= " Положительных оценок: {$firstByNegative->positiveRating}\n"; + $report .= " Отрицательных оценок: {$firstByNegative->negativeRating}\n\n"; + } + + // Анализ эффективности сортировки + $report .= "📈 АНАЛИЗ ЭФФЕКТИВНОСТИ СОРТИРОВКИ:\n"; + + $avgPositiveByDate = $this->calculateAveragePositiveRating($this->reviewsByDate); + $avgPositiveByPositive = $this->calculateAveragePositiveRating($this->reviewsByPositive); + $avgPositiveByNegative = $this->calculateAveragePositiveRating($this->reviewsByNegative); + + $report .= "• Среднее количество положительных оценок:\n"; + $report .= " - По дате: {$avgPositiveByDate}\n"; + $report .= " - По положительным оценкам: {$avgPositiveByPositive}\n"; + $report .= " - По отрицательным оценкам: {$avgPositiveByNegative}\n\n"; + + return $report; + } + + private function calculateAveragePositiveRating(array $reviews): float { + if (empty($reviews)) { + return 0; + } + + $total = 0; + foreach ($reviews as $review) { + $total += $review->positiveRating; + } + + return round($total / count($reviews), 2); + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + // Статистика + $html .= "
\n"; + $html .= "

Статистика

\n"; + $html .= "

Отзывов по дате: " . count($this->reviewsByDate) . "

\n"; + $html .= "

Отзывов по положительным оценкам: " . count($this->reviewsByPositive) . "

\n"; + $html .= "

Отзывов по отрицательным оценкам: " . count($this->reviewsByNegative) . "

\n"; + $html .= "
\n"; + + // Отзывы по дате + if (!empty($this->reviewsByDate)) { + $html .= "
\n"; + $html .= "
📅 Отзывы по дате (новые сначала)
\n"; + $html .= "
\n"; + + foreach (array_slice($this->reviewsByDate, 0, 6) as $review) { + $html .= "
\n"; + $html .= "
{$review->getDisplayName()}
\n"; + $html .= "
Дата: {$review->date}
\n"; + $html .= "
Положительных: {$review->positiveRating}
\n"; + $html .= "
Отрицательных: {$review->negativeRating}
\n"; + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + // Отзывы по положительным оценкам + if (!empty($this->reviewsByPositive)) { + $html .= "
\n"; + $html .= "
👍 Отзывы по положительным оценкам
\n"; + $html .= "
\n"; + + foreach (array_slice($this->reviewsByPositive, 0, 6) as $review) { + $html .= "
\n"; + $html .= "
{$review->getDisplayName()}
\n"; + $html .= "
Дата: {$review->date}
\n"; + $html .= "
Положительных: {$review->positiveRating}
\n"; + $html .= "
Отрицательных: {$review->negativeRating}
\n"; + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + // Отзывы по отрицательным оценкам + if (!empty($this->reviewsByNegative)) { + $html .= "
\n"; + $html .= "
👎 Отзывы по отрицательным оценкам
\n"; + $html .= "
\n"; + + foreach (array_slice($this->reviewsByNegative, 0, 6) as $review) { + $html .= "
\n"; + $html .= "
{$review->getDisplayName()}
\n"; + $html .= "
Дата: {$review->date}
\n"; + $html .= "
Положительных: {$review->positiveRating}
\n"; + $html .= "
Отрицательных: {$review->negativeRating}
\n"; + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + $html .= "
\n\n"; + + return $html; + } +} + +// Использование +$reviewsByDate = $filmService->getReviews(301, 1, ReviewOrder::DATE_DESC); +$reviewsByPositive = $filmService->getReviews(301, 1, ReviewOrder::USER_POSITIVE_RATING_DESC); +$reviewsByNegative = $filmService->getReviews(301, 1, ReviewOrder::USER_NEGATIVE_RATING_DESC); + +$report = new ReviewOrderReport($reviewsByDate, $reviewsByPositive, $reviewsByNegative); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Отчет по сортировке отзывов'); +file_put_contents('review_order_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в review_order_report.html\n"; +``` + +## Анализ сортировки отзывов + +```php +function analyzeReviewOrders(array $reviewsByDate, array $reviewsByPositive, array $reviewsByNegative): array { + $analysis = [ + 'totalReviews' => [ + 'date' => count($reviewsByDate), + 'positive' => count($reviewsByPositive), + 'negative' => count($reviewsByNegative) + ], + 'averagePositiveRating' => [ + 'date' => 0, + 'positive' => 0, + 'negative' => 0 + ], + 'averageNegativeRating' => [ + 'date' => 0, + 'positive' => 0, + 'negative' => 0 + ], + 'effectiveness' => [ + 'date' => 0, + 'positive' => 0, + 'negative' => 0 + ] + ]; + + // Анализ отзывов по дате + if (!empty($reviewsByDate)) { + $totalPositive = 0; + $totalNegative = 0; + foreach ($reviewsByDate as $review) { + $totalPositive += $review->positiveRating; + $totalNegative += $review->negativeRating; + } + $analysis['averagePositiveRating']['date'] = round($totalPositive / count($reviewsByDate), 2); + $analysis['averageNegativeRating']['date'] = round($totalNegative / count($reviewsByDate), 2); + $analysis['effectiveness']['date'] = round(($totalPositive / ($totalPositive + $totalNegative)) * 100, 1); + } + + // Анализ отзывов по положительным оценкам + if (!empty($reviewsByPositive)) { + $totalPositive = 0; + $totalNegative = 0; + foreach ($reviewsByPositive as $review) { + $totalPositive += $review->positiveRating; + $totalNegative += $review->negativeRating; + } + $analysis['averagePositiveRating']['positive'] = round($totalPositive / count($reviewsByPositive), 2); + $analysis['averageNegativeRating']['positive'] = round($totalNegative / count($reviewsByPositive), 2); + $analysis['effectiveness']['positive'] = round(($totalPositive / ($totalPositive + $totalNegative)) * 100, 1); + } + + // Анализ отзывов по отрицательным оценкам + if (!empty($reviewsByNegative)) { + $totalPositive = 0; + $totalNegative = 0; + foreach ($reviewsByNegative as $review) { + $totalPositive += $review->positiveRating; + $totalNegative += $review->negativeRating; + } + $analysis['averagePositiveRating']['negative'] = round($totalPositive / count($reviewsByNegative), 2); + $analysis['averageNegativeRating']['negative'] = round($totalNegative / count($reviewsByNegative), 2); + $analysis['effectiveness']['negative'] = round(($totalPositive / ($totalPositive + $totalNegative)) * 100, 1); + } + + return $analysis; +} + +// Использование +$reviewsByDate = $filmService->getReviews(301, 1, ReviewOrder::DATE_DESC); +$reviewsByPositive = $filmService->getReviews(301, 1, ReviewOrder::USER_POSITIVE_RATING_DESC); +$reviewsByNegative = $filmService->getReviews(301, 1, ReviewOrder::USER_NEGATIVE_RATING_DESC); + +$analysis = analyzeReviewOrders($reviewsByDate, $reviewsByPositive, $reviewsByNegative); + +echo "=== Анализ сортировки отзывов ===\n"; +echo "Всего отзывов:\n"; +echo "- По дате: {$analysis['totalReviews']['date']}\n"; +echo "- По положительным оценкам: {$analysis['totalReviews']['positive']}\n"; +echo "- По отрицательным оценкам: {$analysis['totalReviews']['negative']}\n"; + +echo "\nСреднее количество положительных оценок:\n"; +echo "- По дате: {$analysis['averagePositiveRating']['date']}\n"; +echo "- По положительным оценкам: {$analysis['averagePositiveRating']['positive']}\n"; +echo "- По отрицательным оценкам: {$analysis['averagePositiveRating']['negative']}\n"; + +echo "\nСреднее количество отрицательных оценок:\n"; +echo "- По дате: {$analysis['averageNegativeRating']['date']}\n"; +echo "- По положительным оценкам: {$analysis['averageNegativeRating']['positive']}\n"; +echo "- По отрицательным оценкам: {$analysis['averageNegativeRating']['negative']}\n"; + +echo "\nЭффективность сортировки (% положительных оценок):\n"; +echo "- По дате: {$analysis['effectiveness']['date']}%\n"; +echo "- По положительным оценкам: {$analysis['effectiveness']['positive']}%\n"; +echo "- По отрицательным оценкам: {$analysis['effectiveness']['negative']}%\n"; +``` + +## Связанные классы + +- [`Review`](../models/review.md) - Модель отзыва +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/enums/review-type.md b/docs/dev/notkinopoiskphp/enums/review-type.md new file mode 100644 index 0000000..cb89991 --- /dev/null +++ b/docs/dev/notkinopoiskphp/enums/review-type.md @@ -0,0 +1,276 @@ +# ReviewType + +Типы рецензий в Kinopoisk API. + +## Описание + +`ReviewType` определяет различные типы рецензий, которые могут быть оставлены пользователями: положительные, отрицательные, нейтральные. + +## Значения enum + +### Типы рецензий + +- `POSITIVE` - Положительная рецензия +- `NEGATIVE` - Отрицательная рецензия +- `NEUTRAL` - Нейтральная рецензия + +## Методы + +### isPositive() + +Проверяет, является ли рецензия положительной. + +```php +public function isPositive(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если рецензия положительная, `false` в противном случае + +#### Пример использования + +```php +if (ReviewType::POSITIVE->isPositive()) { + echo "Положительная рецензия"; +} +``` + +### isNegative() + +Проверяет, является ли рецензия отрицательной. + +```php +public function isNegative(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если рецензия отрицательная, `false` в противном случае + +#### Пример использования + +```php +if (ReviewType::NEGATIVE->isNegative()) { + echo "Отрицательная рецензия"; +} +``` + +### getDisplayName() + +Получает человекочитаемое название типа рецензии. + +```php +public function getDisplayName(): string +``` + +#### Возвращаемое значение + +- `string` - Название типа рецензии на русском языке + +#### Пример использования + +```php +echo ReviewType::POSITIVE->getDisplayName(); // "Положительная" +echo ReviewType::NEGATIVE->getDisplayName(); // "Отрицательная" +echo ReviewType::NEUTRAL->getDisplayName(); // "Нейтральная" +``` + +## Полный пример использования + +```php +films; +$reviews = $filmService->getReviews(301); // Матрица + +echo "=== Анализ рецензий ===\n"; + +// Подсчет по типам +$stats = [ + 'positive' => 0, + 'negative' => 0, + 'neutral' => 0 +]; + +foreach ($reviews as $review) { + if ($review->type->isPositive()) { + $stats['positive']++; + } elseif ($review->type->isNegative()) { + $stats['negative']++; + } else { + $stats['neutral']++; + } +} + +echo "📊 Статистика рецензий:\n"; +echo "👍 Положительных: {$stats['positive']}\n"; +echo "👎 Отрицательных: {$stats['negative']}\n"; +echo "😐 Нейтральных: {$stats['neutral']}\n"; + +// Вывод примеров каждого типа +foreach ([ReviewType::POSITIVE, ReviewType::NEGATIVE, ReviewType::NEUTRAL] as $type) { + $typeReviews = array_filter($reviews, fn($r) => $r->type === $type); + + if (!empty($typeReviews)) { + echo "\n📝 {$type->getDisplayName()} рецензии:\n"; + $example = array_values($typeReviews)[0]; + echo "- {$example->title}\n"; + echo "- Автор: {$example->author}\n"; + echo "- " . substr($example->description, 0, 100) . "...\n"; + } +} +``` + +## Работа с типами рецензий + +```php +// Функция для фильтрации рецензий по типу +function filterReviewsByType(array $reviews, ReviewType $type): array { + return array_filter($reviews, fn($review) => $review->type === $type); +} + +// Функция для получения статистики по типам +function getReviewTypeStats(array $reviews): array { + $stats = []; + + foreach (ReviewType::cases() as $type) { + $stats[$type->value] = [ + 'type' => $type, + 'displayName' => $type->getDisplayName(), + 'count' => count(filterReviewsByType($reviews, $type)), + 'percentage' => 0 + ]; + } + + $total = count($reviews); + if ($total > 0) { + foreach ($stats as &$stat) { + $stat['percentage'] = round(($stat['count'] / $total) * 100, 1); + } + } + + return $stats; +} + +// Использование +$reviews = $filmService->getReviews(301); +$stats = getReviewTypeStats($reviews); + +echo "=== Детальная статистика ===\n"; +foreach ($stats as $typeKey => $stat) { + echo "{$stat['displayName']}: {$stat['count']} ({$stat['percentage']}%)\n"; +} +``` + +## Анализ настроений + +```php +function analyzeSentiment(array $reviews): array { + $sentiment = [ + 'overall' => 'neutral', + 'positiveRatio' => 0, + 'negativeRatio' => 0, + 'neutralRatio' => 0 + ]; + + $total = count($reviews); + if ($total === 0) { + return $sentiment; + } + + $positive = count(filterReviewsByType($reviews, ReviewType::POSITIVE)); + $negative = count(filterReviewsByType($reviews, ReviewType::NEGATIVE)); + $neutral = count(filterReviewsByType($reviews, ReviewType::NEUTRAL)); + + $sentiment['positiveRatio'] = round(($positive / $total) * 100, 1); + $sentiment['negativeRatio'] = round(($negative / $total) * 100, 1); + $sentiment['neutralRatio'] = round(($neutral / $total) * 100, 1); + + // Определение общего настроения + if ($sentiment['positiveRatio'] > 50) { + $sentiment['overall'] = 'positive'; + } elseif ($sentiment['negativeRatio'] > 50) { + $sentiment['overall'] = 'negative'; + } + + return $sentiment; +} + +// Использование +$reviews = $filmService->getReviews(301); +$sentiment = analyzeSentiment($reviews); + +echo "=== Анализ настроений ===\n"; +echo "Общее настроение: {$sentiment['overall']}\n"; +echo "Положительных: {$sentiment['positiveRatio']}%\n"; +echo "Отрицательных: {$sentiment['negativeRatio']}%\n"; +echo "Нейтральных: {$sentiment['neutralRatio']}%\n"; + +// Эмодзи для настроения +$emoji = match($sentiment['overall']) { + 'positive' => '😊', + 'negative' => '😞', + default => '😐' +}; +echo "Настроение: {$emoji}\n"; +``` + +## Сравнение рецензий + +```php +function compareReviewTypes(array $reviews): array { + $comparison = []; + + foreach (ReviewType::cases() as $type) { + $typeReviews = filterReviewsByType($reviews, $type); + + $avgPositiveRating = 0; + $avgNegativeRating = 0; + + if (!empty($typeReviews)) { + $totalPositive = array_sum(array_column($typeReviews, 'positiveRating')); + $totalNegative = array_sum(array_column($typeReviews, 'negativeRating')); + + $avgPositiveRating = round($totalPositive / count($typeReviews), 1); + $avgNegativeRating = round($totalNegative / count($typeReviews), 1); + } + + $comparison[$type->value] = [ + 'type' => $type->getDisplayName(), + 'count' => count($typeReviews), + 'avgPositiveRating' => $avgPositiveRating, + 'avgNegativeRating' => $avgNegativeRating + ]; + } + + return $comparison; +} + +// Использование +$reviews = $filmService->getReviews(301); +$comparison = compareReviewTypes($reviews); + +echo "=== Сравнение типов рецензий ===\n"; +foreach ($comparison as $typeKey => $data) { + echo "{$data['type']} ({$data['count']} рецензий):\n"; + echo " Средний положительный рейтинг: {$data['avgPositiveRating']}\n"; + echo " Средний отрицательный рейтинг: {$data['avgNegativeRating']}\n"; + echo "\n"; +} +``` + +## Связанные классы + +- [`Review`](../models/review.md) - Модель рецензии +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами +- [`Film`](../models/film.md) - Модель фильма diff --git a/docs/dev/notkinopoiskphp/enums/sex.md b/docs/dev/notkinopoiskphp/enums/sex.md new file mode 100644 index 0000000..7380e4a --- /dev/null +++ b/docs/dev/notkinopoiskphp/enums/sex.md @@ -0,0 +1,461 @@ +# Sex + +Пол из Kinopoisk API. + +## Описание + +`Sex` представляет пол человека в API Кинопоиска. + +## Значения enum + +### Типы пола + +- `MALE` - Мужской пол +- `FEMALE` - Женский пол +- `UNKNOWN` - Неизвестный пол + +## Методы + +### getDisplayName() + +Получает отображаемое название пола. + +```php +public function getDisplayName(): string +``` + +#### Возвращаемое значение + +- `string` - Отображаемое название пола на русском языке + +#### Пример использования + +```php +echo Sex::MALE->getDisplayName(); // "Мужской" +echo Sex::FEMALE->getDisplayName(); // "Женский" +echo Sex::UNKNOWN->getDisplayName(); // "Неизвестно" +``` + +## Полный пример использования + +```php +persons; +$person = $personService->getPerson(1); // ID персоны + +echo "=== Работа с полом ===\n"; + +// Вывод информации о поле +if ($person->sex) { + echo "Пол: {$person->sex->getDisplayName()}\n"; + echo "Значение: {$person->sex->value}\n"; +} else { + echo "Пол не указан\n"; +} + +// Вывод всех возможных значений +echo "\nДоступные значения пола:\n"; +foreach ([Sex::MALE, Sex::FEMALE, Sex::UNKNOWN] as $sex) { + $icon = getSexIcon($sex); + $displayName = $sex->getDisplayName(); + echo "{$icon} {$displayName} ({$sex->value})\n"; +} +``` + +## Работа с полом + +```php +// Функция для получения иконки пола +function getSexIcon(Sex $sex): string { + return match ($sex) { + Sex::MALE => '👨', + Sex::FEMALE => '👩', + Sex::UNKNOWN => '❓' + }; +} + +// Функция для получения цвета пола +function getSexColor(Sex $sex): string { + return match ($sex) { + Sex::MALE => '#007bff', // Синий + Sex::FEMALE => '#e83e8c', // Розовый + Sex::UNKNOWN => '#6c757d' // Серый + }; +} + +// Функция для проверки, является ли пол известным +function isKnownSex(Sex $sex): bool { + return $sex !== Sex::UNKNOWN; +} + +// Функция для получения статистики по полу +function getSexStats(array $persons): array { + $stats = [ + 'male' => 0, + 'female' => 0, + 'unknown' => 0, + 'total' => count($persons) + ]; + + foreach ($persons as $person) { + if ($person->sex) { + switch ($person->sex) { + case Sex::MALE: + $stats['male']++; + break; + case Sex::FEMALE: + $stats['female']++; + break; + case Sex::UNKNOWN: + $stats['unknown']++; + break; + } + } else { + $stats['unknown']++; + } + } + + return $stats; +} + +// Функция для фильтрации персон по полу +function filterPersonsBySex(array $persons, Sex $sex): array { + return array_filter($persons, fn($person) => $person->sex === $sex); +} + +// Использование +$allSexes = [Sex::MALE, Sex::FEMALE, Sex::UNKNOWN]; + +echo "Работа с полом:\n"; +foreach ($allSexes as $sex) { + $icon = getSexIcon($sex); + $displayName = $sex->getDisplayName(); + $color = getSexColor($sex); + $isKnown = isKnownSex($sex) ? 'Да' : 'Нет'; + + echo "{$icon} {$displayName} (Цвет: {$color}, Известный: {$isKnown})\n"; +} +``` + +## Создание отчета по полу + +```php +class SexReport { + private array $persons; + + public function __construct(array $persons) { + $this->persons = $persons; + } + + public function getPersons(): array { + return $this->persons; + } + + public function getPersonsBySex(Sex $sex): array { + return filterPersonsBySex($this->persons, $sex); + } + + public function getSexStats(): array { + return getSexStats($this->persons); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО ПОЛУ ===\n\n"; + + $stats = $this->getSexStats(); + + // Общая статистика + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего персон: {$stats['total']}\n"; + $report .= "Мужчин: {$stats['male']}\n"; + $report .= "Женщин: {$stats['female']}\n"; + $report .= "Неизвестно: {$stats['unknown']}\n\n"; + + // Процентное распределение + if ($stats['total'] > 0) { + $malePercent = round(($stats['male'] / $stats['total']) * 100, 1); + $femalePercent = round(($stats['female'] / $stats['total']) * 100, 1); + $unknownPercent = round(($stats['unknown'] / $stats['total']) * 100, 1); + + $report .= "📈 ПРОЦЕНТНОЕ РАСПРЕДЕЛЕНИЕ:\n"; + $report .= "• Мужчины: {$malePercent}%\n"; + $report .= "• Женщины: {$femalePercent}%\n"; + $report .= "• Неизвестно: {$unknownPercent}%\n\n"; + } + + // Детали по каждому полу + $malePersons = $this->getPersonsBySex(Sex::MALE); + $femalePersons = $this->getPersonsBySex(Sex::FEMALE); + $unknownPersons = $this->getPersonsBySex(Sex::UNKNOWN); + + if (!empty($malePersons)) { + $report .= "👨 МУЖЧИНЫ (" . count($malePersons) . " персон):\n"; + foreach (array_slice($malePersons, 0, 10) as $person) { + $report .= "• {$person->getDisplayName()}\n"; + } + if (count($malePersons) > 10) { + $report .= "... и еще " . (count($malePersons) - 10) . " человек\n"; + } + $report .= "\n"; + } + + if (!empty($femalePersons)) { + $report .= "👩 ЖЕНЩИНЫ (" . count($femalePersons) . " персон):\n"; + foreach (array_slice($femalePersons, 0, 10) as $person) { + $report .= "• {$person->getDisplayName()}\n"; + } + if (count($femalePersons) > 10) { + $report .= "... и еще " . (count($femalePersons) - 10) . " человек\n"; + } + $report .= "\n"; + } + + if (!empty($unknownPersons)) { + $report .= "❓ НЕИЗВЕСТНЫЙ ПОЛ (" . count($unknownPersons) . " персон):\n"; + foreach (array_slice($unknownPersons, 0, 10) as $person) { + $report .= "• {$person->getDisplayName()}\n"; + } + if (count($unknownPersons) > 10) { + $report .= "... и еще " . (count($unknownPersons) - 10) . " человек\n"; + } + $report .= "\n"; + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $stats = $this->getSexStats(); + $malePersons = $this->getPersonsBySex(Sex::MALE); + $femalePersons = $this->getPersonsBySex(Sex::FEMALE); + $unknownPersons = $this->getPersonsBySex(Sex::UNKNOWN); + + // Статистика + $html .= "
\n"; + $html .= "

Общая статистика

\n"; + $html .= "

Всего персон: {$stats['total']}

\n"; + $html .= "

Мужчин: {$stats['male']}

\n"; + $html .= "

Женщин: {$stats['female']}

\n"; + $html .= "

Неизвестно: {$stats['unknown']}

\n"; + + // Прогресс-бары + if ($stats['total'] > 0) { + $malePercent = round(($stats['male'] / $stats['total']) * 100, 1); + $femalePercent = round(($stats['female'] / $stats['total']) * 100, 1); + $unknownPercent = round(($stats['unknown'] / $stats['total']) * 100, 1); + + $html .= "

Распределение по полу

\n"; + $html .= "
\n"; + $html .= "
\n"; + $html .= "
\n"; + $html .= "

Мужчины: {$malePercent}%

\n"; + + $html .= "
\n"; + $html .= "
\n"; + $html .= "
\n"; + $html .= "

Женщины: {$femalePercent}%

\n"; + + $html .= "
\n"; + $html .= "
\n"; + $html .= "
\n"; + $html .= "

Неизвестно: {$unknownPercent}%

\n"; + } + + $html .= "
\n"; + + // Мужчины + if (!empty($malePersons)) { + $html .= "
\n"; + $html .= "
👨 Мужчины (" . count($malePersons) . " персон)
\n"; + $html .= "
\n"; + + foreach (array_slice($malePersons, 0, 12) as $person) { + $html .= "
\n"; + $html .= "
{$person->getDisplayName()}
\n"; + if ($person->profession) { + $html .= "
Профессия: {$person->profession}
\n"; + } + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + // Женщины + if (!empty($femalePersons)) { + $html .= "
\n"; + $html .= "
👩 Женщины (" . count($femalePersons) . " персон)
\n"; + $html .= "
\n"; + + foreach (array_slice($femalePersons, 0, 12) as $person) { + $html .= "
\n"; + $html .= "
{$person->getDisplayName()}
\n"; + if ($person->profession) { + $html .= "
Профессия: {$person->profession}
\n"; + } + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + // Неизвестный пол + if (!empty($unknownPersons)) { + $html .= "
\n"; + $html .= "
❓ Неизвестный пол (" . count($unknownPersons) . " персон)
\n"; + $html .= "
\n"; + + foreach (array_slice($unknownPersons, 0, 12) as $person) { + $html .= "
\n"; + $html .= "
{$person->getDisplayName()}
\n"; + if ($person->profession) { + $html .= "
Профессия: {$person->profession}
\n"; + } + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + $html .= "
\n\n"; + + return $html; + } +} + +// Использование +$persons = $personService->getPersonsByFilm(301); // ID фильма +$report = new SexReport($persons); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Отчет по полу'); +file_put_contents('sex_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в sex_report.html\n"; +``` + +## Анализ пола + +```php +function analyzeSex(array $persons): array { + $analysis = [ + 'totalPersons' => count($persons), + 'sexDistribution' => [ + 'male' => 0, + 'female' => 0, + 'unknown' => 0 + ], + 'sexPercentages' => [ + 'male' => 0, + 'female' => 0, + 'unknown' => 0 + ], + 'professionBySex' => [ + 'male' => [], + 'female' => [], + 'unknown' => [] + ] + ]; + + foreach ($persons as $person) { + $sex = $person->sex ?? Sex::UNKNOWN; + + switch ($sex) { + case Sex::MALE: + $analysis['sexDistribution']['male']++; + break; + case Sex::FEMALE: + $analysis['sexDistribution']['female']++; + break; + case Sex::UNKNOWN: + $analysis['sexDistribution']['unknown']++; + break; + } + + // Анализ профессий по полу + if ($person->profession) { + $profession = $person->profession; + $sexKey = $sex === Sex::MALE ? 'male' : ($sex === Sex::FEMALE ? 'female' : 'unknown'); + + if (!isset($analysis['professionBySex'][$sexKey][$profession])) { + $analysis['professionBySex'][$sexKey][$profession] = 0; + } + $analysis['professionBySex'][$sexKey][$profession]++; + } + } + + // Вычисление процентов + if ($analysis['totalPersons'] > 0) { + $analysis['sexPercentages']['male'] = round(($analysis['sexDistribution']['male'] / $analysis['totalPersons']) * 100, 1); + $analysis['sexPercentages']['female'] = round(($analysis['sexDistribution']['female'] / $analysis['totalPersons']) * 100, 1); + $analysis['sexPercentages']['unknown'] = round(($analysis['sexDistribution']['unknown'] / $analysis['totalPersons']) * 100, 1); + } + + return $analysis; +} + +// Использование +$persons = $personService->getPersonsByFilm(301); +$analysis = analyzeSex($persons); + +echo "=== Анализ пола ===\n"; +echo "Всего персон: {$analysis['totalPersons']}\n"; + +echo "\nРаспределение по полу:\n"; +echo "- Мужчины: {$analysis['sexDistribution']['male']} ({$analysis['sexPercentages']['male']}%)\n"; +echo "- Женщины: {$analysis['sexDistribution']['female']} ({$analysis['sexPercentages']['female']}%)\n"; +echo "- Неизвестно: {$analysis['sexDistribution']['unknown']} ({$analysis['sexPercentages']['unknown']}%)\n"; + +echo "\nТоп профессий мужчин:\n"; +arsort($analysis['professionBySex']['male']); +foreach (array_slice($analysis['professionBySex']['male'], 0, 5) as $profession => $count) { + echo "- {$profession}: {$count} человек\n"; +} + +echo "\nТоп профессий женщин:\n"; +arsort($analysis['professionBySex']['female']); +foreach (array_slice($analysis['professionBySex']['female'], 0, 5) as $profession => $count) { + echo "- {$profession}: {$count} человек\n"; +} +``` + +## Связанные классы + +- [`Person`](../models/person.md) - Модель персоны +- [`PersonService`](../services/person-service.md) - Сервис для работы с персонами diff --git a/docs/dev/notkinopoiskphp/enums/video-site.md b/docs/dev/notkinopoiskphp/enums/video-site.md new file mode 100644 index 0000000..006038f --- /dev/null +++ b/docs/dev/notkinopoiskphp/enums/video-site.md @@ -0,0 +1,410 @@ +# VideoSite + +Типы видео-сайтов в Kinopoisk API. + +## Описание + +`VideoSite` определяет различные платформы, на которых размещены видео: YouTube, Vimeo, Kinopoisk и другие. + +## Значения enum + +### Основные платформы + +- `YOUTUBE` - YouTube +- `VIMEO` - Vimeo +- `KINOPOISK` - Кинопоиск +- `KINOPOISK_WIDGET` - Кинопоиск-виджет +- `UNKNOWN` - Неизвестный сайт + +## Методы + +### getDisplayName() + +Получает человекочитаемое название сайта. + +```php +public function getDisplayName(): string +``` + +#### Возвращаемое значение + +- `string` - Название сайта на русском языке + +#### Пример использования + +```php +echo VideoSite::YOUTUBE->getDisplayName(); // "YouTube" +echo VideoSite::VIMEO->getDisplayName(); // "Vimeo" +echo VideoSite::KINOPOISK->getDisplayName(); // "Кинопоиск" +``` + +### isExternal() + +Проверяет, является ли сайт внешним (не Kinopoisk). + +```php +public function isExternal(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если внешний сайт, `false` если Kinopoisk + +#### Пример использования + +```php +if (VideoSite::YOUTUBE->isExternal()) { + echo "Внешний сайт"; +} + +if (!VideoSite::KINOPOISK->isExternal()) { + echo "Внутренний сайт Кинопоиска"; +} +``` + +## Полный пример использования + +```php +films; +$videos = $filmService->getVideos(301); // Матрица + +echo "=== Анализ видео по платформам ===\n"; + +// Группировка по платформам +$videosBySite = []; +foreach ($videos as $video) { + $siteKey = $video->site->value; + if (!isset($videosBySite[$siteKey])) { + $videosBySite[$siteKey] = []; + } + $videosBySite[$siteKey][] = $video; +} + +// Вывод статистики по платформам +foreach ($videosBySite as $site => $siteVideos) { + $videoSite = VideoSite::from($site); + echo "\n🎬 {$videoSite->getDisplayName()} (" . count($siteVideos) . " видео):\n"; + + // Проверка типа платформы + if ($videoSite->isExternal()) { + echo " 📤 Внешняя платформа\n"; + } else { + echo " 🏠 Внутренняя платформа Кинопоиска\n"; + } + + // Вывод примеров видео + foreach (array_slice($siteVideos, 0, 3) as $index => $video) { + echo " " . ($index + 1) . ". {$video->name}\n"; + } + + if (count($siteVideos) > 3) { + echo " ... и еще " . (count($siteVideos) - 3) . " видео\n"; + } +} +``` + +## Работа с платформами + +```php +// Функция для фильтрации видео по типу платформы +function filterVideosByPlatformType(array $videos, bool $external = true): array { + return array_filter($videos, function($video) use ($external) { + return $video->site->isExternal() === $external; + }); +} + +// Функция для получения статистики по платформам +function getVideoSiteStats(array $videos): array { + $stats = [ + 'total' => count($videos), + 'sites' => [], + 'external' => 0, + 'internal' => 0 + ]; + + foreach ($videos as $video) { + $siteKey = $video->site->value; + + // Статистика по сайтам + if (!isset($stats['sites'][$siteKey])) { + $stats['sites'][$siteKey] = [ + 'site' => $video->site, + 'count' => 0, + 'external' => $video->site->isExternal() + ]; + } + $stats['sites'][$siteKey]['count']++; + + // Подсчет внешних/внутренних + if ($video->site->isExternal()) { + $stats['external']++; + } else { + $stats['internal']++; + } + } + + return $stats; +} + +// Функция для создания отчета по платформам +function createPlatformReport(array $videos): string { + $stats = getVideoSiteStats($videos); + + $report = "=== ОТЧЕТ ПО ПЛАТФОРМАМ ===\n\n"; + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего видео: {$stats['total']}\n"; + $report .= "Внешних платформ: {$stats['external']}\n"; + $report .= "Внутренних платформ: {$stats['internal']}\n\n"; + + $report .= "🎬 ПО ПЛАТФОРМАМ:\n"; + foreach ($stats['sites'] as $siteKey => $siteData) { + $platformType = $siteData['external'] ? 'Внешняя' : 'Внутренняя'; + $report .= "• {$siteData['site']->getDisplayName()}: {$siteData['count']} видео ({$platformType})\n"; + } + + return $report; +} + +// Использование +$videos = $filmService->getVideos(301); + +// Фильтрация по типу платформы +$externalVideos = filterVideosByPlatformType($videos, true); +$internalVideos = filterVideosByPlatformType($videos, false); + +echo "Внешних видео: " . count($externalVideos) . "\n"; +echo "Внутренних видео: " . count($internalVideos) . "\n"; + +// Получение статистики +$stats = getVideoSiteStats($videos); +echo "Всего видео: {$stats['total']}\n"; +echo "Внешних платформ: {$stats['external']}\n"; +echo "Внутренних платформ: {$stats['internal']}\n"; + +// Создание отчета +$report = createPlatformReport($videos); +echo $report; +``` + +## Анализ платформ + +```php +function analyzeVideoPlatforms(array $videos): array { + $analysis = [ + 'total' => count($videos), + 'platforms' => [], + 'externalCount' => 0, + 'internalCount' => 0, + 'mostPopular' => null, + 'diversity' => 0 + ]; + + $platformCounts = []; + + foreach ($videos as $video) { + $siteKey = $video->site->value; + + // Подсчет по платформам + if (!isset($platformCounts[$siteKey])) { + $platformCounts[$siteKey] = 0; + } + $platformCounts[$siteKey]++; + + // Подсчет внешних/внутренних + if ($video->site->isExternal()) { + $analysis['externalCount']++; + } else { + $analysis['internalCount']++; + } + } + + // Анализ платформ + foreach ($platformCounts as $siteKey => $count) { + $videoSite = VideoSite::from($siteKey); + $analysis['platforms'][$siteKey] = [ + 'site' => $videoSite, + 'displayName' => $videoSite->getDisplayName(), + 'count' => $count, + 'percentage' => round(($count / $analysis['total']) * 100, 1), + 'external' => $videoSite->isExternal() + ]; + } + + // Самая популярная платформа + if (!empty($platformCounts)) { + $mostPopularKey = array_keys($platformCounts, max($platformCounts))[0]; + $analysis['mostPopular'] = VideoSite::from($mostPopularKey); + } + + // Разнообразие платформ + $analysis['diversity'] = count($platformCounts); + + return $analysis; +} + +// Использование +$videos = $filmService->getVideos(301); +$analysis = analyzeVideoPlatforms($videos); + +echo "=== Анализ платформ ===\n"; +echo "Всего видео: {$analysis['total']}\n"; +echo "Внешних платформ: {$analysis['externalCount']}\n"; +echo "Внутренних платформ: {$analysis['internalCount']}\n"; +echo "Разнообразие платформ: {$analysis['diversity']}\n"; + +if ($analysis['mostPopular']) { + echo "Самая популярная платформа: {$analysis['mostPopular']->getDisplayName()}\n"; +} + +echo "\nДетальная статистика:\n"; +foreach ($analysis['platforms'] as $siteKey => $platformData) { + $type = $platformData['external'] ? 'Внешняя' : 'Внутренняя'; + echo "• {$platformData['displayName']}: {$platformData['count']} видео ({$platformData['percentage']}%) - {$type}\n"; +} +``` + +## Создание HTML галереи по платформам + +```php +function createPlatformGallery(array $videos): string { + $html = "\n\n\n"; + $html .= "Видео по платформам\n"; + $html .= "\n\n\n"; + $html .= "\n\n"; + + return $html; +} + +// Использование +$videos = $filmService->getVideos(301); +$html = createPlatformGallery($videos); + +// Сохранение HTML файла +file_put_contents('video_platforms.html', $html); +echo "✅ HTML галерея по платформам сохранена в video_platforms.html\n"; +``` + +## Валидация платформ + +```php +function validateVideoPlatform(VideoSite $site): array { + $validation = [ + 'valid' => true, + 'warnings' => [], + 'recommendations' => [] + ]; + + // Проверка известности платформы + if ($site === VideoSite::UNKNOWN) { + $validation['warnings'][] = 'Неизвестная платформа'; + $validation['recommendations'][] = 'Рекомендуется проверить корректность данных'; + } + + // Проверка типа платформы + if ($site->isExternal()) { + $validation['recommendations'][] = 'Внешняя платформа - может потребоваться дополнительная обработка'; + } else { + $validation['recommendations'][] = 'Внутренняя платформа Кинопоиска - стабильный источник'; + } + + // Специфичные проверки для каждой платформы + switch ($site) { + case VideoSite::YOUTUBE: + $validation['recommendations'][] = 'YouTube - популярная платформа с широкой поддержкой'; + break; + case VideoSite::VIMEO: + $validation['recommendations'][] = 'Vimeo - качественные видео, может потребовать авторизации'; + break; + case VideoSite::KINOPOISK: + $validation['recommendations'][] = 'Кинопоиск - официальный источник, высокая надежность'; + break; + case VideoSite::KINOPOISK_WIDGET: + $validation['recommendations'][] = 'Кинопоиск-виджет - встроенный плеер'; + break; + } + + return $validation; +} + +// Использование +$videos = $filmService->getVideos(301); + +foreach ($videos as $index => $video) { + $validation = validateVideoPlatform($video->site); + + echo "Видео " . ($index + 1) . " - {$video->site->getDisplayName()}:\n"; + + if (!empty($validation['warnings'])) { + foreach ($validation['warnings'] as $warning) { + echo " ⚠️ {$warning}\n"; + } + } + + foreach ($validation['recommendations'] as $recommendation) { + echo " 💡 {$recommendation}\n"; + } + + echo "\n"; +} +``` + +## Связанные классы + +- [`Video`](../models/video.md) - Модель видео +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами +- [`Film`](../models/film.md) - Модель фильма diff --git a/docs/dev/notkinopoiskphp/exceptions/api-exception.md b/docs/dev/notkinopoiskphp/exceptions/api-exception.md new file mode 100644 index 0000000..14fd374 --- /dev/null +++ b/docs/dev/notkinopoiskphp/exceptions/api-exception.md @@ -0,0 +1,403 @@ +# ApiException + +Базовое исключение для всех ошибок, связанных с Kinopoisk API. + +## Описание + +`ApiException` является базовым классом для всех специфичных исключений API. Используется для обработки общих ошибок, которые не подходят под более специфичные категории исключений. + +### Основные возможности + +- Базовый класс для всех исключений API +- Поддержка цепочки исключений +- Коды ошибок (обычно HTTP статус коды) +- Детальные сообщения об ошибках + +## Наследование + +Наследует от стандартного класса `Exception` PHP. + +## Конструктор + +```php +public function __construct( + string $message = '', + int $code = 0, + ?Throwable $previous = null +) +``` + +### Параметры + +- `$message` (string) - Сообщение об ошибке, описывающее проблему с API +- `$code` (int) - Код ошибки (обычно HTTP статус код) +- `$previous` (Throwable|null) - Предыдущее исключение в цепочке + +### Пример использования + +```php +throw new ApiException('Ошибка сети при обращении к API', 500, $previousException); +``` + +## Полный пример использования + +```php +films->getById(999999); + echo "Фильм получен: {$film->getDisplayName()}\n"; +} catch (ApiException $e) { + echo "Ошибка API: {$e->getMessage()}\n"; + echo "Код ошибки: {$e->getCode()}\n"; + echo "Файл: {$e->getFile()}\n"; + echo "Строка: {$e->getLine()}\n"; + + if ($e->getPrevious()) { + echo "Предыдущее исключение: " . $e->getPrevious()->getMessage() . "\n"; + } +} + +// Пример создания пользовательского исключения +try { + // Симуляция ошибки сети + throw new ApiException('Ошибка сети при обращении к API', 500); +} catch (ApiException $e) { + echo "\nПоймано пользовательское исключение:\n"; + echo "Сообщение: {$e->getMessage()}\n"; + echo "Код: {$e->getCode()}\n"; +} + +// Пример с цепочкой исключений +try { + $previousException = new Exception('Ошибка сети'); + throw new ApiException('Не удалось получить данные фильма', 404, $previousException); +} catch (ApiException $e) { + echo "\nИсключение с цепочкой:\n"; + echo "Сообщение: {$e->getMessage()}\n"; + echo "Код: {$e->getCode()}\n"; + + if ($e->getPrevious()) { + echo "Причина: " . $e->getPrevious()->getMessage() . "\n"; + } +} +``` + +## Обработка исключений API + +```php +// Функция для безопасного выполнения API запросов +function safeApiCall(callable $apiCall, string $operation = 'API операция'): mixed { + try { + return $apiCall(); + } catch (ApiException $e) { + logApiError($e, $operation); + return null; + } +} + +// Функция для логирования ошибок API +function logApiError(ApiException $e, string $operation): void { + $logMessage = sprintf( + "[%s] API Error in %s: %s (Code: %d) in %s:%d", + date('Y-m-d H:i:s'), + $operation, + $e->getMessage(), + $e->getCode(), + $e->getFile(), + $e->getLine() + ); + + error_log($logMessage); + + if ($e->getPrevious()) { + error_log("Previous exception: " . $e->getPrevious()->getMessage()); + } +} + +// Функция для получения детальной информации об ошибке +function getApiErrorDetails(ApiException $e): array { + return [ + 'message' => $e->getMessage(), + 'code' => $e->getCode(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'trace' => $e->getTraceAsString(), + 'previous' => $e->getPrevious() ? [ + 'message' => $e->getPrevious()->getMessage(), + 'code' => $e->getPrevious()->getCode(), + 'class' => get_class($e->getPrevious()) + ] : null + ]; +} + +// Функция для определения типа ошибки по коду +function getApiErrorType(int $code): string { + return match (true) { + $code >= 400 && $code < 500 => 'Client Error', + $code >= 500 && $code < 600 => 'Server Error', + $code === 0 => 'Network Error', + default => 'Unknown Error' + }; +} + +// Функция для создания пользовательского сообщения об ошибке +function createUserFriendlyMessage(ApiException $e): string { + $errorType = getApiErrorType($e->getCode()); + + return match ($errorType) { + 'Client Error' => 'Ошибка запроса. Проверьте параметры и попробуйте снова.', + 'Server Error' => 'Ошибка сервера. Попробуйте позже.', + 'Network Error' => 'Ошибка сети. Проверьте подключение к интернету.', + default => 'Произошла неизвестная ошибка: ' . $e->getMessage() + }; +} + +// Использование +$client = new Client('your-api-key'); + +// Безопасный вызов API +$film = safeApiCall( + fn() => $client->films->getById(123), + 'получение фильма по ID' +); + +if ($film === null) { + echo "Не удалось получить фильм\n"; +} + +// Детальная обработка ошибки +try { + $client->films->getById(999999); +} catch (ApiException $e) { + $errorDetails = getApiErrorDetails($e); + $userMessage = createUserFriendlyMessage($e); + + echo "Пользовательское сообщение: {$userMessage}\n"; + echo "Тип ошибки: " . getApiErrorType($e->getCode()) . "\n"; + echo "Детали ошибки:\n"; + print_r($errorDetails); +} +``` + +## Создание отчета об ошибках + +```php +class ApiErrorReport { + private ApiException $exception; + private string $operation; + + public function __construct(ApiException $exception, string $operation = 'Unknown') { + $this->exception = $exception; + $this->operation = $operation; + } + + public function getException(): ApiException { + return $this->exception; + } + + public function getOperation(): string { + return $this->operation; + } + + public function getErrorDetails(): array { + return getApiErrorDetails($this->exception); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ОБ ОШИБКЕ API ===\n\n"; + + $errorDetails = $this->getErrorDetails(); + + // Основная информация + $report .= "🔍 ОСНОВНАЯ ИНФОРМАЦИЯ:\n"; + $report .= "Операция: {$this->operation}\n"; + $report .= "Сообщение: {$errorDetails['message']}\n"; + $report .= "Код ошибки: {$errorDetails['code']}\n"; + $report .= "Тип ошибки: " . getApiErrorType($errorDetails['code']) . "\n"; + $report .= "Файл: {$errorDetails['file']}\n"; + $report .= "Строка: {$errorDetails['line']}\n\n"; + + // Предыдущее исключение + if ($errorDetails['previous']) { + $report .= "🔗 ПРЕДЫДУЩЕЕ ИСКЛЮЧЕНИЕ:\n"; + $report .= "Класс: {$errorDetails['previous']['class']}\n"; + $report .= "Сообщение: {$errorDetails['previous']['message']}\n"; + $report .= "Код: {$errorDetails['previous']['code']}\n\n"; + } + + // Пользовательское сообщение + $report .= "💡 ПОЛЬЗОВАТЕЛЬСКОЕ СООБЩЕНИЕ:\n"; + $report .= createUserFriendlyMessage($this->exception) . "\n\n"; + + // Стек вызовов + $report .= "📋 СТЕК ВЫЗОВОВ:\n"; + $report .= $errorDetails['trace'] . "\n"; + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $errorDetails = $this->getErrorDetails(); + + // Основная информация об ошибке + $html .= "
\n"; + $html .= "

Основная информация

\n"; + $html .= "

Операция: {$this->operation}

\n"; + $html .= "

Сообщение: {$errorDetails['message']}

\n"; + $html .= "

Код ошибки: {$errorDetails['code']}

\n"; + $html .= "

Тип ошибки: " . getApiErrorType($errorDetails['code']) . "

\n"; + $html .= "

Файл: {$errorDetails['file']}

\n"; + $html .= "

Строка: {$errorDetails['line']}

\n"; + $html .= "
\n"; + + // Пользовательское сообщение + $html .= "
\n"; + $html .= "

💡 Пользовательское сообщение

\n"; + $html .= "

" . createUserFriendlyMessage($this->exception) . "

\n"; + $html .= "
\n"; + + // Предыдущее исключение + if ($errorDetails['previous']) { + $html .= "
\n"; + $html .= "
🔗 Предыдущее исключение
\n"; + $html .= "

Класс: {$errorDetails['previous']['class']}

\n"; + $html .= "

Сообщение: {$errorDetails['previous']['message']}

\n"; + $html .= "

Код: {$errorDetails['previous']['code']}

\n"; + $html .= "
\n"; + } + + // Стек вызовов + $html .= "
\n"; + $html .= "
📋 Стек вызовов
\n"; + $html .= "
" . htmlspecialchars($errorDetails['trace']) . "
\n"; + $html .= "
\n"; + + $html .= "
\n\n"; + + return $html; + } +} + +// Использование +try { + $client->films->getById(999999); +} catch (ApiException $e) { + $report = new ApiErrorReport($e, 'получение фильма по ID'); + + // Создание текстового отчета + $textReport = $report->createDetailedReport(); + echo $textReport; + + // Создание HTML отчета + $htmlReport = $report->createHtmlReport('Отчет об ошибке API'); + file_put_contents('api_error_report.html', $htmlReport); + echo "\n✅ HTML отчет сохранен в api_error_report.html\n"; +} +``` + +## Анализ ошибок API + +```php +function analyzeApiErrors(array $exceptions): array { + $analysis = [ + 'totalErrors' => count($exceptions), + 'errorTypes' => [], + 'errorCodes' => [], + 'operations' => [], + 'mostCommonErrors' => [], + 'errorTimeline' => [] + ]; + + foreach ($exceptions as $exception) { + if ($exception instanceof ApiException) { + $errorType = getApiErrorType($exception->getCode()); + $operation = 'Unknown'; // Можно добавить логику определения операции + + // Подсчет типов ошибок + $analysis['errorTypes'][$errorType] = ($analysis['errorTypes'][$errorType] ?? 0) + 1; + + // Подсчет кодов ошибок + $analysis['errorCodes'][$exception->getCode()] = ($analysis['errorCodes'][$exception->getCode()] ?? 0) + 1; + + // Подсчет операций + $analysis['operations'][$operation] = ($analysis['operations'][$operation] ?? 0) + 1; + + // Подсчет сообщений об ошибках + $message = $exception->getMessage(); + $analysis['mostCommonErrors'][$message] = ($analysis['mostCommonErrors'][$message] ?? 0) + 1; + } + } + + // Сортировка по частоте + arsort($analysis['errorTypes']); + arsort($analysis['errorCodes']); + arsort($analysis['operations']); + arsort($analysis['mostCommonErrors']); + + return $analysis; +} + +// Использование +$exceptions = [ + new ApiException('Ошибка сети', 0), + new ApiException('Не найдено', 404), + new ApiException('Ошибка сервера', 500), + new ApiException('Не найдено', 404), + new ApiException('Ошибка сети', 0) +]; + +$analysis = analyzeApiErrors($exceptions); + +echo "=== Анализ ошибок API ===\n"; +echo "Всего ошибок: {$analysis['totalErrors']}\n\n"; + +echo "Типы ошибок:\n"; +foreach ($analysis['errorTypes'] as $type => $count) { + echo "- {$type}: {$count}\n"; +} + +echo "\nКоды ошибок:\n"; +foreach ($analysis['errorCodes'] as $code => $count) { + echo "- {$code}: {$count}\n"; +} + +echo "\nЧастые ошибки:\n"; +foreach (array_slice($analysis['mostCommonErrors'], 0, 5, true) as $message => $count) { + echo "- '{$message}': {$count}\n"; +} +``` + +## Связанные классы + +- [`InvalidApiKeyException`](invalid-api-key-exception.md) - Исключение для неверного API ключа +- [`RateLimitException`](rate-limit-exception.md) - Исключение для превышения лимита запросов +- [`ResourceNotFoundException`](resource-not-found-exception.md) - Исключение для не найденных ресурсов +- [`KpValidationException`](kp-validation-exception.md) - Исключение для ошибок валидации diff --git a/docs/dev/notkinopoiskphp/exceptions/index.md b/docs/dev/notkinopoiskphp/exceptions/index.md new file mode 100644 index 0000000..5a5a067 --- /dev/null +++ b/docs/dev/notkinopoiskphp/exceptions/index.md @@ -0,0 +1,422 @@ +# Исключения + +Классы исключений для обработки ошибок в Kinopoisk API. + +--- + +**📚 Навигация:** [Главная](../index.md) → Исключения + +--- + +## 📋 Список исключений + +### ⚠️ [ApiException](api-exception.md) + +Базовое исключение для всех ошибок, связанных с Kinopoisk API. + +**Основные возможности:** + +- Базовый класс для всех исключений API +- Поддержка цепочки исключений +- Коды ошибок (обычно HTTP статус коды) +- Детальные сообщения об ошибках + +**Наследование:** Наследует от стандартного класса `Exception` PHP + +**Использование:** Обработка общих ошибок API + +### 🔑 [InvalidApiKeyException](invalid-api-key-exception.md) + +Исключение для неверного или отсутствующего API ключа. + +**Основные возможности:** + +- Специализированное исключение для проблем с API ключом +- Предустановленный код ошибки 401 +- Стандартное сообщение об ошибке +- Наследование от ApiException + +**Наследование:** Наследует от `ApiException` + +**Использование:** Обработка ошибок аутентификации + +### ⏱️ [RateLimitException](rate-limit-exception.md) + +Исключение для превышения лимита запросов к API. + +**Основные возможности:** + +- Специализированное исключение для превышения лимитов +- Предустановленный код ошибки 429 +- Стандартное сообщение об ошибке +- Наследование от ApiException + +**Наследование:** Наследует от `ApiException` + +**Использование:** Обработка превышения лимитов запросов + +### 🔍 [ResourceNotFoundException](resource-not-found-exception.md) + +Исключение для ненайденных ресурсов в API. + +**Основные возможности:** + +- Специализированное исключение для не найденных ресурсов +- Предустановленный код ошибки 404 +- Стандартное сообщение об ошибке +- Наследование от ApiException + +**Наследование:** Наследует от `ApiException` + +**Использование:** Обработка отсутствующих ресурсов + +### ✅ [KpValidationException](kp-validation-exception.md) + +Исключение для ошибок валидации данных. + +**Основные возможности:** + +- Специализированное исключение для ошибок валидации +- Детальные сообщения об ошибках валидации +- Поддержка предыдущих исключений +- Наследование от ApiException + +**Наследование:** Наследует от `ApiException` + +**Использование:** Обработка ошибок валидации входных данных + +## 🔗 Связанные компоненты + +### Сервисы + +- [FilmService](../services/film-service.md) - Может выбрасывать все типы исключений +- [PersonService](../services/person-service.md) - Может выбрасывать все типы исключений +- [MediaService](../services/media-service.md) - Может выбрасывать все типы исключений +- [UserService](../services/user-service.md) - Может выбрасывать все типы исключений + +### Модели + +- [Film](../models/film.md) - Может выбрасывать KpValidationException +- [Person](../models/person.md) - Может выбрасывать KpValidationException +- [Staff](../models/staff.md) - Может выбрасывать KpValidationException +- [Review](../models/review.md) - Может выбрасывать KpValidationException + +### Ответы + +- [DefaultResponse](../responses/default-response.md) - Может выбрасывать KpValidationException +- [PaginatedResponse](../responses/paginated-response.md) - Может выбрасывать KpValidationException +- [KeywordSearchResponse](../responses/keyword-search-response.md) - Может выбрасывать KpValidationException + +## 🚀 Быстрый старт + +### Обработка исключений + +```php +films->getById(999999); + echo "Фильм получен: {$film->getDisplayName()}\n"; +} catch (ResourceNotFoundException $e) { + echo "Фильм не найден: {$e->getMessage()}\n"; + echo "Код ошибки: {$e->getCode()}\n"; +} catch (RateLimitException $e) { + echo "Превышен лимит запросов: {$e->getMessage()}\n"; + echo "Код ошибки: {$e->getCode()}\n"; + // Ждем и повторяем + sleep(1); +} catch (InvalidApiKeyException $e) { + echo "Неверный API ключ: {$e->getMessage()}\n"; + echo "Код ошибки: {$e->getCode()}\n"; +} catch (KpValidationException $e) { + echo "Ошибка валидации: {$e->getMessage()}\n"; + echo "Код ошибки: {$e->getCode()}\n"; +} catch (ApiException $e) { + echo "Общая ошибка API: {$e->getMessage()}\n"; + echo "Код ошибки: {$e->getCode()}\n"; +} +``` + +### Создание пользовательских исключений + +```php +// Создание базового исключения API +$apiException = new ApiException('Ошибка сети при обращении к API', 500); + +echo "Сообщение: {$apiException->getMessage()}\n"; +echo "Код: {$apiException->getCode()}\n"; + +// Создание исключения с цепочкой +$previousException = new Exception('Ошибка сети'); +$apiException = new ApiException('Не удалось получить данные', 500, $previousException); + +if ($apiException->getPrevious()) { + echo "Причина: " . $apiException->getPrevious()->getMessage() . "\n"; +} + +// Создание специализированных исключений +$notFoundException = new ResourceNotFoundException('Фильм с ID 12345 не существует'); +$rateLimitException = new RateLimitException(); +$invalidKeyException = new InvalidApiKeyException(); +$validationException = new KpValidationException('Некорректные данные фильма'); +``` + +## 📖 Примеры использования + +### Безопасный вызов API + +```php +// Функция для безопасного выполнения API запросов +function safeApiCall(callable $apiCall, string $operation = 'API операция'): mixed { + try { + return $apiCall(); + } catch (ApiException $e) { + logApiError($e, $operation); + return null; + } +} + +// Функция для логирования ошибок API +function logApiError(ApiException $e, string $operation): void { + $logMessage = sprintf( + "[%s] API Error in %s: %s (Code: %d) in %s:%d", + date('Y-m-d H:i:s'), + $operation, + $e->getMessage(), + $e->getCode(), + $e->getFile(), + $e->getLine() + ); + + error_log($logMessage); + + if ($e->getPrevious()) { + error_log("Previous exception: " . $e->getPrevious()->getMessage()); + } +} + +// Использование +$film = safeApiCall( + fn() => $client->films->getById(123), + 'получение фильма по ID' +); + +if ($film === null) { + echo "Не удалось получить фильм\n"; +} +``` + +### Детальная обработка ошибок + +```php +// Функция для получения детальной информации об ошибке +function getApiErrorDetails(ApiException $e): array { + return [ + 'message' => $e->getMessage(), + 'code' => $e->getCode(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'trace' => $e->getTraceAsString(), + 'previous' => $e->getPrevious() ? [ + 'message' => $e->getPrevious()->getMessage(), + 'code' => $e->getPrevious()->getCode(), + 'class' => get_class($e->getPrevious()) + ] : null + ]; +} + +// Функция для определения типа ошибки по коду +function getApiErrorType(int $code): string { + return match (true) { + $code >= 400 && $code < 500 => 'Client Error', + $code >= 500 && $code < 600 => 'Server Error', + $code === 0 => 'Network Error', + default => 'Unknown Error' + }; +} + +// Функция для создания пользовательского сообщения об ошибке +function createUserFriendlyMessage(ApiException $e): string { + $errorType = getApiErrorType($e->getCode()); + + return match ($errorType) { + 'Client Error' => 'Ошибка запроса. Проверьте параметры и попробуйте снова.', + 'Server Error' => 'Ошибка сервера. Попробуйте позже.', + 'Network Error' => 'Ошибка сети. Проверьте подключение к интернету.', + default => 'Произошла неизвестная ошибка: ' . $e->getMessage() + }; +} + +// Использование +try { + $client->films->getById(999999); +} catch (ApiException $e) { + $errorDetails = getApiErrorDetails($e); + $userMessage = createUserFriendlyMessage($e); + + echo "Пользовательское сообщение: {$userMessage}\n"; + echo "Тип ошибки: " . getApiErrorType($e->getCode()) . "\n"; + echo "Детали ошибки:\n"; + print_r($errorDetails); +} +``` + +### Обработка специфических исключений + +```php +// Обработка не найденных ресурсов +try { + $film = $client->films->getById(999999); +} catch (ResourceNotFoundException $e) { + echo "Ресурс не найден: {$e->getMessage()}\n"; + // Можно попробовать поиск по названию + $searchResults = $client->films->searchByKeyword('матрица'); + if (!$searchResults->isEmpty()) { + echo "Найдены похожие фильмы\n"; + } +} + +// Обработка превышения лимита +try { + $films = $client->films->getTop(); +} catch (RateLimitException $e) { + echo "Превышен лимит запросов\n"; + // Ждем и повторяем + sleep(2); + $films = $client->films->getTop(); +} + +// Обработка неверного API ключа +try { + $apiInfo = $client->user->getApiKeyInfo(); +} catch (InvalidApiKeyException $e) { + echo "Неверный API ключ\n"; + // Можно попросить пользователя проверить ключ + echo "Пожалуйста, проверьте правильность API ключа\n"; +} + +// Обработка ошибок валидации +try { + $film = Film::fromArray(['invalid' => 'data']); +} catch (KpValidationException $e) { + echo "Ошибка валидации: {$e->getMessage()}\n"; + // Можно показать пользователю, какие поля некорректны +} +``` + +## 🔧 Общие методы + +Все исключения наследуют методы от стандартного класса `Exception`: + +### getMessage() + +```php +public function getMessage(): string +``` + +Возвращает сообщение об ошибке. + +### getCode() + +```php +public function getCode(): int +``` + +Возвращает код ошибки. + +### getFile() + +```php +public function getFile(): string +``` + +Возвращает путь к файлу, где произошла ошибка. + +### getLine() + +```php +public function getLine(): int +``` + +Возвращает номер строки, где произошла ошибка. + +### getTrace() + +```php +public function getTrace(): array +``` + +Возвращает стек вызовов. + +### getTraceAsString() + +```php +public function getTraceAsString(): string +``` + +Возвращает стек вызовов в виде строки. + +### getPrevious() + +```php +public function getPrevious(): ?Throwable +``` + +Возвращает предыдущее исключение в цепочке. + +## 📊 Статистика исключений + +### ApiException + +- **Наследование:** Exception +- **Код по умолчанию:** 0 +- **Использование:** Базовый класс для всех исключений API + +### InvalidApiKeyException + +- **Наследование:** ApiException +- **Код по умолчанию:** 401 +- **Использование:** Ошибки аутентификации + +### RateLimitException + +- **Наследование:** ApiException +- **Код по умолчанию:** 429 +- **Использование:** Превышение лимитов запросов + +### ResourceNotFoundException + +- **Наследование:** ApiException +- **Код по умолчанию:** 404 +- **Использование:** Не найденные ресурсы + +### KpValidationException + +- **Наследование:** ApiException +- **Код по умолчанию:** 400 +- **Использование:** Ошибки валидации данных + +## 🔗 Связанные разделы + +- [Сервисы](../services/index.md) - Выбрасывают исключения +- [Модели](../models/index.md) - Могут выбрасывать исключения валидации +- [Ответы](../responses/index.md) - Могут выбрасывать исключения валидации +- [Перечисления](../enums/index.md) - Используются в исключениях +- [Интерфейсы](../interfaces/index.md) - Базовые интерфейсы + +--- + +**📚 Навигация:** [Главная](../index.md) → Исключения diff --git a/docs/dev/notkinopoiskphp/exceptions/invalid-api-key-exception.md b/docs/dev/notkinopoiskphp/exceptions/invalid-api-key-exception.md new file mode 100644 index 0000000..75b9c4d --- /dev/null +++ b/docs/dev/notkinopoiskphp/exceptions/invalid-api-key-exception.md @@ -0,0 +1,226 @@ +# InvalidApiKeyException + +Исключение для неверного или отсутствующего API ключа. + +## Описание + +`InvalidApiKeyException` выбрасывается при попытке обращения к API с неверным, отсутствующим или заблокированным API ключом. Соответствует HTTP статус коду 401 (Unauthorized). + +## Наследование + +```php +InvalidApiKeyException extends ApiException +``` + +## Конструктор + +```php +public function __construct(string $message = 'Неверный или отсутствующий API ключ', int $code = 401) +``` + +### Параметры + +- `$message` (string) - Сообщение об ошибке (по умолчанию стандартное) +- `$code` (int) - Код ошибки (по умолчанию 401 - Unauthorized) + +## Примеры использования + +### Базовое использование + +```php +try { + $client = new Client('invalid-key'); + $client->films->getById(301); +} catch (InvalidApiKeyException $e) { + echo "Проблема с API ключом: " . $e->getMessage(); + // Выведет: "Проблема с API ключом: Неверный или отсутствующий API ключ" +} +``` + +### Создание исключения + +```php +throw new InvalidApiKeyException('API ключ заблокирован администратором'); +``` + +### Обработка в приложении + +```php +users; + + // Попытка получить информацию об API ключе + $keyInfo = $userService->getApiKeyInfo($apiKey); + + echo "✅ API ключ действителен\n"; + echo "Тип аккаунта: {$keyInfo->accountType}\n"; + echo "Осталось запросов: {$keyInfo->getRemainingQuota()}\n"; + + return true; + + } catch (InvalidApiKeyException $e) { + echo "❌ Ошибка API ключа: {$e->getMessage()}\n"; + echo "Код ошибки: {$e->getCode()}\n"; + + // Рекомендации пользователю + echo "\n💡 Рекомендации:\n"; + echo "- Проверьте правильность API ключа\n"; + echo "- Убедитесь, что ключ не заблокирован\n"; + echo "- Получите новый ключ на https://kinopoiskapiunofficial.tech\n"; + + return false; + + } catch (ApiException $e) { + echo "❌ Другая ошибка API: {$e->getMessage()}\n"; + return false; + } +} + +// Использование +$apiKey = $_ENV['KINOPOISK_API_KEY'] ?? ''; +if (empty($apiKey)) { + echo "❌ API ключ не указан в переменных окружения\n"; + exit(1); +} + +if (!checkApiKey($apiKey)) { + exit(1); +} +``` + +### Валидация API ключа + +```php +class ApiKeyValidator { + private Client $client; + + public function __construct(string $apiKey) { + $this->client = new Client($apiKey); + } + + public function validate(): array { + try { + $userService = $this->client->users; + $keyInfo = $userService->getApiKeyInfo($this->client->getApiKey()); + + return [ + 'valid' => true, + 'accountType' => $keyInfo->accountType, + 'remainingQuota' => $keyInfo->getRemainingQuota(), + 'dailyQuota' => $keyInfo->getRemainingDailyQuota(), + 'message' => 'API ключ действителен' + ]; + + } catch (InvalidApiKeyException $e) { + return [ + 'valid' => false, + 'error' => $e->getMessage(), + 'code' => $e->getCode(), + 'suggestions' => [ + 'Проверьте правильность ключа', + 'Убедитесь, что ключ активен', + 'Получите новый ключ при необходимости' + ] + ]; + } + } +} + +// Использование +$validator = new ApiKeyValidator('your-api-key'); +$result = $validator->validate(); + +if ($result['valid']) { + echo "✅ {$result['message']}\n"; + echo "Тип аккаунта: {$result['accountType']}\n"; + echo "Осталось запросов: {$result['remainingQuota']}\n"; +} else { + echo "❌ {$result['error']}\n"; + echo "Код: {$result['code']}\n"; + echo "Рекомендации:\n"; + foreach ($result['suggestions'] as $suggestion) { + echo "- {$suggestion}\n"; + } +} +``` + +### Обработка в middleware + +```php +class ApiKeyMiddleware { + private string $apiKey; + + public function __construct(string $apiKey) { + $this->apiKey = $apiKey; + } + + public function handle(callable $operation) { + try { + return $operation(); + + } catch (InvalidApiKeyException $e) { + // Логирование ошибки + error_log("API Key Error: {$e->getMessage()}"); + + // Возврат ошибки пользователю + return [ + 'success' => false, + 'error' => 'authentication_failed', + 'message' => 'Проблема с аутентификацией API', + 'details' => $e->getMessage() + ]; + + } catch (ApiException $e) { + return [ + 'success' => false, + 'error' => 'api_error', + 'message' => 'Ошибка API', + 'details' => $e->getMessage() + ]; + } + } +} + +// Использование +$middleware = new ApiKeyMiddleware('your-api-key'); + +$result = $middleware->handle(function() { + $client = new Client('your-api-key'); + return $client->films->getById(301); +}); + +if (!$result['success']) { + echo "Ошибка: {$result['message']}\n"; + echo "Детали: {$result['details']}\n"; +} else { + echo "Фильм: " . $result->getDisplayName() . "\n"; +} +``` + +## HTTP статус коды + +- **401 Unauthorized** - Неверный или отсутствующий API ключ +- **403 Forbidden** - API ключ заблокирован или превышены лимиты + +## Рекомендации по обработке + +1. **Проверка ключа** - Всегда валидируйте API ключ перед началом работы +2. **Логирование** - Логируйте ошибки аутентификации для отладки +3. **Пользовательские сообщения** - Предоставляйте понятные сообщения пользователям +4. **Резервные планы** - Имейте план действий при проблемах с API ключом + +## Связанные исключения + +- [`ApiException`](api-exception.md) - Базовое исключение API +- [`RateLimitException`](rate-limit-exception.md) - Превышение лимитов запросов +- [`ResourceNotFoundException`](resource-not-found-exception.md) - Ресурс не найден diff --git a/docs/dev/notkinopoiskphp/exceptions/rate-limit-exception.md b/docs/dev/notkinopoiskphp/exceptions/rate-limit-exception.md new file mode 100644 index 0000000..08017bb --- /dev/null +++ b/docs/dev/notkinopoiskphp/exceptions/rate-limit-exception.md @@ -0,0 +1,323 @@ +# RateLimitException + +Исключение для превышения лимитов запросов к API. + +## Описание + +`RateLimitException` выбрасывается при превышении дневного, месячного лимита запросов или лимита запросов в секунду. Соответствует HTTP статус кодам 402 (Payment Required) и 429 (Too Many Requests). + +## Наследование + +```php +RateLimitException extends ApiException +``` + +## Конструктор + +```php +public function __construct(string $message = 'Превышен лимит запросов', int $code = 429) +``` + +### Параметры + +- `$message` (string) - Сообщение об ошибке (по умолчанию стандартное) +- `$code` (int) - Код ошибки (по умолчанию 429 - Too Many Requests) + +## Примеры использования + +### Базовое использование + +```php +try { + // Множественные запросы в цикле + for ($i = 0; $i < 1000; $i++) { + $client->films->getById($i); + } +} catch (RateLimitException $e) { + echo "Превышен лимит запросов: " . $e->getMessage(); + // Рекомендуется добавить задержку перед повторными запросами +} +``` + +### Создание исключения + +```php +throw new RateLimitException('Превышен дневной лимит запросов', 402); +``` + +### Обработка в приложении + +```php +getMessage()}\n"; + echo "Код: {$e->getCode()}\n"; + + if ($retryCount < $maxRetries) { + $delay = pow(2, $retryCount) * 1000; // Экспоненциальная задержка + echo "⏳ Ожидание {$delay}мс перед повторной попыткой...\n"; + usleep($delay * 1000); + } else { + echo "❌ Превышено максимальное количество попыток\n"; + throw $e; + } + + } catch (ApiException $e) { + echo "❌ Ошибка API: {$e->getMessage()}\n"; + throw $e; + } + } +} + +// Использование +$client = new Client('your-api-key'); + +try { + $result = makeApiRequest($client, function() use ($client) { + return $client->films->getById(301); + }); + + echo "✅ Запрос выполнен успешно\n"; + +} catch (RateLimitException $e) { + echo "❌ Не удалось выполнить запрос из-за лимитов\n"; +} +``` + +### Мониторинг лимитов + +```php +class RateLimitMonitor { + private Client $client; + private array $requestCounts = []; + + public function __construct(Client $client) { + $this->client = $client; + } + + public function checkLimits(): array { + try { + $userService = $this->client->users; + $keyInfo = $userService->getApiKeyInfo($this->client->getApiKey()); + + return [ + 'remainingTotal' => $keyInfo->getRemainingQuota(), + 'remainingDaily' => $keyInfo->getRemainingDailyQuota(), + 'usedTotal' => $keyInfo->getTotalQuotaUsed(), + 'usedDaily' => $keyInfo->dailyQuota->used, + 'accountType' => $keyInfo->accountType->value + ]; + + } catch (\Exception $e) { + return [ + 'error' => $e->getMessage(), + 'remainingTotal' => 0, + 'remainingDaily' => 0 + ]; + } + } + + public function isNearLimit(int $threshold = 10): bool { + $limits = $this->checkLimits(); + + return $limits['remainingDaily'] <= $threshold || + $limits['remainingTotal'] <= $threshold; + } + + public function makeRequest(callable $request): mixed { + // Проверка лимитов перед запросом + if ($this->isNearLimit()) { + echo "⚠️ Внимание: Осталось мало запросов!\n"; + $limits = $this->checkLimits(); + echo "Осталось сегодня: {$limits['remainingDaily']}\n"; + echo "Осталось всего: {$limits['remainingTotal']}\n"; + } + + try { + $result = $request(); + + // Обновление счетчика запросов + $this->incrementRequestCount(); + + return $result; + + } catch (RateLimitException $e) { + echo "❌ Превышен лимит запросов\n"; + $this->handleRateLimit($e); + throw $e; + } + } + + private function incrementRequestCount(): void { + $today = date('Y-m-d'); + if (!isset($this->requestCounts[$today])) { + $this->requestCounts[$today] = 0; + } + $this->requestCounts[$today]++; + } + + private function handleRateLimit(RateLimitException $e): void { + echo "🔄 Стратегия обработки превышения лимитов:\n"; + + switch ($e->getCode()) { + case 402: + echo "- Превышен месячный лимит\n"; + echo "- Рекомендуется обновить план подписки\n"; + break; + case 429: + echo "- Превышен лимит запросов в секунду\n"; + echo "- Рекомендуется добавить задержку между запросами\n"; + break; + default: + echo "- Неизвестный тип превышения лимита\n"; + } + } +} + +// Использование +$monitor = new RateLimitMonitor($client); + +// Проверка текущих лимитов +$limits = $monitor->checkLimits(); +echo "=== Текущие лимиты ===\n"; +echo "Осталось запросов (всего): {$limits['remainingTotal']}\n"; +echo "Осталось запросов (сегодня): {$limits['remainingDaily']}\n"; +echo "Тип аккаунта: {$limits['accountType']}\n"; + +// Выполнение запроса с мониторингом +try { + $film = $monitor->makeRequest(function() use ($client) { + return $client->films->getById(301); + }); + + echo "✅ Фильм получен: {$film->getDisplayName()}\n"; + +} catch (RateLimitException $e) { + echo "❌ Не удалось получить фильм\n"; +} +``` + +### Стратегии обработки лимитов + +```php +class RateLimitStrategy { + private Client $client; + private int $requestDelay = 100; // миллисекунды + + public function __construct(Client $client) { + $this->client = $client; + } + + public function setRequestDelay(int $delay): void { + $this->requestDelay = $delay; + } + + public function makeBatchRequests(array $requests, int $batchSize = 10): array { + $results = []; + $batches = array_chunk($requests, $batchSize); + + foreach ($batches as $batchIndex => $batch) { + echo "Обработка батча " . ($batchIndex + 1) . " из " . count($batches) . "\n"; + + foreach ($batch as $request) { + try { + $result = $this->makeRequestWithRetry($request); + $results[] = $result; + + // Задержка между запросами + usleep($this->requestDelay * 1000); + + } catch (RateLimitException $e) { + echo "Превышен лимит в батче " . ($batchIndex + 1) . "\n"; + echo "Ожидание 60 секунд...\n"; + sleep(60); + + // Повторная попытка для текущего батча + $batchIndex--; + break; + } + } + } + + return $results; + } + + private function makeRequestWithRetry(callable $request, int $maxRetries = 3): mixed { + $retryCount = 0; + + while ($retryCount < $maxRetries) { + try { + return $request(); + + } catch (RateLimitException $e) { + $retryCount++; + + if ($retryCount < $maxRetries) { + $delay = pow(2, $retryCount) * 5; // 5, 10, 20 секунд + echo "Повторная попытка через {$delay} секунд...\n"; + sleep($delay); + } else { + throw $e; + } + } + } + } +} + +// Использование +$strategy = new RateLimitStrategy($client); +$strategy->setRequestDelay(200); // 200мс между запросами + +// Создание массива запросов +$requests = []; +for ($i = 1; $i <= 50; $i++) { + $requests[] = function() use ($client, $i) { + return $client->films->getById($i); + }; +} + +// Выполнение батчевых запросов +try { + $results = $strategy->makeBatchRequests($requests, 5); + echo "✅ Успешно обработано " . count($results) . " запросов\n"; + +} catch (RateLimitException $e) { + echo "❌ Не удалось обработать все запросы\n"; +} +``` + +## HTTP статус коды + +- **402 Payment Required** - Превышен месячный лимит запросов +- **429 Too Many Requests** - Превышен лимит запросов в секунду + +## Рекомендации по обработке + +1. **Мониторинг лимитов** - Регулярно проверяйте оставшиеся запросы +2. **Экспоненциальная задержка** - Увеличивайте время ожидания при повторных попытках +3. **Батчевая обработка** - Группируйте запросы и добавляйте задержки между батчами +4. **Кэширование** - Сохраняйте результаты запросов для уменьшения нагрузки +5. **Планирование** - Распределяйте запросы равномерно в течение дня + +## Связанные исключения + +- [`ApiException`](api-exception.md) - Базовое исключение API +- [`InvalidApiKeyException`](invalid-api-key-exception.md) - Неверный API ключ +- [`ResourceNotFoundException`](resource-not-found-exception.md) - Ресурс не найден diff --git a/docs/dev/notkinopoiskphp/exceptions/resource-not-found-exception.md b/docs/dev/notkinopoiskphp/exceptions/resource-not-found-exception.md new file mode 100644 index 0000000..e394ee1 --- /dev/null +++ b/docs/dev/notkinopoiskphp/exceptions/resource-not-found-exception.md @@ -0,0 +1,472 @@ +# ResourceNotFoundException + +Исключение для ненайденных ресурсов в API. + +## Описание + +`ResourceNotFoundException` выбрасывается при попытке обращения к несуществующему ресурсу (фильму, персоне, сериалу и т.д.). Соответствует HTTP статус коду 404 (Not Found). + +### Основные возможности + +- Специализированное исключение для не найденных ресурсов +- Предустановленный код ошибки 404 +- Стандартное сообщение об ошибке +- Наследование от базового ApiException + +## Наследование + +Наследует от [`ApiException`](api-exception.md). + +## Конструктор + +```php +public function __construct( + string $message = 'Запрашиваемый ресурс не найден', + int $code = 404 +) +``` + +### Параметры + +- `$message` (string) - Сообщение об ошибке (по умолчанию стандартное) +- `$code` (int) - Код ошибки (по умолчанию 404 - Not Found) + +### Пример использования + +```php +throw new ResourceNotFoundException('Фильм с ID 12345 не существует'); +``` + +## Полный пример использования + +```php +films->getById(999999999); + echo "Фильм получен: {$film->getDisplayName()}\n"; +} catch (ResourceNotFoundException $e) { + echo "Ресурс не найден: {$e->getMessage()}\n"; + echo "Код ошибки: {$e->getCode()}\n"; + echo "Файл: {$e->getFile()}\n"; + echo "Строка: {$e->getLine()}\n"; +} + +// Пример с пользовательским сообщением +try { + // Попытка получить несуществующую персону + $person = $client->persons->getById(999999999); + echo "Персона получена: {$person->getDisplayName()}\n"; +} catch (ResourceNotFoundException $e) { + echo "Персона не найдена: {$e->getMessage()}\n"; +} + +// Пример создания пользовательского исключения +try { + throw new ResourceNotFoundException('Фильм с ID 12345 не существует'); +} catch (ResourceNotFoundException $e) { + echo "\nПоймано пользовательское исключение:\n"; + echo "Сообщение: {$e->getMessage()}\n"; + echo "Код: {$e->getCode()}\n"; +} + +// Пример с цепочкой исключений +try { + $previousException = new Exception('Ошибка сети'); + throw new ResourceNotFoundException('Фильм не найден в базе данных', 404, $previousException); +} catch (ResourceNotFoundException $e) { + echo "\nИсключение с цепочкой:\n"; + echo "Сообщение: {$e->getMessage()}\n"; + echo "Код: {$e->getCode()}\n"; + + if ($e->getPrevious()) { + echo "Причина: " . $e->getPrevious()->getMessage() . "\n"; + } +} +``` + +## Обработка не найденных ресурсов + +```php +// Функция для безопасного получения ресурса с обработкой ошибок +function safeGetResource(callable $apiCall, string $resourceType = 'ресурс'): mixed { + try { + return $apiCall(); + } catch (ResourceNotFoundException $e) { + logResourceNotFound($e, $resourceType); + return null; + } +} + +// Функция для логирования не найденных ресурсов +function logResourceNotFound(ResourceNotFoundException $e, string $resourceType): void { + $logMessage = sprintf( + "[%s] Resource Not Found: %s - %s (Code: %d) in %s:%d", + date('Y-m-d H:i:s'), + $resourceType, + $e->getMessage(), + $e->getCode(), + $e->getFile(), + $e->getLine() + ); + + error_log($logMessage); +} + +// Функция для получения детальной информации об ошибке +function getResourceNotFoundDetails(ResourceNotFoundException $e): array { + return [ + 'message' => $e->getMessage(), + 'code' => $e->getCode(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'trace' => $e->getTraceAsString(), + 'previous' => $e->getPrevious() ? [ + 'message' => $e->getPrevious()->getMessage(), + 'code' => $e->getPrevious()->getCode(), + 'class' => get_class($e->getPrevious()) + ] : null + ]; +} + +// Функция для создания пользовательского сообщения об ошибке +function createResourceNotFoundMessage(ResourceNotFoundException $e, string $resourceType = 'ресурс'): string { + return "Запрашиваемый {$resourceType} не найден. Пожалуйста, проверьте правильность идентификатора."; +} + +// Функция для проверки существования ресурса +function checkResourceExists(callable $apiCall): bool { + try { + $apiCall(); + return true; + } catch (ResourceNotFoundException $e) { + return false; + } +} + +// Функция для получения альтернативных ресурсов +function getAlternativeResources(callable $apiCall, callable $fallbackCall): mixed { + try { + return $apiCall(); + } catch (ResourceNotFoundException $e) { + // Попытка получить альтернативный ресурс + try { + return $fallbackCall(); + } catch (ResourceNotFoundException $fallbackException) { + // Если и альтернативный ресурс не найден + throw $e; // Возвращаем оригинальное исключение + } + } +} + +// Использование +$client = new Client('your-api-key'); + +// Безопасное получение фильма +$film = safeGetResource( + fn() => $client->films->getById(123), + 'фильм' +); + +if ($film === null) { + echo "Фильм не найден\n"; +} + +// Проверка существования ресурса +$exists = checkResourceExists(fn() => $client->films->getById(123)); +echo "Фильм существует: " . ($exists ? 'Да' : 'Нет') . "\n"; + +// Получение альтернативного ресурса +try { + $film = getAlternativeResources( + fn() => $client->films->getById(999999), // Основной запрос + fn() => $client->films->getById(123) // Альтернативный запрос + ); + echo "Получен фильм: {$film->getDisplayName()}\n"; +} catch (ResourceNotFoundException $e) { + echo "Ни основной, ни альтернативный фильм не найдены\n"; +} + +// Детальная обработка ошибки +try { + $client->films->getById(999999); +} catch (ResourceNotFoundException $e) { + $errorDetails = getResourceNotFoundDetails($e); + $userMessage = createResourceNotFoundMessage($e, 'фильм'); + + echo "Пользовательское сообщение: {$userMessage}\n"; + echo "Детали ошибки:\n"; + print_r($errorDetails); +} +``` + +## Создание отчета о не найденных ресурсах + +```php +class ResourceNotFoundReport { + private ResourceNotFoundException $exception; + private string $resourceType; + private string $resourceId; + + public function __construct(ResourceNotFoundException $exception, string $resourceType = 'Unknown', string $resourceId = 'Unknown') { + $this->exception = $exception; + $this->resourceType = $resourceType; + $this->resourceId = $resourceId; + } + + public function getException(): ResourceNotFoundException { + return $this->exception; + } + + public function getResourceType(): string { + return $this->resourceType; + } + + public function getResourceId(): string { + return $this->resourceId; + } + + public function getErrorDetails(): array { + return getResourceNotFoundDetails($this->exception); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ О НЕ НАЙДЕННОМ РЕСУРСЕ ===\n\n"; + + $errorDetails = $this->getErrorDetails(); + + // Основная информация + $report .= "🔍 ОСНОВНАЯ ИНФОРМАЦИЯ:\n"; + $report .= "Тип ресурса: {$this->resourceType}\n"; + $report .= "ID ресурса: {$this->resourceId}\n"; + $report .= "Сообщение: {$errorDetails['message']}\n"; + $report .= "Код ошибки: {$errorDetails['code']}\n"; + $report .= "Файл: {$errorDetails['file']}\n"; + $report .= "Строка: {$errorDetails['line']}\n\n"; + + // Предыдущее исключение + if ($errorDetails['previous']) { + $report .= "🔗 ПРЕДЫДУЩЕЕ ИСКЛЮЧЕНИЕ:\n"; + $report .= "Класс: {$errorDetails['previous']['class']}\n"; + $report .= "Сообщение: {$errorDetails['previous']['message']}\n"; + $report .= "Код: {$errorDetails['previous']['code']}\n\n"; + } + + // Пользовательское сообщение + $report .= "💡 ПОЛЬЗОВАТЕЛЬСКОЕ СООБЩЕНИЕ:\n"; + $report .= createResourceNotFoundMessage($this->exception, $this->resourceType) . "\n\n"; + + // Рекомендации + $report .= "💡 РЕКОМЕНДАЦИИ:\n"; + $report .= "• Проверьте правильность ID ресурса\n"; + $report .= "• Убедитесь, что ресурс существует в базе данных\n"; + $report .= "• Попробуйте поиск по названию вместо ID\n"; + $report .= "• Проверьте права доступа к ресурсу\n\n"; + + // Стек вызовов + $report .= "📋 СТЕК ВЫЗОВОВ:\n"; + $report .= $errorDetails['trace'] . "\n"; + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $errorDetails = $this->getErrorDetails(); + + // Информация о ресурсе + $html .= "
\n"; + $html .= "

Информация о ресурсе

\n"; + $html .= "

Тип ресурса: {$this->resourceType}

\n"; + $html .= "

ID ресурса: {$this->resourceId}

\n"; + $html .= "
\n"; + + // Основная информация об ошибке + $html .= "
\n"; + $html .= "

Основная информация

\n"; + $html .= "

Сообщение: {$errorDetails['message']}

\n"; + $html .= "

Код ошибки: {$errorDetails['code']}

\n"; + $html .= "

Файл: {$errorDetails['file']}

\n"; + $html .= "

Строка: {$errorDetails['line']}

\n"; + $html .= "
\n"; + + // Пользовательское сообщение + $html .= "
\n"; + $html .= "

💡 Пользовательское сообщение

\n"; + $html .= "

" . createResourceNotFoundMessage($this->exception, $this->resourceType) . "

\n"; + $html .= "
\n"; + + // Рекомендации + $html .= "
\n"; + $html .= "

💡 Рекомендации

\n"; + $html .= "
    \n"; + $html .= "
  • Проверьте правильность ID ресурса
  • \n"; + $html .= "
  • Убедитесь, что ресурс существует в базе данных
  • \n"; + $html .= "
  • Попробуйте поиск по названию вместо ID
  • \n"; + $html .= "
  • Проверьте права доступа к ресурсу
  • \n"; + $html .= "
\n"; + $html .= "
\n"; + + // Предыдущее исключение + if ($errorDetails['previous']) { + $html .= "
\n"; + $html .= "
🔗 Предыдущее исключение
\n"; + $html .= "

Класс: {$errorDetails['previous']['class']}

\n"; + $html .= "

Сообщение: {$errorDetails['previous']['message']}

\n"; + $html .= "

Код: {$errorDetails['previous']['code']}

\n"; + $html .= "
\n"; + } + + // Стек вызовов + $html .= "
\n"; + $html .= "
📋 Стек вызовов
\n"; + $html .= "
" . htmlspecialchars($errorDetails['trace']) . "
\n"; + $html .= "
\n"; + + $html .= "
\n\n"; + + return $html; + } +} + +// Использование +try { + $client->films->getById(999999); +} catch (ResourceNotFoundException $e) { + $report = new ResourceNotFoundReport($e, 'фильм', '999999'); + + // Создание текстового отчета + $textReport = $report->createDetailedReport(); + echo $textReport; + + // Создание HTML отчета + $htmlReport = $report->createHtmlReport('Отчет о не найденном ресурсе'); + file_put_contents('resource_not_found_report.html', $htmlReport); + echo "\n✅ HTML отчет сохранен в resource_not_found_report.html\n"; +} +``` + +## Анализ не найденных ресурсов + +```php +function analyzeResourceNotFoundErrors(array $exceptions): array { + $analysis = [ + 'totalErrors' => count($exceptions), + 'resourceTypes' => [], + 'resourceIds' => [], + 'mostCommonErrors' => [], + 'errorPatterns' => [] + ]; + + foreach ($exceptions as $exception) { + if ($exception instanceof ResourceNotFoundException) { + // Извлекаем информацию о ресурсе из сообщения (если возможно) + $message = $exception->getMessage(); + + // Простая логика извлечения типа ресурса + $resourceType = 'Unknown'; + if (strpos($message, 'фильм') !== false) { + $resourceType = 'Film'; + } elseif (strpos($message, 'персона') !== false) { + $resourceType = 'Person'; + } elseif (strpos($message, 'сериал') !== false) { + $resourceType = 'Series'; + } + + // Подсчет типов ресурсов + $analysis['resourceTypes'][$resourceType] = ($analysis['resourceTypes'][$resourceType] ?? 0) + 1; + + // Подсчет сообщений об ошибках + $analysis['mostCommonErrors'][$message] = ($analysis['mostCommonErrors'][$message] ?? 0) + 1; + + // Анализ паттернов ошибок + $pattern = getErrorPattern($message); + $analysis['errorPatterns'][$pattern] = ($analysis['errorPatterns'][$pattern] ?? 0) + 1; + } + } + + // Сортировка по частоте + arsort($analysis['resourceTypes']); + arsort($analysis['mostCommonErrors']); + arsort($analysis['errorPatterns']); + + return $analysis; +} + +// Функция для определения паттерна ошибки +function getErrorPattern(string $message): string { + if (strpos($message, 'не найден') !== false) { + return 'Resource Not Found'; + } elseif (strpos($message, 'не существует') !== false) { + return 'Resource Does Not Exist'; + } elseif (strpos($message, 'недоступен') !== false) { + return 'Resource Unavailable'; + } else { + return 'Unknown Pattern'; + } +} + +// Использование +$exceptions = [ + new ResourceNotFoundException('Фильм не найден'), + new ResourceNotFoundException('Персона не найдена'), + new ResourceNotFoundException('Сериал не найден'), + new ResourceNotFoundException('Фильм не найден'), + new ResourceNotFoundException('Запрашиваемый ресурс не найден') +]; + +$analysis = analyzeResourceNotFoundErrors($exceptions); + +echo "=== Анализ не найденных ресурсов ===\n"; +echo "Всего ошибок: {$analysis['totalErrors']}\n\n"; + +echo "Типы ресурсов:\n"; +foreach ($analysis['resourceTypes'] as $type => $count) { + echo "- {$type}: {$count}\n"; +} + +echo "\nПаттерны ошибок:\n"; +foreach ($analysis['errorPatterns'] as $pattern => $count) { + echo "- {$pattern}: {$count}\n"; +} + +echo "\nЧастые ошибки:\n"; +foreach (array_slice($analysis['mostCommonErrors'], 0, 5, true) as $message => $count) { + echo "- '{$message}': {$count}\n"; +} +``` + +## Связанные классы + +- [`ApiException`](api-exception.md) - Базовое исключение для всех ошибок API +- [`InvalidApiKeyException`](invalid-api-key-exception.md) - Исключение для неверного API ключа +- [`RateLimitException`](rate-limit-exception.md) - Исключение для превышения лимита запросов +- [`KpValidationException`](kp-validation-exception.md) - Исключение для ошибок валидации diff --git a/docs/dev/notkinopoiskphp/index.md b/docs/dev/notkinopoiskphp/index.md new file mode 100644 index 0000000..18af33a --- /dev/null +++ b/docs/dev/notkinopoiskphp/index.md @@ -0,0 +1,269 @@ +# NotKinopoisk PHP Library - Документация + +Полная документация PHP библиотеки для работы с Kinopoisk Unofficial API. + +## 📚 Содержание + +### 🚀 Быстрый старт + +- [Основной клиент](client.md) - Главный класс для работы с API +- [Примеры использования](../examples/) - Готовые примеры кода +- [Карта навигации](navigation-map.md) - Интерактивная карта всей документации + +### 📦 Основные компоненты + +#### 🔧 Сервисы + +- [FilmService](services/film-service.md) - Работа с фильмами +- [PersonService](services/person-service.md) - Работа с персонами +- [MediaService](services/media-service.md) - Работа с медиа +- [UserService](services/user-service.md) - Работа с пользователями + +#### 📊 Модели данных + +- [Film](models/film.md) - Модель фильма +- [Person](models/person.md) - Модель персоны +- [Staff](models/staff.md) - Модель съемочной группы +- [Review](models/review.md) - Модель отзыва +- [Image](models/image.md) - Модель изображения +- [Video](models/video.md) - Модель видео +- [Fact](models/fact.md) - Модель факта +- [Award](models/award.md) - Модель награды +- [BoxOffice](models/box-office.md) - Модель кассовых сборов +- [Country](models/country.md) - Модель страны +- [Genre](models/genre.md) - Модель жанра +- [Episode](models/episode.md) - Модель эпизода +- [Season](models/season.md) - Модель сезона +- [ExternalSource](models/external-source.md) - Модель внешнего источника +- [Distribution](models/distribution.md) - Модель дистрибуции +- [FilmSearchResult](models/film-search-result.md) - Модель результата поиска +- [PersonSpouse](models/person-spouse.md) - Модель супруга +- [PersonFilm](models/person-film.md) - Модель фильма персоны +- [UserVote](models/user-vote.md) - Модель голоса пользователя +- [FilmCollection](models/film-collection.md) - Модель коллекции фильмов +- [Filters](models/filters.md) - Модель фильтров +- [RelatedFilm](models/related-film.md) - Модель связанного фильма +- [ApiKeyInfo](models/api-key-info.md) - Модель информации об API ключе +- [ApiKeyQouta](models/api-key-qouta.md) - Модель квоты API ключа +- [MediaPost](models/media-post.md) - Модель медиа поста + +#### 🔢 Перечисления (Enums) + +- [ImageType](enums/image-type.md) - Типы изображений +- [ReviewOrder](enums/review-order.md) - Порядок сортировки отзывов +- [ReviewType](enums/review-type.md) - Типы отзывов +- [FactType](enums/fact-type.md) - Типы фактов +- [ProfessionKey](enums/profession-key.md) - Ключи профессий +- [VideoSite](enums/video-site.md) - Сайты видео +- [BoxOfficeType](enums/box-office-type.md) - Типы кассовых сборов +- [DistributionType](enums/distribution-type.md) - Типы дистрибуции +- [RelationType](enums/relation-type.md) - Типы связей +- [Sex](enums/sex.md) - Пол +- [ApiVersion](enums/api-version.md) - Версии API +- [Month](enums/month.md) - Месяцы +- [FilmOrder](enums/film-order.md) - Порядок сортировки фильмов +- [ContentType](enums/content-type.md) - Типы контента +- [CollectionType](enums/collection-type.md) - Типы коллекций +- [DistributionSubType](enums/distribution-sub-type.md) - Подтипы дистрибуции +- [AccountType](enums/account-type.md) - Типы аккаунтов + +#### 📤 Ответы API + +- [DefaultResponse](responses/default-response.md) - Базовый ответ +- [PaginatedResponse](responses/paginated-response.md) - Пагинированный ответ +- [KeywordSearchResponse](responses/keyword-search-response.md) - Ответ поиска по ключевым словам + +#### ⚠️ Исключения + +- [ApiException](exceptions/api-exception.md) - Базовое исключение API +- [InvalidApiKeyException](exceptions/invalid-api-key-exception.md) - Неверный API ключ +- [RateLimitException](exceptions/rate-limit-exception.md) - Превышение лимита запросов +- [ResourceNotFoundException](exceptions/resource-not-found-exception.md) - Ресурс не найден +- [KpValidationException](exceptions/kp-validation-exception.md) - Ошибка валидации + +#### 🔗 Интерфейсы + +- [ModelInterface](interfaces/model-interface.md) - Интерфейс модели +- [ResponseInterface](interfaces/response-interface.md) - Интерфейс ответа + +## 🚀 Быстрый старт + +### Установка + +```bash +composer require devcraftclub/kinopoiskapiunofficialtech +``` + +### Базовое использование + +```php +films->getById(301); + +echo "Фильм: {$film->getDisplayName()}\n"; +echo "Рейтинг: {$film->rating}\n"; +echo "Год: {$film->year}\n"; +``` + +## 📖 Подробные примеры + +### Работа с фильмами + +```php +// Поиск фильмов +$searchResults = $client->films->searchByKeyword('матрица'); + +// Получение топ фильмов +$topFilms = $client->films->getTop(); + +// Получение информации о фильме +$film = $client->films->getById(301); +$staff = $client->films->getStaff(301); +$facts = $client->films->getFacts(301); +``` + +### Работа с персонами + +```php +// Поиск персон +$persons = $client->persons->searchByName('Том Круз'); + +// Получение информации о персоне +$person = $client->persons->getById(123); +$films = $client->persons->getFilms(123); +``` + +### Работа с медиа + +```php +// Получение изображений +$images = $client->media->getImages(301, ImageType::POSTER); + +// Получение видео +$videos = $client->media->getVideos(301); +``` + +## 🔧 Конфигурация + +### Настройка клиента + +```php +$client = new Client('your-api-key', [ + 'timeout' => 30, + 'retry_attempts' => 3, + 'api_version' => ApiVersion::V2_1 +]); +``` + +### Обработка ошибок + +```php +try { + $film = $client->films->getById(999999); +} catch (ResourceNotFoundException $e) { + echo "Фильм не найден: {$e->getMessage()}\n"; +} catch (RateLimitException $e) { + echo "Превышен лимит запросов\n"; +} catch (ApiException $e) { + echo "Ошибка API: {$e->getMessage()}\n"; +} +``` + +## 📊 Структура проекта + +``` +src/ +├── Client.php # Основной клиент +├── Services/ # Сервисы для работы с API +│ ├── FilmService.php +│ ├── PersonService.php +│ ├── MediaService.php +│ └── UserService.php +├── Models/ # Модели данных +│ ├── Film.php +│ ├── Person.php +│ ├── Staff.php +│ └── ... +├── Enums/ # Перечисления +│ ├── ImageType.php +│ ├── ReviewOrder.php +│ └── ... +├── Responses/ # Классы ответов +│ ├── DefaultResponse.php +│ ├── PaginatedResponse.php +│ └── ... +├── Exceptions/ # Исключения +│ ├── ApiException.php +│ ├── InvalidApiKeyException.php +│ └── ... +└── Interfaces/ # Интерфейсы + ├── ModelInterface.php + └── ResponseInterface.php +``` + +## 🧪 Тестирование + +### Запуск тестов + +```bash +# Все тесты +XDEBUG_MODE=coverage KINOPOISK_API_KEY=your-key composer test + +# Только unit тесты +composer test -- --testsuite=Unit + +# Только интеграционные тесты +composer test -- --testsuite=Integration +``` + +### Проверка качества кода + +```bash +# PHPStan анализ +XDEBUG_MODE=coverage KINOPOISK_API_KEY=your-key composer phpstan + +# PHP CodeSniffer +composer phpcs +``` + +## 📝 Примеры использования + +Смотрите папку [`examples/`](../examples/) для готовых примеров: + +- [Базовое использование](../examples/basic_usage.php) +- [Примеры конфигурации](../examples/configuration_examples.php) +- [Работа с перечислениями](../examples/enums_usage.php) +- [Использование с .env](../examples/with_dotenv.php) + +## 🔗 Полезные ссылки + +- [GitHub репозиторий](https://github.com/your-username/NotKinopoiskPHP) +- [Kinopoisk Unofficial API](https://kinopoiskapiunofficial.tech/) +- [Composer](https://getcomposer.org/) +- [PHP](https://www.php.net/) + +## 📄 Лицензия + +Этот проект распространяется под лицензией MIT. См. файл [LICENSE](../LICENSE) для подробностей. + +## 🤝 Поддержка + +Если у вас есть вопросы или предложения: + +1. Создайте [Issue](https://github.com/your-username/NotKinopoiskPHP/issues) +2. Напишите на email: dev@devcraft.club +3. Обратитесь к [документации](https://kinopoiskapiunofficial.tech/) + +--- + +**Версия документации:** 1.0.0 +**Последнее обновление:** diff --git a/docs/dev/notkinopoiskphp/interfaces.md b/docs/dev/notkinopoiskphp/interfaces.md new file mode 100644 index 0000000..9b3c4b6 --- /dev/null +++ b/docs/dev/notkinopoiskphp/interfaces.md @@ -0,0 +1,192 @@ +# Интерфейсы + +Интерфейсы библиотеки NotKinopoisk PHP Wrapper. + +## ModelInterface + +Интерфейс для моделей с возможностью преобразования в/из массива. + +### Описание + +Определяет базовый контракт для всех моделей данных в системе. Обеспечивает единообразную работу с преобразованием объектов в массивы и создания объектов из массивов данных API. + +### Основные возможности + +- Унификация процесса создания моделей из данных API +- Стандартизация сериализации объектов в массивы +- Обеспечение совместимости с системами кэширования +- Упрощение работы с JSON API responses + +### Методы + +#### `fromArray(array $data): object` + +Создает экземпляр модели из массива данных API. + +**Параметры:** + +- `$data` (array) - Массив данных от API, содержащий все необходимые поля для модели + +**Возвращает:** + +- `object` - Новый экземпляр класса-реализации с данными из массива + +**Исключения:** + +- `InvalidArgumentException` - Если обязательные поля отсутствуют в массиве данных +- `InvalidArgumentException` - Если данные имеют неверный тип или формат + +**Пример:** + +```php +$apiData = [ + 'id' => 123, + 'name' => 'Название', + 'description' => 'Описание' +]; +$model = ConcreteModel::fromArray($apiData); +``` + +#### `toArray(): array` + +Преобразует объект модели в массив. + +**Возвращает:** + +- `array` - Ассоциативный массив со всеми данными объекта + +**Пример:** + +```php +$model = new ConcreteModel('value1', 'value2'); +$array = $model->toArray(); +// Результат: ['field1' => 'value1', 'field2' => 'value2'] +``` + +### Пример реализации + +```php +class ExampleModel implements ModelInterface { + public function __construct( + private string $field1, + private string $field2 + ) {} + + public static function fromArray(array $data): object { + return new static($data['field1'], $data['field2']); + } + + public function toArray(): array { + return [ + 'field1' => $this->field1, + 'field2' => $this->field2 + ]; + } +} + +// Использование +$model = ExampleModel::fromArray($apiData); +$serialized = $model->toArray(); +``` + +## ResponseInterface + +Интерфейс для объектов ответов Kinopoisk API. + +### Описание + +Определяет общий контракт для всех типов ответов API, включая методы для создания объектов из массивов данных и валидации целевых классов. Обеспечивает единообразие в обработке различных типов ответов. + +### Основные возможности + +- Создание экземпляров ответов из данных API +- Валидация классов для преобразования элементов +- Унификация работы с различными типами ответов + +### Методы + +#### `fromArray(array $data, string $cls): object` + +Создает экземпляр ответа из массива данных API. + +**Параметры:** + +- `$data` (array) - Массив данных от API, содержащий структуру ответа +- `$cls` (string) - Полное имя класса для преобразования элементов массива + +**Возвращает:** + +- `object` - Новый экземпляр класса-реализации с преобразованными данными + +**Исключения:** + +- `KpValidationException` - Если указанный класс не существует +- `KpValidationException` - Если класс не имеет метода fromArray +- `KpValidationException` - Если данные имеют неверный формат + +#### `checkClass(string $cls): void` + +Проверяет существование и совместимость класса. + +**Параметры:** + +- `$cls` (string) - Полное имя класса для проверки + +**Исключения:** + +- `KpValidationException` - Если указанный класс не существует +- `KpValidationException` - Если класс не имеет статического метода fromArray + +#### `toArray(): array` + +Преобразует объект в массив данных. + +**Возвращает:** + +- `array` - Массив данных, представляющий объект ответа + +**Пример:** + +```php +$response = SomeResponse::fromArray($apiData); +$array = $response->toArray(); + +// Использование для сериализации +$json = json_encode($array); + +// Использование для логирования +$logger->info('Ответ API', $array); +``` + +### Пример использования + +```php +// Создание ответа из данных API +$apiData = [ + 'total' => 100, + 'items' => [ + ['id' => 1, 'name' => 'Фильм 1'], + ['id' => 2, 'name' => 'Фильм 2'] + ] +]; + +$response = DefaultResponse::fromArray($apiData, Film::class); + +// Преобразование в массив +$array = $response->toArray(); +``` + +## Связанные классы + +- `\NotKinopoisk\Models\Film` - Реализация ModelInterface +- `\NotKinopoisk\Models\Person` - Реализация ModelInterface +- `\NotKinopoisk\Models\Review` - Реализация ModelInterface +- `\NotKinopoisk\Responses\DefaultResponse` - Реализация ResponseInterface +- `\NotKinopoisk\Responses\PaginatedResponse` - Реализация ResponseInterface +- `\NotKinopoisk\Responses\SimpleResponse` - Реализация ResponseInterface + +## Информация о пакете + +- **Пакет:** NotKinopoisk\Models, NotKinopoisk\Responses +- **Версия:** 1.0.0 +- **Автор:** Maxim Harder diff --git a/docs/dev/notkinopoiskphp/interfaces/index.md b/docs/dev/notkinopoiskphp/interfaces/index.md new file mode 100644 index 0000000..9339e11 --- /dev/null +++ b/docs/dev/notkinopoiskphp/interfaces/index.md @@ -0,0 +1,435 @@ +# Интерфейсы + +Базовые интерфейсы для определения контрактов в Kinopoisk API. + +--- + +**📚 Навигация:** [Главная](../index.md) → Интерфейсы + +--- + +## 📋 Список интерфейсов + +### 📄 [ModelInterface](model-interface.md) + +Базовый интерфейс для всех моделей данных. + +**Основные возможности:** + +- Определяет контракт для создания моделей из массива данных +- Обеспечивает единообразие в работе с моделями +- Определяет базовые методы для всех моделей + +**Методы:** + +- `fromArray(array $data): object` - Создание модели из массива данных +- `toArray(): array` - Преобразование модели в массив +- `getDisplayName(): string` - Получение отображаемого имени + +**Реализуется:** Все модели данных + +### 📄 [ResponseInterface](response-interface.md) + +Базовый интерфейс для всех ответов API. + +**Основные возможности:** + +- Определяет контракт для создания ответов из массива данных +- Обеспечивает единообразие в работе с ответами +- Определяет базовые методы для всех ответов + +**Методы:** + +- `fromArray(array $data, string $cls): object` - Создание ответа из массива данных + +**Реализуется:** Все классы ответов + +## 🔗 Связанные компоненты + +### Модели (реализуют ModelInterface) + +- [Film](../models/film.md) - Основная модель фильма +- [Person](../models/person.md) - Модель персоны +- [Staff](../models/staff.md) - Модель съемочной группы +- [Review](../models/review.md) - Модель отзыва +- [Fact](../models/fact.md) - Модель факта +- [Image](../models/image.md) - Модель изображения +- [Video](../models/video.md) - Модель видео +- [Award](../models/award.md) - Модель награды +- [BoxOffice](../models/box-office.md) - Модель кассовых сборов +- [Country](../models/country.md) - Модель страны +- [Genre](../models/genre.md) - Модель жанра +- [Episode](../models/episode.md) - Модель эпизода +- [Season](../models/season.md) - Модель сезона +- [ExternalSource](../models/external-source.md) - Модель внешнего источника +- [Distribution](../models/distribution.md) - Модель дистрибуции +- [FilmSearchResult](../models/film-search-result.md) - Модель результата поиска +- [PersonSpouse](../models/person-spouse.md) - Модель супруга +- [PersonFilm](../models/person-film.md) - Модель фильма персоны +- [UserVote](../models/user-vote.md) - Модель голоса пользователя +- [FilmCollection](../models/film-collection.md) - Модель коллекции фильмов +- [Filters](../models/filters.md) - Модель фильтров +- [RelatedFilm](../models/related-film.md) - Модель связанного фильма +- [ApiKeyInfo](../models/api-key-info.md) - Модель информации об API ключе +- [ApiKeyQouta](../models/api-key-qouta.md) - Модель квоты API ключа +- [MediaPost](../models/media-post.md) - Модель медиа поста + +### Ответы (реализуют ResponseInterface) + +- [DefaultResponse](../responses/default-response.md) - Базовый ответ +- [PaginatedResponse](../responses/paginated-response.md) - Пагинированный ответ +- [KeywordSearchResponse](../responses/keyword-search-response.md) - Ответ поиска + +### Сервисы (используют интерфейсы) + +- [FilmService](../services/film-service.md) - Работает с моделями и ответами +- [PersonService](../services/person-service.md) - Работает с моделями и ответами +- [MediaService](../services/media-service.md) - Работает с моделями и ответами +- [UserService](../services/user-service.md) - Работает с моделями и ответами + +## 🚀 Быстрый старт + +### Работа с ModelInterface + +```php + 301, + 'nameRu' => 'Матрица', + 'nameEn' => 'The Matrix', + 'rating' => 8.7, + 'year' => 1999 +]; + +$film = Film::fromArray($filmData); + +// Использование методов интерфейса +echo "Отображаемое имя: {$film->getDisplayName()}\n"; + +$filmArray = $film->toArray(); +echo "Массив данных:\n"; +print_r($filmArray); + +// Работа с другой моделью +$personData = [ + 'kinopoiskId' => 123, + 'nameRu' => 'Киану Ривз', + 'nameEn' => 'Keanu Reeves', + 'sex' => 'MALE' +]; + +$person = Person::fromArray($personData); +echo "Персона: {$person->getDisplayName()}\n"; +``` + +### Работа с ResponseInterface + +```php +use NotKinopoisk\Interfaces\ResponseInterface; +use NotKinopoisk\Responses\DefaultResponse; +use NotKinopoisk\Responses\PaginatedResponse; + +// Проверка, что ответ реализует интерфейс +if (DefaultResponse::class instanceof ResponseInterface) { + echo "DefaultResponse реализует ResponseInterface\n"; +} + +// Создание ответа из массива данных +$responseData = [ + 'total' => 2, + 'items' => [ + ['kinopoiskId' => 301, 'nameRu' => 'Матрица'], + ['kinopoiskId' => 302, 'nameRu' => 'Матрица: Перезагрузка'] + ] +]; + +$response = DefaultResponse::fromArray($responseData, Film::class); + +echo "Создан ответ с {$response->total} элементами\n"; + +// Работа с пагинированным ответом +$paginatedData = [ + 'total' => 100, + 'items' => $responseData['items'], + 'currentPage' => 1, + 'totalPages' => 10 +]; + +$paginatedResponse = PaginatedResponse::fromArray($paginatedData, Film::class); + +echo "Страница {$paginatedResponse->currentPage} из {$paginatedResponse->totalPages}\n"; +``` + +## 📖 Примеры использования + +### Создание универсальной функции для работы с моделями + +```php +// Функция для создания любой модели из массива данных +function createModelFromArray(string $modelClass, array $data): object { + if (!is_subclass_of($modelClass, ModelInterface::class)) { + throw new InvalidArgumentException("Класс {$modelClass} должен реализовывать ModelInterface"); + } + + return $modelClass::fromArray($data); +} + +// Функция для создания массива моделей +function createModelsFromArray(string $modelClass, array $dataArray): array { + return array_map( + fn($data) => createModelFromArray($modelClass, $data), + $dataArray + ); +} + +// Функция для получения отображаемых имен +function getDisplayNames(array $models): array { + return array_map( + fn($model) => $model->getDisplayName(), + $models + ); +} + +// Использование +$filmData = [ + 'kinopoiskId' => 301, + 'nameRu' => 'Матрица', + 'nameEn' => 'The Matrix' +]; + +$personData = [ + 'kinopoiskId' => 123, + 'nameRu' => 'Киану Ривз', + 'nameEn' => 'Keanu Reeves' +]; + +// Создание моделей +$film = createModelFromArray(Film::class, $filmData); +$person = createModelFromArray(Person::class, $personData); + +echo "Фильм: {$film->getDisplayName()}\n"; +echo "Персона: {$person->getDisplayName()}\n"; + +// Создание массива моделей +$filmsData = [ + ['kinopoiskId' => 301, 'nameRu' => 'Матрица'], + ['kinopoiskId' => 302, 'nameRu' => 'Матрица: Перезагрузка'] +]; + +$films = createModelsFromArray(Film::class, $filmsData); +$names = getDisplayNames($films); + +echo "Фильмы: " . implode(', ', $names) . "\n"; +``` + +### Создание универсальной функции для работы с ответами + +```php +// Функция для создания любого ответа из массива данных +function createResponseFromArray(string $responseClass, array $data, string $modelClass): object { + if (!is_subclass_of($responseClass, ResponseInterface::class)) { + throw new InvalidArgumentException("Класс {$responseClass} должен реализовывать ResponseInterface"); + } + + return $responseClass::fromArray($data, $modelClass); +} + +// Функция для обработки ответов +function processResponse(object $response): array { + $result = [ + 'total' => $response->total, + 'count' => $response->getCount(), + 'isEmpty' => $response->isEmpty(), + 'items' => [] + ]; + + // Добавляем информацию о пагинации, если есть + if (property_exists($response, 'currentPage')) { + $result['pagination'] = [ + 'currentPage' => $response->currentPage, + 'totalPages' => $response->totalPages, + 'hasNextPage' => $response->hasNextPage(), + 'hasPreviousPage' => $response->hasPreviousPage() + ]; + } + + // Добавляем информацию о поиске, если есть + if (property_exists($response, 'keyword')) { + $result['search'] = [ + 'keyword' => $response->keyword, + 'pagesCount' => $response->pagesCount, + 'searchFilmsCountResult' => $response->searchFilmsCountResult + ]; + } + + // Обрабатываем элементы + foreach ($response->items as $item) { + $result['items'][] = [ + 'displayName' => $item->getDisplayName(), + 'data' => $item->toArray() + ]; + } + + return $result; +} + +// Использование +$responseData = [ + 'total' => 2, + 'items' => [ + ['kinopoiskId' => 301, 'nameRu' => 'Матрица'], + ['kinopoiskId' => 302, 'nameRu' => 'Матрица: Перезагрузка'] + ] +]; + +$response = createResponseFromArray(DefaultResponse::class, $responseData, Film::class); +$processed = processResponse($response); + +echo "Обработанный ответ:\n"; +print_r($processed); +``` + +### Проверка типов и валидация + +```php +// Функция для проверки, что объект реализует интерфейс +function implementsInterface(object $object, string $interface): bool { + $reflection = new ReflectionClass($object); + return $reflection->implementsInterface($interface); +} + +// Функция для валидации модели +function validateModel(object $model): array { + $errors = []; + + if (!implementsInterface($model, ModelInterface::class)) { + $errors[] = "Объект должен реализовывать ModelInterface"; + return $errors; + } + + // Проверяем обязательные методы + $requiredMethods = ['fromArray', 'toArray', 'getDisplayName']; + + foreach ($requiredMethods as $method) { + if (!method_exists($model, $method)) { + $errors[] = "Отсутствует обязательный метод: {$method}"; + } + } + + // Проверяем, что getDisplayName возвращает строку + try { + $displayName = $model->getDisplayName(); + if (!is_string($displayName)) { + $errors[] = "Метод getDisplayName должен возвращать строку"; + } + } catch (Exception $e) { + $errors[] = "Ошибка при вызове getDisplayName: " . $e->getMessage(); + } + + // Проверяем, что toArray возвращает массив + try { + $array = $model->toArray(); + if (!is_array($array)) { + $errors[] = "Метод toArray должен возвращать массив"; + } + } catch (Exception $e) { + $errors[] = "Ошибка при вызове toArray: " . $e->getMessage(); + } + + return $errors; +} + +// Использование +$film = Film::fromArray(['kinopoiskId' => 301, 'nameRu' => 'Матрица']); + +if (implementsInterface($film, ModelInterface::class)) { + echo "Film реализует ModelInterface\n"; +} + +$validationErrors = validateModel($film); +if (empty($validationErrors)) { + echo "Модель валидна\n"; +} else { + echo "Ошибки валидации:\n"; + foreach ($validationErrors as $error) { + echo "- {$error}\n"; + } +} +``` + +## 🔧 Общие методы + +### ModelInterface + +#### fromArray() + +```php +public static function fromArray(array $data): object +``` + +Создает экземпляр модели из массива данных API. + +#### toArray() + +```php +public function toArray(): array +``` + +Преобразует модель в массив. + +#### getDisplayName() + +```php +public function getDisplayName(): string +``` + +Возвращает отображаемое имя объекта. + +### ResponseInterface + +#### fromArray() + +```php +public static function fromArray(array $data, string $cls): object +``` + +Создает экземпляр ответа из массива данных API. + +## 📊 Статистика интерфейсов + +### ModelInterface + +- **Реализуется:** 25+ моделями +- **Методы:** 3 +- **Использование:** Все модели данных + +### ResponseInterface + +- **Реализуется:** 3+ классами ответов +- **Методы:** 1 +- **Использование:** Все классы ответов + +## 🔗 Связанные разделы + +- [Модели](../models/index.md) - Реализуют ModelInterface +- [Ответы](../responses/index.md) - Реализуют ResponseInterface +- [Сервисы](../services/index.md) - Используют интерфейсы +- [Перечисления](../enums/index.md) - Используются в моделях +- [Исключения](../exceptions/index.md) - Обработка ошибок + +--- + +**📚 Навигация:** [Главная](../index.md) → Интерфейсы diff --git a/docs/dev/notkinopoiskphp/models/award.md b/docs/dev/notkinopoiskphp/models/award.md new file mode 100644 index 0000000..2e38233 --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/award.md @@ -0,0 +1,499 @@ +# Award + +Модель награды из Kinopoisk API. + +## Описание + +`Award` представляет информацию о награде или номинации фильма, включая название награды, год, номинацию и связанных персон. + +### Основные возможности + +- Хранение информации о награде в неизменяемом виде +- Создание объекта из массива данных API +- Доступ к метаданным награды и связанным персонам + +**API Endpoint:** `/api/v2.2/films/{id}/awards` + +## Свойства + +### Основная информация + +- `$name` (string) - Название награды +- `$win` (bool) - Флаг победы в номинации +- `$imageUrl` (string|null) - URL изображения награды +- `$nominationName` (string) - Название номинации +- `$year` (int) - Год вручения награды +- `$persons` (array) - Массив связанных персон + +## Конструктор + +```php +public function __construct( + public readonly string $name, + public readonly bool $win, + public readonly ?string $imageUrl, + public readonly string $nominationName, + public readonly int $year, + public readonly array $persons, +) +``` + +### Пример создания + +```php +$award = new Award( + name: 'Оскар', + win: true, + imageUrl: 'https://...', + nominationName: 'Лучший фильм', + year: 2023, + persons: [$person1, $person2] +); +``` + +## Методы + +### fromArray() + +Создает экземпляр награды из массива данных API. + +```php +public static function fromArray(array $data): self +``` + +#### Параметры + +- `$data` (array) - Массив данных награды от API + +#### Возвращаемое значение + +- `self` - Новый экземпляр награды + +#### Исключения + +- `\InvalidArgumentException` - Если данные имеют неверный формат + +#### Пример использования + +```php +$apiData = [ + 'name' => 'Оскар', + 'win' => true, + 'imageUrl' => 'https://...', + 'nominationName' => 'Лучший фильм', + 'year' => 2023, + 'persons' => [$personData1, $personData2] +]; + +$award = Award::fromArray($apiData); +``` + +### toArray() + +Преобразует объект награды в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Массив с данными награды + +#### Пример использования + +```php +$awardArray = $award->toArray(); +echo json_encode($awardArray); // JSON представление награды +``` + +## Полный пример использования + +```php +films; +$awards = $filmService->getAwards(301); // Матрица + +echo "=== Награды фильма 'Матрица' ===\n"; + +// Группировка по статусу +$wins = []; +$nominations = []; + +foreach ($awards as $award) { + if ($award->win) { + $wins[] = $award; + } else { + $nominations[] = $award; + } +} + +// Вывод побед +if (!empty($wins)) { + echo "\n🏆 Победы (" . count($wins) . "):\n"; + foreach ($wins as $award) { + echo "• {$award->name} ({$award->year})\n"; + echo " Номинация: {$award->nominationName}\n"; + if (!empty($award->persons)) { + echo " Участники: " . implode(', ', array_column($award->persons, 'name')) . "\n"; + } + echo " ---\n"; + } +} + +// Вывод номинаций +if (!empty($nominations)) { + echo "\n🎭 Номинации (" . count($nominations) . "):\n"; + foreach ($nominations as $award) { + echo "• {$award->name} ({$award->year})\n"; + echo " Номинация: {$award->nominationName}\n"; + if (!empty($award->persons)) { + echo " Участники: " . implode(', ', array_column($award->persons, 'name')) . "\n"; + } + echo " ---\n"; + } +} +``` + +## Работа с наградами + +```php +// Функция для фильтрации наград по статусу +function filterAwardsByStatus(array $awards, bool $win): array { + return array_filter($awards, fn($award) => $award->win === $win); +} + +// Функция для фильтрации наград по году +function filterAwardsByYear(array $awards, int $year): array { + return array_filter($awards, fn($award) => $award->year === $year); +} + +// Функция для поиска наград по названию +function searchAwardsByName(array $awards, string $keyword): array { + $keyword = strtolower($keyword); + + return array_filter($awards, function($award) use ($keyword) { + return strpos(strtolower($award->name), $keyword) !== false || + strpos(strtolower($award->nominationName), $keyword) !== false; + }); +} + +// Функция для получения статистики наград +function getAwardsStatistics(array $awards): array { + $stats = [ + 'total' => count($awards), + 'wins' => 0, + 'nominations' => 0, + 'years' => [], + 'awards' => [], + 'nominations' => [] + ]; + + foreach ($awards as $award) { + // Подсчет побед и номинаций + if ($award->win) { + $stats['wins']++; + } else { + $stats['nominations']++; + } + + // Статистика по годам + if (!isset($stats['years'][$award->year])) { + $stats['years'][$award->year] = ['wins' => 0, 'nominations' => 0]; + } + if ($award->win) { + $stats['years'][$award->year]['wins']++; + } else { + $stats['years'][$award->year]['nominations']++; + } + + // Статистика по наградам + if (!isset($stats['awards'][$award->name])) { + $stats['awards'][$award->name] = ['wins' => 0, 'nominations' => 0]; + } + if ($award->win) { + $stats['awards'][$award->name]['wins']++; + } else { + $stats['awards'][$award->name]['nominations']++; + } + + // Статистика по номинациям + if (!isset($stats['nominations'][$award->nominationName])) { + $stats['nominations'][$award->nominationName] = 0; + } + $stats['nominations'][$award->nominationName]++; + } + + return $stats; +} + +// Использование +$awards = $filmService->getAwards(301); + +// Фильтрация по статусу +$wins = filterAwardsByStatus($awards, true); +$nominations = filterAwardsByStatus($awards, false); +echo "Побед: " . count($wins) . "\n"; +echo "Номинаций: " . count($nominations) . "\n"; + +// Фильтрация по году +$recentAwards = filterAwardsByYear($awards, 2023); +echo "Наград в 2023 году: " . count($recentAwards) . "\n"; + +// Поиск по названию +$oscarAwards = searchAwardsByName($awards, 'оскар'); +echo "Наград с 'Оскар': " . count($oscarAwards) . "\n"; + +// Получение статистики +$stats = getAwardsStatistics($awards); +echo "Всего наград: {$stats['total']}\n"; +echo "Побед: {$stats['wins']}\n"; +echo "Номинаций: {$stats['nominations']}\n"; +``` + +## Создание отчета о наградах + +```php +class AwardsReport { + private array $awards; + + public function __construct(array $awards) { + $this->awards = $awards; + } + + public function getWins(): array { + return filterAwardsByStatus($this->awards, true); + } + + public function getNominations(): array { + return filterAwardsByStatus($this->awards, false); + } + + public function getByYear(int $year): array { + return filterAwardsByYear($this->awards, $year); + } + + public function getByAwardName(string $awardName): array { + return searchAwardsByName($this->awards, $awardName); + } + + public function createDetailedReport(): string { + $report = "=== ДЕТАЛЬНЫЙ ОТЧЕТ О НАГРАДАХ ===\n\n"; + + $stats = getAwardsStatistics($this->awards); + + // Общая статистика + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего наград: {$stats['total']}\n"; + $report .= "Побед: {$stats['wins']}\n"; + $report .= "Номинаций: {$stats['nominations']}\n"; + $report .= "Процент побед: " . round(($stats['wins'] / $stats['total']) * 100, 1) . "%\n\n"; + + // Статистика по годам + $report .= "📅 ПО ГОДАМ:\n"; + krsort($stats['years']); // Сортировка по убыванию года + foreach ($stats['years'] as $year => $yearStats) { + $total = $yearStats['wins'] + $yearStats['nominations']; + $report .= "{$year}: {$yearStats['wins']} побед, {$yearStats['nominations']} номинаций (всего: {$total})\n"; + } + $report .= "\n"; + + // Статистика по наградам + $report .= "🏆 ПО НАГРАДАМ:\n"; + foreach ($stats['awards'] as $awardName => $awardStats) { + $total = $awardStats['wins'] + $awardStats['nominations']; + $report .= "{$awardName}: {$awardStats['wins']} побед, {$awardStats['nominations']} номинаций (всего: {$total})\n"; + } + $report .= "\n"; + + // Самые частые номинации + $report .= "🎭 САМЫЕ ЧАСТЫЕ НОМИНАЦИИ:\n"; + arsort($stats['nominations']); + foreach (array_slice($stats['nominations'], 0, 5, true) as $nomination => $count) { + $report .= "{$nomination}: {$count} раз\n"; + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + // Статистика + $stats = getAwardsStatistics($this->awards); + $html .= "
\n"; + $html .= "

Статистика

\n"; + $html .= "

Всего наград: {$stats['total']}

\n"; + $html .= "

Побед: {$stats['wins']}

\n"; + $html .= "

Номинаций: {$stats['nominations']}

\n"; + $html .= "

Процент побед: " . round(($stats['wins'] / $stats['total']) * 100, 1) . "%

\n"; + $html .= "
\n"; + + // Список наград + $html .= "

Все награды

\n"; + foreach ($this->awards as $award) { + $cssClass = $award->win ? 'award-win' : 'award-nomination'; + $status = $award->win ? '🏆 ПОБЕДА' : '🎭 НОМИНАЦИЯ'; + + $html .= "
\n"; + $html .= "
{$award->name} - {$status}
\n"; + $html .= "
{$award->nominationName}
\n"; + $html .= "
{$award->year}
\n"; + + if (!empty($award->persons)) { + $html .= "
Участники: " . implode(', ', array_column($award->persons, 'name')) . "
\n"; + } + + $html .= "
\n"; + } + + $html .= "
\n\n"; + + return $html; + } +} + +// Использование +$awards = $filmService->getAwards(301); +$report = new AwardsReport($awards); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Награды фильма "Матрица"'); +file_put_contents('matrix_awards.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в matrix_awards.html\n"; +``` + +## Анализ наград + +```php +function analyzeAwards(array $awards): array { + $analysis = [ + 'total' => count($awards), + 'wins' => 0, + 'nominations' => 0, + 'winRate' => 0, + 'years' => [], + 'awards' => [], + 'nominations' => [], + 'persons' => [] + ]; + + $personStats = []; + + foreach ($awards as $award) { + // Подсчет побед и номинаций + if ($award->win) { + $analysis['wins']++; + } else { + $analysis['nominations']++; + } + + // Статистика по годам + if (!isset($analysis['years'][$award->year])) { + $analysis['years'][$award->year] = 0; + } + $analysis['years'][$award->year]++; + + // Статистика по наградам + if (!isset($analysis['awards'][$award->name])) { + $analysis['awards'][$award->name] = 0; + } + $analysis['awards'][$award->name]++; + + // Статистика по номинациям + if (!isset($analysis['nominations'][$award->nominationName])) { + $analysis['nominations'][$award->nominationName] = 0; + } + $analysis['nominations'][$award->nominationName]++; + + // Статистика по персонам + foreach ($award->persons as $person) { + $personName = $person['name'] ?? 'Неизвестно'; + if (!isset($personStats[$personName])) { + $personStats[$personName] = ['wins' => 0, 'nominations' => 0]; + } + if ($award->win) { + $personStats[$personName]['wins']++; + } else { + $personStats[$personName]['nominations']++; + } + } + } + + // Процент побед + if ($analysis['total'] > 0) { + $analysis['winRate'] = round(($analysis['wins'] / $analysis['total']) * 100, 1); + } + + // Сортировка статистики + arsort($analysis['years']); + arsort($analysis['awards']); + arsort($analysis['nominations']); + + // Статистика по персонам + foreach ($personStats as $person => $stats) { + $analysis['persons'][$person] = $stats; + } + + return $analysis; +} + +// Использование +$awards = $filmService->getAwards(301); +$analysis = analyzeAwards($awards); + +echo "=== Анализ наград ===\n"; +echo "Всего наград: {$analysis['total']}\n"; +echo "Побед: {$analysis['wins']}\n"; +echo "Номинаций: {$analysis['nominations']}\n"; +echo "Процент побед: {$analysis['winRate']}%\n"; + +echo "\nТоп наград:\n"; +foreach (array_slice($analysis['awards'], 0, 5, true) as $award => $count) { + echo "- {$award}: {$count} наград\n"; +} + +echo "\nТоп номинаций:\n"; +foreach (array_slice($analysis['nominations'], 0, 5, true) as $nomination => $count) { + echo "- {$nomination}: {$count} раз\n"; +} + +echo "\nТоп персон:\n"; +foreach (array_slice($analysis['persons'], 0, 5, true) as $person => $stats) { + $total = $stats['wins'] + $stats['nominations']; + echo "- {$person}: {$stats['wins']} побед, {$stats['nominations']} номинаций (всего: {$total})\n"; +} +``` + +## Связанные классы + +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами +- [`Film`](film.md) - Модель фильма diff --git a/docs/dev/notkinopoiskphp/models/box-office.md b/docs/dev/notkinopoiskphp/models/box-office.md new file mode 100644 index 0000000..688acda --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/box-office.md @@ -0,0 +1,554 @@ +# BoxOffice + +Модель кассовых сборов из Kinopoisk API. + +## Описание + +`BoxOffice` представляет финансовую информацию о фильме: бюджет, сборы в различных странах и мировые сборы. + +### Основные возможности + +- Хранение финансовой информации в неизменяемом виде +- Создание объекта из массива данных API +- Форматирование сумм для отображения +- Определение типа данных (бюджет или сборы) + +**API Endpoint:** `/api/v2.2/films/{id}/box_office` + +## Свойства + +### Основная информация + +- `$type` (BoxOfficeType) - Тип кассовых сборов (бюджет, сборы и т.д.) +- `$amount` (int) - Сумма в минимальных единицах валюты +- `$currency` (string|null) - Код валюты (USD, EUR, RUB и т.д.) +- `$symbol` (string) - Символ валюты для отображения + +## Конструктор + +```php +public function __construct( + public readonly BoxOfficeType $type, + public readonly int $amount, + public readonly ?string $currency, + public readonly string $symbol, +) +``` + +### Пример создания + +```php +$boxOffice = new BoxOffice( + type: BoxOfficeType::BUDGET, + amount: 100000000, + currency: 'USD', + symbol: '$' +); +``` + +## Методы + +### fromArray() + +Создает объект кассовых сборов из массива данных API. + +```php +public static function fromArray(array $data): self +``` + +#### Параметры + +- `$data` (array) - Массив данных от API + +#### Возвращаемое значение + +- `self` - Новый объект кассовых сборов + +#### Исключения + +- `\ValueError` - Если тип кассовых сборов не поддерживается + +#### Пример использования + +```php +$apiData = [ + 'type' => 'BUDGET', + 'amount' => 100000000, + 'currency' => 'USD', + 'symbol' => '$' +]; + +$boxOffice = BoxOffice::fromArray($apiData); +``` + +### getFormattedAmount() + +Получает отформатированную сумму для отображения. + +```php +public function getFormattedAmount(): string +``` + +#### Возвращаемое значение + +- `string` - Отформатированная сумма + +#### Описание + +Возвращает сумму в удобном для чтения формате с символом валюты и разделителями тысяч. + +#### Пример использования + +```php +echo $boxOffice->getFormattedAmount(); // "$100,000,000" +``` + +### isBudget() + +Проверяет, является ли тип бюджетом. + +```php +public function isBudget(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если это бюджет, `false` в противном случае + +#### Описание + +Определяет, относятся ли данные к бюджету фильма. + +#### Пример использования + +```php +if ($boxOffice->isBudget()) { + echo "Бюджет фильма: {$boxOffice->getFormattedAmount()}"; +} +``` + +### isRevenue() + +Проверяет, является ли тип сборами. + +```php +public function isRevenue(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если это сборы, `false` в противном случае + +#### Описание + +Определяет, относятся ли данные к сборам фильма (в России, США или мировым сборам). + +#### Пример использования + +```php +if ($boxOffice->isRevenue()) { + echo "Сборы: {$boxOffice->getFormattedAmount()}"; +} +``` + +### toArray() + +Преобразует объект кассовых сборов в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Массив с данными кассовых сборов + +#### Пример использования + +```php +$boxOfficeArray = $boxOffice->toArray(); +echo json_encode($boxOfficeArray); // JSON представление кассовых сборов +``` + +## Полный пример использования + +```php +films; +$boxOffice = $filmService->getBoxOffice(301); // Матрица + +echo "=== Кассовые сборы фильма 'Матрица' ===\n"; + +// Группировка по типам +$budgets = []; +$revenues = []; + +foreach ($boxOffice as $item) { + if ($item->isBudget()) { + $budgets[] = $item; + } elseif ($item->isRevenue()) { + $revenues[] = $item; + } +} + +// Вывод бюджетов +if (!empty($budgets)) { + echo "\n💰 Бюджеты:\n"; + foreach ($budgets as $budget) { + echo "• {$budget->type->getDisplayName()}: {$budget->getFormattedAmount()}\n"; + } +} + +// Вывод сборов +if (!empty($revenues)) { + echo "\n💵 Сборы:\n"; + foreach ($revenues as $revenue) { + echo "• {$revenue->type->getDisplayName()}: {$revenue->getFormattedAmount()}\n"; + } +} +``` + +## Работа с кассовыми сборами + +```php +// Функция для фильтрации по типу +function filterBoxOfficeByType(array $boxOffice, BoxOfficeType $type): array { + return array_filter($boxOffice, fn($item) => $item->type === $type); +} + +// Функция для получения бюджета и сборов +function getBudgetAndRevenue(array $boxOffice): array { + $budget = null; + $revenue = []; + + foreach ($boxOffice as $item) { + if ($item->isBudget()) { + $budget = $item; + } elseif ($item->isRevenue()) { + $revenue[] = $item; + } + } + + return ['budget' => $budget, 'revenue' => $revenue]; +} + +// Функция для расчета рентабельности +function calculateProfitability(array $boxOffice): ?float { + $budgetAndRevenue = getBudgetAndRevenue($boxOffice); + + if (!$budgetAndRevenue['budget'] || empty($budgetAndRevenue['revenue'])) { + return null; + } + + $budget = $budgetAndRevenue['budget']->amount; + $totalRevenue = array_sum(array_map(fn($item) => $item->amount, $budgetAndRevenue['revenue'])); + + if ($budget > 0) { + return round((($totalRevenue - $budget) / $budget) * 100, 2); + } + + return null; +} + +// Функция для получения статистики по валютам +function getCurrencyStats(array $boxOffice): array { + $stats = []; + + foreach ($boxOffice as $item) { + $currency = $item->currency ?? 'Unknown'; + if (!isset($stats[$currency])) { + $stats[$currency] = [ + 'count' => 0, + 'total' => 0, + 'symbol' => $item->symbol + ]; + } + $stats[$currency]['count']++; + $stats[$currency]['total'] += $item->amount; + } + + return $stats; +} + +// Использование +$boxOffice = $filmService->getBoxOffice(301); + +// Фильтрация по типу +$budgets = filterBoxOfficeByType($boxOffice, BoxOfficeType::BUDGET); +echo "Бюджетов: " . count($budgets) . "\n"; + +// Получение бюджета и сборов +$budgetAndRevenue = getBudgetAndRevenue($boxOffice); +if ($budgetAndRevenue['budget']) { + echo "Бюджет: {$budgetAndRevenue['budget']->getFormattedAmount()}\n"; +} +echo "Типов сборов: " . count($budgetAndRevenue['revenue']) . "\n"; + +// Расчет рентабельности +$profitability = calculateProfitability($boxOffice); +if ($profitability !== null) { + echo "Рентабельность: {$profitability}%\n"; +} + +// Статистика по валютам +$currencyStats = getCurrencyStats($boxOffice); +echo "Статистика по валютам:\n"; +foreach ($currencyStats as $currency => $data) { + echo "- {$currency}: {$data['count']} записей, {$data['symbol']}" . number_format($data['total']) . "\n"; +} +``` + +## Создание финансового отчета + +```php +class FinancialReport { + private array $boxOffice; + + public function __construct(array $boxOffice) { + $this->boxOffice = $boxOffice; + } + + public function getBudget(): ?BoxOffice { + foreach ($this->boxOffice as $item) { + if ($item->isBudget()) { + return $item; + } + } + return null; + } + + public function getRevenue(): array { + return array_filter($this->boxOffice, fn($item) => $item->isRevenue()); + } + + public function getTotalRevenue(): int { + $revenue = $this->getRevenue(); + return array_sum(array_map(fn($item) => $item->amount, $revenue)); + } + + public function getProfitability(): ?float { + $budget = $this->getBudget(); + if (!$budget) return null; + + $totalRevenue = $this->getTotalRevenue(); + if ($budget->amount > 0) { + return round((($totalRevenue - $budget->amount) / $budget->amount) * 100, 2); + } + + return null; + } + + public function createDetailedReport(): string { + $report = "=== ФИНАНСОВЫЙ ОТЧЕТ ===\n\n"; + + // Бюджет + $budget = $this->getBudget(); + if ($budget) { + $report .= "💰 БЮДЖЕТ:\n"; + $report .= "Тип: {$budget->type->getDisplayName()}\n"; + $report .= "Сумма: {$budget->getFormattedAmount()}\n"; + $report .= "Валюта: {$budget->currency}\n\n"; + } + + // Сборы + $revenue = $this->getRevenue(); + if (!empty($revenue)) { + $report .= "💵 СБОРЫ:\n"; + foreach ($revenue as $item) { + $report .= "• {$item->type->getDisplayName()}: {$item->getFormattedAmount()}\n"; + } + $report .= "\n"; + + $totalRevenue = $this->getTotalRevenue(); + $report .= "Общие сборы: " . number_format($totalRevenue) . "\n\n"; + } + + // Рентабельность + $profitability = $this->getProfitability(); + if ($profitability !== null) { + $report .= "📊 РЕНТАБЕЛЬНОСТЬ:\n"; + $report .= "Процент: {$profitability}%\n"; + + if ($profitability > 0) { + $report .= "Статус: Прибыльный проект ✅\n"; + } else { + $report .= "Статус: Убыточный проект ❌\n"; + } + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + // Бюджет + $budget = $this->getBudget(); + if ($budget) { + $html .= "
\n"; + $html .= "
💰 Бюджет
\n"; + $html .= "
\n"; + $html .= "
Тип: {$budget->type->getDisplayName()}
\n"; + $html .= "
{$budget->getFormattedAmount()}
\n"; + $html .= "
Валюта: {$budget->currency}
\n"; + $html .= "
\n
\n"; + } + + // Сборы + $revenue = $this->getRevenue(); + if (!empty($revenue)) { + $html .= "
\n"; + $html .= "
💵 Сборы
\n"; + + foreach ($revenue as $item) { + $html .= "
\n"; + $html .= "
{$item->type->getDisplayName()}:
\n"; + $html .= "
{$item->getFormattedAmount()}
\n"; + $html .= "
\n"; + } + + $totalRevenue = $this->getTotalRevenue(); + $html .= "
\n"; + $html .= "
Общие сборы: " . number_format($totalRevenue) . "
\n"; + $html .= "
\n"; + $html .= "
\n"; + } + + // Рентабельность + $profitability = $this->getProfitability(); + if ($profitability !== null) { + $html .= "
\n"; + $html .= "
📊 Рентабельность
\n"; + $html .= "
\n"; + $cssClass = $profitability > 0 ? 'profit' : 'loss'; + $status = $profitability > 0 ? 'Прибыльный проект ✅' : 'Убыточный проект ❌'; + $html .= "
{$profitability}%
\n"; + $html .= "
{$status}
\n"; + $html .= "
\n
\n"; + } + + $html .= "
\n\n"; + + return $html; + } +} + +// Использование +$boxOffice = $filmService->getBoxOffice(301); +$report = new FinancialReport($boxOffice); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Финансовый отчет фильма "Матрица"'); +file_put_contents('financial_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в financial_report.html\n"; +``` + +## Анализ финансовых показателей + +```php +function analyzeFinancialPerformance(array $boxOffice): array { + $analysis = [ + 'totalItems' => count($boxOffice), + 'budgets' => 0, + 'revenues' => 0, + 'currencies' => [], + 'totalBudget' => 0, + 'totalRevenue' => 0, + 'profitability' => null, + 'currencyBreakdown' => [] + ]; + + $budgetAmount = 0; + $revenueAmount = 0; + $currencyStats = []; + + foreach ($boxOffice as $item) { + // Подсчет типов + if ($item->isBudget()) { + $analysis['budgets']++; + $budgetAmount += $item->amount; + } elseif ($item->isRevenue()) { + $analysis['revenues']++; + $revenueAmount += $item->amount; + } + + // Статистика по валютам + $currency = $item->currency ?? 'Unknown'; + if (!isset($currencyStats[$currency])) { + $currencyStats[$currency] = [ + 'count' => 0, + 'total' => 0, + 'symbol' => $item->symbol + ]; + } + $currencyStats[$currency]['count']++; + $currencyStats[$currency]['total'] += $item->amount; + } + + $analysis['totalBudget'] = $budgetAmount; + $analysis['totalRevenue'] = $revenueAmount; + $analysis['currencyBreakdown'] = $currencyStats; + + // Расчет рентабельности + if ($budgetAmount > 0) { + $analysis['profitability'] = round((($revenueAmount - $budgetAmount) / $budgetAmount) * 100, 2); + } + + return $analysis; +} + +// Использование +$boxOffice = $filmService->getBoxOffice(301); +$analysis = analyzeFinancialPerformance($boxOffice); + +echo "=== Анализ финансовых показателей ===\n"; +echo "Всего записей: {$analysis['totalItems']}\n"; +echo "Бюджетов: {$analysis['budgets']}\n"; +echo "Типов сборов: {$analysis['revenues']}\n"; +echo "Общий бюджет: " . number_format($analysis['totalBudget']) . "\n"; +echo "Общие сборы: " . number_format($analysis['totalRevenue']) . "\n"; + +if ($analysis['profitability'] !== null) { + echo "Рентабельность: {$analysis['profitability']}%\n"; +} + +echo "\nСтатистика по валютам:\n"; +foreach ($analysis['currencyBreakdown'] as $currency => $data) { + echo "- {$currency}: {$data['count']} записей, {$data['symbol']}" . number_format($data['total']) . "\n"; +} +``` + +## Связанные классы + +- [`BoxOfficeType`](../enums/box-office-type.md) - Типы кассовых сборов +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами +- [`Film`](film.md) - Модель фильма diff --git a/docs/dev/notkinopoiskphp/models/country.md b/docs/dev/notkinopoiskphp/models/country.md new file mode 100644 index 0000000..4bc9c61 --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/country.md @@ -0,0 +1,434 @@ +# Country + +Модель страны из Kinopoisk API. + +## Описание + +`Country` представляет информацию о стране производства фильма или сериала. Простая модель для хранения названия страны. + +### Основные возможности + +- Хранение названия страны в неизменяемом виде +- Создание объекта из массива данных API +- Автоматическое преобразование в строку + +## Свойства + +### Основная информация + +- `$country` (string) - Название страны +- `$id` (int|null) - Уникальный идентификатор страны в Кинопоиске + +## Конструктор + +```php +public function __construct( + public readonly string $country, + public readonly ?int $id = NULL, +) +``` + +### Пример создания + +```php +$country = new Country('США'); +$countryWithId = new Country('Россия', 1); +``` + +## Методы + +### \_\_toString() + +Преобразует объект страны в строку. + +```php +public function __toString(): string +``` + +#### Возвращаемое значение + +- `string` - Название страны + +#### Описание + +Магический метод, который автоматически вызывается при попытке преобразования объекта Country в строковое представление. + +#### Пример использования + +```php +$country = new Country('США'); +echo $country; // Выведет: США +echo (string) $country; // Выведет: США + +// Использование в строковом контексте +$message = "Фильм снят в стране: {$country}"; +``` + +### fromArray() + +Создает экземпляр страны из массива данных API. + +```php +public static function fromArray(array $data): self +``` + +#### Параметры + +- `$data` (array) - Массив данных страны от API + +#### Возвращаемое значение + +- `self` - Новый экземпляр страны + +#### Исключения + +- `\InvalidArgumentException` - Если данные имеют неверный формат + +#### Пример использования + +```php +$apiData = ['country' => 'США']; +$country = Country::fromArray($apiData); +``` + +### toArray() + +Преобразует объект страны в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Массив с данными страны + +#### Пример использования + +```php +$countryArray = $country->toArray(); +echo json_encode($countryArray); // JSON представление страны +``` + +## Полный пример использования + +```php +films; +$film = $filmService->getById(301); // Матрица + +echo "=== Страны производства фильма 'Матрица' ===\n"; + +// Вывод стран производства +if (!empty($film->countries)) { + echo "Страны производства:\n"; + foreach ($film->countries as $index => $country) { + echo ($index + 1) . ". {$country}\n"; + } +} else { + echo "Информация о странах производства отсутствует\n"; +} + +// Создание объектов стран +$countries = [ + new Country('США'), + new Country('Австралия'), + new Country('Великобритания') +]; + +echo "\nСозданные страны:\n"; +foreach ($countries as $country) { + echo "- {$country}\n"; +} +``` + +## Работа со странами + +```php +// Функция для фильтрации фильмов по стране +function filterFilmsByCountry(array $films, string $countryName): array { + return array_filter($films, function($film) use ($countryName) { + foreach ($film->countries as $country) { + if (strcasecmp($country->country, $countryName) === 0) { + return true; + } + } + return false; + }); +} + +// Функция для получения статистики по странам +function getCountriesStatistics(array $films): array { + $stats = []; + + foreach ($films as $film) { + foreach ($film->countries as $country) { + $countryName = $country->country; + if (!isset($stats[$countryName])) { + $stats[$countryName] = 0; + } + $stats[$countryName]++; + } + } + + arsort($stats); + return $stats; +} + +// Функция для поиска фильмов по нескольким странам +function findFilmsByMultipleCountries(array $films, array $countryNames): array { + return array_filter($films, function($film) use ($countryNames) { + $filmCountries = array_map(fn($country) => $country->country, $film->countries); + + foreach ($countryNames as $countryName) { + if (in_array($countryName, $filmCountries)) { + return true; + } + } + return false; + }); +} + +// Использование +$films = $filmService->getTopFilms(); + +// Фильтрация по стране +$usFilms = filterFilmsByCountry($films, 'США'); +echo "Фильмов из США: " . count($usFilms) . "\n"; + +// Получение статистики +$stats = getCountriesStatistics($films); +echo "Топ стран по количеству фильмов:\n"; +foreach (array_slice($stats, 0, 10, true) as $country => $count) { + echo "- {$country}: {$count} фильмов\n"; +} + +// Поиск по нескольким странам +$europeanFilms = findFilmsByMultipleCountries($films, ['Франция', 'Германия', 'Италия']); +echo "Европейских фильмов: " . count($europeanFilms) . "\n"; +``` + +## Создание отчета по странам + +```php +class CountriesReport { + private array $films; + + public function __construct(array $films) { + $this->films = $films; + } + + public function getTopCountries(int $limit = 10): array { + $stats = getCountriesStatistics($this->films); + return array_slice($stats, 0, $limit, true); + } + + public function getFilmsByCountry(string $countryName): array { + return filterFilmsByCountry($this->films, $countryName); + } + + public function getCountriesWithMostFilms(int $minFilms = 5): array { + $stats = getCountriesStatistics($this->films); + return array_filter($stats, fn($count) => $count >= $minFilms); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО СТРАНАМ ПРОИЗВОДСТВА ===\n\n"; + + $stats = getCountriesStatistics($this->films); + $totalFilms = count($this->films); + + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего фильмов: {$totalFilms}\n"; + $report .= "Уникальных стран: " . count($stats) . "\n\n"; + + $report .= "🏆 ТОП-10 СТРАН:\n"; + foreach (array_slice($stats, 0, 10, true) as $country => $count) { + $percentage = round(($count / $totalFilms) * 100, 1); + $report .= "• {$country}: {$count} фильмов ({$percentage}%)\n"; + } + + $report .= "\n📈 СТРАНЫ С БОЛЬШИМ КОЛИЧЕСТВОМ ФИЛЬМОВ:\n"; + $majorCountries = $this->getCountriesWithMostFilms(10); + foreach ($majorCountries as $country => $count) { + $report .= "• {$country}: {$count} фильмов\n"; + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $stats = getCountriesStatistics($this->films); + $totalFilms = count($this->films); + + // Статистика + $html .= "
\n"; + $html .= "

Общая статистика

\n"; + $html .= "

Всего фильмов: {$totalFilms}

\n"; + $html .= "

Уникальных стран: " . count($stats) . "

\n"; + $html .= "
\n"; + + // Топ стран + $html .= "

Топ стран по количеству фильмов

\n"; + foreach (array_slice($stats, 0, 20, true) as $country => $count) { + $percentage = round(($count / $totalFilms) * 100, 1); + $html .= "
\n"; + $html .= "
{$country}
\n"; + $html .= "
{$count} фильмов
\n"; + $html .= "
{$percentage}%
\n"; + $html .= "
\n"; + } + + $html .= "
\n\n"; + + return $html; + } +} + +// Использование +$films = $filmService->getTopFilms(); +$report = new CountriesReport($films); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Статистика по странам производства'); +file_put_contents('countries_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в countries_report.html\n"; +``` + +## Анализ географического распределения + +```php +function analyzeGeographicDistribution(array $films): array { + $analysis = [ + 'total' => count($films), + 'countries' => [], + 'continents' => [], + 'mostDiverse' => [], + 'singleCountry' => 0, + 'multiCountry' => 0 + ]; + + // Определение континентов + $continentMap = [ + 'США' => 'Северная Америка', + 'Канада' => 'Северная Америка', + 'Мексика' => 'Северная Америка', + 'Великобритания' => 'Европа', + 'Франция' => 'Европа', + 'Германия' => 'Европа', + 'Италия' => 'Европа', + 'Испания' => 'Европа', + 'Россия' => 'Европа', + 'Китай' => 'Азия', + 'Япония' => 'Азия', + 'Южная Корея' => 'Азия', + 'Индия' => 'Азия', + 'Австралия' => 'Океания', + 'Бразилия' => 'Южная Америка', + 'Аргентина' => 'Южная Америка' + ]; + + $countryStats = []; + $continentStats = []; + + foreach ($films as $film) { + $filmCountries = $film->countries; + $countryCount = count($filmCountries); + + // Подсчет фильмов по количеству стран + if ($countryCount === 1) { + $analysis['singleCountry']++; + } else { + $analysis['multiCountry']++; + } + + // Статистика по странам + foreach ($filmCountries as $country) { + $countryName = $country->country; + + if (!isset($countryStats[$countryName])) { + $countryStats[$countryName] = 0; + } + $countryStats[$countryName]++; + + // Статистика по континентам + $continent = $continentMap[$countryName] ?? 'Другое'; + if (!isset($continentStats[$continent])) { + $continentStats[$continent] = 0; + } + $continentStats[$continent]++; + } + + // Фильмы с наибольшим разнообразием стран + if ($countryCount > 3) { + $analysis['mostDiverse'][] = [ + 'film' => $film, + 'countries' => $filmCountries, + 'count' => $countryCount + ]; + } + } + + $analysis['countries'] = $countryStats; + $analysis['continents'] = $continentStats; + + // Сортировка + arsort($analysis['countries']); + arsort($analysis['continents']); + + return $analysis; +} + +// Использование +$films = $filmService->getTopFilms(); +$analysis = analyzeGeographicDistribution($films); + +echo "=== Анализ географического распределения ===\n"; +echo "Всего фильмов: {$analysis['total']}\n"; +echo "Фильмов из одной страны: {$analysis['singleCountry']}\n"; +echo "Международных фильмов: {$analysis['multiCountry']}\n"; + +echo "\nТоп континентов:\n"; +foreach (array_slice($analysis['continents'], 0, 5, true) as $continent => $count) { + echo "- {$continent}: {$count} упоминаний\n"; +} + +echo "\nФильмы с наибольшим разнообразием стран:\n"; +foreach (array_slice($analysis['mostDiverse'], 0, 5) as $item) { + $film = $item['film']; + $countries = array_map(fn($c) => $c->country, $item['countries']); + echo "- {$film->nameRu}: " . implode(', ', $countries) . " ({$item['count']} стран)\n"; +} +``` + +## Связанные классы + +- [`Film`](film.md) - Модель фильма +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/models/distribution.md b/docs/dev/notkinopoiskphp/models/distribution.md new file mode 100644 index 0000000..ff8c4b0 --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/distribution.md @@ -0,0 +1,678 @@ +# Distribution + +Модель данных о прокате фильма из Kinopoisk API. + +## Описание + +`Distribution` представляет информацию о прокате фильма в различных странах, включая тип проката, дату, страну и компании-дистрибьюторы. + +### Основные возможности + +- Хранение информации о прокате в неизменяемом виде +- Создание объекта из массива данных API +- Доступ к метаданным проката и дистрибьюторам + +**API Endpoint:** `/api/v2.2/films/{id}/distributions` + +## Свойства + +### Основная информация + +- `$type` (DistributionType) - Тип проката +- `$subType` (string|null) - Подтип проката +- `$date` (string|null) - Дата проката +- `$reRelease` (bool|null) - Флаг повторного проката +- `$country` (Country|null) - Страна проката +- `$companies` (array) - Массив компаний-дистрибьюторов + +## Конструктор + +```php +public function __construct( + public readonly DistributionType $type, + public readonly ?string $subType, + public readonly ?string $date, + public readonly ?bool $reRelease, + public readonly ?Country $country, + public readonly array $companies, +) +``` + +### Пример создания + +```php +$distribution = new Distribution( + type: DistributionType::CINEMA, + subType: 'WIDE', + date: '2023-01-15', + reRelease: false, + country: $country, + companies: ['Компания 1', 'Компания 2'] +); +``` + +## Методы + +### fromArray() + +Создает экземпляр проката из массива данных API. + +```php +public static function fromArray(array $data): self +``` + +#### Параметры + +- `$data` (array) - Массив данных проката от API + +#### Возвращаемое значение + +- `self` - Новый экземпляр проката + +#### Исключения + +- `\InvalidArgumentException` - Если данные имеют неверный формат + +#### Описание + +Статический метод для удобного создания объекта Distribution из данных, полученных от Kinopoisk API. Автоматически обрабатывает nullable поля и создает объект Country при необходимости. + +#### Пример использования + +```php +$apiData = [ + 'type' => 'CINEMA', + 'subType' => 'WIDE', + 'date' => '2023-01-15', + 'reRelease' => false, + 'country' => ['country' => 'США'], + 'companies' => ['Компания 1', 'Компания 2'] +]; + +$distribution = Distribution::fromArray($apiData); +``` + +### toArray() + +Преобразует объект проката в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Массив с данными проката + +#### Пример использования + +```php +$distributionArray = $distribution->toArray(); +echo json_encode($distributionArray); // JSON представление проката +``` + +## Полный пример использования + +```php +films; +$distributions = $filmService->getDistributions(301); // Матрица + +echo "=== Данные о прокате фильма 'Матрица' ===\n"; + +// Обработка всех записей о прокате +foreach ($distributions as $distribution) { + echo "\n🎬 {$distribution->type->getDisplayName()}:\n"; + + if ($distribution->subType) { + echo " Подтип: {$distribution->subType}\n"; + } + + if ($distribution->date) { + echo " Дата: {$distribution->date}\n"; + } + + if ($distribution->country) { + echo " Страна: {$distribution->country->country}\n"; + } + + if ($distribution->reRelease !== null) { + echo " Повторный прокат: " . ($distribution->reRelease ? 'Да' : 'Нет') . "\n"; + } + + if (!empty($distribution->companies)) { + echo " Дистрибьюторы: " . implode(', ', $distribution->companies) . "\n"; + } +} +``` + +## Работа с данными о прокате + +```php +// Функция для фильтрации по типу проката +function filterDistributionsByType(array $distributions, DistributionType $type): array { + return array_filter($distributions, fn($dist) => $dist->type === $type); +} + +// Функция для фильтрации по стране +function filterDistributionsByCountry(array $distributions, string $countryName): array { + return array_filter($distributions, function($dist) use ($countryName) { + return $dist->country && strcasecmp($dist->country->country, $countryName) === 0; + }); +} + +// Функция для получения прокатов с датами +function getDistributionsWithDates(array $distributions): array { + return array_filter($distributions, fn($dist) => $dist->date !== null); +} + +// Функция для получения повторных прокатов +function getReReleases(array $distributions): array { + return array_filter($distributions, fn($dist) => $dist->reRelease === true); +} + +// Функция для получения статистики по прокату +function getDistributionStatistics(array $distributions): array { + $stats = [ + 'totalDistributions' => count($distributions), + 'types' => [], + 'countries' => [], + 'companies' => [], + 'withDates' => 0, + 'reReleases' => 0, + 'dateRange' => [ + 'earliest' => null, + 'latest' => null + ] + ]; + + $dates = []; + + foreach ($distributions as $distribution) { + // Статистика по типам + $typeKey = $distribution->type->value; + if (!isset($stats['types'][$typeKey])) { + $stats['types'][$typeKey] = 0; + } + $stats['types'][$typeKey]++; + + // Статистика по странам + if ($distribution->country) { + $countryName = $distribution->country->country; + if (!isset($stats['countries'][$countryName])) { + $stats['countries'][$countryName] = 0; + } + $stats['countries'][$countryName]++; + } + + // Статистика по компаниям + foreach ($distribution->companies as $company) { + if (!isset($stats['companies'][$company])) { + $stats['companies'][$company] = 0; + } + $stats['companies'][$company]++; + } + + // Подсчет с датами и повторных прокатов + if ($distribution->date) { + $stats['withDates']++; + $dates[] = $distribution->date; + } + + if ($distribution->reRelease === true) { + $stats['reReleases']++; + } + } + + // Анализ диапазона дат + if (!empty($dates)) { + sort($dates); + $stats['dateRange']['earliest'] = $dates[0]; + $stats['dateRange']['latest'] = end($dates); + } + + return $stats; +} + +// Использование +$distributions = $filmService->getDistributions(301); + +// Фильтрация по типу +$cinemaDistributions = filterDistributionsByType($distributions, DistributionType::CINEMA); +echo "Кинопрокатов: " . count($cinemaDistributions) . "\n"; + +// Фильтрация по стране +$usDistributions = filterDistributionsByCountry($distributions, 'США'); +echo "Прокатов в США: " . count($usDistributions) . "\n"; + +// Прокаты с датами +$distributionsWithDates = getDistributionsWithDates($distributions); +echo "Прокатов с датами: " . count($distributionsWithDates) . "\n"; + +// Повторные прокаты +$reReleases = getReReleases($distributions); +echo "Повторных прокатов: " . count($reReleases) . "\n"; + +// Статистика +$stats = getDistributionStatistics($distributions); +echo "Всего записей о прокате: {$stats['totalDistributions']}\n"; +echo "Уникальных стран: " . count($stats['countries']) . "\n"; +echo "Уникальных компаний: " . count($stats['companies']) . "\n"; +``` + +## Создание отчета о прокате + +```php +class DistributionReport { + private array $distributions; + + public function __construct(array $distributions) { + $this->distributions = $distributions; + } + + public function getDistributionsByType(DistributionType $type): array { + return filterDistributionsByType($this->distributions, $type); + } + + public function getDistributionsByCountry(string $countryName): array { + return filterDistributionsByCountry($this->distributions, $countryName); + } + + public function getDistributionsWithDates(): array { + return getDistributionsWithDates($this->distributions); + } + + public function getReReleases(): array { + return getReReleases($this->distributions); + } + + public function getStatistics(): array { + return getDistributionStatistics($this->distributions); + } + + public function getTopCountries(int $limit = 5): array { + $stats = $this->getStatistics(); + arsort($stats['countries']); + return array_slice($stats['countries'], 0, $limit, true); + } + + public function getTopCompanies(int $limit = 5): array { + $stats = $this->getStatistics(); + arsort($stats['companies']); + return array_slice($stats['companies'], 0, $limit, true); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ О ПРОКАТЕ ===\n\n"; + + $stats = $this->getStatistics(); + + // Общая статистика + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего записей о прокате: {$stats['totalDistributions']}\n"; + $report .= "Уникальных типов проката: " . count($stats['types']) . "\n"; + $report .= "Уникальных стран: " . count($stats['countries']) . "\n"; + $report .= "Уникальных компаний: " . count($stats['companies']) . "\n"; + $report .= "Прокатов с датами: {$stats['withDates']}\n"; + $report .= "Повторных прокатов: {$stats['reReleases']}\n"; + + if ($stats['dateRange']['earliest']) { + $report .= "Диапазон дат: {$stats['dateRange']['earliest']} - {$stats['dateRange']['latest']}\n"; + } + + $report .= "\n"; + + // Статистика по типам + $report .= "🎬 СТАТИСТИКА ПО ТИПАМ ПРОКАТА:\n"; + foreach ($stats['types'] as $typeKey => $count) { + $type = DistributionType::from($typeKey); + $report .= "• {$type->getDisplayName()}: {$count} записей\n"; + } + + $report .= "\n"; + + // Топ стран + $topCountries = $this->getTopCountries(); + $report .= "🌍 ТОП СТРАН ПО ПРОКАТУ:\n"; + foreach ($topCountries as $country => $count) { + $report .= "• {$country}: {$count} прокатов\n"; + } + + $report .= "\n"; + + // Топ компаний + $topCompanies = $this->getTopCompanies(); + $report .= "🏢 ТОП КОМПАНИЙ-ДИСТРИБЬЮТОРОВ:\n"; + foreach ($topCompanies as $company => $count) { + $report .= "• {$company}: {$count} прокатов\n"; + } + + $report .= "\n"; + + // Детали по прокатам + $report .= "📋 ДЕТАЛИ ПО ПРОКАТАМ:\n"; + foreach ($this->distributions as $index => $distribution) { + $report .= "\n" . ($index + 1) . ". {$distribution->type->getDisplayName()}:\n"; + + if ($distribution->subType) { + $report .= " Подтип: {$distribution->subType}\n"; + } + + if ($distribution->date) { + $report .= " Дата: {$distribution->date}\n"; + } + + if ($distribution->country) { + $report .= " Страна: {$distribution->country->country}\n"; + } + + if ($distribution->reRelease !== null) { + $report .= " Повторный прокат: " . ($distribution->reRelease ? 'Да' : 'Нет') . "\n"; + } + + if (!empty($distribution->companies)) { + $report .= " Дистрибьюторы: " . implode(', ', $distribution->companies) . "\n"; + } + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $stats = $this->getStatistics(); + $topCountries = $this->getTopCountries(); + $topCompanies = $this->getTopCompanies(); + + // Общая статистика + $html .= "
\n"; + $html .= "

Общая статистика

\n"; + $html .= "

Всего записей о прокате: {$stats['totalDistributions']}

\n"; + $html .= "

Уникальных типов проката: " . count($stats['types']) . "

\n"; + $html .= "

Уникальных стран: " . count($stats['countries']) . "

\n"; + $html .= "

Уникальных компаний: " . count($stats['companies']) . "

\n"; + $html .= "

Прокатов с датами: {$stats['withDates']}

\n"; + $html .= "

Повторных прокатов: {$stats['reReleases']}

\n"; + + if ($stats['dateRange']['earliest']) { + $html .= "

Диапазон дат: {$stats['dateRange']['earliest']} - {$stats['dateRange']['latest']}

\n"; + } + + $html .= "
\n"; + + // Топ стран + $html .= "
\n"; + $html .= "
Топ стран по прокату
\n"; + + foreach ($topCountries as $country => $count) { + $html .= "
\n"; + $html .= "
{$country}: {$count} прокатов
\n"; + $html .= "
\n"; + } + + $html .= "
\n"; + + // Топ компаний + $html .= "
\n"; + $html .= "
Топ компаний-дистрибьюторов
\n"; + + foreach ($topCompanies as $company => $count) { + $html .= "
\n"; + $html .= "
{$company}: {$count} прокатов
\n"; + $html .= "
\n"; + } + + $html .= "
\n"; + + // Детали по прокатам + $html .= "
\n"; + $html .= "
Детали по прокатам
\n"; + + foreach ($this->distributions as $index => $distribution) { + $cssClass = ''; + if ($distribution->reRelease === true) { + $cssClass = 're-release'; + } + + switch ($distribution->type) { + case DistributionType::CINEMA: + $cssClass .= ' cinema'; + break; + case DistributionType::DIGITAL: + $cssClass .= ' digital'; + break; + case DistributionType::PHYSICAL: + $cssClass .= ' physical'; + break; + } + + $html .= "
\n"; + $html .= "
{$distribution->type->getDisplayName()}
\n"; + $html .= "
\n"; + + if ($distribution->subType) { + $html .= "
Подтип: {$distribution->subType}
\n"; + } + + if ($distribution->date) { + $html .= "
Дата: {$distribution->date}
\n"; + } + + if ($distribution->country) { + $html .= "
Страна: {$distribution->country->country}
\n"; + } + + if ($distribution->reRelease !== null) { + $html .= "
Повторный прокат: " . ($distribution->reRelease ? 'Да' : 'Нет') . "
\n"; + } + + if (!empty($distribution->companies)) { + $html .= "
Дистрибьюторы: " . implode(', ', $distribution->companies) . "
\n"; + } + + $html .= "
\n
\n"; + } + + $html .= "
\n
\n\n"; + + return $html; + } +} + +// Использование +$distributions = $filmService->getDistributions(301); +$report = new DistributionReport($distributions); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Отчет о прокате фильма'); +file_put_contents('distribution_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в distribution_report.html\n"; +``` + +## Анализ проката + +```php +function analyzeDistribution(array $distributions): array { + $analysis = [ + 'totalDistributions' => count($distributions), + 'typeDistribution' => [], + 'countryDistribution' => [], + 'companyDistribution' => [], + 'dateAnalysis' => [ + 'withDates' => 0, + 'dateRange' => null, + 'monthlyDistribution' => [], + 'yearlyDistribution' => [] + ], + 'reReleaseAnalysis' => [ + 'total' => 0, + 'byType' => [], + 'byCountry' => [] + ] + ]; + + $dates = []; + $monthlyStats = []; + $yearlyStats = []; + + foreach ($distributions as $distribution) { + // Распределение по типам + $typeKey = $distribution->type->value; + if (!isset($analysis['typeDistribution'][$typeKey])) { + $analysis['typeDistribution'][$typeKey] = 0; + } + $analysis['typeDistribution'][$typeKey]++; + + // Распределение по странам + if ($distribution->country) { + $countryName = $distribution->country->country; + if (!isset($analysis['countryDistribution'][$countryName])) { + $analysis['countryDistribution'][$countryName] = 0; + } + $analysis['countryDistribution'][$countryName]++; + } + + // Распределение по компаниям + foreach ($distribution->companies as $company) { + if (!isset($analysis['companyDistribution'][$company])) { + $analysis['companyDistribution'][$company] = 0; + } + $analysis['companyDistribution'][$company]++; + } + + // Анализ дат + if ($distribution->date) { + $analysis['dateAnalysis']['withDates']++; + $dates[] = $distribution->date; + + // Месячное распределение + $month = date('Y-m', strtotime($distribution->date)); + if (!isset($monthlyStats[$month])) { + $monthlyStats[$month] = 0; + } + $monthlyStats[$month]++; + + // Годовое распределение + $year = date('Y', strtotime($distribution->date)); + if (!isset($yearlyStats[$year])) { + $yearlyStats[$year] = 0; + } + $yearlyStats[$year]++; + } + + // Анализ повторных прокатов + if ($distribution->reRelease === true) { + $analysis['reReleaseAnalysis']['total']++; + + // По типам + $typeKey = $distribution->type->value; + if (!isset($analysis['reReleaseAnalysis']['byType'][$typeKey])) { + $analysis['reReleaseAnalysis']['byType'][$typeKey] = 0; + } + $analysis['reReleaseAnalysis']['byType'][$typeKey]++; + + // По странам + if ($distribution->country) { + $countryName = $distribution->country->country; + if (!isset($analysis['reReleaseAnalysis']['byCountry'][$countryName])) { + $analysis['reReleaseAnalysis']['byCountry'][$countryName] = 0; + } + $analysis['reReleaseAnalysis']['byCountry'][$countryName]++; + } + } + } + + // Анализ диапазона дат + if (!empty($dates)) { + sort($dates); + $analysis['dateAnalysis']['dateRange'] = [ + 'earliest' => $dates[0], + 'latest' => end($dates) + ]; + } + + $analysis['dateAnalysis']['monthlyDistribution'] = $monthlyStats; + $analysis['dateAnalysis']['yearlyDistribution'] = $yearlyStats; + + return $analysis; +} + +// Использование +$distributions = $filmService->getDistributions(301); +$analysis = analyzeDistribution($distributions); + +echo "=== Анализ проката ===\n"; +echo "Всего записей о прокате: {$analysis['totalDistributions']}\n"; +echo "Уникальных типов: " . count($analysis['typeDistribution']) . "\n"; +echo "Уникальных стран: " . count($analysis['countryDistribution']) . "\n"; +echo "Уникальных компаний: " . count($analysis['companyDistribution']) . "\n"; + +echo "\nРаспределение по типам:\n"; +foreach ($analysis['typeDistribution'] as $typeKey => $count) { + $type = DistributionType::from($typeKey); + $percentage = round(($count / $analysis['totalDistributions']) * 100, 1); + echo "- {$type->getDisplayName()}: {$count} ({$percentage}%)\n"; +} + +echo "\nТоп стран:\n"; +arsort($analysis['countryDistribution']); +$topCountries = array_slice($analysis['countryDistribution'], 0, 5, true); +foreach ($topCountries as $country => $count) { + echo "- {$country}: {$count} прокатов\n"; +} + +echo "\nТоп компаний:\n"; +arsort($analysis['companyDistribution']); +$topCompanies = array_slice($analysis['companyDistribution'], 0, 5, true); +foreach ($topCompanies as $company => $count) { + echo "- {$company}: {$count} прокатов\n"; +} + +if ($analysis['dateAnalysis']['dateRange']) { + echo "\nДиапазон дат проката:\n"; + echo "Самый ранний: {$analysis['dateAnalysis']['dateRange']['earliest']}\n"; + echo "Самый поздний: {$analysis['dateAnalysis']['dateRange']['latest']}\n"; +} + +echo "\nПовторных прокатов: {$analysis['reReleaseAnalysis']['total']}\n"; +``` + +## Связанные классы + +- [`DistributionType`](../enums/distribution-type.md) - Типы проката +- [`Country`](country.md) - Модель страны +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/models/episode.md b/docs/dev/notkinopoiskphp/models/episode.md new file mode 100644 index 0000000..b6e741e --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/episode.md @@ -0,0 +1,540 @@ +# Episode + +Модель эпизода сериала из Kinopoisk API. + +## Описание + +`Episode` представляет информацию об отдельном эпизоде сериала, включая номер сезона, номер эпизода, название, синопсис и дату выхода. + +### Основные возможности + +- Хранение информации об эпизоде в неизменяемом виде +- Создание объекта из массива данных API +- Получение отображаемого названия эпизода +- Поддержка многоязычных названий + +## Свойства + +### Основная информация + +- `$seasonNumber` (int) - Номер сезона +- `$episodeNumber` (int) - Номер эпизода в сезоне +- `$nameRu` (string|null) - Название эпизода на русском языке +- `$nameEn` (string|null) - Название эпизода на английском языке +- `$synopsis` (string|null) - Краткое описание сюжета эпизода +- `$releaseDate` (string|null) - Дата выхода эпизода + +## Конструктор + +```php +public function __construct( + public readonly int $seasonNumber, + public readonly int $episodeNumber, + public readonly ?string $nameRu, + public readonly ?string $nameEn, + public readonly ?string $synopsis, + public readonly ?string $releaseDate, +) +``` + +### Пример создания + +```php +$episode = new Episode( + seasonNumber: 1, + episodeNumber: 1, + nameRu: 'Пилот', + nameEn: 'Pilot', + synopsis: 'Первый эпизод сериала...', + releaseDate: '2023-01-15' +); +``` + +## Методы + +### fromArray() + +Создает экземпляр эпизода из массива данных API. + +```php +public static function fromArray(array $data): self +``` + +#### Параметры + +- `$data` (array) - Массив данных эпизода от API + +#### Возвращаемое значение + +- `self` - Новый экземпляр эпизода + +#### Исключения + +- `\InvalidArgumentException` - Если данные имеют неверный формат + +#### Пример использования + +```php +$apiData = [ + 'seasonNumber' => 1, + 'episodeNumber' => 1, + 'nameRu' => 'Пилот', + 'nameEn' => 'Pilot', + 'synopsis' => 'Первый эпизод сериала...', + 'releaseDate' => '2023-01-15' +]; + +$episode = Episode::fromArray($apiData); +``` + +### getDisplayName() + +Получает отображаемое название эпизода. + +```php +public function getDisplayName(): string +``` + +#### Возвращаемое значение + +- `string` - Отображаемое название эпизода + +#### Описание + +Возвращает наиболее подходящее название для отображения пользователю. Приоритет: русское название → английское название → "Эпизод N" + +#### Пример использования + +```php +echo $episode->getDisplayName(); // "Пилот" или "Pilot" или "Эпизод 1" +``` + +### toArray() + +Преобразует объект эпизода в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Массив с данными эпизода + +#### Пример использования + +```php +$episodeArray = $episode->toArray(); +echo json_encode($episodeArray); // JSON представление эпизода +``` + +## Полный пример использования + +```php +films; +$seasons = $filmService->getSeasons(123); // ID сериала + +echo "=== Эпизоды сериала ===\n"; + +// Обработка всех сезонов и эпизодов +foreach ($seasons as $season) { + echo "\n📺 Сезон {$season->number} ({$season->episodesCount} эпизодов):\n"; + + if (!empty($season->episodes)) { + foreach ($season->episodes as $episode) { + echo " • S{$episode->seasonNumber}E{$episode->episodeNumber}: {$episode->getDisplayName()}\n"; + + if ($episode->releaseDate) { + echo " Дата выхода: {$episode->releaseDate}\n"; + } + + if ($episode->synopsis) { + echo " Синопсис: " . substr($episode->synopsis, 0, 100) . "...\n"; + } + } + } +} +``` + +## Работа с эпизодами + +```php +// Функция для фильтрации эпизодов по сезону +function filterEpisodesBySeason(array $episodes, int $seasonNumber): array { + return array_filter($episodes, fn($episode) => $episode->seasonNumber === $seasonNumber); +} + +// Функция для поиска эпизодов по названию +function searchEpisodesByName(array $episodes, string $searchTerm): array { + $searchTerm = strtolower($searchTerm); + return array_filter($episodes, function($episode) use ($searchTerm) { + $nameRu = strtolower($episode->nameRu ?? ''); + $nameEn = strtolower($episode->nameEn ?? ''); + return str_contains($nameRu, $searchTerm) || str_contains($nameEn, $searchTerm); + }); +} + +// Функция для получения статистики по эпизодам +function getEpisodesStatistics(array $episodes): array { + $stats = [ + 'totalEpisodes' => count($episodes), + 'seasons' => [], + 'episodesWithSynopsis' => 0, + 'episodesWithReleaseDate' => 0, + 'episodesWithRussianName' => 0, + 'episodesWithEnglishName' => 0 + ]; + + foreach ($episodes as $episode) { + // Статистика по сезонам + $seasonKey = $episode->seasonNumber; + if (!isset($stats['seasons'][$seasonKey])) { + $stats['seasons'][$seasonKey] = 0; + } + $stats['seasons'][$seasonKey]++; + + // Подсчет эпизодов с различными данными + if ($episode->synopsis) $stats['episodesWithSynopsis']++; + if ($episode->releaseDate) $stats['episodesWithReleaseDate']++; + if ($episode->nameRu) $stats['episodesWithRussianName']++; + if ($episode->nameEn) $stats['episodesWithEnglishName']++; + } + + ksort($stats['seasons']); + return $stats; +} + +// Функция для получения эпизодов в хронологическом порядке +function getEpisodesInChronologicalOrder(array $episodes): array { + $sorted = $episodes; + usort($sorted, function($a, $b) { + // Сначала по номеру сезона, затем по номеру эпизода + if ($a->seasonNumber !== $b->seasonNumber) { + return $a->seasonNumber <=> $b->seasonNumber; + } + return $a->episodeNumber <=> $b->episodeNumber; + }); + return $sorted; +} + +// Функция для получения последних эпизодов +function getLatestEpisodes(array $episodes, int $limit = 10): array { + $withDates = array_filter($episodes, fn($episode) => $episode->releaseDate !== null); + + usort($withDates, function($a, $b) { + return strcmp($b->releaseDate, $a->releaseDate); + }); + + return array_slice($withDates, 0, $limit); +} + +// Использование +$seasons = $filmService->getSeasons(123); +$allEpisodes = []; + +// Сбор всех эпизодов +foreach ($seasons as $season) { + if (!empty($season->episodes)) { + $allEpisodes = array_merge($allEpisodes, $season->episodes); + } +} + +// Фильтрация по сезону +$season1Episodes = filterEpisodesBySeason($allEpisodes, 1); +echo "Эпизодов в 1 сезоне: " . count($season1Episodes) . "\n"; + +// Поиск по названию +$pilotEpisodes = searchEpisodesByName($allEpisodes, 'пилот'); +echo "Эпизодов с 'пилот' в названии: " . count($pilotEpisodes) . "\n"; + +// Статистика +$stats = getEpisodesStatistics($allEpisodes); +echo "Всего эпизодов: {$stats['totalEpisodes']}\n"; +echo "Эпизодов с синопсисом: {$stats['episodesWithSynopsis']}\n"; +echo "Эпизодов с датой выхода: {$stats['episodesWithReleaseDate']}\n"; + +// Последние эпизоды +$latestEpisodes = getLatestEpisodes($allEpisodes, 5); +echo "Последние 5 эпизодов:\n"; +foreach ($latestEpisodes as $episode) { + echo "- S{$episode->seasonNumber}E{$episode->episodeNumber}: {$episode->getDisplayName()} ({$episode->releaseDate})\n"; +} +``` + +## Создание отчета по эпизодам + +```php +class EpisodesReport { + private array $episodes; + + public function __construct(array $episodes) { + $this->episodes = $episodes; + } + + public function getEpisodesBySeason(int $seasonNumber): array { + return filterEpisodesBySeason($this->episodes, $seasonNumber); + } + + public function getStatistics(): array { + return getEpisodesStatistics($this->episodes); + } + + public function getChronologicalEpisodes(): array { + return getEpisodesInChronologicalOrder($this->episodes); + } + + public function getLatestEpisodes(int $limit = 10): array { + return getLatestEpisodes($this->episodes, $limit); + } + + public function getEpisodesWithSynopsis(): array { + return array_filter($this->episodes, fn($episode) => $episode->synopsis !== null); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО ЭПИЗОДАМ ===\n\n"; + + $stats = $this->getStatistics(); + + // Общая статистика + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего эпизодов: {$stats['totalEpisodes']}\n"; + $report .= "Эпизодов с синопсисом: {$stats['episodesWithSynopsis']}\n"; + $report .= "Эпизодов с датой выхода: {$stats['episodesWithReleaseDate']}\n"; + $report .= "Эпизодов с русским названием: {$stats['episodesWithRussianName']}\n"; + $report .= "Эпизодов с английским названием: {$stats['episodesWithEnglishName']}\n\n"; + + // Статистика по сезонам + $report .= "📺 СТАТИСТИКА ПО СЕЗОНАМ:\n"; + foreach ($stats['seasons'] as $seasonNumber => $episodeCount) { + $report .= "• Сезон {$seasonNumber}: {$episodeCount} эпизодов\n"; + } + + // Последние эпизоды + $latestEpisodes = $this->getLatestEpisodes(5); + if (!empty($latestEpisodes)) { + $report .= "\n🆕 ПОСЛЕДНИЕ ЭПИЗОДЫ:\n"; + foreach ($latestEpisodes as $episode) { + $report .= "• S{$episode->seasonNumber}E{$episode->episodeNumber}: {$episode->getDisplayName()} ({$episode->releaseDate})\n"; + } + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $stats = $this->getStatistics(); + $chronologicalEpisodes = $this->getChronologicalEpisodes(); + + // Общая статистика + $html .= "
\n"; + $html .= "

Общая статистика

\n"; + $html .= "

Всего эпизодов: {$stats['totalEpisodes']}

\n"; + $html .= "

Эпизодов с синопсисом: {$stats['episodesWithSynopsis']}

\n"; + $html .= "

Эпизодов с датой выхода: {$stats['episodesWithReleaseDate']}

\n"; + $html .= "

Эпизодов с русским названием: {$stats['episodesWithRussianName']}

\n"; + $html .= "

Эпизодов с английским названием: {$stats['episodesWithEnglishName']}

\n"; + $html .= "
\n"; + + // Эпизоды по сезонам + $html .= "
\n"; + $html .= "
Эпизоды по сезонам
\n"; + + $currentSeason = null; + foreach ($chronologicalEpisodes as $episode) { + if ($currentSeason !== $episode->seasonNumber) { + if ($currentSeason !== null) { + $html .= "
\n"; + } + $currentSeason = $episode->seasonNumber; + $seasonEpisodes = $this->getEpisodesBySeason($currentSeason); + $html .= "
\n"; + $html .= "

Сезон {$currentSeason} (" . count($seasonEpisodes) . " эпизодов)

\n"; + } + + $html .= "
\n"; + $html .= "
S{$episode->seasonNumber}E{$episode->episodeNumber}: {$episode->getDisplayName()}
\n"; + $html .= "
\n"; + + if ($episode->nameRu && $episode->nameEn) { + $html .= "
Названия: {$episode->nameRu} / {$episode->nameEn}
\n"; + } elseif ($episode->nameRu) { + $html .= "
Название: {$episode->nameRu}
\n"; + } elseif ($episode->nameEn) { + $html .= "
Название: {$episode->nameEn}
\n"; + } + + if ($episode->releaseDate) { + $html .= "
Дата выхода: {$episode->releaseDate}
\n"; + } + + if ($episode->synopsis) { + $html .= "
Синопсис: " . htmlspecialchars(substr($episode->synopsis, 0, 150)) . "...
\n"; + } + + $html .= "
\n
\n"; + } + + if ($currentSeason !== null) { + $html .= "
\n"; + } + + $html .= "
\n"; + + $html .= "\n\n"; + + return $html; + } +} + +// Использование +$seasons = $filmService->getSeasons(123); +$allEpisodes = []; + +foreach ($seasons as $season) { + if (!empty($season->episodes)) { + $allEpisodes = array_merge($allEpisodes, $season->episodes); + } +} + +$report = new EpisodesReport($allEpisodes); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Отчет по эпизодам сериала'); +file_put_contents('episodes_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в episodes_report.html\n"; +``` + +## Анализ эпизодов + +```php +function analyzeEpisodes(array $episodes): array { + $analysis = [ + 'totalEpisodes' => count($episodes), + 'seasons' => [], + 'episodesPerSeason' => [], + 'averageEpisodesPerSeason' => 0, + 'episodesWithData' => [ + 'synopsis' => 0, + 'releaseDate' => 0, + 'nameRu' => 0, + 'nameEn' => 0 + ], + 'releaseDateRange' => [ + 'earliest' => null, + 'latest' => null + ], + 'seasonDistribution' => [] + ]; + + $seasonStats = []; + $releaseDates = []; + + foreach ($episodes as $episode) { + // Статистика по сезонам + $seasonKey = $episode->seasonNumber; + if (!isset($seasonStats[$seasonKey])) { + $seasonStats[$seasonKey] = 0; + } + $seasonStats[$seasonKey]++; + + // Подсчет эпизодов с данными + if ($episode->synopsis) $analysis['episodesWithData']['synopsis']++; + if ($episode->releaseDate) { + $analysis['episodesWithData']['releaseDate']++; + $releaseDates[] = $episode->releaseDate; + } + if ($episode->nameRu) $analysis['episodesWithData']['nameRu']++; + if ($episode->nameEn) $analysis['episodesWithData']['nameEn']++; + } + + // Анализ сезонов + $analysis['seasons'] = array_keys($seasonStats); + $analysis['episodesPerSeason'] = $seasonStats; + $analysis['averageEpisodesPerSeason'] = count($episodes) / count($seasonStats); + + // Анализ дат выпуска + if (!empty($releaseDates)) { + sort($releaseDates); + $analysis['releaseDateRange']['earliest'] = $releaseDates[0]; + $analysis['releaseDateRange']['latest'] = end($releaseDates); + } + + // Распределение по сезонам + foreach ($seasonStats as $season => $count) { + $analysis['seasonDistribution'][$season] = round(($count / count($episodes)) * 100, 1); + } + + return $analysis; +} + +// Использование +$seasons = $filmService->getSeasons(123); +$allEpisodes = []; + +foreach ($seasons as $season) { + if (!empty($season->episodes)) { + $allEpisodes = array_merge($allEpisodes, $season->episodes); + } +} + +$analysis = analyzeEpisodes($allEpisodes); + +echo "=== Анализ эпизодов ===\n"; +echo "Всего эпизодов: {$analysis['totalEpisodes']}\n"; +echo "Количество сезонов: " . count($analysis['seasons']) . "\n"; +echo "Среднее количество эпизодов на сезон: " . round($analysis['averageEpisodesPerSeason'], 1) . "\n"; + +echo "\nЭпизоды с данными:\n"; +foreach ($analysis['episodesWithData'] as $type => $count) { + $percentage = round(($count / $analysis['totalEpisodes']) * 100, 1); + echo "- {$type}: {$count} ({$percentage}%)\n"; +} + +if ($analysis['releaseDateRange']['earliest']) { + echo "\nДиапазон дат выпуска:\n"; + echo "Самый ранний: {$analysis['releaseDateRange']['earliest']}\n"; + echo "Самый поздний: {$analysis['releaseDateRange']['latest']}\n"; +} + +echo "\nРаспределение по сезонам:\n"; +foreach ($analysis['seasonDistribution'] as $season => $percentage) { + echo "- Сезон {$season}: {$percentage}%\n"; +} +``` + +## Связанные классы + +- [`Season`](season.md) - Модель сезона +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/models/external-source.md b/docs/dev/notkinopoiskphp/models/external-source.md new file mode 100644 index 0000000..d48cac5 --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/external-source.md @@ -0,0 +1,596 @@ +# ExternalSource + +Модель внешнего источника из Kinopoisk API. + +## Описание + +`ExternalSource` представляет информацию о рецензии или отзыве с внешней платформы, включая данные о платформе, рейтинге и содержании. + +### Основные возможности + +- Хранение информации о внешнем источнике в неизменяемом виде +- Создание объекта из массива данных API +- Доступ к данным платформы и рейтингу + +**API Endpoint:** `/api/v2.2/films/{id}/external_sources` + +## Свойства + +### Основная информация + +- `$url` (string) - URL источника +- `$platform` (string) - Название платформы +- `$logoUrl` (string) - URL логотипа платформы +- `$positiveRating` (int|null) - Количество положительных оценок +- `$negativeRating` (int|null) - Количество отрицательных оценок +- `$author` (string|null) - Автор отзыва +- `$title` (string|null) - Заголовок отзыва +- `$description` (string|null) - Содержание отзыва + +## Конструктор + +```php +public function __construct( + public readonly string $url, + public readonly string $platform, + public readonly string $logoUrl, + public readonly ?int $positiveRating, + public readonly ?int $negativeRating, + public readonly ?string $author, + public readonly ?string $title, + public readonly ?string $description, +) +``` + +### Пример создания + +```php +$externalSource = new ExternalSource( + url: 'https://example.com/review', + platform: 'IMDb', + logoUrl: 'https://...', + positiveRating: 90, + negativeRating: 10, + author: 'Пользователь', + title: 'Отличный фильм', + description: 'Подробный отзыв...' +); +``` + +## Методы + +### fromArray() + +Создает экземпляр внешнего источника из массива данных API. + +```php +public static function fromArray(array $data): self +``` + +#### Параметры + +- `$data` (array) - Массив данных внешнего источника от API + +#### Возвращаемое значение + +- `self` - Новый экземпляр внешнего источника + +#### Исключения + +- `\InvalidArgumentException` - Если данные имеют неверный формат + +#### Пример использования + +```php +$apiData = [ + 'url' => 'https://example.com/review', + 'platform' => 'IMDb', + 'logoUrl' => 'https://...', + 'positiveRating' => 90, + 'negativeRating' => 10, + 'author' => 'Пользователь', + 'title' => 'Отличный фильм', + 'description' => 'Подробный отзыв...' +]; + +$externalSource = ExternalSource::fromArray($apiData); +``` + +### toArray() + +Преобразует объект внешнего источника в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Массив с данными внешнего источника + +#### Пример использования + +```php +$externalSourceArray = $externalSource->toArray(); +echo json_encode($externalSourceArray); // JSON представление внешнего источника +``` + +## Полный пример использования + +```php +films; +$externalSources = $filmService->getExternalSources(301); // Матрица + +echo "=== Внешние источники фильма 'Матрица' ===\n"; + +// Обработка всех источников +foreach ($externalSources as $source) { + echo "\n📰 {$source->platform}:\n"; + echo " URL: {$source->url}\n"; + + if ($source->title) { + echo " Заголовок: {$source->title}\n"; + } + + if ($source->author) { + echo " Автор: {$source->author}\n"; + } + + if ($source->positiveRating !== null && $source->negativeRating !== null) { + $total = $source->positiveRating + $source->negativeRating; + $percentage = $total > 0 ? round(($source->positiveRating / $total) * 100, 1) : 0; + echo " Рейтинг: {$source->positiveRating}/{$source->negativeRating} ({$percentage}% положительных)\n"; + } + + if ($source->description) { + echo " Описание: " . substr($source->description, 0, 100) . "...\n"; + } +} +``` + +## Работа с внешними источниками + +```php +// Функция для фильтрации по платформе +function filterSourcesByPlatform(array $sources, string $platform): array { + return array_filter($sources, fn($source) => strcasecmp($source->platform, $platform) === 0); +} + +// Функция для получения источников с рейтингом +function getSourcesWithRating(array $sources): array { + return array_filter($sources, fn($source) => + $source->positiveRating !== null && $source->negativeRating !== null + ); +} + +// Функция для получения источников с автором +function getSourcesWithAuthor(array $sources): array { + return array_filter($sources, fn($source) => $source->author !== null); +} + +// Функция для получения статистики по платформам +function getPlatformStatistics(array $sources): array { + $stats = []; + + foreach ($sources as $source) { + $platform = $source->platform; + if (!isset($stats[$platform])) { + $stats[$platform] = [ + 'count' => 0, + 'withRating' => 0, + 'withAuthor' => 0, + 'withDescription' => 0, + 'totalPositive' => 0, + 'totalNegative' => 0 + ]; + } + + $stats[$platform]['count']++; + + if ($source->positiveRating !== null && $source->negativeRating !== null) { + $stats[$platform]['withRating']++; + $stats[$platform]['totalPositive'] += $source->positiveRating; + $stats[$platform]['totalNegative'] += $source->negativeRating; + } + + if ($source->author !== null) { + $stats[$platform]['withAuthor']++; + } + + if ($source->description !== null) { + $stats[$platform]['withDescription']++; + } + } + + return $stats; +} + +// Функция для расчета общего рейтинга +function calculateOverallRating(array $sources): ?float { + $sourcesWithRating = getSourcesWithRating($sources); + + if (empty($sourcesWithRating)) { + return null; + } + + $totalPositive = 0; + $totalNegative = 0; + + foreach ($sourcesWithRating as $source) { + $totalPositive += $source->positiveRating; + $totalNegative += $source->negativeRating; + } + + $total = $totalPositive + $totalNegative; + + return $total > 0 ? round(($totalPositive / $total) * 100, 1) : null; +} + +// Использование +$externalSources = $filmService->getExternalSources(301); + +// Фильтрация по платформе +$imdbSources = filterSourcesByPlatform($externalSources, 'IMDb'); +echo "Источников IMDb: " . count($imdbSources) . "\n"; + +// Источники с рейтингом +$sourcesWithRating = getSourcesWithRating($externalSources); +echo "Источников с рейтингом: " . count($sourcesWithRating) . "\n"; + +// Источники с автором +$sourcesWithAuthor = getSourcesWithAuthor($externalSources); +echo "Источников с автором: " . count($sourcesWithAuthor) . "\n"; + +// Статистика по платформам +$platformStats = getPlatformStatistics($externalSources); +echo "Статистика по платформам:\n"; +foreach ($platformStats as $platform => $stats) { + echo "- {$platform}: {$stats['count']} источников\n"; + if ($stats['withRating'] > 0) { + $avgRating = round(($stats['totalPositive'] / ($stats['totalPositive'] + $stats['totalNegative'])) * 100, 1); + echo " Средний рейтинг: {$avgRating}%\n"; + } +} + +// Общий рейтинг +$overallRating = calculateOverallRating($externalSources); +if ($overallRating !== null) { + echo "Общий рейтинг по всем источникам: {$overallRating}%\n"; +} +``` + +## Создание отчета по внешним источникам + +```php +class ExternalSourcesReport { + private array $sources; + + public function __construct(array $sources) { + $this->sources = $sources; + } + + public function getSourcesByPlatform(string $platform): array { + return filterSourcesByPlatform($this->sources, $platform); + } + + public function getSourcesWithRating(): array { + return getSourcesWithRating($this->sources); + } + + public function getSourcesWithAuthor(): array { + return getSourcesWithAuthor($this->sources); + } + + public function getPlatformStatistics(): array { + return getPlatformStatistics($this->sources); + } + + public function getOverallRating(): ?float { + return calculateOverallRating($this->sources); + } + + public function getTopPlatforms(int $limit = 5): array { + $stats = $this->getPlatformStatistics(); + + // Сортировка по количеству источников + uasort($stats, fn($a, $b) => $b['count'] <=> $a['count']); + + return array_slice($stats, 0, $limit, true); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО ВНЕШНИМ ИСТОЧНИКАМ ===\n\n"; + + $stats = $this->getPlatformStatistics(); + $overallRating = $this->getOverallRating(); + + // Общая статистика + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего источников: " . count($this->sources) . "\n"; + $report .= "Уникальных платформ: " . count($stats) . "\n"; + $report .= "Источников с рейтингом: " . count($this->getSourcesWithRating()) . "\n"; + $report .= "Источников с автором: " . count($this->getSourcesWithAuthor()) . "\n"; + + if ($overallRating !== null) { + $report .= "Общий рейтинг: {$overallRating}%\n"; + } + + $report .= "\n"; + + // Топ платформ + $topPlatforms = $this->getTopPlatforms(); + $report .= "🏆 ТОП ПЛАТФОРМ:\n"; + foreach ($topPlatforms as $platform => $platformStats) { + $report .= "• {$platform}: {$platformStats['count']} источников\n"; + + if ($platformStats['withRating'] > 0) { + $avgRating = round(($platformStats['totalPositive'] / ($platformStats['totalPositive'] + $platformStats['totalNegative'])) * 100, 1); + $report .= " Средний рейтинг: {$avgRating}%\n"; + } + + if ($platformStats['withAuthor'] > 0) { + $report .= " С автором: {$platformStats['withAuthor']}\n"; + } + + if ($platformStats['withDescription'] > 0) { + $report .= " С описанием: {$platformStats['withDescription']}\n"; + } + } + + // Детали по источникам + $report .= "\n📋 ДЕТАЛИ ПО ИСТОЧНИКАМ:\n"; + foreach ($this->sources as $index => $source) { + $report .= "\n" . ($index + 1) . ". {$source->platform}:\n"; + $report .= " URL: {$source->url}\n"; + + if ($source->title) { + $report .= " Заголовок: {$source->title}\n"; + } + + if ($source->author) { + $report .= " Автор: {$source->author}\n"; + } + + if ($source->positiveRating !== null && $source->negativeRating !== null) { + $total = $source->positiveRating + $source->negativeRating; + $percentage = $total > 0 ? round(($source->positiveRating / $total) * 100, 1) : 0; + $report .= " Рейтинг: {$source->positiveRating}/{$source->negativeRating} ({$percentage}%)\n"; + } + + if ($source->description) { + $report .= " Описание: " . substr($source->description, 0, 150) . "...\n"; + } + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $stats = $this->getPlatformStatistics(); + $overallRating = $this->getOverallRating(); + $topPlatforms = $this->getTopPlatforms(); + + // Общая статистика + $html .= "
\n"; + $html .= "

Общая статистика

\n"; + $html .= "

Всего источников: " . count($this->sources) . "

\n"; + $html .= "

Уникальных платформ: " . count($stats) . "

\n"; + $html .= "

Источников с рейтингом: " . count($this->getSourcesWithRating()) . "

\n"; + $html .= "

Источников с автором: " . count($this->getSourcesWithAuthor()) . "

\n"; + + if ($overallRating !== null) { + $html .= "

Общий рейтинг: {$overallRating}%

\n"; + } + + $html .= "
\n"; + + // Топ платформ + $html .= "
\n"; + $html .= "
Топ платформ
\n"; + + foreach ($topPlatforms as $platform => $platformStats) { + $html .= "
\n"; + $html .= "
{$platform}
\n"; + $html .= "
\n"; + $html .= "
Источников: {$platformStats['count']}
\n"; + + if ($platformStats['withRating'] > 0) { + $avgRating = round(($platformStats['totalPositive'] / ($platformStats['totalPositive'] + $platformStats['totalNegative'])) * 100, 1); + $html .= "
Средний рейтинг: {$avgRating}%
\n"; + } + + if ($platformStats['withAuthor'] > 0) { + $html .= "
С автором: {$platformStats['withAuthor']}
\n"; + } + + if ($platformStats['withDescription'] > 0) { + $html .= "
С описанием: {$platformStats['withDescription']}
\n"; + } + + $html .= "
\n
\n"; + } + + $html .= "
\n"; + + // Детали по источникам + $html .= "
\n"; + $html .= "
Детали по источникам
\n"; + + foreach ($this->sources as $index => $source) { + $html .= "
\n"; + $html .= "
{$source->platform}
\n"; + $html .= "
\n"; + + $html .= "\n"; + + if ($source->title) { + $html .= "
Заголовок: {$source->title}
\n"; + } + + if ($source->author) { + $html .= "
Автор: {$source->author}
\n"; + } + + if ($source->positiveRating !== null && $source->negativeRating !== null) { + $total = $source->positiveRating + $source->negativeRating; + $percentage = $total > 0 ? round(($source->positiveRating / $total) * 100, 1) : 0; + $html .= "
Рейтинг: {$source->positiveRating}/{$source->negativeRating} ({$percentage}%)
\n"; + } + + if ($source->description) { + $html .= "
Описание: " . htmlspecialchars(substr($source->description, 0, 200)) . "...
\n"; + } + + $html .= "
\n
\n"; + } + + $html .= "
\n
\n\n"; + + return $html; + } +} + +// Использование +$externalSources = $filmService->getExternalSources(301); +$report = new ExternalSourcesReport($externalSources); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Отчет по внешним источникам'); +file_put_contents('external_sources_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в external_sources_report.html\n"; +``` + +## Анализ внешних источников + +```php +function analyzeExternalSources(array $sources): array { + $analysis = [ + 'totalSources' => count($sources), + 'platforms' => [], + 'sourcesWithRating' => 0, + 'sourcesWithAuthor' => 0, + 'sourcesWithDescription' => 0, + 'averageRating' => null, + 'ratingDistribution' => [], + 'topPlatforms' => [], + 'ratingRanges' => [ + 'excellent' => 0, // 90-100% + 'good' => 0, // 70-89% + 'average' => 0, // 50-69% + 'poor' => 0 // 0-49% + ] + ]; + + $platformStats = []; + $ratings = []; + + foreach ($sources as $source) { + // Статистика по платформам + $platform = $source->platform; + if (!isset($platformStats[$platform])) { + $platformStats[$platform] = 0; + } + $platformStats[$platform]++; + + // Подсчет источников с данными + if ($source->positiveRating !== null && $source->negativeRating !== null) { + $analysis['sourcesWithRating']++; + $total = $source->positiveRating + $source->negativeRating; + if ($total > 0) { + $rating = ($source->positiveRating / $total) * 100; + $ratings[] = $rating; + + // Распределение по диапазонам + if ($rating >= 90) $analysis['ratingRanges']['excellent']++; + elseif ($rating >= 70) $analysis['ratingRanges']['good']++; + elseif ($rating >= 50) $analysis['ratingRanges']['average']++; + else $analysis['ratingRanges']['poor']++; + } + } + + if ($source->author !== null) { + $analysis['sourcesWithAuthor']++; + } + + if ($source->description !== null) { + $analysis['sourcesWithDescription']++; + } + } + + // Анализ платформ + $analysis['platforms'] = array_keys($platformStats); + arsort($platformStats); + $analysis['topPlatforms'] = array_slice($platformStats, 0, 5, true); + + // Средний рейтинг + if (!empty($ratings)) { + $analysis['averageRating'] = round(array_sum($ratings) / count($ratings), 1); + } + + return $analysis; +} + +// Использование +$externalSources = $filmService->getExternalSources(301); +$analysis = analyzeExternalSources($externalSources); + +echo "=== Анализ внешних источников ===\n"; +echo "Всего источников: {$analysis['totalSources']}\n"; +echo "Уникальных платформ: " . count($analysis['platforms']) . "\n"; +echo "Источников с рейтингом: {$analysis['sourcesWithRating']}\n"; +echo "Источников с автором: {$analysis['sourcesWithAuthor']}\n"; +echo "Источников с описанием: {$analysis['sourcesWithDescription']}\n"; + +if ($analysis['averageRating'] !== null) { + echo "Средний рейтинг: {$analysis['averageRating']}%\n"; +} + +echo "\nТоп платформ:\n"; +foreach ($analysis['topPlatforms'] as $platform => $count) { + echo "- {$platform}: {$count} источников\n"; +} + +echo "\nРаспределение рейтингов:\n"; +echo "- Отличные (90-100%): {$analysis['ratingRanges']['excellent']}\n"; +echo "- Хорошие (70-89%): {$analysis['ratingRanges']['good']}\n"; +echo "- Средние (50-69%): {$analysis['ratingRanges']['average']}\n"; +echo "- Плохие (0-49%): {$analysis['ratingRanges']['poor']}\n"; +``` + +## Связанные классы + +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/models/fact.md b/docs/dev/notkinopoiskphp/models/fact.md new file mode 100644 index 0000000..a170814 --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/fact.md @@ -0,0 +1,384 @@ +# Fact + +Модель факта из Kinopoisk API. + +## Описание + +`Fact` представляет информацию о факте или ошибке, связанной с фильмом: интересные факты о съемках, ошибки в фильме (блуперы) и т.д. + +### Основные возможности + +- Хранение информации о факте в неизменяемом виде +- Создание объекта из массива данных API +- Определение типа факта (ошибка или интересный факт) +- Проверка на спойлеры + +**API Endpoint:** `/api/v2.2/films/{id}/facts` + +## Свойства + +### Основная информация + +- `$text` (string) - Текст факта или описания ошибки +- `$type` (FactType) - Тип факта (FACT, BLOOPER и т.д.) +- `$spoiler` (bool) - Флаг, указывающий на наличие спойлера + +## Конструктор + +```php +public function __construct( + public readonly string $text, + public readonly FactType $type, + public readonly bool $spoiler, +) +``` + +### Пример создания + +```php +$fact = new Fact( + text: 'В сцене драки видно, что актер использует дублера', + type: FactType::BLOOPER, + spoiler: false +); +``` + +## Методы + +### fromArray() + +Создает экземпляр факта из массива данных API. + +```php +public static function fromArray(array $data): self +``` + +#### Параметры + +- `$data` (array) - Массив данных факта от API + +#### Возвращаемое значение + +- `self` - Новый экземпляр факта + +#### Исключения + +- `\InvalidArgumentException` - Если данные имеют неверный формат + +#### Пример использования + +```php +$apiData = [ + 'text' => 'Интересный факт о съемках...', + 'type' => 'FACT', + 'spoiler' => false +]; + +$fact = Fact::fromArray($apiData); +``` + +### isBlooper() + +Проверяет, является ли факт ошибкой в фильме. + +```php +public function isBlooper(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если это ошибка в фильме, `false` в противном случае + +#### Описание + +Определяет, относится ли факт к категории ошибок (блуперов), которые были допущены во время съемок фильма. + +#### Пример использования + +```php +if ($fact->isBlooper()) { + echo "Найдена ошибка: {$fact->text}"; +} +``` + +### isFact() + +Проверяет, является ли факт интересным фактом. + +```php +public function isFact(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если это интересный факт, `false` в противном случае + +#### Описание + +Определяет, относится ли факт к категории интересных фактов о съемках, актерах или других аспектах создания фильма. + +#### Пример использования + +```php +if ($fact->isFact()) { + echo "Интересный факт: {$fact->text}"; +} +``` + +### toArray() + +Преобразует объект факта в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Массив с данными факта + +#### Пример использования + +```php +$factArray = $fact->toArray(); +echo json_encode($factArray); // JSON представление факта +``` + +## Полный пример использования + +```php +films; +$facts = $filmService->getFacts(301); // Матрица + +echo "=== Факты о фильме 'Матрица' ===\n"; + +// Группировка по типам +$bloopers = []; +$interestingFacts = []; +$spoilers = []; + +foreach ($facts as $fact) { + if ($fact->spoiler) { + $spoilers[] = $fact; + } + + if ($fact->isBlooper()) { + $bloopers[] = $fact; + } elseif ($fact->isFact()) { + $interestingFacts[] = $fact; + } +} + +// Вывод интересных фактов +if (!empty($interestingFacts)) { + echo "\n🎬 Интересные факты (" . count($interestingFacts) . "):\n"; + foreach (array_slice($interestingFacts, 0, 5) as $fact) { + echo "📝 {$fact->text}\n"; + if ($fact->spoiler) { + echo "⚠️ Содержит спойлер!\n"; + } + echo "---\n"; + } +} + +// Вывод ошибок в фильме +if (!empty($bloopers)) { + echo "\n🎭 Ошибки в фильме (" . count($bloopers) . "):\n"; + foreach (array_slice($bloopers, 0, 3) as $fact) { + echo "❌ {$fact->text}\n"; + if ($fact->spoiler) { + echo "⚠️ Содержит спойлер!\n"; + } + echo "---\n"; + } +} + +// Вывод спойлеров +if (!empty($spoilers)) { + echo "\n🚨 Факты со спойлерами (" . count($spoilers) . "):\n"; + foreach (array_slice($spoilers, 0, 2) as $fact) { + echo "⚠️ {$fact->text}\n"; + echo "Тип: {$fact->type->getDisplayName()}\n"; + echo "---\n"; + } +} +``` + +## Работа с фактами + +```php +// Функция для фильтрации фактов по типу +function filterFactsByType(array $facts, FactType $type): array { + return array_filter($facts, fn($fact) => $fact->type === $type); +} + +// Функция для поиска фактов по ключевому слову +function searchFactsByKeyword(array $facts, string $keyword): array { + $keyword = strtolower($keyword); + + return array_filter($facts, function($fact) use ($keyword) { + return strpos(strtolower($fact->text), $keyword) !== false; + }); +} + +// Функция для получения фактов без спойлеров +function getFactsWithoutSpoilers(array $facts): array { + return array_filter($facts, fn($fact) => !$fact->spoiler); +} + +// Использование +$facts = $filmService->getFacts(301); + +// Фильтрация по типу +$bloopersOnly = filterFactsByType($facts, FactType::BLOOPER); +echo "Ошибок в фильме: " . count($bloopersOnly) . "\n"; + +// Поиск по ключевому слову +$matrixFacts = searchFactsByKeyword($facts, 'матрица'); +echo "Фактов о матрице: " . count($matrixFacts) . "\n"; + +// Факты без спойлеров +$safeFacts = getFactsWithoutSpoilers($facts); +echo "Фактов без спойлеров: " . count($safeFacts) . "\n"; +``` + +## Анализ фактов + +```php +function analyzeFacts(array $facts): array { + $analysis = [ + 'total' => count($facts), + 'bloopers' => 0, + 'interestingFacts' => 0, + 'spoilers' => 0, + 'avgTextLength' => 0, + 'types' => [] + ]; + + $totalLength = 0; + $typeStats = []; + + foreach ($facts as $fact) { + // Подсчет типов + if ($fact->isBlooper()) { + $analysis['bloopers']++; + } elseif ($fact->isFact()) { + $analysis['interestingFacts']++; + } + + // Подсчет спойлеров + if ($fact->spoiler) { + $analysis['spoilers']++; + } + + // Статистика по типам + $typeKey = $fact->type->value; + if (!isset($typeStats[$typeKey])) { + $typeStats[$typeKey] = 0; + } + $typeStats[$typeKey]++; + + // Длина текста + $totalLength += strlen($fact->text); + } + + // Средняя длина текста + if ($analysis['total'] > 0) { + $analysis['avgTextLength'] = round($totalLength / $analysis['total']); + } + + // Статистика по типам + foreach ($typeStats as $type => $count) { + $analysis['types'][$type] = [ + 'count' => $count, + 'percentage' => round(($count / $analysis['total']) * 100, 1) + ]; + } + + return $analysis; +} + +// Использование +$facts = $filmService->getFacts(301); +$analysis = analyzeFacts($facts); + +echo "=== Анализ фактов ===\n"; +echo "Всего фактов: {$analysis['total']}\n"; +echo "Ошибок в фильме: {$analysis['bloopers']}\n"; +echo "Интересных фактов: {$analysis['interestingFacts']}\n"; +echo "Спойлеров: {$analysis['spoilers']}\n"; +echo "Средняя длина текста: {$analysis['avgTextLength']} символов\n"; + +echo "\nСтатистика по типам:\n"; +foreach ($analysis['types'] as $type => $data) { + echo "- {$type}: {$data['count']} ({$data['percentage']}%)\n"; +} +``` + +## Создание галереи фактов + +```php +function createFactsGallery(array $facts, bool $includeSpoilers = false): array { + $gallery = [ + 'bloopers' => [], + 'interestingFacts' => [], + 'spoilers' => [] + ]; + + foreach ($facts as $fact) { + // Факты со спойлерами + if ($fact->spoiler) { + if ($includeSpoilers) { + $gallery['spoilers'][] = $fact; + } + continue; + } + + // Ошибки в фильме + if ($fact->isBlooper()) { + $gallery['bloopers'][] = $fact; + } + + // Интересные факты + if ($fact->isFact()) { + $gallery['interestingFacts'][] = $fact; + } + } + + return $gallery; +} + +// Использование +$facts = $filmService->getFacts(301); +$gallery = createFactsGallery($facts, false); // Без спойлеров + +echo "=== Галерея фактов ===\n"; +echo "🎭 Ошибок в фильме: " . count($gallery['bloopers']) . "\n"; +echo "🎬 Интересных фактов: " . count($gallery['interestingFacts']) . "\n"; + +// Вывод примеров +if (!empty($gallery['interestingFacts'])) { + echo "\n📝 Примеры интересных фактов:\n"; + foreach (array_slice($gallery['interestingFacts'], 0, 3) as $fact) { + echo "- " . substr($fact->text, 0, 100) . "...\n"; + } +} +``` + +## Связанные классы + +- [`FactType`](../enums/fact-type.md) - Типы фактов +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами +- [`Film`](film.md) - Модель фильма diff --git a/docs/dev/notkinopoiskphp/models/film-collection.md b/docs/dev/notkinopoiskphp/models/film-collection.md new file mode 100644 index 0000000..115fe79 --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/film-collection.md @@ -0,0 +1,735 @@ +# FilmCollection + +Модель элемента коллекции фильмов из Kinopoisk API. + +## Описание + +`FilmCollection` представляет упрощенную информацию о фильме в коллекциях, содержащую только основные поля без детальной информации. Используется в ответах API для коллекций фильмов. + +### Основные возможности + +- Хранение базовой информации о фильме в неизменяемом виде +- Создание объекта из массива данных API +- Удобные методы для работы с названиями фильмов + +## Свойства + +### Основная информация + +- `$kinopoiskId` (int) - Уникальный идентификатор фильма в Кинопоиске +- `$imdbId` (string|null) - Идентификатор фильма в IMDb +- `$nameRu` (string|null) - Название фильма на русском языке +- `$nameEn` (string|null) - Название фильма на английском языке +- `$nameOriginal` (string|null) - Оригинальное название фильма +- `$countries` (array) - Массив стран производства +- `$genres` (array) - Массив жанров фильма +- `$ratingKinopoisk` (float|null) - Рейтинг на Кинопоиске +- `$ratingImbd` (float|null) - Рейтинг на IMDb +- `$year` (int|null) - Год выпуска фильма +- `$type` (ContentType) - Тип контента (фильм, сериал и т.д.) +- `$posterUrl` (string) - URL постера фильма +- `$posterUrlPreview` (string) - URL превью постера фильма + +## Конструктор + +```php +public function __construct( + public readonly int $kinopoiskId, + public readonly ?string $imdbId, + public readonly ?string $nameRu, + public readonly ?string $nameEn, + public readonly ?string $nameOriginal, + public readonly array $countries, + public readonly array $genres, + public readonly ?float $ratingKinopoisk, + public readonly ?float $ratingImbd, + public readonly ?int $year, + public readonly ContentType $type, + public readonly string $posterUrl, + public readonly string $posterUrlPreview, +) +``` + +### Пример создания + +```php +$item = new FilmCollection( + kinopoiskId: 301, + nameRu: 'Матрица', + nameEn: 'The Matrix', + nameOriginal: 'The Matrix', + countries: [new Country('США')], + genres: [new Genre('боевик')], + ratingKinopoisk: 8.7, + ratingImbd: 8.7, + year: 1999, + type: ContentType::FILM, + posterUrl: 'https://example.com/poster.jpg', + posterUrlPreview: 'https://example.com/poster_small.jpg' +); +``` + +## Методы + +### fromArray() + +Создает экземпляр элемента коллекции из массива данных API. + +```php +public static function fromArray(array $data): self +``` + +#### Параметры + +- `$data` (array) - Массив данных элемента коллекции от API + +#### Возвращаемое значение + +- `self` - Новый экземпляр элемента коллекции + +#### Исключения + +- `\InvalidArgumentException` - Если данные имеют неверный формат + +#### Описание + +Статический метод для удобного создания объекта FilmCollection из данных, полученных от Kinopoisk API. Автоматически обрабатывает nullable поля и устанавливает значения по умолчанию. + +#### Пример использования + +```php +$apiData = [ + 'kinopoiskId' => 301, + 'nameRu' => 'Матрица', + 'nameEn' => 'The Matrix', + 'nameOriginal' => 'The Matrix', + 'countries' => [['country' => 'США']], + 'genres' => [['genre' => 'боевик']], + 'ratingKinopoisk' => 8.7, + 'ratingImbd' => 8.7, + 'year' => 1999, + 'type' => 'FILM', + 'posterUrl' => 'https://example.com/poster.jpg', + 'posterUrlPreview' => 'https://example.com/poster_small.jpg' +]; + +$item = FilmCollection::fromArray($apiData); +``` + +### getDisplayName() + +Получает отображаемое название фильма. + +```php +public function getDisplayName(): string +``` + +#### Возвращаемое значение + +- `string` - Отображаемое название фильма + +#### Описание + +Возвращает наиболее подходящее название для отображения пользователю. Приоритет: русское название → английское название → оригинальное название → "Без названия" + +#### Пример использования + +```php +echo $item->getDisplayName(); // "Матрица" или "The Matrix" или "Без названия" +``` + +### getMainRating() + +Получает основной рейтинг фильма. + +```php +public function getMainRating(): ?float +``` + +#### Возвращаемое значение + +- `float|null` - Основной рейтинг или null если рейтинги отсутствуют + +#### Описание + +Возвращает наиболее значимый рейтинг из доступных. Приоритет: рейтинг Кинопоиска → рейтинг IMDb + +#### Пример использования + +```php +$rating = $item->getMainRating(); +if ($rating !== null) { + echo "Рейтинг: {$rating}"; +} else { + echo "Рейтинг не указан"; +} +``` + +### isSerial() + +Проверяет, является ли контент сериалом. + +```php +public function isSerial(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если это сериал, `false` если фильм + +#### Описание + +Определяет тип контента на основе поля type. Возвращает true для сериалов и телешоу. + +#### Пример использования + +```php +if ($item->isSerial()) { + echo "Это сериал"; +} else { + echo "Это фильм"; +} +``` + +### getCountriesString() + +Получает список стран в виде строки. + +```php +public function getCountriesString(): string +``` + +#### Возвращаемое значение + +- `string` - Список стран или пустая строка + +#### Пример использования + +```php +echo "Страны: {$item->getCountriesString()}"; // "США, Великобритания" +``` + +### getGenresString() + +Получает список жанров в виде строки. + +```php +public function getGenresString(): string +``` + +#### Возвращаемое значение + +- `string` - Список жанров или пустая строка + +#### Пример использования + +```php +echo "Жанры: {$item->getGenresString()}"; // "боевик, фантастика" +``` + +### toArray() + +Преобразует объект элемента коллекции фильмов в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Массив с данными элемента коллекции + +#### Пример использования + +```php +$itemArray = $item->toArray(); +echo json_encode($itemArray); // JSON представление элемента коллекции +``` + +## Полный пример использования + +```php +films; +$collection = $filmService->getTopFilms(); + +echo "=== Коллекция топ фильмов ===\n"; + +// Обработка элементов коллекции +foreach ($collection as $index => $item) { + echo "\n" . ($index + 1) . ". {$item->getDisplayName()}\n"; + echo " ID: {$item->kinopoiskId}\n"; + echo " Тип: {$item->type->getDisplayName()}\n"; + + if ($item->year) { + echo " Год: {$item->year}\n"; + } + + $rating = $item->getMainRating(); + if ($rating !== null) { + echo " Рейтинг: {$rating}\n"; + } + + $countries = $item->getCountriesString(); + if ($countries) { + echo " Страны: {$countries}\n"; + } + + $genres = $item->getGenresString(); + if ($genres) { + echo " Жанры: {$genres}\n"; + } + + echo " Постер: {$item->posterUrl}\n"; +} +``` + +## Работа с коллекциями фильмов + +```php +// Функция для фильтрации по типу контента +function filterByContentType(array $collection, ContentType $type): array { + return array_filter($collection, fn($item) => $item->type === $type); +} + +// Функция для фильтрации по году +function filterByYear(array $collection, int $year): array { + return array_filter($collection, fn($item) => $item->year == $year); +} + +// Функция для фильтрации по рейтингу +function filterByRating(array $collection, float $minRating): array { + return array_filter($collection, function($item) use ($minRating) { + $rating = $item->getMainRating(); + return $rating !== null && $rating >= $minRating; + }); +} + +// Функция для фильтрации по жанру +function filterByGenre(array $collection, string $genreName): array { + return array_filter($collection, function($item) use ($genreName) { + foreach ($item->genres as $genre) { + if (stripos($genre->genre, $genreName) !== false) { + return true; + } + } + return false; + }); +} + +// Функция для фильтрации по стране +function filterByCountry(array $collection, string $countryName): array { + return array_filter($collection, function($item) use ($countryName) { + foreach ($item->countries as $country) { + if (stripos($country->country, $countryName) !== false) { + return true; + } + } + return false; + }); +} + +// Функция для сортировки по рейтингу +function sortByRating(array $collection, bool $descending = true): array { + usort($collection, function($a, $b) use ($descending) { + $ratingA = $a->getMainRating() ?? 0; + $ratingB = $b->getMainRating() ?? 0; + + if ($descending) { + return $ratingB <=> $ratingA; + } + return $ratingA <=> $ratingB; + }); + + return $collection; +} + +// Функция для сортировки по году +function sortByYear(array $collection, bool $descending = true): array { + usort($collection, function($a, $b) use ($descending) { + $yearA = $a->year ?? 0; + $yearB = $b->year ?? 0; + + if ($descending) { + return $yearB <=> $yearA; + } + return $yearA <=> $yearB; + }); + + return $collection; +} + +// Использование +$collection = $filmService->getTopFilms(); + +// Фильтрация +$films = filterByContentType($collection, ContentType::FILM); +$series = filterByContentType($collection, ContentType::SERIES); +$highRated = filterByRating($collection, 8.0); +$recent = filterByYear($collection, 2020); +$action = filterByGenre($collection, 'боевик'); +$usFilms = filterByCountry($collection, 'США'); + +echo "Фильмов: " . count($films) . "\n"; +echo "Сериалов: " . count($series) . "\n"; +echo "С высоким рейтингом: " . count($highRated) . "\n"; +echo "Современных: " . count($recent) . "\n"; +echo "Боевиков: " . count($action) . "\n"; +echo "Американских: " . count($usFilms) . "\n"; + +// Сортировка +$topRated = sortByRating($collection, true); +$newest = sortByYear($collection, true); +``` + +## Создание галереи коллекции + +```php +class FilmCollectionGallery { + private array $collection; + + public function __construct(array $collection) { + $this->collection = $collection; + } + + public function getCollection(): array { + return $this->collection; + } + + public function getFilms(): array { + return filterByContentType($this->collection, ContentType::FILM); + } + + public function getSeries(): array { + return filterByContentType($this->collection, ContentType::SERIES); + } + + public function getHighRated(float $minRating = 8.0): array { + return filterByRating($this->collection, $minRating); + } + + public function getByGenre(string $genreName): array { + return filterByGenre($this->collection, $genreName); + } + + public function getByCountry(string $countryName): array { + return filterByCountry($this->collection, $countryName); + } + + public function getTopRated(int $limit = 10): array { + $sorted = sortByRating($this->collection, true); + return array_slice($sorted, 0, $limit); + } + + public function getNewest(int $limit = 10): array { + $sorted = sortByYear($this->collection, true); + return array_slice($sorted, 0, $limit); + } + + public function createHtmlGallery(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "\n\n"; + + return $html; + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО КОЛЛЕКЦИИ ФИЛЬМОВ ===\n\n"; + + $films = $this->getFilms(); + $series = $this->getSeries(); + $highRated = $this->getHighRated(); + $topRated = $this->getTopRated(5); + $newest = $this->getNewest(5); + + // Общая статистика + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего элементов: " . count($this->collection) . "\n"; + $report .= "Фильмов: " . count($films) . "\n"; + $report .= "Сериалов: " . count($series) . "\n"; + $report .= "С рейтингом 8+: " . count($highRated) . "\n\n"; + + // Топ рейтинг + $report .= "🏆 ТОП ПО РЕЙТИНГУ:\n"; + foreach ($topRated as $index => $item) { + $report .= ($index + 1) . ". {$item->getDisplayName()}"; + $rating = $item->getMainRating(); + if ($rating !== null) { + $report .= " (★ {$rating})"; + } + if ($item->year) { + $report .= " ({$item->year})"; + } + $report .= "\n"; + } + + $report .= "\n"; + + // Новейшие + $report .= "🆕 НОВЕЙШИЕ:\n"; + foreach ($newest as $index => $item) { + $report .= ($index + 1) . ". {$item->getDisplayName()}"; + if ($item->year) { + $report .= " ({$item->year})"; + } + $rating = $item->getMainRating(); + if ($rating !== null) { + $report .= " (★ {$rating})"; + } + $report .= "\n"; + } + + $report .= "\n"; + + // Детали по всем элементам + $report .= "📋 ВСЕ ЭЛЕМЕНТЫ КОЛЛЕКЦИИ:\n"; + foreach ($this->collection as $index => $item) { + $report .= "\n" . ($index + 1) . ". {$item->getDisplayName()}\n"; + $report .= " ID: {$item->kinopoiskId}\n"; + $report .= " Тип: {$item->type->getDisplayName()}\n"; + + if ($item->year) { + $report .= " Год: {$item->year}\n"; + } + + $rating = $item->getMainRating(); + if ($rating !== null) { + $report .= " Рейтинг: {$rating}\n"; + } + + $countries = $item->getCountriesString(); + if ($countries) { + $report .= " Страны: {$countries}\n"; + } + + $genres = $item->getGenresString(); + if ($genres) { + $report .= " Жанры: {$genres}\n"; + } + } + + return $report; + } +} + +// Использование +$collection = $filmService->getTopFilms(); +$gallery = new FilmCollectionGallery($collection); + +// Создание HTML галереи +$htmlGallery = $gallery->createHtmlGallery('Коллекция топ фильмов'); +file_put_contents('film_collection_gallery.html', $htmlGallery); +echo "✅ HTML галерея сохранена в film_collection_gallery.html\n"; + +// Создание текстового отчета +$textReport = $gallery->createDetailedReport(); +echo $textReport; +``` + +## Анализ коллекции + +```php +function analyzeFilmCollection(array $collection): array { + $analysis = [ + 'totalItems' => count($collection), + 'contentTypes' => [], + 'years' => [], + 'ratings' => [], + 'countries' => [], + 'genres' => [], + 'averageRating' => 0, + 'ratingDistribution' => [ + 'excellent' => 0, // 9+ + 'good' => 0, // 7-8.9 + 'average' => 0, // 5-6.9 + 'poor' => 0 // <5 + ] + ]; + + $totalRating = 0; + $ratedCount = 0; + + foreach ($collection as $item) { + // Статистика по типам контента + $typeKey = $item->type->value; + if (!isset($analysis['contentTypes'][$typeKey])) { + $analysis['contentTypes'][$typeKey] = 0; + } + $analysis['contentTypes'][$typeKey]++; + + // Статистика по годам + if ($item->year) { + $year = (int) $item->year; + if (!isset($analysis['years'][$year])) { + $analysis['years'][$year] = 0; + } + $analysis['years'][$year]++; + } + + // Статистика по рейтингам + $rating = $item->getMainRating(); + if ($rating !== null) { + $totalRating += $rating; + $ratedCount++; + + if (!isset($analysis['ratings'][$rating])) { + $analysis['ratings'][$rating] = 0; + } + $analysis['ratings'][$rating]++; + + // Распределение по качеству + if ($rating >= 9) { + $analysis['ratingDistribution']['excellent']++; + } elseif ($rating >= 7) { + $analysis['ratingDistribution']['good']++; + } elseif ($rating >= 5) { + $analysis['ratingDistribution']['average']++; + } else { + $analysis['ratingDistribution']['poor']++; + } + } + + // Статистика по странам + foreach ($item->countries as $country) { + $countryName = $country->country; + if (!isset($analysis['countries'][$countryName])) { + $analysis['countries'][$countryName] = 0; + } + $analysis['countries'][$countryName]++; + } + + // Статистика по жанрам + foreach ($item->genres as $genre) { + $genreName = $genre->genre; + if (!isset($analysis['genres'][$genreName])) { + $analysis['genres'][$genreName] = 0; + } + $analysis['genres'][$genreName]++; + } + } + + // Средний рейтинг + if ($ratedCount > 0) { + $analysis['averageRating'] = round($totalRating / $ratedCount, 2); + } + + return $analysis; +} + +// Использование +$collection = $filmService->getTopFilms(); +$analysis = analyzeFilmCollection($collection); + +echo "=== Анализ коллекции фильмов ===\n"; +echo "Всего элементов: {$analysis['totalItems']}\n"; +echo "Средний рейтинг: {$analysis['averageRating']}\n"; + +echo "\nРаспределение по типам контента:\n"; +foreach ($analysis['contentTypes'] as $typeKey => $count) { + $type = ContentType::from($typeKey); + $percentage = round(($count / $analysis['totalItems']) * 100, 1); + echo "- {$type->getDisplayName()}: {$count} ({$percentage}%)\n"; +} + +echo "\nРаспределение по рейтингам:\n"; +echo "- Отличные (9+): {$analysis['ratingDistribution']['excellent']}\n"; +echo "- Хорошие (7-8.9): {$analysis['ratingDistribution']['good']}\n"; +echo "- Средние (5-6.9): {$analysis['ratingDistribution']['average']}\n"; +echo "- Плохие (<5): {$analysis['ratingDistribution']['poor']}\n"; + +echo "\nТоп стран:\n"; +arsort($analysis['countries']); +$topCountries = array_slice($analysis['countries'], 0, 5, true); +foreach ($topCountries as $country => $count) { + echo "- {$country}: {$count} фильмов\n"; +} + +echo "\nТоп жанров:\n"; +arsort($analysis['genres']); +$topGenres = array_slice($analysis['genres'], 0, 5, true); +foreach ($topGenres as $genre => $count) { + echo "- {$genre}: {$count} фильмов\n"; +} +``` + +## Связанные классы + +- [`ContentType`](../enums/content-type.md) - Типы контента +- [`Country`](country.md) - Модель страны +- [`Genre`](genre.md) - Модель жанра +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/models/film-search-result.md b/docs/dev/notkinopoiskphp/models/film-search-result.md new file mode 100644 index 0000000..c21ff76 --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/film-search-result.md @@ -0,0 +1,777 @@ +# FilmSearchResult + +Модель результата поиска фильма из Kinopoisk API. + +## Описание + +`FilmSearchResult` представляет краткую информацию о фильме в результатах поиска по ключевому слову. Содержит основные данные: названия, тип контента, год, описание, рейтинг и постер. + +### Основные возможности + +- Хранение информации о фильме в неизменяемом виде +- Создание объекта из массива данных API +- Получение отображаемого названия фильма +- Доступ к метаданным поискового результата + +**API Endpoint:** `/api/v2.1/films/search-by-keyword` + +## Свойства + +### Основная информация + +- `$filmId` (int) - Уникальный идентификатор фильма в Кинопоиске +- `$nameRu` (string|null) - Название фильма на русском языке +- `$nameEn` (string|null) - Название фильма на английском языке +- `$type` (ContentType) - Тип контента (FILM, TV_SHOW, VIDEO, MINI_SERIES, TV_SERIES, UNKNOWN) +- `$year` (string|null) - Год выпуска фильма (строка) +- `$description` (string|null) - Краткое описание фильма +- `$filmLength` (string|null) - Длительность фильма в формате "2:17" +- `$countries` (array) - Массив стран производства +- `$genres` (array) - Массив жанров фильма +- `$rating` (string|null) - Рейтинг фильма (строка, может быть "7.9" или "99%") +- `$ratingVoteCount` (int|null) - Количество голосов за рейтинг +- `$posterUrl` (string) - URL постера фильма +- `$posterUrlPreview` (string) - URL превью постера фильма + +## Конструктор + +```php +public function __construct( + public readonly int $filmId, + public readonly ?string $nameRu, + public readonly ?string $nameEn, + public readonly ContentType $type, + public readonly ?string $year, + public readonly ?string $description, + public readonly ?string $filmLength, + public readonly array $countries, + public readonly array $genres, + public readonly ?string $rating, + public readonly ?int $ratingVoteCount, + public readonly string $posterUrl, + public readonly string $posterUrlPreview, +) +``` + +### Пример создания + +```php +$searchResult = new FilmSearchResult( + filmId: 263531, + nameRu: 'Мстители', + nameEn: 'The Avengers', + type: ContentType::FILM, + year: '2012', + description: 'США, Джосс Уидон(фантастика)', + filmLength: '2:17', + countries: [$country1, $country2], + genres: [$genre1, $genre2], + rating: '7.9', + ratingVoteCount: 284245, + posterUrl: 'https://example.com/poster.jpg', + posterUrlPreview: 'https://example.com/poster_small.jpg' +); +``` + +## Методы + +### fromArray() + +Создает экземпляр результата поиска из массива данных API. + +```php +public static function fromArray(array $data): self +``` + +#### Параметры + +- `$data` (array) - Массив данных результата поиска от API + +#### Возвращаемое значение + +- `self` - Новый экземпляр результата поиска + +#### Исключения + +- `\InvalidArgumentException` - Если данные имеют неверный формат + +#### Описание + +Статический метод для удобного создания объекта FilmSearchResult из данных, полученных от Kinopoisk API. Автоматически создает объекты Country и Genre для каждого элемента в соответствующих массивах. + +#### Пример использования + +```php +$apiData = [ + 'filmId' => 263531, + 'nameRu' => 'Мстители', + 'nameEn' => 'The Avengers', + 'type' => 'FILM', + 'year' => '2012', + 'description' => 'США, Джосс Уидон(фантастика)', + 'filmLength' => '2:17', + 'countries' => [['country' => 'США']], + 'genres' => [['genre' => 'фантастика']], + 'rating' => '7.9', + 'ratingVoteCount' => 284245, + 'posterUrl' => 'https://example.com/poster.jpg', + 'posterUrlPreview' => 'https://example.com/poster_small.jpg' +]; + +$searchResult = FilmSearchResult::fromArray($apiData); +``` + +### getDisplayName() + +Получает отображаемое название фильма. + +```php +public function getDisplayName(): string +``` + +#### Возвращаемое значение + +- `string` - Отображаемое название фильма + +#### Описание + +Возвращает наиболее подходящее название для отображения пользователю. Приоритет: русское название → английское название → "Без названия" + +#### Пример использования + +```php +echo $searchResult->getDisplayName(); // "Мстители" или "The Avengers" или "Без названия" +``` + +### isSerial() + +Проверяет, является ли контент сериалом. + +```php +public function isSerial(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если это сериал, `false` если фильм + +#### Описание + +Определяет тип контента на основе поля type. Возвращает true для TV_SERIES, MINI_SERIES и TV_SHOW. + +#### Пример использования + +```php +if ($searchResult->isSerial()) { + echo "Это сериал"; +} else { + echo "Это фильм"; +} +``` + +### getCountriesString() + +Получает список стран в виде строки. + +```php +public function getCountriesString(): string +``` + +#### Возвращаемое значение + +- `string` - Список стран или пустая строка + +#### Пример использования + +```php +echo "Страны: {$searchResult->getCountriesString()}"; // "США, Великобритания" +``` + +### getGenresString() + +Получает список жанров в виде строки. + +```php +public function getGenresString(): string +``` + +#### Возвращаемое значение + +- `string` - Список жанров или пустая строка + +#### Пример использования + +```php +echo "Жанры: {$searchResult->getGenresString()}"; // "фантастика, боевик" +``` + +### toArray() + +Преобразует объект результата поиска в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Массив данных результата поиска + +#### Описание + +Возвращает массив со всеми свойствами результата поиска, включая преобразованные в массивы объекты стран и жанров. + +#### Пример использования + +```php +$array = $searchResult->toArray(); +// [ +// 'filmId' => 263531, +// 'nameRu' => 'Мстители', +// 'nameEn' => 'The Avengers', +// 'type' => 'FILM', +// 'year' => '2012', +// 'description' => 'США, Джосс Уидон(фантастика)', +// 'filmLength' => '2:17', +// 'countries' => [['country' => 'США']], +// 'genres' => [['genre' => 'фантастика']], +// 'rating' => '7.9', +// 'ratingVoteCount' => 284245, +// 'posterUrl' => 'https://example.com/poster.jpg', +// 'posterUrlPreview' => 'https://example.com/poster_small.jpg' +// ] +``` + +## Полный пример использования + +```php +films; +$searchResults = $filmService->searchByKeyword('матрица'); + +echo "=== Результаты поиска по запросу 'матрица' ===\n"; + +// Обработка результатов поиска +foreach ($searchResults as $index => $result) { + echo "\n" . ($index + 1) . ". {$result->getDisplayName()}\n"; + echo " ID: {$result->filmId}\n"; + echo " Тип: {$result->type->getDisplayName()}\n"; + + if ($result->year) { + echo " Год: {$result->year}\n"; + } + + if ($result->filmLength) { + echo " Длительность: {$result->filmLength}\n"; + } + + if ($result->rating) { + echo " Рейтинг: {$result->rating}"; + if ($result->ratingVoteCount) { + echo " ({$result->ratingVoteCount} голосов)"; + } + echo "\n"; + } + + $countries = $result->getCountriesString(); + if ($countries) { + echo " Страны: {$countries}\n"; + } + + $genres = $result->getGenresString(); + if ($genres) { + echo " Жанры: {$genres}\n"; + } + + if ($result->description) { + echo " Описание: {$result->description}\n"; + } + + echo " Постер: {$result->posterUrl}\n"; +} +``` + +## Работа с результатами поиска + +```php +// Функция для фильтрации по типу контента +function filterByContentType(array $searchResults, ContentType $type): array { + return array_filter($searchResults, fn($result) => $result->type === $type); +} + +// Функция для фильтрации по году +function filterByYear(array $searchResults, int $year): array { + return array_filter($searchResults, fn($result) => $result->year == $year); +} + +// Функция для фильтрации по рейтингу +function filterByRating(array $searchResults, float $minRating): array { + return array_filter($searchResults, function($result) use ($minRating) { + if (!$result->rating) return false; + $rating = (float) $result->rating; + return $rating >= $minRating; + }); +} + +// Функция для фильтрации по жанру +function filterByGenre(array $searchResults, string $genreName): array { + return array_filter($searchResults, function($result) use ($genreName) { + foreach ($result->genres as $genre) { + if (stripos($genre->genre, $genreName) !== false) { + return true; + } + } + return false; + }); +} + +// Функция для фильтрации по стране +function filterByCountry(array $searchResults, string $countryName): array { + return array_filter($searchResults, function($result) use ($countryName) { + foreach ($result->countries as $country) { + if (stripos($country->country, $countryName) !== false) { + return true; + } + } + return false; + }); +} + +// Функция для сортировки по рейтингу +function sortByRating(array $searchResults, bool $descending = true): array { + usort($searchResults, function($a, $b) use ($descending) { + $ratingA = $a->rating ? (float) $a->rating : 0; + $ratingB = $b->rating ? (float) $b->rating : 0; + + if ($descending) { + return $ratingB <=> $ratingA; + } + return $ratingA <=> $ratingB; + }); + + return $searchResults; +} + +// Функция для сортировки по году +function sortByYear(array $searchResults, bool $descending = true): array { + usort($searchResults, function($a, $b) use ($descending) { + $yearA = $a->year ? (int) $a->year : 0; + $yearB = $b->year ? (int) $b->year : 0; + + if ($descending) { + return $yearB <=> $yearA; + } + return $yearA <=> $yearB; + }); + + return $searchResults; +} + +// Использование +$searchResults = $filmService->searchByKeyword('матрица'); + +// Фильтрация +$films = filterByContentType($searchResults, ContentType::FILM); +$series = filterByContentType($searchResults, ContentType::TV_SERIES); +$highRated = filterByRating($searchResults, 7.0); +$recent = filterByYear($searchResults, 2020); +$action = filterByGenre($searchResults, 'боевик'); +$usFilms = filterByCountry($searchResults, 'США'); + +echo "Фильмов: " . count($films) . "\n"; +echo "Сериалов: " . count($series) . "\n"; +echo "С высоким рейтингом: " . count($highRated) . "\n"; +echo "Современных: " . count($recent) . "\n"; +echo "Боевиков: " . count($action) . "\n"; +echo "Американских: " . count($usFilms) . "\n"; + +// Сортировка +$topRated = sortByRating($searchResults, true); +$newest = sortByYear($searchResults, true); +``` + +## Создание галереи результатов поиска + +```php +class SearchResultsGallery { + private array $searchResults; + + public function __construct(array $searchResults) { + $this->searchResults = $searchResults; + } + + public function getResults(): array { + return $this->searchResults; + } + + public function getFilms(): array { + return filterByContentType($this->searchResults, ContentType::FILM); + } + + public function getSeries(): array { + return filterByContentType($this->searchResults, ContentType::TV_SERIES); + } + + public function getHighRated(float $minRating = 7.0): array { + return filterByRating($this->searchResults, $minRating); + } + + public function getByGenre(string $genreName): array { + return filterByGenre($this->searchResults, $genreName); + } + + public function getByCountry(string $countryName): array { + return filterByCountry($this->searchResults, $countryName); + } + + public function getTopRated(int $limit = 10): array { + $sorted = sortByRating($this->searchResults, true); + return array_slice($sorted, 0, $limit); + } + + public function getNewest(int $limit = 10): array { + $sorted = sortByYear($this->searchResults, true); + return array_slice($sorted, 0, $limit); + } + + public function createHtmlGallery(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "\n\n"; + + return $html; + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО РЕЗУЛЬТАТАМ ПОИСКА ===\n\n"; + + $films = $this->getFilms(); + $series = $this->getSeries(); + $highRated = $this->getHighRated(); + $topRated = $this->getTopRated(5); + $newest = $this->getNewest(5); + + // Общая статистика + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего результатов: " . count($this->searchResults) . "\n"; + $report .= "Фильмов: " . count($films) . "\n"; + $report .= "Сериалов: " . count($series) . "\n"; + $report .= "С рейтингом 7+: " . count($highRated) . "\n\n"; + + // Топ рейтинг + $report .= "🏆 ТОП ПО РЕЙТИНГУ:\n"; + foreach ($topRated as $index => $result) { + $report .= ($index + 1) . ". {$result->getDisplayName()}"; + if ($result->rating) { + $report .= " (★ {$result->rating})"; + } + if ($result->year) { + $report .= " ({$result->year})"; + } + $report .= "\n"; + } + + $report .= "\n"; + + // Новейшие + $report .= "🆕 НОВЕЙШИЕ:\n"; + foreach ($newest as $index => $result) { + $report .= ($index + 1) . ". {$result->getDisplayName()}"; + if ($result->year) { + $report .= " ({$result->year})"; + } + if ($result->rating) { + $report .= " (★ {$result->rating})"; + } + $report .= "\n"; + } + + $report .= "\n"; + + // Детали по всем результатам + $report .= "📋 ВСЕ РЕЗУЛЬТАТЫ:\n"; + foreach ($this->searchResults as $index => $result) { + $report .= "\n" . ($index + 1) . ". {$result->getDisplayName()}\n"; + $report .= " ID: {$result->filmId}\n"; + $report .= " Тип: {$result->type->getDisplayName()}\n"; + + if ($result->year) { + $report .= " Год: {$result->year}\n"; + } + + if ($result->filmLength) { + $report .= " Длительность: {$result->filmLength}\n"; + } + + if ($result->rating) { + $report .= " Рейтинг: {$result->rating}"; + if ($result->ratingVoteCount) { + $report .= " ({$result->ratingVoteCount} голосов)"; + } + $report .= "\n"; + } + + $countries = $result->getCountriesString(); + if ($countries) { + $report .= " Страны: {$countries}\n"; + } + + $genres = $result->getGenresString(); + if ($genres) { + $report .= " Жанры: {$genres}\n"; + } + + if ($result->description) { + $report .= " Описание: {$result->description}\n"; + } + } + + return $report; + } +} + +// Использование +$searchResults = $filmService->searchByKeyword('матрица'); +$gallery = new SearchResultsGallery($searchResults); + +// Создание HTML галереи +$htmlGallery = $gallery->createHtmlGallery('Результаты поиска: Матрица'); +file_put_contents('search_results_gallery.html', $htmlGallery); +echo "✅ HTML галерея сохранена в search_results_gallery.html\n"; + +// Создание текстового отчета +$textReport = $gallery->createDetailedReport(); +echo $textReport; +``` + +## Анализ результатов поиска + +```php +function analyzeSearchResults(array $searchResults): array { + $analysis = [ + 'totalResults' => count($searchResults), + 'contentTypes' => [], + 'years' => [], + 'ratings' => [], + 'countries' => [], + 'genres' => [], + 'averageRating' => 0, + 'ratingDistribution' => [ + 'excellent' => 0, // 9+ + 'good' => 0, // 7-8.9 + 'average' => 0, // 5-6.9 + 'poor' => 0 // <5 + ] + ]; + + $totalRating = 0; + $ratedCount = 0; + + foreach ($searchResults as $result) { + // Статистика по типам контента + $typeKey = $result->type->value; + if (!isset($analysis['contentTypes'][$typeKey])) { + $analysis['contentTypes'][$typeKey] = 0; + } + $analysis['contentTypes'][$typeKey]++; + + // Статистика по годам + if ($result->year) { + $year = (int) $result->year; + if (!isset($analysis['years'][$year])) { + $analysis['years'][$year] = 0; + } + $analysis['years'][$year]++; + } + + // Статистика по рейтингам + if ($result->rating) { + $rating = (float) $result->rating; + $totalRating += $rating; + $ratedCount++; + + if (!isset($analysis['ratings'][$rating])) { + $analysis['ratings'][$rating] = 0; + } + $analysis['ratings'][$rating]++; + + // Распределение по качеству + if ($rating >= 9) { + $analysis['ratingDistribution']['excellent']++; + } elseif ($rating >= 7) { + $analysis['ratingDistribution']['good']++; + } elseif ($rating >= 5) { + $analysis['ratingDistribution']['average']++; + } else { + $analysis['ratingDistribution']['poor']++; + } + } + + // Статистика по странам + foreach ($result->countries as $country) { + $countryName = $country->country; + if (!isset($analysis['countries'][$countryName])) { + $analysis['countries'][$countryName] = 0; + } + $analysis['countries'][$countryName]++; + } + + // Статистика по жанрам + foreach ($result->genres as $genre) { + $genreName = $genre->genre; + if (!isset($analysis['genres'][$genreName])) { + $analysis['genres'][$genreName] = 0; + } + $analysis['genres'][$genreName]++; + } + } + + // Средний рейтинг + if ($ratedCount > 0) { + $analysis['averageRating'] = round($totalRating / $ratedCount, 2); + } + + return $analysis; +} + +// Использование +$searchResults = $filmService->searchByKeyword('матрица'); +$analysis = analyzeSearchResults($searchResults); + +echo "=== Анализ результатов поиска ===\n"; +echo "Всего результатов: {$analysis['totalResults']}\n"; +echo "Средний рейтинг: {$analysis['averageRating']}\n"; + +echo "\nРаспределение по типам контента:\n"; +foreach ($analysis['contentTypes'] as $typeKey => $count) { + $type = ContentType::from($typeKey); + $percentage = round(($count / $analysis['totalResults']) * 100, 1); + echo "- {$type->getDisplayName()}: {$count} ({$percentage}%)\n"; +} + +echo "\nРаспределение по рейтингам:\n"; +echo "- Отличные (9+): {$analysis['ratingDistribution']['excellent']}\n"; +echo "- Хорошие (7-8.9): {$analysis['ratingDistribution']['good']}\n"; +echo "- Средние (5-6.9): {$analysis['ratingDistribution']['average']}\n"; +echo "- Плохие (<5): {$analysis['ratingDistribution']['poor']}\n"; + +echo "\nТоп стран:\n"; +arsort($analysis['countries']); +$topCountries = array_slice($analysis['countries'], 0, 5, true); +foreach ($topCountries as $country => $count) { + echo "- {$country}: {$count} результатов\n"; +} + +echo "\nТоп жанров:\n"; +arsort($analysis['genres']); +$topGenres = array_slice($analysis['genres'], 0, 5, true); +foreach ($topGenres as $genre => $count) { + echo "- {$genre}: {$count} результатов\n"; +} +``` + +## Связанные классы + +- [`ContentType`](../enums/content-type.md) - Типы контента +- [`Country`](country.md) - Модель страны +- [`Genre`](genre.md) - Модель жанра +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/models/film.md b/docs/dev/notkinopoiskphp/models/film.md new file mode 100644 index 0000000..3a31a20 --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/film.md @@ -0,0 +1,554 @@ +# Film - Модель фильма + +Модель фильма из Kinopoisk API. + +--- + +**📚 Навигация:** [Главная](../index.md) → [Модели](index.md) → Film + +**🔗 Связанные классы:** + +- [FilmService](../services/film-service.md) - Сервис для работы с фильмами +- [Staff](../models/staff.md) - Съемочная группа +- [Review](../models/review.md) - Отзывы +- [Fact](../models/fact.md) - Факты +- [Image](../models/image.md) - Изображения +- [Video](../models/video.md) - Видео +- [Award](../models/award.md) - Награды +- [BoxOffice](../models/box-office.md) - Кассовые сборы +- [Country](../models/country.md) - Страны +- [Genre](../models/genre.md) - Жанры +- [Episode](../models/episode.md) - Эпизоды +- [Season](../models/season.md) - Сезоны +- [RelatedFilm](../models/related-film.md) - Связанные фильмы +- [ContentType](../enums/content-type.md) - Типы контента +- [FilmOrder](../enums/film-order.md) - Порядок сортировки фильмов +- [ImageType](../enums/image-type.md) - Типы изображений +- [ReviewOrder](../enums/review-order.md) - Порядок сортировки отзывов +- [ReviewType](../enums/review-type.md) - Типы отзывов +- [FactType](../enums/fact-type.md) - Типы фактов +- [VideoSite](../enums/video-site.md) - Сайты видео +- [BoxOfficeType](../enums/box-office-type.md) - Типы кассовых сборов +- [DistributionType](../enums/distribution-type.md) - Типы дистрибуции +- [Month](../enums/month.md) - Месяцы +- [ApiException](../exceptions/api-exception.md) - Базовое исключение API +- [ResourceNotFoundException](../exceptions/resource-not-found-exception.md) - Ресурс не найден +- [KpValidationException](../exceptions/kp-validation-exception.md) - Ошибка валидации + +--- + +## Описание + +Представляет полную информацию о фильме, полученную из Kinopoisk Unofficial API. Содержит все основные данные: названия, рейтинги, описания, технические характеристики и метаданные фильма. + +## Основные возможности + +- Хранение полной информации о фильме в неизменяемом виде (readonly свойства) +- Создание объекта из массива данных API +- Удобные методы для получения отображаемого названия и основного рейтинга +- Определение типа контента (фильм/сериал) + +## Свойства + +### Основные идентификаторы + +#### `$kinopoiskId` + +**Тип:** `int` + +Уникальный идентификатор фильма в Кинопоиске. + +#### `$kinopoiskHDId` + +**Тип:** `string|null` + +Идентификатор фильма в Кинопоиск HD (если доступен). + +#### `$imdbId` + +**Тип:** `string|null` + +Идентификатор фильма в IMDb. + +### Названия + +#### `$nameRu` + +**Тип:** `string|null` + +Название фильма на русском языке. + +#### `$nameEn` + +**Тип:** `string|null` + +Название фильма на английском языке. + +#### `$nameOriginal` + +**Тип:** `string|null` + +Оригинальное название фильма. + +### Изображения + +#### `$posterUrl` + +**Тип:** `string` + +URL постера фильма в высоком разрешении. + +#### `$posterUrlPreview` + +**Тип:** `string` + +URL постера фильма в низком разрешении. + +#### `$coverUrl` + +**Тип:** `string|null` + +URL обложки фильма. + +#### `$logoUrl` + +**Тип:** `string|null` + +URL логотипа фильма. + +### Рейтинги + +#### `$ratingKinopoisk` + +**Тип:** `float|null` + +Рейтинг Кинопоиска. + +#### `$ratingKinopoiskVoteCount` + +**Тип:** `int|null` + +Количество голосов на Кинопоиске. + +#### `$ratingImdb` + +**Тип:** `float|null` + +Рейтинг IMDb. + +#### `$ratingImdbVoteCount` + +**Тип:** `int|null` + +Количество голосов на IMDb. + +#### `$ratingFilmCritics` + +**Тип:** `float|null` + +Рейтинг кинокритиков. + +#### `$ratingFilmCriticsVoteCount` + +**Тип:** `int|null` + +Количество голосов кинокритиков. + +#### `$ratingGoodReview` + +**Тип:** `float|null` + +Рейтинг хороших рецензий. + +#### `$ratingGoodReviewVoteCount` + +**Тип:** `int|null` + +Количество голосов за хорошие рецензии. + +#### `$ratingAwait` + +**Тип:** `float|null` + +Рейтинг ожидания. + +#### `$ratingAwaitCount` + +**Тип:** `int|null` + +Количество голосов ожидания. + +#### `$ratingRfCritics` + +**Тип:** `float|null` + +Рейтинг российских кинокритиков. + +#### `$ratingRfCriticsVoteCount` + +**Тип:** `int|null` + +Количество голосов российских кинокритиков. + +### Основная информация + +#### `$year` + +**Тип:** `int|null` + +Год выпуска фильма. + +#### `$filmLength` + +**Тип:** `int|null` + +Длительность фильма в минутах. + +#### `$startYear` + +**Тип:** `int|null` + +Год начала производства (для сериалов). + +#### `$endYear` + +**Тип:** `int|null` + +Год окончания производства (для сериалов). + +#### `$type` + +**Тип:** `ContentType` + +Тип контента (FILM, SERIES, MINI_SERIES, TV_SHOW). + +#### `$productionStatus` + +**Тип:** `ProductionStatus|null` + +Статус производства фильма. + +### Описания + +#### `$slogan` + +**Тип:** `string|null` + +Слоган фильма. + +#### `$description` + +**Тип:** `string|null` + +Полное описание фильма. + +#### `$shortDescription` + +**Тип:** `string|null` + +Краткое описание фильма. + +#### `$editorAnnotation` + +**Тип:** `string|null` + +Редакторская аннотация. + +### Дополнительная информация + +#### `$webUrl` + +**Тип:** `string|null` + +URL страницы фильма на Кинопоиске. + +#### `$reviewsCount` + +**Тип:** `int|null` + +Количество рецензий на фильм. + +#### `$isTicketsAvailable` + +**Тип:** `bool|null` + +Доступны ли билеты в кинотеатрах. + +#### `$ratingMpaa` + +**Тип:** `string|null` + +Рейтинг MPAA. + +#### `$ratingAgeLimits` + +**Тип:** `string|null` + +Возрастные ограничения. + +#### `$hasImax` + +**Тип:** `bool|null` + +Доступен ли в формате IMAX. + +#### `$has3D` + +**Тип:** `bool|null` + +Доступен ли в формате 3D. + +#### `$lastSync` + +**Тип:** `string|null` + +Время последней синхронизации данных. + +### Коллекции + +#### `$countries` + +**Тип:** `Country[]` + +Массив стран производства. + +#### `$genres` + +**Тип:** `Genre[]` + +Массив жанров фильма. + +### Флаги + +#### `$serial` + +**Тип:** `bool|null` + +Является ли сериалом. + +#### `$shortFilm` + +**Тип:** `bool|null` + +Является ли короткометражным фильмом. + +#### `$completed` + +**Тип:** `bool|null` + +Завершен ли (для сериалов). + +## Методы + +### `__construct(...)` + +Конструктор модели фильма. + +**Параметры:** +См. свойства выше для полного списка параметров. + +### `fromArray(array $data): self` + +Создает экземпляр фильма из массива данных API. + +**Параметры:** + +- `$data` (array) - Массив данных от API + +**Возвращает:** + +- `self` - Новый экземпляр фильма + +**Пример:** + +```php +$film = Film::fromArray($apiData); +``` + +### `getDisplayName(): string` + +Получает отображаемое название фильма. + +**Возвращает:** + +- `string` - Название на русском языке, если доступно, иначе на английском + +**Пример:** + +```php +echo $film->getDisplayName(); // "Матрица" или "The Matrix" +``` + +### `isSerial(): bool` + +Проверяет, является ли фильм сериалом. + +**Возвращает:** + +- `bool` - true, если это сериал + +**Пример:** + +```php +if ($film->isSerial()) { + echo "Это сериал"; +} +``` + +### `getMainRating(): ?float` + +Получает основной рейтинг фильма. + +**Возвращает:** + +- `float|null` - Рейтинг Кинопоиска, если доступен, иначе null + +**Пример:** + +```php +$rating = $film->getMainRating(); // 8.7 +``` + +### `getCountriesString(): string` + +Получает строку со странами производства. + +**Возвращает:** + +- `string` - Строка с названиями стран через запятую + +**Пример:** + +```php +echo $film->getCountriesString(); // "США, Великобритания" +``` + +### `getGenresString(): string` + +Получает строку с жанрами фильма. + +**Возвращает:** + +- `string` - Строка с названиями жанров через запятую + +**Пример:** + +```php +echo $film->getGenresString(); // "боевик, фантастика, триллер" +``` + +### `formatDuration(): string` + +Форматирует длительность фильма. + +**Возвращает:** + +- `string` - Отформатированная длительность (например, "02:16:00") + +**Пример:** + +```php +echo $film->formatDuration(); // "00:02:16" +``` + +### `formatDurationString(): string` + +Форматирует длительность фильма в виде строки. + +**Возвращает:** + +- `string` - Отформатированная длительность с полными словами + +**Пример:** + +```php +echo $film->formatDurationString(); // "2 часа 16 минут" +``` + +### `toArray(): array` + +Преобразует объект фильма в массив. + +**Возвращает:** + +- `array` - Ассоциативный массив со всеми данными фильма + +## Примеры использования + +### Создание из данных API + +```php +getDisplayName(); // "Матрица" или "The Matrix" + +// Проверка типа контента +if ($film->isSerial()) { + echo "Это сериал"; +} + +// Получение основного рейтинга +$rating = $film->getMainRating(); // 8.7 +``` + +### Работа с информацией о фильме + +```php +// Получение основной информации +echo "Название: " . $film->getDisplayName() . "\n"; +echo "Год: " . $film->year . "\n"; +echo "Длительность: " . $film->formatDuration() . "\n"; +echo "Страны: " . $film->getCountriesString() . "\n"; +echo "Жанры: " . $film->getGenresString() . "\n"; + +// Проверка рейтингов +if ($film->ratingKinopoisk) { + echo "Рейтинг Кинопоиска: " . $film->ratingKinopoisk . "\n"; +} + +if ($film->ratingImdb) { + echo "Рейтинг IMDb: " . $film->ratingImdb . "\n"; +} +``` + +### Проверка типа контента + +```php +switch ($film->type) { + case ContentType::FILM: + echo "Это фильм"; + break; + case ContentType::SERIES: + echo "Это сериал"; + break; + case ContentType::MINI_SERIES: + echo "Это мини-сериал"; + break; + case ContentType::TV_SHOW: + echo "Это телешоу"; + break; +} +``` + +## Связанные классы + +- `\NotKinopoisk\Models\FilmCollection` - Коллекция фильмов +- `\NotKinopoisk\Services\FilmService` - Сервис для работы с фильмами +- `\NotKinopoisk\Models\Country` - Модель страны +- `\NotKinopoisk\Models\Genre` - Модель жанра +- `\NotKinopoisk\Enums\ContentType` - Типы контента +- `\NotKinopoisk\Enums\ProductionStatus` - Статусы производства + +## Информация о пакете + +- **Пакет:** NotKinopoisk\Models +- **API:** /api/v2.2/films/{id} +- **Документация API:** https://kinopoiskapiunofficial.tech/documentation/api/#/films/get_api_v2_2_films__id_ +- **Автор:** Maxim Harder +- **Версия:** 1.0.0 diff --git a/docs/dev/notkinopoiskphp/models/filters.md b/docs/dev/notkinopoiskphp/models/filters.md new file mode 100644 index 0000000..7c0c22a --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/filters.md @@ -0,0 +1,527 @@ +# Filters + +Модель фильтров из Kinopoisk API. + +## Описание + +`Filters` представляет доступные фильтры для поиска фильмов, включая списки жанров и стран для фильтрации. + +### Основные возможности + +- Хранение списков доступных фильтров в неизменяемом виде +- Создание объекта из массива данных API +- Доступ к спискам жанров и стран + +**API Endpoint:** `/api/v2.2/films/filters` + +## Свойства + +### Основная информация + +- `$genres` (array) - Массив доступных жанров +- `$countries` (array) - Массив доступных стран + +## Конструктор + +```php +public function __construct( + public readonly array $genres, + public readonly array $countries, +) +``` + +### Пример создания + +```php +$filters = new Filters( + genres: [$genre1, $genre2, $genre3], + countries: [$country1, $country2] +); +``` + +## Методы + +### fromArray() + +Создает экземпляр фильтров из массива данных API. + +```php +public static function fromArray(array $data): self +``` + +#### Параметры + +- `$data` (array) - Массив данных фильтров от API + +#### Возвращаемое значение + +- `self` - Новый экземпляр фильтров + +#### Исключения + +- `\InvalidArgumentException` - Если данные имеют неверный формат + +#### Описание + +Статический метод для удобного создания объекта Filters из данных, полученных от Kinopoisk API. Автоматически создает объекты Genre и Country для каждого элемента в соответствующих массивах. + +#### Пример использования + +```php +$apiData = [ + 'genres' => [ + ['genre' => 'Боевик'], + ['genre' => 'Драма'], + ['genre' => 'Комедия'] + ], + 'countries' => [ + ['country' => 'США'], + ['country' => 'Россия'], + ['country' => 'Великобритания'] + ] +]; + +$filters = Filters::fromArray($apiData); +``` + +### toArray() + +Преобразует объект фильтров в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Массив с данными фильтров + +#### Пример использования + +```php +$filtersArray = $filters->toArray(); +echo json_encode($filtersArray); // JSON представление фильтров +``` + +## Полный пример использования + +```php +films; +$filters = $filmService->getFilters(); + +echo "=== Доступные фильтры для поиска ===\n"; + +// Общая статистика +echo "Доступно жанров: " . count($filters->genres) . "\n"; +echo "Доступно стран: " . count($filters->countries) . "\n\n"; + +// Вывод всех жанров +echo "🎭 ДОСТУПНЫЕ ЖАНРЫ:\n"; +foreach ($filters->genres as $genre) { + echo "- {$genre->genre}\n"; +} + +echo "\n"; + +// Вывод всех стран +echo "🌍 ДОСТУПНЫЕ СТРАНЫ:\n"; +foreach ($filters->countries as $country) { + echo "- {$country->country}\n"; +} +``` + +## Работа с фильтрами + +```php +// Функция для поиска жанра по названию +function findGenreByName(array $genres, string $name): ?Genre { + foreach ($genres as $genre) { + if (stripos($genre->genre, $name) !== false) { + return $genre; + } + } + return null; +} + +// Функция для поиска страны по названию +function findCountryByName(array $countries, string $name): ?Country { + foreach ($countries as $country) { + if (stripos($country->country, $name) !== false) { + return $country; + } + } + return null; +} + +// Функция для получения жанров по категориям +function categorizeGenres(array $genres): array { + $categories = [ + 'action' => [], + 'drama' => [], + 'comedy' => [], + 'horror' => [], + 'sci_fi' => [], + 'other' => [] + ]; + + foreach ($genres as $genre) { + $genreName = strtolower($genre->genre); + + if (strpos($genreName, 'боевик') !== false || strpos($genreName, 'экшен') !== false) { + $categories['action'][] = $genre; + } elseif (strpos($genreName, 'драма') !== false) { + $categories['drama'][] = $genre; + } elseif (strpos($genreName, 'комедия') !== false) { + $categories['comedy'][] = $genre; + } elseif (strpos($genreName, 'ужас') !== false || strpos($genreName, 'хоррор') !== false) { + $categories['horror'][] = $genre; + } elseif (strpos($genreName, 'фантастика') !== false || strpos($genreName, 'научная') !== false) { + $categories['sci_fi'][] = $genre; + } else { + $categories['other'][] = $genre; + } + } + + return $categories; +} + +// Функция для получения стран по регионам +function categorizeCountries(array $countries): array { + $regions = [ + 'europe' => [], + 'asia' => [], + 'america' => [], + 'africa' => [], + 'other' => [] + ]; + + $europeanCountries = ['россия', 'великобритания', 'франция', 'германия', 'италия', 'испания']; + $asianCountries = ['китай', 'япония', 'корея', 'индия', 'тайвань']; + $americanCountries = ['сша', 'канада', 'мексика', 'бразилия', 'аргентина']; + $africanCountries = ['египет', 'юар', 'нигерия', 'кения']; + + foreach ($countries as $country) { + $countryName = strtolower($country->country); + + if (in_array($countryName, $europeanCountries)) { + $regions['europe'][] = $country; + } elseif (in_array($countryName, $asianCountries)) { + $regions['asia'][] = $country; + } elseif (in_array($countryName, $americanCountries)) { + $regions['america'][] = $country; + } elseif (in_array($countryName, $africanCountries)) { + $regions['africa'][] = $country; + } else { + $regions['other'][] = $country; + } + } + + return $regions; +} + +// Использование +$filters = $filmService->getFilters(); + +// Поиск конкретных жанров и стран +$actionGenre = findGenreByName($filters->genres, 'боевик'); +$usCountry = findCountryByName($filters->countries, 'сша'); + +if ($actionGenre) { + echo "Найден жанр: {$actionGenre->genre}\n"; +} + +if ($usCountry) { + echo "Найдена страна: {$usCountry->country}\n"; +} + +// Категоризация жанров +$genreCategories = categorizeGenres($filters->genres); +echo "Боевиков: " . count($genreCategories['action']) . "\n"; +echo "Драм: " . count($genreCategories['drama']) . "\n"; +echo "Комедий: " . count($genreCategories['comedy']) . "\n"; +echo "Ужасов: " . count($genreCategories['horror']) . "\n"; +echo "Фантастики: " . count($genreCategories['sci_fi']) . "\n"; +echo "Прочих: " . count($genreCategories['other']) . "\n"; + +// Категоризация стран +$countryRegions = categorizeCountries($filters->countries); +echo "Европейских стран: " . count($countryRegions['europe']) . "\n"; +echo "Азиатских стран: " . count($countryRegions['asia']) . "\n"; +echo "Американских стран: " . count($countryRegions['america']) . "\n"; +echo "Африканских стран: " . count($countryRegions['africa']) . "\n"; +echo "Прочих стран: " . count($countryRegions['other']) . "\n"; +``` + +## Создание отчета по фильтрам + +```php +class FiltersReport { + private Filters $filters; + + public function __construct(Filters $filters) { + $this->filters = $filters; + } + + public function getFilters(): Filters { + return $this->filters; + } + + public function getGenres(): array { + return $this->filters->genres; + } + + public function getCountries(): array { + return $this->filters->countries; + } + + public function findGenreByName(string $name): ?Genre { + return findGenreByName($this->filters->genres, $name); + } + + public function findCountryByName(string $name): ?Country { + return findCountryByName($this->filters->countries, $name); + } + + public function getGenreCategories(): array { + return categorizeGenres($this->filters->genres); + } + + public function getCountryRegions(): array { + return categorizeCountries($this->filters->countries); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО ДОСТУПНЫМ ФИЛЬТРАМ ===\n\n"; + + $genreCategories = $this->getGenreCategories(); + $countryRegions = $this->getCountryRegions(); + + // Общая статистика + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего жанров: " . count($this->filters->genres) . "\n"; + $report .= "Всего стран: " . count($this->filters->countries) . "\n\n"; + + // Статистика по жанрам + $report .= "🎭 СТАТИСТИКА ПО ЖАНРАМ:\n"; + $report .= "• Боевики: " . count($genreCategories['action']) . "\n"; + $report .= "• Драмы: " . count($genreCategories['drama']) . "\n"; + $report .= "• Комедии: " . count($genreCategories['comedy']) . "\n"; + $report .= "• Ужасы: " . count($genreCategories['horror']) . "\n"; + $report .= "• Фантастика: " . count($genreCategories['sci_fi']) . "\n"; + $report .= "• Прочие: " . count($genreCategories['other']) . "\n\n"; + + // Статистика по странам + $report .= "🌍 СТАТИСТИКА ПО СТРАНАМ:\n"; + $report .= "• Европа: " . count($countryRegions['europe']) . "\n"; + $report .= "• Азия: " . count($countryRegions['asia']) . "\n"; + $report .= "• Америка: " . count($countryRegions['america']) . "\n"; + $report .= "• Африка: " . count($countryRegions['africa']) . "\n"; + $report .= "• Прочие: " . count($countryRegions['other']) . "\n\n"; + + // Все жанры + $report .= "📋 ВСЕ ДОСТУПНЫЕ ЖАНРЫ:\n"; + foreach ($this->filters->genres as $genre) { + $report .= "• {$genre->genre}\n"; + } + + $report .= "\n"; + + // Все страны + $report .= "🌍 ВСЕ ДОСТУПНЫЕ СТРАНЫ:\n"; + foreach ($this->filters->countries as $country) { + $report .= "• {$country->country}\n"; + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $genreCategories = $this->getGenreCategories(); + $countryRegions = $this->getCountryRegions(); + + // Общая статистика + $html .= "
\n"; + $html .= "

Общая статистика

\n"; + $html .= "

Всего жанров: " . count($this->filters->genres) . "

\n"; + $html .= "

Всего стран: " . count($this->filters->countries) . "

\n"; + $html .= "
\n"; + + // Статистика по жанрам + $html .= "
\n"; + $html .= "
🎭 Статистика по жанрам
\n"; + $html .= "

Боевики: " . count($genreCategories['action']) . "

\n"; + $html .= "

Драмы: " . count($genreCategories['drama']) . "

\n"; + $html .= "

Комедии: " . count($genreCategories['comedy']) . "

\n"; + $html .= "

Ужасы: " . count($genreCategories['horror']) . "

\n"; + $html .= "

Фантастика: " . count($genreCategories['sci_fi']) . "

\n"; + $html .= "

Прочие: " . count($genreCategories['other']) . "

\n"; + $html .= "
\n"; + + // Статистика по странам + $html .= "
\n"; + $html .= "
🌍 Статистика по странам
\n"; + $html .= "

Европа: " . count($countryRegions['europe']) . "

\n"; + $html .= "

Азия: " . count($countryRegions['asia']) . "

\n"; + $html .= "

Америка: " . count($countryRegions['america']) . "

\n"; + $html .= "

Африка: " . count($countryRegions['africa']) . "

\n"; + $html .= "

Прочие: " . count($countryRegions['other']) . "

\n"; + $html .= "
\n"; + + // Все жанры + $html .= "
\n"; + $html .= "
📋 Все доступные жанры
\n"; + $html .= "
\n"; + + foreach ($this->filters->genres as $genre) { + $cssClass = 'other'; + $genreName = strtolower($genre->genre); + + if (strpos($genreName, 'боевик') !== false || strpos($genreName, 'экшен') !== false) { + $cssClass = 'action'; + } elseif (strpos($genreName, 'драма') !== false) { + $cssClass = 'drama'; + } elseif (strpos($genreName, 'комедия') !== false) { + $cssClass = 'comedy'; + } elseif (strpos($genreName, 'ужас') !== false || strpos($genreName, 'хоррор') !== false) { + $cssClass = 'horror'; + } elseif (strpos($genreName, 'фантастика') !== false || strpos($genreName, 'научная') !== false) { + $cssClass = 'sci-fi'; + } + + $html .= "
\n"; + $html .= "
{$genre->genre}
\n"; + $html .= "
\n"; + } + + $html .= "
\n
\n"; + + // Все страны + $html .= "
\n"; + $html .= "
🌍 Все доступные страны
\n"; + $html .= "
\n"; + + foreach ($this->filters->countries as $country) { + $html .= "
\n"; + $html .= "
{$country->country}
\n"; + $html .= "
\n"; + } + + $html .= "
\n
\n
\n\n"; + + return $html; + } +} + +// Использование +$filters = $filmService->getFilters(); +$report = new FiltersReport($filters); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Отчет по доступным фильтрам'); +file_put_contents('filters_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в filters_report.html\n"; +``` + +## Анализ фильтров + +```php +function analyzeFilters(Filters $filters): array { + $analysis = [ + 'totalGenres' => count($filters->genres), + 'totalCountries' => count($filters->countries), + 'genreCategories' => categorizeGenres($filters->genres), + 'countryRegions' => categorizeCountries($filters->countries), + 'popularGenres' => [], + 'popularCountries' => [] + ]; + + // Анализ популярных жанров (по длине названия как пример) + $genreLengths = []; + foreach ($filters->genres as $genre) { + $genreLengths[$genre->genre] = strlen($genre->genre); + } + arsort($genreLengths); + $analysis['popularGenres'] = array_slice($genreLengths, 0, 10, true); + + // Анализ популярных стран (по алфавиту как пример) + $countryNames = []; + foreach ($filters->countries as $country) { + $countryNames[] = $country->country; + } + sort($countryNames); + $analysis['popularCountries'] = array_slice($countryNames, 0, 10); + + return $analysis; +} + +// Использование +$filters = $filmService->getFilters(); +$analysis = analyzeFilters($filters); + +echo "=== Анализ фильтров ===\n"; +echo "Всего жанров: {$analysis['totalGenres']}\n"; +echo "Всего стран: {$analysis['totalCountries']}\n"; + +echo "\nРаспределение жанров по категориям:\n"; +foreach ($analysis['genreCategories'] as $category => $genres) { + $percentage = round((count($genres) / $analysis['totalGenres']) * 100, 1); + echo "- {$category}: " . count($genres) . " жанров ({$percentage}%)\n"; +} + +echo "\nРаспределение стран по регионам:\n"; +foreach ($analysis['countryRegions'] as $region => $countries) { + $percentage = round((count($countries) / $analysis['totalCountries']) * 100, 1); + echo "- {$region}: " . count($countries) . " стран ({$percentage}%)\n"; +} + +echo "\nТоп жанров по длине названия:\n"; +foreach ($analysis['popularGenres'] as $genre => $length) { + echo "- {$genre}: {$length} символов\n"; +} + +echo "\nПервые 10 стран по алфавиту:\n"; +foreach ($analysis['popularCountries'] as $country) { + echo "- {$country}\n"; +} +``` + +## Связанные классы + +- [`Genre`](genre.md) - Модель жанра +- [`Country`](country.md) - Модель страны +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/models/genre.md b/docs/dev/notkinopoiskphp/models/genre.md new file mode 100644 index 0000000..cee71c0 --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/genre.md @@ -0,0 +1,500 @@ +# Genre + +Модель жанра из Kinopoisk API. + +## Описание + +`Genre` представляет информацию о жанре фильма или сериала. Простая модель для хранения названия жанра. + +### Основные возможности + +- Хранение названия жанра в неизменяемом виде +- Создание объекта из массива данных API +- Автоматическое преобразование в строку + +## Свойства + +### Основная информация + +- `$genre` (string) - Название жанра +- `$id` (int|null) - Уникальный идентификатор жанра в Кинопоиске + +## Конструктор + +```php +public function __construct( + public readonly string $genre, + public readonly ?int $id = NULL, +) +``` + +### Пример создания + +```php +$genre = new Genre('Боевик'); +$genreWithId = new Genre('Драма', 1); +``` + +## Методы + +### \_\_toString() + +Преобразует объект жанра в строку. + +```php +public function __toString(): string +``` + +#### Возвращаемое значение + +- `string` - Название жанра + +#### Описание + +Магический метод, который автоматически вызывается при попытке преобразования объекта Genre в строковое представление. + +#### Пример использования + +```php +$genre = new Genre('Боевик'); +echo $genre; // Выведет: Боевик +echo (string) $genre; // Выведет: Боевик + +// Использование в строковом контексте +$message = "Жанр фильма: {$genre}"; +echo $message; // Выведет: Жанр фильма: Боевик + +// Использование в массивах и сравнениях +$genres = [$genre1, $genre2]; +$genreNames = array_map('strval', $genres); +``` + +### fromArray() + +Создает экземпляр жанра из массива данных API. + +```php +public static function fromArray(array $data): self +``` + +#### Параметры + +- `$data` (array) - Массив данных жанра от API + +#### Возвращаемое значение + +- `self` - Новый экземпляр жанра + +#### Исключения + +- `\InvalidArgumentException` - Если данные имеют неверный формат + +#### Пример использования + +```php +$apiData = ['genre' => 'Боевик']; +$genre = Genre::fromArray($apiData); +``` + +### toArray() + +Преобразует объект жанра в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Массив с данными жанра + +#### Пример использования + +```php +$genreArray = $genre->toArray(); +echo json_encode($genreArray); // JSON представление жанра +``` + +## Полный пример использования + +```php +films; +$film = $filmService->getById(301); // Матрица + +echo "=== Жанры фильма 'Матрица' ===\n"; + +// Вывод жанров фильма +if (!empty($film->genres)) { + echo "Жанры фильма:\n"; + foreach ($film->genres as $index => $genre) { + echo ($index + 1) . ". {$genre}\n"; + } +} else { + echo "Информация о жанрах отсутствует\n"; +} + +// Создание объектов жанров +$genres = [ + new Genre('Боевик'), + new Genre('Фантастика'), + new Genre('Триллер') +]; + +echo "\nСозданные жанры:\n"; +foreach ($genres as $genre) { + echo "- {$genre}\n"; +} +``` + +## Работа с жанрами + +```php +// Функция для фильтрации фильмов по жанру +function filterFilmsByGenre(array $films, string $genreName): array { + return array_filter($films, function($film) use ($genreName) { + foreach ($film->genres as $genre) { + if (strcasecmp($genre->genre, $genreName) === 0) { + return true; + } + } + return false; + }); +} + +// Функция для получения статистики по жанрам +function getGenresStatistics(array $films): array { + $stats = []; + + foreach ($films as $film) { + foreach ($film->genres as $genre) { + $genreName = $genre->genre; + if (!isset($stats[$genreName])) { + $stats[$genreName] = 0; + } + $stats[$genreName]++; + } + } + + arsort($stats); + return $stats; +} + +// Функция для поиска фильмов по нескольким жанрам +function findFilmsByMultipleGenres(array $films, array $genreNames): array { + return array_filter($films, function($film) use ($genreNames) { + $filmGenres = array_map(fn($genre) => $genre->genre, $film->genres); + + foreach ($genreNames as $genreName) { + if (in_array($genreName, $filmGenres)) { + return true; + } + } + return false; + }); +} + +// Функция для анализа жанровых комбинаций +function analyzeGenreCombinations(array $films): array { + $combinations = []; + + foreach ($films as $film) { + if (count($film->genres) > 1) { + $genreNames = array_map(fn($genre) => $genre->genre, $film->genres); + sort($genreNames); // Сортировка для единообразия + $combination = implode(' + ', $genreNames); + + if (!isset($combinations[$combination])) { + $combinations[$combination] = 0; + } + $combinations[$combination]++; + } + } + + arsort($combinations); + return $combinations; +} + +// Использование +$films = $filmService->getTopFilms(); + +// Фильтрация по жанру +$actionFilms = filterFilmsByGenre($films, 'Боевик'); +echo "Боевиков: " . count($actionFilms) . "\n"; + +// Получение статистики +$stats = getGenresStatistics($films); +echo "Топ жанров по количеству фильмов:\n"; +foreach (array_slice($stats, 0, 10, true) as $genre => $count) { + echo "- {$genre}: {$count} фильмов\n"; +} + +// Поиск по нескольким жанрам +$actionThrillers = findFilmsByMultipleGenres($films, ['Боевик', 'Триллер']); +echo "Боевиков-триллеров: " . count($actionThrillers) . "\n"; + +// Анализ комбинаций +$combinations = analyzeGenreCombinations($films); +echo "Популярные жанровые комбинации:\n"; +foreach (array_slice($combinations, 0, 10, true) as $combination => $count) { + echo "- {$combination}: {$count} фильмов\n"; +} +``` + +## Создание отчета по жанрам + +```php +class GenresReport { + private array $films; + + public function __construct(array $films) { + $this->films = $films; + } + + public function getTopGenres(int $limit = 10): array { + $stats = getGenresStatistics($this->films); + return array_slice($stats, 0, $limit, true); + } + + public function getFilmsByGenre(string $genreName): array { + return filterFilmsByGenre($this->films, $genreName); + } + + public function getGenreCombinations(int $minFilms = 3): array { + $combinations = analyzeGenreCombinations($this->films); + return array_filter($combinations, fn($count) => $count >= $minFilms); + } + + public function getFilmsWithMultipleGenres(): array { + return array_filter($this->films, fn($film) => count($film->genres) > 1); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО ЖАНРАМ ===\n\n"; + + $stats = getGenresStatistics($this->films); + $totalFilms = count($this->films); + $multiGenreFilms = count($this->getFilmsWithMultipleGenres()); + + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего фильмов: {$totalFilms}\n"; + $report .= "Уникальных жанров: " . count($stats) . "\n"; + $report .= "Фильмов с несколькими жанрами: {$multiGenreFilms}\n\n"; + + $report .= "🏆 ТОП-15 ЖАНРОВ:\n"; + foreach (array_slice($stats, 0, 15, true) as $genre => $count) { + $percentage = round(($count / $totalFilms) * 100, 1); + $report .= "• {$genre}: {$count} фильмов ({$percentage}%)\n"; + } + + $report .= "\n🎭 ПОПУЛЯРНЫЕ ЖАНРОВЫЕ КОМБИНАЦИИ:\n"; + $combinations = $this->getGenreCombinations(5); + foreach (array_slice($combinations, 0, 10, true) as $combination => $count) { + $report .= "• {$combination}: {$count} фильмов\n"; + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $stats = getGenresStatistics($this->films); + $totalFilms = count($this->films); + $multiGenreFilms = count($this->getFilmsWithMultipleGenres()); + + // Статистика + $html .= "
\n"; + $html .= "

Общая статистика

\n"; + $html .= "

Всего фильмов: {$totalFilms}

\n"; + $html .= "

Уникальных жанров: " . count($stats) . "

\n"; + $html .= "

Фильмов с несколькими жанрами: {$multiGenreFilms}

\n"; + $html .= "
\n"; + + $html .= "
\n"; + + // Топ жанров + $html .= "
\n"; + $html .= "

Топ жанров

\n"; + foreach (array_slice($stats, 0, 20, true) as $genre => $count) { + $percentage = round(($count / $totalFilms) * 100, 1); + $html .= "
\n"; + $html .= "
{$genre}
\n"; + $html .= "
{$count} фильмов
\n"; + $html .= "
{$percentage}%
\n"; + $html .= "
\n"; + } + $html .= "
\n"; + + // Жанровые комбинации + $html .= "
\n"; + $html .= "

Популярные комбинации

\n"; + $combinations = $this->getGenreCombinations(3); + foreach (array_slice($combinations, 0, 15, true) as $combination => $count) { + $html .= "
\n"; + $html .= "
{$combination}
\n"; + $html .= "
{$count} фильмов
\n"; + $html .= "
\n"; + } + $html .= "
\n"; + + $html .= "
\n
\n\n"; + + return $html; + } +} + +// Использование +$films = $filmService->getTopFilms(); +$report = new GenresReport($films); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Статистика по жанрам'); +file_put_contents('genres_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в genres_report.html\n"; +``` + +## Анализ жанрового разнообразия + +```php +function analyzeGenreDiversity(array $films): array { + $analysis = [ + 'total' => count($films), + 'genres' => [], + 'singleGenre' => 0, + 'multiGenre' => 0, + 'avgGenresPerFilm' => 0, + 'mostDiverse' => [], + 'genreCategories' => [] + ]; + + // Категории жанров + $genreCategories = [ + 'Драма' => 'Драматические', + 'Комедия' => 'Комедийные', + 'Боевик' => 'Экшн', + 'Триллер' => 'Триллеры', + 'Ужасы' => 'Хоррор', + 'Фантастика' => 'Научная фантастика', + 'Фэнтези' => 'Фэнтези', + 'Детектив' => 'Детективы', + 'Мелодрама' => 'Романтические', + 'Приключения' => 'Приключенческие', + 'Семейный' => 'Семейные', + 'Документальный' => 'Документальные', + 'Анимация' => 'Анимационные', + 'Вестерн' => 'Вестерны', + 'Военный' => 'Военные', + 'Исторический' => 'Исторические', + 'Мюзикл' => 'Мюзиклы', + 'Спорт' => 'Спортивные' + ]; + + $genreStats = []; + $categoryStats = []; + $totalGenres = 0; + + foreach ($films as $film) { + $filmGenres = $film->genres; + $genreCount = count($filmGenres); + $totalGenres += $genreCount; + + // Подсчет фильмов по количеству жанров + if ($genreCount === 1) { + $analysis['singleGenre']++; + } else { + $analysis['multiGenre']++; + } + + // Статистика по жанрам + foreach ($filmGenres as $genre) { + $genreName = $genre->genre; + + if (!isset($genreStats[$genreName])) { + $genreStats[$genreName] = 0; + } + $genreStats[$genreName]++; + + // Статистика по категориям + $category = $genreCategories[$genreName] ?? 'Другое'; + if (!isset($categoryStats[$category])) { + $categoryStats[$category] = 0; + } + $categoryStats[$category]++; + } + + // Фильмы с наибольшим количеством жанров + if ($genreCount > 4) { + $analysis['mostDiverse'][] = [ + 'film' => $film, + 'genres' => $filmGenres, + 'count' => $genreCount + ]; + } + } + + $analysis['genres'] = $genreStats; + $analysis['genreCategories'] = $categoryStats; + $analysis['avgGenresPerFilm'] = $totalGenres / count($films); + + // Сортировка + arsort($analysis['genres']); + arsort($analysis['genreCategories']); + + return $analysis; +} + +// Использование +$films = $filmService->getTopFilms(); +$analysis = analyzeGenreDiversity($films); + +echo "=== Анализ жанрового разнообразия ===\n"; +echo "Всего фильмов: {$analysis['total']}\n"; +echo "Фильмов с одним жанром: {$analysis['singleGenre']}\n"; +echo "Фильмов с несколькими жанрами: {$analysis['multiGenre']}\n"; +echo "Среднее количество жанров на фильм: " . round($analysis['avgGenresPerFilm'], 1) . "\n"; + +echo "\nТоп категорий жанров:\n"; +foreach (array_slice($analysis['genreCategories'], 0, 10, true) as $category => $count) { + echo "- {$category}: {$count} упоминаний\n"; +} + +echo "\nФильмы с наибольшим жанровым разнообразием:\n"; +foreach (array_slice($analysis['mostDiverse'], 0, 5) as $item) { + $film = $item['film']; + $genres = array_map(fn($g) => $g->genre, $item['genres']); + echo "- {$film->nameRu}: " . implode(', ', $genres) . " ({$item['count']} жанров)\n"; +} +``` + +## Связанные классы + +- [`Film`](film.md) - Модель фильма +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/models/image.md b/docs/dev/notkinopoiskphp/models/image.md new file mode 100644 index 0000000..ab23c26 --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/image.md @@ -0,0 +1,386 @@ +# Image + +Модель изображения из Kinopoisk API. + +## Описание + +`Image` представляет информацию об изображении, связанном с фильмом, включая URL полного изображения и превью. + +### Основные возможности + +- Хранение информации об изображении в неизменяемом виде +- Создание объекта из массива данных API +- Доступ к полному и превью изображениям + +**API Endpoint:** `/api/v2.2/films/{id}/images` + +## Свойства + +### Основная информация + +- `$imageUrl` (string) - URL полного изображения +- `$previewUrl` (string) - URL превью изображения + +## Конструктор + +```php +public function __construct( + public readonly string $imageUrl, + public readonly string $previewUrl, +) +``` + +### Пример создания + +```php +$image = new Image( + imageUrl: 'https://kinopoisk.ru/images/full.jpg', + previewUrl: 'https://kinopoisk.ru/images/preview.jpg' +); +``` + +## Методы + +### fromArray() + +Создает экземпляр изображения из массива данных API. + +```php +public static function fromArray(array $data): self +``` + +#### Параметры + +- `$data` (array) - Массив данных изображения от API + +#### Возвращаемое значение + +- `self` - Новый экземпляр изображения + +#### Исключения + +- `\InvalidArgumentException` - Если данные имеют неверный формат + +#### Пример использования + +```php +$apiData = [ + 'imageUrl' => 'https://kinopoisk.ru/images/full.jpg', + 'previewUrl' => 'https://kinopoisk.ru/images/preview.jpg' +]; + +$image = Image::fromArray($apiData); +``` + +### toArray() + +Преобразует объект изображения в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Массив с данными изображения + +#### Пример использования + +```php +$imageArray = $image->toArray(); +echo json_encode($imageArray); // JSON представление изображения +``` + +## Полный пример использования + +```php +films; +$filmId = 301; // Матрица + +echo "=== Изображения фильма 'Матрица' ===\n"; + +// Получение различных типов изображений +$imageTypes = [ + ImageType::POSTER => 'Постеры', + ImageType::STILL => 'Кадры из фильма', + ImageType::FAN_ART => 'Фан-арты', + ImageType::CONCEPT => 'Концепт-арты' +]; + +foreach ($imageTypes as $type => $description) { + try { + $images = $filmService->getImages($filmId, $type); + + echo "\n🖼️ {$description} (" . count($images) . "):\n"; + + // Вывод первых 3 изображений + foreach (array_slice($images, 0, 3) as $index => $image) { + echo ($index + 1) . ". Полное: {$image->imageUrl}\n"; + echo " Превью: {$image->previewUrl}\n"; + echo " ---\n"; + } + + } catch (\Exception $e) { + echo "❌ Ошибка при получении {$description}: {$e->getMessage()}\n"; + } +} +``` + +## Работа с изображениями + +```php +// Функция для получения всех изображений фильма +function getAllFilmImages(Client $client, int $filmId): array { + $allImages = []; + $imageTypes = [ + ImageType::POSTER, + ImageType::STILL, + ImageType::FAN_ART, + ImageType::CONCEPT, + ImageType::PROMO, + ImageType::WALLPAPER + ]; + + foreach ($imageTypes as $type) { + try { + $images = $client->films->getImages($filmId, $type); + $allImages[$type->value] = [ + 'type' => $type->getDisplayName(), + 'count' => count($images), + 'images' => $images + ]; + } catch (\Exception $e) { + echo "Ошибка при получении {$type->getDisplayName()}: {$e->getMessage()}\n"; + } + } + + return $allImages; +} + +// Функция для создания галереи изображений +function createImageGallery(array $allImages): array { + $gallery = [ + 'total' => 0, + 'types' => [], + 'preview' => [] + ]; + + foreach ($allImages as $typeKey => $typeData) { + $gallery['total'] += $typeData['count']; + $gallery['types'][$typeKey] = $typeData; + + // Добавляем превью для каждого типа + if (!empty($typeData['images'])) { + $gallery['preview'][$typeKey] = $typeData['images'][0]; + } + } + + return $gallery; +} + +// Использование +$client = new Client('your-api-key'); +$allImages = getAllFilmImages($client, 301); +$gallery = createImageGallery($allImages); + +echo "=== Галерея изображений ===\n"; +echo "Всего изображений: {$gallery['total']}\n\n"; + +foreach ($gallery['types'] as $typeKey => $typeData) { + echo "📁 {$typeData['type']}: {$typeData['count']} изображений\n"; + + if (isset($gallery['preview'][$typeKey])) { + $preview = $gallery['preview'][$typeKey]; + echo " Превью: {$preview->previewUrl}\n"; + } + echo "\n"; +} +``` + +## Скачивание изображений + +```php +class ImageDownloader { + private string $downloadDir; + + public function __construct(string $downloadDir = 'downloads') { + $this->downloadDir = $downloadDir; + + if (!is_dir($this->downloadDir)) { + mkdir($this->downloadDir, 0755, true); + } + } + + public function downloadImage(Image $image, string $filename): bool { + try { + $fullPath = $this->downloadDir . '/' . $filename; + + // Скачивание полного изображения + $imageData = file_get_contents($image->imageUrl); + if ($imageData === false) { + throw new \Exception("Не удалось скачать изображение"); + } + + if (file_put_contents($fullPath, $imageData) === false) { + throw new \Exception("Не удалось сохранить файл"); + } + + echo "✅ Скачано: {$filename}\n"; + return true; + + } catch (\Exception $e) { + echo "❌ Ошибка при скачивании {$filename}: {$e->getMessage()}\n"; + return false; + } + } + + public function downloadImagesByType(array $images, string $type, int $limit = 5): int { + $downloaded = 0; + + foreach (array_slice($images, 0, $limit) as $index => $image) { + $filename = "{$type}_{" . ($index + 1) . "}.jpg"; + + if ($this->downloadImage($image, $filename)) { + $downloaded++; + } + } + + return $downloaded; + } +} + +// Использование +$downloader = new ImageDownloader('matrix_images'); + +// Скачивание постеров +$posters = $filmService->getImages(301, ImageType::POSTER); +$downloadedPosters = $downloader->downloadImagesByType($posters, 'poster', 3); +echo "Скачано постеров: {$downloadedPosters}\n"; + +// Скачивание кадров из фильма +$stills = $filmService->getImages(301, ImageType::STILL); +$downloadedStills = $downloader->downloadImagesByType($stills, 'still', 5); +echo "Скачано кадров: {$downloadedStills}\n"; +``` + +## Создание HTML галереи + +```php +function createHtmlGallery(array $images, string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "

{$title}

\n"; + $html .= "\n\n"; + + return $html; +} + +// Использование +$posters = $filmService->getImages(301, ImageType::POSTER); +$html = createHtmlGallery($posters, 'Постеры фильма "Матрица"'); + +// Сохранение HTML файла +file_put_contents('matrix_posters.html', $html); +echo "✅ HTML галерея сохранена в matrix_posters.html\n"; +``` + +## Валидация изображений + +```php +function validateImage(Image $image): array { + $validation = [ + 'valid' => true, + 'errors' => [], + 'warnings' => [] + ]; + + // Проверка URL полного изображения + if (empty($image->imageUrl)) { + $validation['valid'] = false; + $validation['errors'][] = 'URL полного изображения пустой'; + } elseif (!filter_var($image->imageUrl, FILTER_VALIDATE_URL)) { + $validation['valid'] = false; + $validation['errors'][] = 'Некорректный URL полного изображения'; + } + + // Проверка URL превью + if (empty($image->previewUrl)) { + $validation['valid'] = false; + $validation['errors'][] = 'URL превью пустой'; + } elseif (!filter_var($image->previewUrl, FILTER_VALIDATE_URL)) { + $validation['valid'] = false; + $validation['errors'][] = 'Некорректный URL превью'; + } + + // Проверка доступности изображений + if ($validation['valid']) { + $headers = @get_headers($image->imageUrl); + if (!$headers || strpos($headers[0], '200') === false) { + $validation['warnings'][] = 'Полное изображение недоступно'; + } + + $headers = @get_headers($image->previewUrl); + if (!$headers || strpos($headers[0], '200') === false) { + $validation['warnings'][] = 'Превью недоступно'; + } + } + + return $validation; +} + +// Использование +$images = $filmService->getImages(301, ImageType::POSTER); + +foreach ($images as $index => $image) { + $validation = validateImage($image); + + if (!$validation['valid']) { + echo "❌ Изображение " . ($index + 1) . " невалидно:\n"; + foreach ($validation['errors'] as $error) { + echo " - {$error}\n"; + } + } elseif (!empty($validation['warnings'])) { + echo "⚠️ Изображение " . ($index + 1) . " имеет предупреждения:\n"; + foreach ($validation['warnings'] as $warning) { + echo " - {$warning}\n"; + } + } else { + echo "✅ Изображение " . ($index + 1) . " валидно\n"; + } +} +``` + +## Связанные классы + +- [`ImageType`](../enums/image-type.md) - Типы изображений +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами +- [`Film`](film.md) - Модель фильма diff --git a/docs/dev/notkinopoiskphp/models/index.md b/docs/dev/notkinopoiskphp/models/index.md new file mode 100644 index 0000000..5075789 --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/index.md @@ -0,0 +1,308 @@ +# Модели данных + +Модели для представления данных из Kinopoisk API. + +--- + +**📚 Навигация:** [Главная](../index.md) → Модели + +--- + +## 📋 Категории моделей + +### 🎬 Основные модели фильмов + +- [Film](film.md) - Основная модель фильма/сериала +- [FilmSearchResult](film-search-result.md) - Результат поиска фильмов +- [FilmCollection](film-collection.md) - Коллекция фильмов +- [RelatedFilm](related-film.md) - Связанный фильм +- [Episode](episode.md) - Эпизод сериала +- [Season](season.md) - Сезон сериала + +### 👥 Модели персон + +- [Person](person.md) - Основная модель персоны +- [Staff](staff.md) - Съемочная группа +- [PersonFilm](person-film.md) - Фильм персоны +- [PersonSpouse](person-spouse.md) - Супруг персоны + +### 📊 Модели контента + +- [Review](review.md) - Отзыв +- [Fact](fact.md) - Факт +- [Image](image.md) - Изображение +- [Video](video.md) - Видео +- [MediaPost](media-post.md) - Медиа пост + +### 🏆 Модели наград и статистики + +- [Award](award.md) - Награда +- [BoxOffice](box-office.md) - Кассовые сборы +- [UserVote](user-vote.md) - Голос пользователя +- [ExternalSource](external-source.md) - Внешний источник + +### 🌍 Справочные модели + +- [Country](country.md) - Страна +- [Genre](genre.md) - Жанр +- [Distribution](distribution.md) - Дистрибуция + +### 🔑 Модели API + +- [ApiKeyInfo](api-key-info.md) - Информация об API ключе +- [ApiKeyQouta](api-key-qouta.md) - Квота API ключа +- [Filters](filters.md) - Фильтры + +## 🔗 Связанные компоненты + +### Сервисы + +- [FilmService](../services/film-service.md) - Работа с фильмами +- [PersonService](../services/person-service.md) - Работа с персонами +- [MediaService](../services/media-service.md) - Работа с медиа +- [UserService](../services/user-service.md) - Работа с пользователями + +### Перечисления + +- [ImageType](../enums/image-type.md) - Типы изображений +- [ReviewType](../enums/review-type.md) - Типы отзывов +- [FactType](../enums/fact-type.md) - Типы фактов +- [ProfessionKey](../enums/profession-key.md) - Ключи профессий +- [VideoSite](../enums/video-site.md) - Сайты видео +- [BoxOfficeType](../enums/box-office-type.md) - Типы кассовых сборов +- [ContentType](../enums/content-type.md) - Типы контента +- [Sex](../enums/sex.md) - Пол +- [RelationType](../enums/relation-type.md) - Типы связей + +### Интерфейсы + +- [ModelInterface](../interfaces/model-interface.md) - Базовый интерфейс модели + +## 🚀 Быстрый старт + +### Создание модели из массива данных + +```php + 301, + 'nameRu' => 'Матрица', + 'nameEn' => 'The Matrix', + 'rating' => 8.7, + 'year' => 1999 +]; + +$film = Film::fromArray($filmData); + +// Создание модели персоны +$personData = [ + 'kinopoiskId' => 123, + 'nameRu' => 'Киану Ривз', + 'nameEn' => 'Keanu Reeves', + 'sex' => 'MALE' +]; + +$person = Person::fromArray($personData); + +// Создание модели съемочной группы +$staffData = [ + 'kinopoiskId' => 456, + 'nameRu' => 'Лана Вачовски', + 'nameEn' => 'Lana Wachowski', + 'professionKey' => 'DIRECTOR' +]; + +$staff = Staff::fromArray($staffData); +``` + +### Работа с моделями + +```php +// Получение отображаемого имени +echo $film->getDisplayName(); // "Матрица (The Matrix)" + +// Проверка свойств +if ($film->hasRating()) { + echo "Рейтинг: {$film->rating}\n"; +} + +if ($person->isMale()) { + echo "Пол: Мужской\n"; +} + +if ($staff->isDirector()) { + echo "Профессия: Режиссер\n"; +} + +// Преобразование в массив +$filmArray = $film->toArray(); +``` + +## 📊 Статистика моделей + +### Основные модели фильмов (6) + +- **Film** - Самая сложная модель с 30+ свойствами +- **FilmSearchResult** - Упрощенная версия для поиска +- **FilmCollection** - Коллекция фильмов +- **RelatedFilm** - Связанные фильмы +- **Episode** - Эпизоды сериалов +- **Season** - Сезоны сериалов + +### Модели персон (4) + +- **Person** - Основная модель персоны +- **Staff** - Съемочная группа +- **PersonFilm** - Фильмография +- **PersonSpouse** - Семейные связи + +### Модели контента (5) + +- **Review** - Отзывы пользователей +- **Fact** - Интересные факты +- **Image** - Изображения +- **Video** - Видео контент +- **MediaPost** - Медиа посты + +### Модели наград и статистики (4) + +- **Award** - Награды и номинации +- **BoxOffice** - Кассовые сборы +- **UserVote** - Пользовательские голоса +- **ExternalSource** - Внешние источники + +### Справочные модели (3) + +- **Country** - Страны +- **Genre** - Жанры +- **Distribution** - Дистрибуция + +### Модели API (3) + +- **ApiKeyInfo** - Информация об API ключе +- **ApiKeyQouta** - Квоты запросов +- **Filters** - Фильтры для поиска + +## 🔧 Общие методы + +Все модели реализуют общие методы: + +### fromArray() + +```php +public static function fromArray(array $data): self +``` + +Создает экземпляр модели из массива данных API. + +### toArray() + +```php +public function toArray(): array +``` + +Преобразует модель в массив. + +### getDisplayName() + +```php +public function getDisplayName(): string +``` + +Возвращает отображаемое имя объекта. + +## 📖 Примеры использования + +### Работа с фильмом + +```php +$film = Film::fromArray($filmData); + +echo "Название: {$film->getDisplayName()}\n"; +echo "Год: {$film->year}\n"; +echo "Рейтинг: {$film->rating}\n"; +echo "Описание: " . substr($film->description, 0, 100) . "...\n"; + +// Проверка свойств +if ($film->isSeries()) { + echo "Тип: Сериал\n"; + echo "Количество сезонов: {$film->seasonsCount}\n"; +} else { + echo "Тип: Фильм\n"; + echo "Длительность: {$film->filmLength} мин\n"; +} + +// Работа с жанрами +if (!empty($film->genres)) { + echo "Жанры: " . implode(', ', array_map(fn($g) => $g->genre, $film->genres)) . "\n"; +} + +// Работа со странами +if (!empty($film->countries)) { + echo "Страны: " . implode(', ', array_map(fn($c) => $c->country, $film->countries)) . "\n"; +} +``` + +### Работа с персоной + +```php +$person = Person::fromArray($personData); + +echo "Имя: {$person->getDisplayName()}\n"; +echo "Дата рождения: {$person->birthday}\n"; +echo "Место рождения: {$person->birthplace}\n"; + +// Проверка пола +if ($person->isMale()) { + echo "Пол: Мужской\n"; +} elseif ($person->isFemale()) { + echo "Пол: Женский\n"; +} else { + echo "Пол: Не указан\n"; +} + +// Работа с профессиями +if (!empty($person->profession)) { + echo "Профессии: " . implode(', ', $person->profession) . "\n"; +} +``` + +### Работа со съемочной группой + +```php +$staff = Staff::fromArray($staffData); + +echo "Имя: {$staff->getDisplayName()}\n"; +echo "Профессия: {$staff->getProfessionName()}\n"; + +// Проверка конкретных профессий +if ($staff->isDirector()) { + echo "Роль: Режиссер\n"; +} elseif ($staff->isActor()) { + echo "Роль: Актер\n"; +} elseif ($staff->isWriter()) { + echo "Роль: Сценарист\n"; +} elseif ($staff->isProducer()) { + echo "Роль: Продюсер\n"; +} +``` + +## 🔗 Связанные разделы + +- [Сервисы](../services/index.md) - Работа с API +- [Перечисления](../enums/index.md) - Константы и типы +- [Ответы](../responses/index.md) - Классы ответов +- [Исключения](../exceptions/index.md) - Обработка ошибок +- [Интерфейсы](../interfaces/index.md) - Базовые интерфейсы + +--- + +**📚 Навигация:** [Главная](../index.md) → Модели diff --git a/docs/dev/notkinopoiskphp/models/person-film.md b/docs/dev/notkinopoiskphp/models/person-film.md new file mode 100644 index 0000000..911145b --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/person-film.md @@ -0,0 +1,1044 @@ +# PersonFilm + +Модель фильма персоны из Kinopoisk API. + +## Описание + +`PersonFilm` представляет информацию о фильме, в котором участвовала персона (актер, режиссер, сценарист и т.д.), полученную из Kinopoisk API. Содержит данные о роли персоны в фильме. + +### Основные возможности + +- Хранение информации о фильме в неизменяемом виде +- Создание объекта из массива данных API +- Удобные методы для работы с названиями фильмов + +**API Endpoint:** `/api/v1/persons/{id}` + +## Свойства + +### Основная информация + +- `$filmId` (int) - Уникальный идентификатор фильма в Кинопоиске +- `$nameRu` (string|null) - Название фильма на русском языке +- `$nameEn` (string|null) - Название фильма на английском языке +- `$rating` (string|null) - Рейтинг фильма +- `$general` (bool) - Является ли фильм общим (не специфичным для персоны) +- `$description` (string|null) - Описание роли персоны в фильме +- `$professionKey` (ProfessionKey) - Ключ профессии персоны в фильме + +## Конструктор + +```php +public function __construct( + public readonly int $filmId, + public readonly ?string $nameRu, + public readonly ?string $nameEn, + public readonly ?string $rating, + public readonly bool $general, + public readonly ?string $description, + public readonly ProfessionKey $professionKey, +) +``` + +### Пример создания + +```php +$film = new PersonFilm( + filmId: 32169, + nameRu: 'Солист', + nameEn: 'The Soloist', + rating: '7.2', + general: false, + description: 'Steve Lopez', + professionKey: ProfessionKey::ACTOR +); +``` + +## Методы + +### fromArray() + +Создает экземпляр фильма персоны из массива данных API. + +```php +public static function fromArray(array $data): self +``` + +#### Параметры + +- `$data` (array) - Массив данных фильма от API + +#### Возвращаемое значение + +- `self` - Новый экземпляр фильма персоны + +#### Исключения + +- `\InvalidArgumentException` - Если данные имеют неверный формат + +#### Пример использования + +```php +$apiData = [ + 'filmId' => 32169, + 'nameRu' => 'Солист', + 'nameEn' => 'The Soloist', + 'rating' => '7.2', + 'general' => false, + 'description' => 'Steve Lopez', + 'professionKey' => 'ACTOR' +]; + +$film = PersonFilm::fromArray($apiData); +``` + +### isActor() + +Проверяет, является ли персона актером в этом фильме. + +```php +public function isActor(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если актер, `false` в противном случае + +#### Пример использования + +```php +if ($film->isActor()) { + echo "Актерская роль"; +} +``` + +### isDirector() + +Проверяет, является ли персона режиссером в этом фильме. + +```php +public function isDirector(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если режиссер, `false` в противном случае + +#### Пример использования + +```php +if ($film->isDirector()) { + echo "Режиссерская работа"; +} +``` + +### isWriter() + +Проверяет, является ли персона сценаристом в этом фильме. + +```php +public function isWriter(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если сценарист, `false` в противном случае + +#### Пример использования + +```php +if ($film->isWriter()) { + echo "Сценарная работа"; +} +``` + +### isProducer() + +Проверяет, является ли персона продюсером в этом фильме. + +```php +public function isProducer(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если продюсер, `false` в противном случае + +#### Пример использования + +```php +if ($film->isProducer()) { + echo "Продюсерская работа"; +} +``` + +### isGeneral() + +Проверяет, является ли фильм общим. + +```php +public function isGeneral(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если общий фильм, `false` если специфичный + +#### Описание + +Общие фильмы - это те, которые не специфичны для конкретной персоны, а показывают общую фильмографию. + +#### Пример использования + +```php +if ($film->isGeneral()) { + echo "Общий фильм"; +} else { + echo "Специфичный для персоны"; +} +``` + +### getFullInfo() + +Получает полную информацию о фильме. + +```php +public function getFullInfo(): string +``` + +#### Возвращаемое значение + +- `string` - Полная информация о фильме + +#### Описание + +Возвращает строку с полной информацией о фильме, включая название, профессию, рейтинг и описание роли. + +#### Пример использования + +```php +echo $film->getFullInfo(); +// "Солист (Актер, 7.2) - Steve Lopez" +``` + +### getDisplayName() + +Получает отображаемое название фильма. + +```php +public function getDisplayName(): string +``` + +#### Возвращаемое значение + +- `string` - Отображаемое название фильма + +#### Описание + +Возвращает наиболее подходящее название для отображения пользователю. Приоритет: русское название → английское название → "Без названия" + +#### Пример использования + +```php +echo $film->getDisplayName(); // "Солист" или "The Soloist" или "Без названия" +``` + +### getProfessionName() + +Получает человекочитаемое название профессии. + +```php +public function getProfessionName(): string +``` + +#### Возвращаемое значение + +- `string` - Название профессии + +#### Описание + +Возвращает название профессии на русском языке. + +#### Пример использования + +```php +echo $film->getProfessionName(); // "Актер", "Режиссер", "Сценарист" +``` + +### getRatingInfo() + +Получает информацию о рейтинге в виде строки. + +```php +public function getRatingInfo(): string +``` + +#### Возвращаемое значение + +- `string` - Информация о рейтинге + +#### Описание + +Возвращает рейтинг или "Нет рейтинга", если рейтинг отсутствует. + +#### Пример использования + +```php +echo "Рейтинг: {$film->getRatingInfo()}"; // "7.2" или "Нет рейтинга" +``` + +### isCreativeProfession() + +Проверяет, является ли профессия творческой. + +```php +public function isCreativeProfession(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если творческая профессия, `false` в противном случае + +#### Пример использования + +```php +if ($film->isCreativeProfession()) { + echo "Творческая работа"; +} +``` + +### isTechnicalProfession() + +Проверяет, является ли профессия технической. + +```php +public function isTechnicalProfession(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если техническая профессия, `false` в противном случае + +#### Пример использования + +```php +if ($film->isTechnicalProfession()) { + echo "Техническая работа"; +} +``` + +### isManagementProfession() + +Проверяет, является ли профессия управленческой. + +```php +public function isManagementProfession(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если управленческая профессия, `false` в противном случае + +#### Пример использования + +```php +if ($film->isManagementProfession()) { + echo "Управленческая работа"; +} +``` + +### isSpecialProfession() + +Проверяет, является ли профессия специальной. + +```php +public function isSpecialProfession(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если специальная профессия, `false` в противном случае + +#### Пример использования + +```php +if ($film->isSpecialProfession()) { + echo "Специальная роль"; +} +``` + +### getProfessionCategory() + +Получает категорию профессии. + +```php +public function getProfessionCategory(): string +``` + +#### Возвращаемое значение + +- `string` - Категория профессии + +#### Пример использования + +```php +echo $film->getProfessionCategory(); // "Творческая", "Техническая", etc. +``` + +### toArray() + +Преобразует объект фильма персоны в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Массив с данными фильма персоны + +#### Описание + +Возвращает все свойства объекта в виде ассоциативного массива. Полезно для сериализации, логирования или передачи данных. + +#### Пример использования + +```php +$filmArray = $film->toArray(); +echo json_encode($filmArray); // JSON представление фильма персоны +``` + +## Полный пример использования + +```php +persons; +$person = $personService->getPerson(1); // ID персоны + +echo "=== Фильмография персоны ===\n"; + +if (!empty($person->films)) { + echo "Всего фильмов: " . count($person->films) . "\n\n"; + + foreach ($person->films as $index => $film) { + echo ($index + 1) . ". {$film->getFullInfo()}\n"; + echo " ID фильма: {$film->filmId}\n"; + echo " Название: {$film->getDisplayName()}\n"; + echo " Профессия: {$film->getProfessionName()}\n"; + echo " Рейтинг: {$film->getRatingInfo()}\n"; + + if ($film->description) { + echo " Описание роли: {$film->description}\n"; + } + + echo " Тип: " . ($film->isGeneral() ? 'Общий' : 'Специфичный') . "\n"; + echo " Категория: {$film->getProfessionCategory()}\n"; + echo "\n"; + } +} else { + echo "Фильмография не найдена\n"; +} +``` + +## Работа с фильмами персоны + +```php +// Функция для фильтрации фильмов по профессии +function filterFilmsByProfession(array $films, string $profession): array { + return array_filter($films, function($film) use ($profession) { + return match ($profession) { + 'actor' => $film->isActor(), + 'director' => $film->isDirector(), + 'writer' => $film->isWriter(), + 'producer' => $film->isProducer(), + default => false + }; + }); +} + +// Функция для фильтрации фильмов по категории профессии +function filterFilmsByProfessionCategory(array $films, string $category): array { + return array_filter($films, fn($film) => $film->getProfessionCategory() === $category); +} + +// Функция для получения фильмов с рейтингом выше определенного +function getFilmsWithRatingAbove(array $films, float $minRating): array { + return array_filter($films, function($film) use ($minRating) { + if (!$film->rating) return false; + return (float)$film->rating >= $minRating; + }); +} + +// Функция для получения статистики по фильмам +function getFilmStats(array $films): array { + $stats = [ + 'total' => count($films), + 'actors' => 0, + 'directors' => 0, + 'writers' => 0, + 'producers' => 0, + 'general' => 0, + 'specific' => 0, + 'withRating' => 0, + 'averageRating' => 0, + 'professionCategories' => [ + 'creative' => 0, + 'technical' => 0, + 'management' => 0, + 'special' => 0 + ] + ]; + + $totalRating = 0; + $ratedFilms = 0; + + foreach ($films as $film) { + // Профессии + if ($film->isActor()) $stats['actors']++; + if ($film->isDirector()) $stats['directors']++; + if ($film->isWriter()) $stats['writers']++; + if ($film->isProducer()) $stats['producers']++; + + // Тип фильма + if ($film->isGeneral()) { + $stats['general']++; + } else { + $stats['specific']++; + } + + // Рейтинг + if ($film->rating) { + $stats['withRating']++; + $totalRating += (float)$film->rating; + $ratedFilms++; + } + + // Категории профессий + $category = strtolower($film->getProfessionCategory()); + if (isset($stats['professionCategories'][$category])) { + $stats['professionCategories'][$category]++; + } + } + + // Средний рейтинг + if ($ratedFilms > 0) { + $stats['averageRating'] = round($totalRating / $ratedFilms, 2); + } + + return $stats; +} + +// Функция для получения топ фильмов по рейтингу +function getTopRatedFilms(array $films, int $limit = 10): array { + $ratedFilms = array_filter($films, fn($film) => $film->rating !== null); + + usort($ratedFilms, function($a, $b) { + return (float)$b->rating <=> (float)$a->rating; + }); + + return array_slice($ratedFilms, 0, $limit); +} + +// Функция для получения фильмов по году (если доступно) +function getFilmsByYear(array $films, int $year): array { + // Примечание: год не доступен в текущей модели PersonFilm + // Это пример для возможного расширения + return array_filter($films, function($film) use ($year) { + // Здесь можно добавить логику фильтрации по году + return true; // Пока возвращаем все фильмы + }); +} + +// Использование +$films = $person->films ?? []; + +// Фильтрация по профессиям +$actorFilms = filterFilmsByProfession($films, 'actor'); +$directorFilms = filterFilmsByProfession($films, 'director'); +$writerFilms = filterFilmsByProfession($films, 'writer'); +$producerFilms = filterFilmsByProfession($films, 'producer'); + +echo "Фильтрация по профессиям:\n"; +echo "Актерские роли: " . count($actorFilms) . "\n"; +echo "Режиссерские работы: " . count($directorFilms) . "\n"; +echo "Сценарные работы: " . count($writerFilms) . "\n"; +echo "Продюсерские работы: " . count($producerFilms) . "\n"; + +// Фильтрация по категориям +$creativeFilms = filterFilmsByProfessionCategory($films, 'Творческая'); +$technicalFilms = filterFilmsByProfessionCategory($films, 'Техническая'); + +echo "\nФильтрация по категориям:\n"; +echo "Творческие работы: " . count($creativeFilms) . "\n"; +echo "Технические работы: " . count($technicalFilms) . "\n"; + +// Фильмы с высоким рейтингом +$highRatedFilms = getFilmsWithRatingAbove($films, 7.0); +echo "\nФильмы с рейтингом выше 7.0: " . count($highRatedFilms) . "\n"; + +// Статистика +$stats = getFilmStats($films); +echo "\nСтатистика:\n"; +echo "Всего фильмов: {$stats['total']}\n"; +echo "Актерских ролей: {$stats['actors']}\n"; +echo "Режиссерских работ: {$stats['directors']}\n"; +echo "Сценарных работ: {$stats['writers']}\n"; +echo "Продюсерских работ: {$stats['producers']}\n"; +echo "Общих фильмов: {$stats['general']}\n"; +echo "Специфичных фильмов: {$stats['specific']}\n"; +echo "Фильмов с рейтингом: {$stats['withRating']}\n"; +echo "Средний рейтинг: {$stats['averageRating']}\n"; + +// Топ фильмов +$topFilms = getTopRatedFilms($films, 5); +echo "\nТоп-5 фильмов по рейтингу:\n"; +foreach ($topFilms as $index => $film) { + echo ($index + 1) . ". {$film->getDisplayName()} ({$film->getRatingInfo()})\n"; +} +``` + +## Создание отчета по фильмографии + +```php +class PersonFilmReport { + private array $films; + + public function __construct(array $films) { + $this->films = $films; + } + + public function getFilms(): array { + return $this->films; + } + + public function getFilmsByProfession(string $profession): array { + return filterFilmsByProfession($this->films, $profession); + } + + public function getFilmsByCategory(string $category): array { + return filterFilmsByProfessionCategory($this->films, $category); + } + + public function getFilmStats(): array { + return getFilmStats($this->films); + } + + public function getTopRatedFilms(int $limit = 10): array { + return getTopRatedFilms($this->films, $limit); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО ФИЛЬМОГРАФИИ ===\n\n"; + + $stats = $this->getFilmStats(); + + // Общая статистика + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего фильмов: {$stats['total']}\n"; + $report .= "Актерских ролей: {$stats['actors']}\n"; + $report .= "Режиссерских работ: {$stats['directors']}\n"; + $report .= "Сценарных работ: {$stats['writers']}\n"; + $report .= "Продюсерских работ: {$stats['producers']}\n"; + $report .= "Общих фильмов: {$stats['general']}\n"; + $report .= "Специфичных фильмов: {$stats['specific']}\n"; + $report .= "Фильмов с рейтингом: {$stats['withRating']}\n"; + $report .= "Средний рейтинг: {$stats['averageRating']}\n\n"; + + // Распределение по категориям профессий + $report .= "📈 РАСПРЕДЕЛЕНИЕ ПО КАТЕГОРИЯМ:\n"; + foreach ($stats['professionCategories'] as $category => $count) { + $report .= "• {$category}: {$count}\n"; + } + $report .= "\n"; + + // Актерские роли + $actorFilms = $this->getFilmsByProfession('actor'); + if (!empty($actorFilms)) { + $report .= "🎭 АКТЕРСКИЕ РОЛИ (" . count($actorFilms) . " фильмов):\n"; + foreach (array_slice($actorFilms, 0, 10) as $film) { + $report .= "• {$film->getFullInfo()}\n"; + } + if (count($actorFilms) > 10) { + $report .= "... и еще " . (count($actorFilms) - 10) . " фильмов\n"; + } + $report .= "\n"; + } + + // Режиссерские работы + $directorFilms = $this->getFilmsByProfession('director'); + if (!empty($directorFilms)) { + $report .= "🎬 РЕЖИССЕРСКИЕ РАБОТЫ (" . count($directorFilms) . " фильмов):\n"; + foreach (array_slice($directorFilms, 0, 10) as $film) { + $report .= "• {$film->getFullInfo()}\n"; + } + if (count($directorFilms) > 10) { + $report .= "... и еще " . (count($directorFilms) - 10) . " фильмов\n"; + } + $report .= "\n"; + } + + // Сценарные работы + $writerFilms = $this->getFilmsByProfession('writer'); + if (!empty($writerFilms)) { + $report .= "✍️ СЦЕНАРНЫЕ РАБОТЫ (" . count($writerFilms) . " фильмов):\n"; + foreach (array_slice($writerFilms, 0, 10) as $film) { + $report .= "• {$film->getFullInfo()}\n"; + } + if (count($writerFilms) > 10) { + $report .= "... и еще " . (count($writerFilms) - 10) . " фильмов\n"; + } + $report .= "\n"; + } + + // Продюсерские работы + $producerFilms = $this->getFilmsByProfession('producer'); + if (!empty($producerFilms)) { + $report .= "💰 ПРОДЮСЕРСКИЕ РАБОТЫ (" . count($producerFilms) . " фильмов):\n"; + foreach (array_slice($producerFilms, 0, 10) as $film) { + $report .= "• {$film->getFullInfo()}\n"; + } + if (count($producerFilms) > 10) { + $report .= "... и еще " . (count($producerFilms) - 10) . " фильмов\n"; + } + $report .= "\n"; + } + + // Топ фильмов по рейтингу + $topFilms = $this->getTopRatedFilms(10); + if (!empty($topFilms)) { + $report .= "🏆 ТОП-10 ФИЛЬМОВ ПО РЕЙТИНГУ:\n"; + foreach ($topFilms as $index => $film) { + $report .= ($index + 1) . ". {$film->getFullInfo()}\n"; + } + $report .= "\n"; + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $stats = $this->getFilmStats(); + $actorFilms = $this->getFilmsByProfession('actor'); + $directorFilms = $this->getFilmsByProfession('director'); + $writerFilms = $this->getFilmsByProfession('writer'); + $producerFilms = $this->getFilmsByProfession('producer'); + $topFilms = $this->getTopRatedFilms(10); + + // Статистика + $html .= "
\n"; + $html .= "

Общая статистика

\n"; + $html .= "

Всего фильмов: {$stats['total']}

\n"; + $html .= "

Актерских ролей: {$stats['actors']}

\n"; + $html .= "

Режиссерских работ: {$stats['directors']}

\n"; + $html .= "

Сценарных работ: {$stats['writers']}

\n"; + $html .= "

Продюсерских работ: {$stats['producers']}

\n"; + $html .= "

Средний рейтинг: {$stats['averageRating']}

\n"; + + // Прогресс-бары + if ($stats['total'] > 0) { + $actorPercent = round(($stats['actors'] / $stats['total']) * 100, 1); + $directorPercent = round(($stats['directors'] / $stats['total']) * 100, 1); + $writerPercent = round(($stats['writers'] / $stats['total']) * 100, 1); + $producerPercent = round(($stats['producers'] / $stats['total']) * 100, 1); + + $html .= "

Распределение по профессиям

\n"; + $html .= "
\n"; + $html .= "
\n"; + $html .= "
\n"; + $html .= "

Актерские роли: {$actorPercent}%

\n"; + + $html .= "
\n"; + $html .= "
\n"; + $html .= "
\n"; + $html .= "

Режиссерские работы: {$directorPercent}%

\n"; + + $html .= "
\n"; + $html .= "
\n"; + $html .= "
\n"; + $html .= "

Сценарные работы: {$writerPercent}%

\n"; + + $html .= "
\n"; + $html .= "
\n"; + $html .= "
\n"; + $html .= "

Продюсерские работы: {$producerPercent}%

\n"; + } + + $html .= "
\n"; + + // Актерские роли + if (!empty($actorFilms)) { + $html .= "
\n"; + $html .= "
🎭 Актерские роли (" . count($actorFilms) . " фильмов)
\n"; + $html .= "
\n"; + + foreach (array_slice($actorFilms, 0, 12) as $film) { + $html .= "
\n"; + $html .= "
{$film->getDisplayName()}
\n"; + $html .= "
Профессия: {$film->getProfessionName()}
\n"; + $html .= "
Рейтинг: {$film->getRatingInfo()}
\n"; + if ($film->description) { + $html .= "
Роль: {$film->description}
\n"; + } + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + // Режиссерские работы + if (!empty($directorFilms)) { + $html .= "
\n"; + $html .= "
🎬 Режиссерские работы (" . count($directorFilms) . " фильмов)
\n"; + $html .= "
\n"; + + foreach (array_slice($directorFilms, 0, 12) as $film) { + $html .= "
\n"; + $html .= "
{$film->getDisplayName()}
\n"; + $html .= "
Профессия: {$film->getProfessionName()}
\n"; + $html .= "
Рейтинг: {$film->getRatingInfo()}
\n"; + if ($film->description) { + $html .= "
Описание: {$film->description}
\n"; + } + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + // Сценарные работы + if (!empty($writerFilms)) { + $html .= "
\n"; + $html .= "
✍️ Сценарные работы (" . count($writerFilms) . " фильмов)
\n"; + $html .= "
\n"; + + foreach (array_slice($writerFilms, 0, 12) as $film) { + $html .= "
\n"; + $html .= "
{$film->getDisplayName()}
\n"; + $html .= "
Профессия: {$film->getProfessionName()}
\n"; + $html .= "
Рейтинг: {$film->getRatingInfo()}
\n"; + if ($film->description) { + $html .= "
Описание: {$film->description}
\n"; + } + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + // Продюсерские работы + if (!empty($producerFilms)) { + $html .= "
\n"; + $html .= "
💰 Продюсерские работы (" . count($producerFilms) . " фильмов)
\n"; + $html .= "
\n"; + + foreach (array_slice($producerFilms, 0, 12) as $film) { + $html .= "
\n"; + $html .= "
{$film->getDisplayName()}
\n"; + $html .= "
Профессия: {$film->getProfessionName()}
\n"; + $html .= "
Рейтинг: {$film->getRatingInfo()}
\n"; + if ($film->description) { + $html .= "
Описание: {$film->description}
\n"; + } + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + // Топ фильмов + if (!empty($topFilms)) { + $html .= "
\n"; + $html .= "
🏆 Топ-10 фильмов по рейтингу
\n"; + $html .= "
\n"; + + foreach ($topFilms as $index => $film) { + $cssClass = $film->isActor() ? 'actor' : ($film->isDirector() ? 'director' : ($film->isWriter() ? 'writer' : 'producer')); + $html .= "
\n"; + $html .= "
" . ($index + 1) . ". {$film->getDisplayName()}
\n"; + $html .= "
Профессия: {$film->getProfessionName()}
\n"; + $html .= "
Рейтинг: {$film->getRatingInfo()}
\n"; + if ($film->description) { + $html .= "
Описание: {$film->description}
\n"; + } + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + $html .= "
\n\n"; + + return $html; + } +} + +// Использование +$films = $person->films ?? []; +$report = new PersonFilmReport($films); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Отчет по фильмографии'); +file_put_contents('person_films_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в person_films_report.html\n"; +``` + +## Анализ фильмографии + +```php +function analyzePersonFilms(array $films): array { + $analysis = [ + 'totalFilms' => count($films), + 'professionDistribution' => [ + 'actors' => 0, + 'directors' => 0, + 'writers' => 0, + 'producers' => 0 + ], + 'professionPercentages' => [ + 'actors' => 0, + 'directors' => 0, + 'writers' => 0, + 'producers' => 0 + ], + 'categoryDistribution' => [ + 'creative' => 0, + 'technical' => 0, + 'management' => 0, + 'special' => 0 + ], + 'ratingAnalysis' => [ + 'withRating' => 0, + 'averageRating' => 0, + 'highestRating' => 0, + 'lowestRating' => 0 + ], + 'filmTypes' => [ + 'general' => 0, + 'specific' => 0 + ] + ]; + + $totalRating = 0; + $ratedFilms = 0; + $ratings = []; + + foreach ($films as $film) { + // Распределение по профессиям + if ($film->isActor()) $analysis['professionDistribution']['actors']++; + if ($film->isDirector()) $analysis['professionDistribution']['directors']++; + if ($film->isWriter()) $analysis['professionDistribution']['writers']++; + if ($film->isProducer()) $analysis['professionDistribution']['producers']++; + + // Распределение по категориям + $category = strtolower($film->getProfessionCategory()); + if (isset($analysis['categoryDistribution'][$category])) { + $analysis['categoryDistribution'][$category]++; + } + + // Анализ рейтингов + if ($film->rating) { + $analysis['ratingAnalysis']['withRating']++; + $rating = (float)$film->rating; + $totalRating += $rating; + $ratings[] = $rating; + } + + // Типы фильмов + if ($film->isGeneral()) { + $analysis['filmTypes']['general']++; + } else { + $analysis['filmTypes']['specific']++; + } + } + + // Вычисление процентов + if ($analysis['totalFilms'] > 0) { + $analysis['professionPercentages']['actors'] = round(($analysis['professionDistribution']['actors'] / $analysis['totalFilms']) * 100, 1); + $analysis['professionPercentages']['directors'] = round(($analysis['professionDistribution']['directors'] / $analysis['totalFilms']) * 100, 1); + $analysis['professionPercentages']['writers'] = round(($analysis['professionDistribution']['writers'] / $analysis['totalFilms']) * 100, 1); + $analysis['professionPercentages']['producers'] = round(($analysis['professionDistribution']['producers'] / $analysis['totalFilms']) * 100, 1); + } + + // Анализ рейтингов + if (!empty($ratings)) { + $analysis['ratingAnalysis']['averageRating'] = round($totalRating / count($ratings), 2); + $analysis['ratingAnalysis']['highestRating'] = max($ratings); + $analysis['ratingAnalysis']['lowestRating'] = min($ratings); + } + + return $analysis; +} + +// Использование +$films = $person->films ?? []; +$analysis = analyzePersonFilms($films); + +echo "=== Анализ фильмографии ===\n"; +echo "Всего фильмов: {$analysis['totalFilms']}\n"; + +echo "\nРаспределение по профессиям:\n"; +echo "- Актерские роли: {$analysis['professionDistribution']['actors']} ({$analysis['professionPercentages']['actors']}%)\n"; +echo "- Режиссерские работы: {$analysis['professionDistribution']['directors']} ({$analysis['professionPercentages']['directors']}%)\n"; +echo "- Сценарные работы: {$analysis['professionDistribution']['writers']} ({$analysis['professionPercentages']['writers']}%)\n"; +echo "- Продюсерские работы: {$analysis['professionDistribution']['producers']} ({$analysis['professionPercentages']['producers']}%)\n"; + +echo "\nРаспределение по категориям:\n"; +foreach ($analysis['categoryDistribution'] as $category => $count) { + echo "- {$category}: {$count}\n"; +} + +echo "\nАнализ рейтингов:\n"; +echo "- Фильмов с рейтингом: {$analysis['ratingAnalysis']['withRating']}\n"; +echo "- Средний рейтинг: {$analysis['ratingAnalysis']['averageRating']}\n"; +echo "- Высший рейтинг: {$analysis['ratingAnalysis']['highestRating']}\n"; +echo "- Низший рейтинг: {$analysis['ratingAnalysis']['lowestRating']}\n"; + +echo "\nТипы фильмов:\n"; +echo "- Общих фильмов: {$analysis['filmTypes']['general']}\n"; +echo "- Специфичных фильмов: {$analysis['filmTypes']['specific']}\n"; +``` + +## Связанные классы + +- [`Person`](person.md) - Модель персоны +- [`ProfessionKey`](../enums/profession-key.md) - Enum профессий +- [`PersonService`](../services/person-service.md) - Сервис для работы с персонами diff --git a/docs/dev/notkinopoiskphp/models/person-spouse.md b/docs/dev/notkinopoiskphp/models/person-spouse.md new file mode 100644 index 0000000..81c03f3 --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/person-spouse.md @@ -0,0 +1,722 @@ +# PersonSpouse + +Модель супруга персоны из Kinopoisk API. + +## Описание + +`PersonSpouse` представляет информацию о супруге персоны, полученную из Kinopoisk API. Содержит данные о браке, детях, причинах развода и других аспектах семейной жизни персоны. + +### Основные возможности + +- Хранение информации о супруге в неизменяемом виде +- Создание объекта из массива данных API +- Удобные методы для работы с семейным статусом +- Поддержка информации о детях и браке + +## Свойства + +### Основная информация + +- `$personId` (int) - Уникальный идентификатор супруга в Кинопоиске +- `$name` (string|null) - Имя супруга +- `$divorced` (bool) - Статус развода +- `$divorcedReason` (string|null) - Причина развода (если применимо) +- `$sex` (string) - Пол супруга +- `$children` (int) - Количество детей +- `$webUrl` (string) - URL страницы супруга на Кинопоиске +- `$relation` (string) - Тип отношений (супруга, супруг и т.д.) + +## Конструктор + +```php +public function __construct( + public readonly int $personId, + public readonly ?string $name, + public readonly bool $divorced, + public readonly ?string $divorcedReason, + public readonly string $sex, + public readonly int $children, + public readonly string $webUrl, + public readonly string $relation, +) +``` + +### Пример создания + +```php +$spouse = new PersonSpouse( + personId: 32169, + name: 'Сьюзан Дауни', + divorced: false, + divorcedReason: null, + sex: 'FEMALE', + children: 2, + webUrl: 'https://www.kinopoisk.ru/name/32169/', + relation: 'супруга' +); +``` + +## Методы + +### fromArray() + +Создает экземпляр супруга из массива данных API. + +```php +public static function fromArray(array $data): self +``` + +#### Параметры + +- `$data` (array) - Массив данных супруга от API + +#### Возвращаемое значение + +- `self` - Новый экземпляр супруга + +#### Исключения + +- `\InvalidArgumentException` - Если данные имеют неверный формат + +#### Пример использования + +```php +$apiData = [ + 'personId' => 32169, + 'name' => 'Сьюзан Дауни', + 'divorced' => false, + 'divorcedReason' => null, + 'sex' => 'FEMALE', + 'children' => 2, + 'webUrl' => 'https://www.kinopoisk.ru/name/32169/', + 'relation' => 'супруга' +]; + +$spouse = PersonSpouse::fromArray($apiData); +``` + +### isDivorced() + +Проверяет, разведен ли супруг. + +```php +public function isDivorced(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если разведен, `false` если в браке + +#### Пример использования + +```php +if ($spouse->isDivorced()) { + echo "Разведен"; + if ($spouse->divorcedReason) { + echo "Причина: {$spouse->divorcedReason}"; + } +} else { + echo "В браке"; +} +``` + +### isMale() + +Проверяет, является ли супруг мужчиной. + +```php +public function isMale(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если мужчина, `false` если женщина + +#### Пример использования + +```php +if ($spouse->isMale()) { + echo "Супруг"; +} else { + echo "Супруга"; +} +``` + +### isFemale() + +Проверяет, является ли супруг женщиной. + +```php +public function isFemale(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если женщина, `false` если мужчина + +#### Пример использования + +```php +if ($spouse->isFemale()) { + echo "Супруга"; +} else { + echo "Супруг"; +} +``` + +### getDisplayName() + +Получает отображаемое имя супруга. + +```php +public function getDisplayName(): string +``` + +#### Возвращаемое значение + +- `string` - Отображаемое имя супруга + +#### Описание + +Возвращает имя супруга или "Неизвестно", если имя не указано. + +#### Пример использования + +```php +echo "Супруг: {$spouse->getDisplayName()}"; +``` + +### getMarriageInfo() + +Получает полную информацию о браке. + +```php +public function getMarriageInfo(): string +``` + +#### Возвращаемое значение + +- `string` - Полная информация о браке + +#### Описание + +Возвращает строку с полной информацией о браке, включая статус, причину развода (если применимо) и количество детей. + +#### Пример использования + +```php +echo $spouse->getMarriageInfo(); +// "В браке, 2 ребенка" или "Разведен (причина), 1 ребенок" +``` + +### isMarried() + +Проверяет, в браке ли супруг. + +```php +public function isMarried(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если в браке, `false` если разведен + +#### Пример использования + +```php +if ($spouse->isMarried()) { + echo "В браке"; +} else { + echo "Разведен"; +} +``` + +### getChildrenInfo() + +Получает информацию о детях в виде строки. + +```php +public function getChildrenInfo(): string +``` + +#### Возвращаемое значение + +- `string` - Информация о детях + +#### Описание + +Возвращает строку с количеством детей или "Нет детей". + +#### Пример использования + +```php +echo "Дети: {$spouse->getChildrenInfo()}"; // "2 ребенка" или "Нет детей" +``` + +### toArray() + +Преобразует объект супруга в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Массив с данными супруга + +#### Описание + +Возвращает все свойства объекта в виде ассоциативного массива. Полезно для сериализации, логирования или передачи данных. + +#### Пример использования + +```php +$spouseArray = $spouse->toArray(); +echo json_encode($spouseArray); // JSON представление супруга +``` + +## Полный пример использования + +```php +persons; +$person = $personService->getPerson(1); // ID персоны + +echo "=== Информация о супругах ===\n"; + +if (!empty($person->spouses)) { + echo "Количество супругов: " . count($person->spouses) . "\n\n"; + + foreach ($person->spouses as $index => $spouse) { + echo ($index + 1) . ". {$spouse->getDisplayName()}\n"; + echo " ID: {$spouse->personId}\n"; + echo " Пол: " . ($spouse->isMale() ? 'Мужской' : 'Женский') . "\n"; + echo " Статус: " . ($spouse->isMarried() ? 'В браке' : 'Разведен') . "\n"; + + if ($spouse->isDivorced() && $spouse->divorcedReason) { + echo " Причина развода: {$spouse->divorcedReason}\n"; + } + + echo " Дети: {$spouse->getChildrenInfo()}\n"; + echo " Отношения: {$spouse->relation}\n"; + echo " URL: {$spouse->webUrl}\n"; + echo "\n"; + } +} else { + echo "Информация о супругах не найдена\n"; +} +``` + +## Работа с супругами + +```php +// Функция для фильтрации супругов по статусу брака +function filterSpousesByMaritalStatus(array $spouses, bool $married): array { + return array_filter($spouses, fn($spouse) => $spouse->isMarried() === $married); +} + +// Функция для фильтрации супругов по полу +function filterSpousesBySex(array $spouses, string $sex): array { + return array_filter($spouses, function($spouse) use ($sex) { + return $sex === 'MALE' ? $spouse->isMale() : $spouse->isFemale(); + }); +} + +// Функция для получения супругов с детьми +function getSpousesWithChildren(array $spouses): array { + return array_filter($spouses, fn($spouse) => $spouse->children > 0); +} + +// Функция для получения статистики по супругам +function getSpouseStats(array $spouses): array { + $stats = [ + 'total' => count($spouses), + 'married' => 0, + 'divorced' => 0, + 'male' => 0, + 'female' => 0, + 'withChildren' => 0, + 'totalChildren' => 0 + ]; + + foreach ($spouses as $spouse) { + if ($spouse->isMarried()) { + $stats['married']++; + } else { + $stats['divorced']++; + } + + if ($spouse->isMale()) { + $stats['male']++; + } else { + $stats['female']++; + } + + if ($spouse->children > 0) { + $stats['withChildren']++; + $stats['totalChildren'] += $spouse->children; + } + } + + return $stats; +} + +// Функция для получения причин развода +function getDivorceReasons(array $spouses): array { + $reasons = []; + + foreach ($spouses as $spouse) { + if ($spouse->isDivorced() && $spouse->divorcedReason) { + if (!isset($reasons[$spouse->divorcedReason])) { + $reasons[$spouse->divorcedReason] = 0; + } + $reasons[$spouse->divorcedReason]++; + } + } + + return $reasons; +} + +// Использование +$spouses = $person->spouses ?? []; + +// Фильтрация +$marriedSpouses = filterSpousesByMaritalStatus($spouses, true); +$divorcedSpouses = filterSpousesByMaritalStatus($spouses, false); +$maleSpouses = filterSpousesBySex($spouses, 'MALE'); +$femaleSpouses = filterSpousesBySex($spouses, 'FEMALE'); +$spousesWithChildren = getSpousesWithChildren($spouses); + +echo "Статистика по супругам:\n"; +echo "В браке: " . count($marriedSpouses) . "\n"; +echo "Разведены: " . count($divorcedSpouses) . "\n"; +echo "Мужчин: " . count($maleSpouses) . "\n"; +echo "Женщин: " . count($femaleSpouses) . "\n"; +echo "С детьми: " . count($spousesWithChildren) . "\n"; + +// Статистика +$stats = getSpouseStats($spouses); +echo "\nПодробная статистика:\n"; +echo "Всего супругов: {$stats['total']}\n"; +echo "В браке: {$stats['married']}\n"; +echo "Разведены: {$stats['divorced']}\n"; +echo "Мужчин: {$stats['male']}\n"; +echo "Женщин: {$stats['female']}\n"; +echo "С детьми: {$stats['withChildren']}\n"; +echo "Общее количество детей: {$stats['totalChildren']}\n"; + +// Причины развода +$divorceReasons = getDivorceReasons($spouses); +if (!empty($divorceReasons)) { + echo "\nПричины развода:\n"; + foreach ($divorceReasons as $reason => $count) { + echo "- {$reason}: {$count} раз\n"; + } +} +``` + +## Создание отчета по супругам + +```php +class SpouseReport { + private array $spouses; + + public function __construct(array $spouses) { + $this->spouses = $spouses; + } + + public function getSpouses(): array { + return $this->spouses; + } + + public function getMarriedSpouses(): array { + return filterSpousesByMaritalStatus($this->spouses, true); + } + + public function getDivorcedSpouses(): array { + return filterSpousesByMaritalStatus($this->spouses, false); + } + + public function getMaleSpouses(): array { + return filterSpousesBySex($this->spouses, 'MALE'); + } + + public function getFemaleSpouses(): array { + return filterSpousesBySex($this->spouses, 'FEMALE'); + } + + public function getSpousesWithChildren(): array { + return getSpousesWithChildren($this->spouses); + } + + public function getSpouseStats(): array { + return getSpouseStats($this->spouses); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО СУПРУГАМ ===\n\n"; + + $stats = $this->getSpouseStats(); + + // Общая статистика + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего супругов: {$stats['total']}\n"; + $report .= "В браке: {$stats['married']}\n"; + $report .= "Разведены: {$stats['divorced']}\n"; + $report .= "Мужчин: {$stats['male']}\n"; + $report .= "Женщин: {$stats['female']}\n"; + $report .= "С детьми: {$stats['withChildren']}\n"; + $report .= "Общее количество детей: {$stats['totalChildren']}\n\n"; + + // Супруги в браке + $marriedSpouses = $this->getMarriedSpouses(); + if (!empty($marriedSpouses)) { + $report .= "💍 В БРАКЕ (" . count($marriedSpouses) . " человек):\n"; + foreach ($marriedSpouses as $spouse) { + $report .= "• {$spouse->getDisplayName()}\n"; + $report .= " Пол: " . ($spouse->isMale() ? 'Мужской' : 'Женский') . "\n"; + $report .= " Дети: {$spouse->getChildrenInfo()}\n"; + $report .= " Отношения: {$spouse->relation}\n"; + } + $report .= "\n"; + } + + // Разведенные супруги + $divorcedSpouses = $this->getDivorcedSpouses(); + if (!empty($divorcedSpouses)) { + $report .= "💔 РАЗВЕДЕНЫ (" . count($divorcedSpouses) . " человек):\n"; + foreach ($divorcedSpouses as $spouse) { + $report .= "• {$spouse->getDisplayName()}\n"; + $report .= " Пол: " . ($spouse->isMale() ? 'Мужской' : 'Женский') . "\n"; + if ($spouse->divorcedReason) { + $report .= " Причина развода: {$spouse->divorcedReason}\n"; + } + $report .= " Дети: {$spouse->getChildrenInfo()}\n"; + $report .= " Отношения: {$spouse->relation}\n"; + } + $report .= "\n"; + } + + // Супруги с детьми + $spousesWithChildren = $this->getSpousesWithChildren(); + if (!empty($spousesWithChildren)) { + $report .= "👶 СУПРУГИ С ДЕТЬМИ (" . count($spousesWithChildren) . " человек):\n"; + foreach ($spousesWithChildren as $spouse) { + $report .= "• {$spouse->getDisplayName()}\n"; + $report .= " Статус: " . ($spouse->isMarried() ? 'В браке' : 'Разведен') . "\n"; + $report .= " Дети: {$spouse->getChildrenInfo()}\n"; + } + $report .= "\n"; + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $stats = $this->getSpouseStats(); + $marriedSpouses = $this->getMarriedSpouses(); + $divorcedSpouses = $this->getDivorcedSpouses(); + + // Статистика + $html .= "
\n"; + $html .= "

Общая статистика

\n"; + $html .= "

Всего супругов: {$stats['total']}

\n"; + $html .= "

В браке: {$stats['married']}

\n"; + $html .= "

Разведены: {$stats['divorced']}

\n"; + $html .= "

Мужчин: {$stats['male']}

\n"; + $html .= "

Женщин: {$stats['female']}

\n"; + $html .= "

С детьми: {$stats['withChildren']}

\n"; + $html .= "

Общее количество детей: {$stats['totalChildren']}

\n"; + $html .= "
\n"; + + // Супруги в браке + if (!empty($marriedSpouses)) { + $html .= "
\n"; + $html .= "
💍 В браке (" . count($marriedSpouses) . " человек)
\n"; + $html .= "
\n"; + + foreach ($marriedSpouses as $spouse) { + $cssClass = $spouse->isMale() ? 'male' : 'female'; + $html .= "
\n"; + $html .= "
{$spouse->getDisplayName()}
\n"; + $html .= "
Пол: " . ($spouse->isMale() ? 'Мужской' : 'Женский') . "
\n"; + $html .= "
Дети: {$spouse->getChildrenInfo()}
\n"; + $html .= "
Отношения: {$spouse->relation}
\n"; + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + // Разведенные супруги + if (!empty($divorcedSpouses)) { + $html .= "
\n"; + $html .= "
💔 Разведены (" . count($divorcedSpouses) . " человек)
\n"; + $html .= "
\n"; + + foreach ($divorcedSpouses as $spouse) { + $cssClass = $spouse->isMale() ? 'male' : 'female'; + $html .= "
\n"; + $html .= "
{$spouse->getDisplayName()}
\n"; + $html .= "
Пол: " . ($spouse->isMale() ? 'Мужской' : 'Женский') . "
\n"; + if ($spouse->divorcedReason) { + $html .= "
Причина развода: {$spouse->divorcedReason}
\n"; + } + $html .= "
Дети: {$spouse->getChildrenInfo()}
\n"; + $html .= "
Отношения: {$spouse->relation}
\n"; + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + $html .= "
\n\n"; + + return $html; + } +} + +// Использование +$spouses = $person->spouses ?? []; +$report = new SpouseReport($spouses); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Отчет по супругам'); +file_put_contents('spouse_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в spouse_report.html\n"; +``` + +## Анализ супругов + +```php +function analyzeSpouses(array $spouses): array { + $analysis = [ + 'totalSpouses' => count($spouses), + 'maritalStatus' => [ + 'married' => 0, + 'divorced' => 0 + ], + 'genderDistribution' => [ + 'male' => 0, + 'female' => 0 + ], + 'childrenAnalysis' => [ + 'withChildren' => 0, + 'withoutChildren' => 0, + 'totalChildren' => 0, + 'averageChildren' => 0 + ], + 'divorceReasons' => [], + 'marriageDuration' => [] + ]; + + foreach ($spouses as $spouse) { + // Статус брака + if ($spouse->isMarried()) { + $analysis['maritalStatus']['married']++; + } else { + $analysis['maritalStatus']['divorced']++; + } + + // Пол + if ($spouse->isMale()) { + $analysis['genderDistribution']['male']++; + } else { + $analysis['genderDistribution']['female']++; + } + + // Дети + if ($spouse->children > 0) { + $analysis['childrenAnalysis']['withChildren']++; + $analysis['childrenAnalysis']['totalChildren'] += $spouse->children; + } else { + $analysis['childrenAnalysis']['withoutChildren']++; + } + + // Причины развода + if ($spouse->isDivorced() && $spouse->divorcedReason) { + if (!isset($analysis['divorceReasons'][$spouse->divorcedReason])) { + $analysis['divorceReasons'][$spouse->divorcedReason] = 0; + } + $analysis['divorceReasons'][$spouse->divorcedReason]++; + } + } + + // Среднее количество детей + if ($analysis['childrenAnalysis']['withChildren'] > 0) { + $analysis['childrenAnalysis']['averageChildren'] = round( + $analysis['childrenAnalysis']['totalChildren'] / $analysis['childrenAnalysis']['withChildren'], + 1 + ); + } + + return $analysis; +} + +// Использование +$spouses = $person->spouses ?? []; +$analysis = analyzeSpouses($spouses); + +echo "=== Анализ супругов ===\n"; +echo "Всего супругов: {$analysis['totalSpouses']}\n"; + +echo "\nСтатус брака:\n"; +echo "- В браке: {$analysis['maritalStatus']['married']}\n"; +echo "- Разведены: {$analysis['maritalStatus']['divorced']}\n"; + +echo "\nРаспределение по полу:\n"; +echo "- Мужчин: {$analysis['genderDistribution']['male']}\n"; +echo "- Женщин: {$analysis['genderDistribution']['female']}\n"; + +echo "\nАнализ детей:\n"; +echo "- С детьми: {$analysis['childrenAnalysis']['withChildren']}\n"; +echo "- Без детей: {$analysis['childrenAnalysis']['withoutChildren']}\n"; +echo "- Общее количество детей: {$analysis['childrenAnalysis']['totalChildren']}\n"; +echo "- Среднее количество детей: {$analysis['childrenAnalysis']['averageChildren']}\n"; + +if (!empty($analysis['divorceReasons'])) { + echo "\nПричины развода:\n"; + arsort($analysis['divorceReasons']); + foreach ($analysis['divorceReasons'] as $reason => $count) { + echo "- {$reason}: {$count} раз\n"; + } +} +``` + +## Связанные классы + +- [`Person`](person.md) - Модель персоны +- [`PersonService`](../services/person-service.md) - Сервис для работы с персонами diff --git a/docs/dev/notkinopoiskphp/models/person.md b/docs/dev/notkinopoiskphp/models/person.md new file mode 100644 index 0000000..a7ecce0 --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/person.md @@ -0,0 +1,292 @@ +# Person + +Модель персоны из Kinopoisk API. + +## Описание + +`Person` представляет полную информацию о персоне (актер, режиссер, сценарист и т.д.), полученную из Kinopoisk API. Содержит биографические данные, фильмографию, информацию о супругах и другие детали. + +### Основные возможности + +- Хранение полной информации о персоне в неизменяемом виде +- Создание объекта из массива данных API +- Удобные методы для работы с именами и отображением +- Поддержка фильмографии и информации о супругах + +**API Endpoint:** `/api/v1/staff/{id}` + +## Свойства + +### Основная информация + +- `$personId` (int|null) - Уникальный идентификатор персоны в Кинопоиске +- `$nameRu` (string|null) - Имя персоны на русском языке +- `$nameEn` (string|null) - Имя персоны на английском языке +- `$sex` (string|null) - Пол персоны +- `$posterUrl` (string|null) - URL постера/фотографии персоны + +### Физические характеристики + +- `$growth` (string|null) - Рост персоны +- `$age` (int|null) - Возраст персоны + +### Даты и места + +- `$birthday` (string|null) - Дата рождения +- `$death` (string|null) - Дата смерти (если применимо) +- `$birthplace` (string|null) - Место рождения +- `$deathplace` (string|null) - Место смерти + +### Профессиональная информация + +- `$profession` (string|null) - Профессия персоны +- `$hasAwards` (int|null) - Наличие наград +- `$facts` (string|null) - Интересные факты +- `$biography` (string|null) - Биография персоны + +### Связанные данные + +- `$spouses` (PersonSpouse[]) - Массив информации о супругах +- `$films` (PersonFilm[]) - Массив информации о фильмах +- `$total` (string|null) - Информация о количестве работ + +### Дополнительная информация + +- `$births` (string|null) - Информация о рождении +- `$deaths` (string|null) - Информация о смерти + +## Конструктор + +```php +public function __construct( + public readonly ?int $personId, + public readonly ?string $nameRu, + public readonly ?string $nameEn, + public readonly ?string $sex, + public readonly ?string $posterUrl, + public readonly ?string $growth, + public readonly ?string $birthday, + public readonly ?string $death, + public readonly ?int $age, + public readonly ?string $birthplace, + public readonly ?string $deathplace, + public readonly array $spouses, + public readonly ?int $hasAwards, + public readonly ?string $profession, + public readonly ?string $facts, + public readonly array $films, + public readonly ?string $biography, + public readonly ?string $births, + public readonly ?string $deaths, + public readonly ?string $total, +) +``` + +### Пример создания + +```php +$person = new Person( + personId: 12345, + nameRu: 'Том Круз', + nameEn: 'Tom Cruise', + posterUrl: 'https://...', + profession: 'Актер', + biography: 'Биография...', + spouses: [new PersonSpouse(...)] +); +``` + +## Методы + +### fromArray() + +Создает объект персоны из массива данных API. + +```php +public static function fromArray(array $data): self +``` + +#### Параметры + +- `$data` (array) - Массив данных от API + +#### Возвращаемое значение + +- `self` - Новый экземпляр Person + +#### Пример использования + +```php +$apiData = [ + 'personId' => 12345, + 'nameRu' => 'Том Круз', + 'nameEn' => 'Tom Cruise', + 'profession' => 'Актер', + 'spouses' => [ + [ + 'personId' => 67890, + 'name' => 'Кэти Холмс', + 'divorced' => true, + 'divorcedReason' => 'Развод' + ] + ], + 'films' => [ + [ + 'filmId' => 301, + 'nameRu' => 'Матрица', + 'professionKey' => 'ACTOR' + ] + ] +]; + +$person = Person::fromArray($apiData); +``` + +### getDisplayName() + +Получает отображаемое имя персоны. + +```php +public function getDisplayName(): string +``` + +#### Возвращаемое значение + +- `string` - Отображаемое имя персоны (приоритет: русское имя → английское имя → "Неизвестно") + +#### Пример использования + +```php +echo $person->getDisplayName(); // "Том Круз" или "Tom Cruise" или "Неизвестно" +``` + +### toArray() + +Преобразует объект персоны в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Массив с данными персоны + +#### Пример использования + +```php +$personArray = $person->toArray(); +echo json_encode($personArray); // JSON представление персоны +``` + +## Полный пример использования + +```php +persons; +$person = $personService->getById(12345); + +// Основная информация +echo "=== Информация о персоне ===\n"; +echo "ID: {$person->personId}\n"; +echo "Имя: " . $person->getDisplayName() . "\n"; +echo "Профессия: {$person->profession}\n"; +echo "Пол: {$person->sex}\n"; +echo "Возраст: {$person->age}\n"; + +// Физические характеристики +if ($person->growth) { + echo "Рост: {$person->growth}\n"; +} + +// Даты и места +if ($person->birthday) { + echo "Дата рождения: {$person->birthday}\n"; +} +if ($person->birthplace) { + echo "Место рождения: {$person->birthplace}\n"; +} +if ($person->death) { + echo "Дата смерти: {$person->death}\n"; +} +if ($person->deathplace) { + echo "Место смерти: {$person->deathplace}\n"; +} + +// Биография +if ($person->biography) { + echo "\n=== Биография ===\n"; + echo substr($person->biography, 0, 300) . "...\n"; +} + +// Интересные факты +if ($person->facts) { + echo "\n=== Интересные факты ===\n"; + echo $person->facts . "\n"; +} + +// Супруги +if (!empty($person->spouses)) { + echo "\n=== Супруги ===\n"; + foreach ($person->spouses as $spouse) { + echo "- {$spouse->name}"; + if ($spouse->divorced) { + echo " (разведены: {$spouse->divorcedReason})"; + } + echo "\n"; + } +} + +// Фильмография +if (!empty($person->films)) { + echo "\n=== Фильмография ===\n"; + foreach ($person->films as $film) { + echo "- {$film->getDisplayName()} ({$film->professionKey})\n"; + } +} + +// Награды +if ($person->hasAwards) { + echo "\n🏆 Имеет награды\n"; +} + +// Статистика +if ($person->total) { + echo "\n📊 {$person->total}\n"; +} +``` + +## Работа с фильмографией + +```php +// Фильтрация фильмов по профессии +$actorFilms = array_filter($person->films, fn($film) => $film->professionKey === 'ACTOR'); +$directorFilms = array_filter($person->films, fn($film) => $film->professionKey === 'DIRECTOR'); + +echo "Фильмов как актер: " . count($actorFilms) . "\n"; +echo "Фильмов как режиссер: " . count($directorFilms) . "\n"; + +// Сортировка фильмов по году +usort($person->films, fn($a, $b) => $a->year <=> $b->year); + +echo "Фильмы по хронологии:\n"; +foreach ($person->films as $film) { + echo "- {$film->year}: {$film->getDisplayName()}\n"; +} +``` + +## Связанные классы + +- [`PersonSpouse`](person-spouse.md) - Информация о супруге +- [`PersonFilm`](person-film.md) - Информация о фильме в фильмографии +- [`PersonService`](../services/person-service.md) - Сервис для работы с персонами diff --git a/docs/dev/notkinopoiskphp/models/premiere.md b/docs/dev/notkinopoiskphp/models/premiere.md new file mode 100644 index 0000000..0ea0880 --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/premiere.md @@ -0,0 +1,655 @@ +# Premiere + +Модель премьеры из Kinopoisk API. + +## Описание + +`Premiere` представляет информацию о премьере фильма, включая название, год, страны, жанры и дату премьеры в России. + +### Основные возможности + +- Хранение информации о премьере в неизменяемом виде +- Создание объекта из массива данных API +- Получение отображаемого названия фильма +- Доступ к метаданным премьеры + +**API Endpoint:** `/api/v2.2/films/premieres` + +## Свойства + +### Основная информация + +- `$kinopoiskId` (int) - Уникальный идентификатор фильма в Кинопоиске +- `$nameRu` (string|null) - Название фильма на русском языке +- `$nameEn` (string|null) - Название фильма на английском языке +- `$year` (int) - Год выпуска фильма +- `$posterUrl` (string) - URL постера фильма +- `$posterUrlPreview` (string) - URL превью постера фильма +- `$countries` (array) - Массив стран производства +- `$genres` (array) - Массив жанров фильма +- `$duration` (int|null) - Продолжительность фильма в минутах +- `$premiereRu` (string) - Дата премьеры в России + +## Конструктор + +```php +public function __construct( + public readonly int $kinopoiskId, + public readonly ?string $nameRu, + public readonly ?string $nameEn, + public readonly int $year, + public readonly string $posterUrl, + public readonly string $posterUrlPreview, + public readonly array $countries, + public readonly array $genres, + public readonly ?int $duration, + public readonly string $premiereRu, +) +``` + +### Пример создания + +```php +$premiere = new Premiere( + kinopoiskId: 12345, + nameRu: 'Новый фильм', + nameEn: 'New Movie', + year: 2023, + posterUrl: 'https://...', + posterUrlPreview: 'https://...', + countries: [$country1, $country2], + genres: [$genre1, $genre2], + duration: 120, + premiereRu: '2023-12-01' +); +``` + +## Методы + +### fromArray() + +Создает экземпляр премьеры из массива данных API. + +```php +public static function fromArray(array $data): self +``` + +#### Параметры + +- `$data` (array) - Массив данных премьеры от API + +#### Возвращаемое значение + +- `self` - Новый экземпляр премьеры + +#### Исключения + +- `\InvalidArgumentException` - Если данные имеют неверный формат + +#### Описание + +Статический метод для удобного создания объекта Premiere из данных, полученных от Kinopoisk API. Автоматически создает объекты Country и Genre для каждого элемента в соответствующих массивах. + +#### Пример использования + +```php +$apiData = [ + 'kinopoiskId' => 12345, + 'nameRu' => 'Новый фильм', + 'nameEn' => 'New Movie', + 'year' => 2023, + 'posterUrl' => 'https://...', + 'posterUrlPreview' => 'https://...', + 'countries' => [['country' => 'США'], ['country' => 'Великобритания']], + 'genres' => [['genre' => 'Боевик'], ['genre' => 'Драма']], + 'duration' => 120, + 'premiereRu' => '2023-12-01' +]; + +$premiere = Premiere::fromArray($apiData); +``` + +### getDisplayName() + +Получает отображаемое название премьеры. + +```php +public function getDisplayName(): string +``` + +#### Возвращаемое значение + +- `string` - Отображаемое название премьеры + +#### Описание + +Возвращает наиболее подходящее название для отображения пользователю. Приоритет: русское название → английское название → "Без названия" + +#### Пример использования + +```php +echo $premiere->getDisplayName(); // "Новый фильм" или "New Movie" или "Без названия" +``` + +### toArray() + +Преобразует объект премьеры в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Массив данных премьеры + +#### Пример использования + +```php +$array = $premiere->toArray(); +// [ +// 'kinopoiskId' => 12345, +// 'nameRu' => 'Новый фильм', +// 'nameEn' => 'New Movie', +// 'year' => 2023, +// 'posterUrl' => 'https://...', +// 'posterUrlPreview' => 'https://...', +// 'countries' => [['country' => 'США'], ['country' => 'Великобритания']], +// 'genres' => [['genre' => 'Боевик'], ['genre' => 'Драма']], +// 'duration' => 120, +// 'premiereRu' => '2023-12-01' +// ] +``` + +## Полный пример использования + +```php +films; +$premieres = $filmService->getPremieres(); + +echo "=== Премьеры фильмов ===\n"; + +// Общая статистика +echo "Всего премьер: " . count($premieres) . "\n\n"; + +// Вывод информации о премьерах +foreach ($premieres as $index => $premiere) { + echo ($index + 1) . ". {$premiere->getDisplayName()}\n"; + echo " ID: {$premiere->kinopoiskId}\n"; + echo " Год: {$premiere->year}\n"; + echo " Премьера в России: {$premiere->premiereRu}\n"; + + if ($premiere->duration) { + echo " Продолжительность: {$premiere->duration} мин.\n"; + } + + $countries = implode(', ', array_map('strval', $premiere->countries)); + if ($countries) { + echo " Страны: {$countries}\n"; + } + + $genres = implode(', ', array_map('strval', $premiere->genres)); + if ($genres) { + echo " Жанры: {$genres}\n"; + } + + echo " Постер: {$premiere->posterUrl}\n"; + echo "\n"; +} +``` + +## Работа с премьерами + +```php +// Функция для фильтрации премьер по году +function filterPremieresByYear(array $premieres, int $year): array { + return array_filter($premieres, fn($premiere) => $premiere->year === $year); +} + +// Функция для фильтрации премьер по жанру +function filterPremieresByGenre(array $premieres, string $genreName): array { + return array_filter($premieres, function($premiere) use ($genreName) { + foreach ($premiere->genres as $genre) { + if (stripos($genre->genre, $genreName) !== false) { + return true; + } + } + return false; + }); +} + +// Функция для фильтрации премьер по стране +function filterPremieresByCountry(array $premieres, string $countryName): array { + return array_filter($premieres, function($premiere) use ($countryName) { + foreach ($premiere->countries as $country) { + if (stripos($country->country, $countryName) !== false) { + return true; + } + } + return false; + }); +} + +// Функция для фильтрации премьер по дате +function filterPremieresByDate(array $premieres, string $startDate, string $endDate): array { + return array_filter($premieres, function($premiere) use ($startDate, $endDate) { + $premiereDate = $premiere->premiereRu; + return $premiereDate >= $startDate && $premiereDate <= $endDate; + }); +} + +// Функция для сортировки премьер по дате +function sortPremieresByDate(array $premieres, bool $ascending = true): array { + usort($premieres, function($a, $b) use ($ascending) { + $comparison = strcmp($a->premiereRu, $b->premiereRu); + return $ascending ? $comparison : -$comparison; + }); + + return $premieres; +} + +// Функция для сортировки премьер по году +function sortPremieresByYear(array $premieres, bool $ascending = true): array { + usort($premieres, function($a, $b) use ($ascending) { + $comparison = $a->year <=> $b->year; + return $ascending ? $comparison : -$comparison; + }); + + return $premieres; +} + +// Использование +$premieres = $filmService->getPremieres(); + +// Фильтрация +$premieres2023 = filterPremieresByYear($premieres, 2023); +$actionPremieres = filterPremieresByGenre($premieres, 'боевик'); +$usPremieres = filterPremieresByCountry($premieres, 'сша'); +$decemberPremieres = filterPremieresByDate($premieres, '2023-12-01', '2023-12-31'); + +echo "Премьер 2023 года: " . count($premieres2023) . "\n"; +echo "Боевиков: " . count($actionPremieres) . "\n"; +echo "Американских фильмов: " . count($usPremieres) . "\n"; +echo "Декабрьских премьер: " . count($decemberPremieres) . "\n"; + +// Сортировка +$sortedByDate = sortPremieresByDate($premieres, true); +$sortedByYear = sortPremieresByYear($premieres, false); +``` + +## Создание отчета по премьерам + +```php +class PremiereReport { + private array $premieres; + + public function __construct(array $premieres) { + $this->premieres = $premieres; + } + + public function getPremieres(): array { + return $this->premieres; + } + + public function getPremieresByYear(int $year): array { + return filterPremieresByYear($this->premieres, $year); + } + + public function getPremieresByGenre(string $genreName): array { + return filterPremieresByGenre($this->premieres, $genreName); + } + + public function getPremieresByCountry(string $countryName): array { + return filterPremieresByCountry($this->premieres, $countryName); + } + + public function getPremieresByDate(string $startDate, string $endDate): array { + return filterPremieresByDate($this->premieres, $startDate, $endDate); + } + + public function getUpcomingPremieres(int $days = 30): array { + $today = date('Y-m-d'); + $futureDate = date('Y-m-d', strtotime("+{$days} days")); + return filterPremieresByDate($this->premieres, $today, $futureDate); + } + + public function getRecentPremieres(int $days = 30): array { + $today = date('Y-m-d'); + $pastDate = date('Y-m-d', strtotime("-{$days} days")); + return filterPremieresByDate($this->premieres, $pastDate, $today); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО ПРЕМЬЕРАМ ===\n\n"; + + // Общая статистика + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего премьер: " . count($this->premieres) . "\n"; + + // Статистика по годам + $years = []; + foreach ($this->premieres as $premiere) { + $year = $premiere->year; + if (!isset($years[$year])) { + $years[$year] = 0; + } + $years[$year]++; + } + krsort($years); + + $report .= "Премьер по годам:\n"; + foreach ($years as $year => $count) { + $report .= "- {$year}: {$count} премьер\n"; + } + + $report .= "\n"; + + // Статистика по жанрам + $genres = []; + foreach ($this->premieres as $premiere) { + foreach ($premiere->genres as $genre) { + $genreName = $genre->genre; + if (!isset($genres[$genreName])) { + $genres[$genreName] = 0; + } + $genres[$genreName]++; + } + } + arsort($genres); + + $report .= "Премьер по жанрам:\n"; + foreach (array_slice($genres, 0, 10) as $genre => $count) { + $report .= "- {$genre}: {$count} премьер\n"; + } + + $report .= "\n"; + + // Статистика по странам + $countries = []; + foreach ($this->premieres as $premiere) { + foreach ($premiere->countries as $country) { + $countryName = $country->country; + if (!isset($countries[$countryName])) { + $countries[$countryName] = 0; + } + $countries[$countryName]++; + } + } + arsort($countries); + + $report .= "Премьер по странам:\n"; + foreach (array_slice($countries, 0, 10) as $country => $count) { + $report .= "- {$country}: {$count} премьер\n"; + } + + $report .= "\n"; + + // Ближайшие премьеры + $upcoming = $this->getUpcomingPremieres(30); + $report .= "🎬 БЛИЖАЙШИЕ ПРЕМЬЕРЫ (30 дней):\n"; + foreach (array_slice($upcoming, 0, 10) as $premiere) { + $report .= "• {$premiere->getDisplayName()} ({$premiere->premiereRu})\n"; + } + + $report .= "\n"; + + // Все премьеры + $report .= "📋 ВСЕ ПРЕМЬЕРЫ:\n"; + $sortedPremieres = sortPremieresByDate($this->premieres, true); + foreach ($sortedPremieres as $premiere) { + $report .= "\n• {$premiere->getDisplayName()}\n"; + $report .= " ID: {$premiere->kinopoiskId}\n"; + $report .= " Год: {$premiere->year}\n"; + $report .= " Премьера: {$premiere->premiereRu}\n"; + + if ($premiere->duration) { + $report .= " Продолжительность: {$premiere->duration} мин.\n"; + } + + $countries = implode(', ', array_map('strval', $premiere->countries)); + if ($countries) { + $report .= " Страны: {$countries}\n"; + } + + $genres = implode(', ', array_map('strval', $premiere->genres)); + if ($genres) { + $report .= " Жанры: {$genres}\n"; + } + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + // Статистика + $html .= "
\n"; + $html .= "

Общая статистика

\n"; + $html .= "

Всего премьер: " . count($this->premieres) . "

\n"; + $html .= "
\n"; + + // Ближайшие премьеры + $upcoming = $this->getUpcomingPremieres(30); + $html .= "
\n"; + $html .= "
🎬 Ближайшие премьеры (30 дней)
\n"; + $html .= "
\n"; + + foreach (array_slice($upcoming, 0, 12) as $premiere) { + $html .= "
\n"; + $html .= "{$premiere->getDisplayName()}\n"; + $html .= "
{$premiere->getDisplayName()}
\n"; + $html .= "
Год: {$premiere->year}
\n"; + $html .= "
Премьера: {$premiere->premiereRu}
\n"; + + if ($premiere->duration) { + $html .= "
Продолжительность: {$premiere->duration} мин.
\n"; + } + + $countries = implode(', ', array_map('strval', $premiere->countries)); + if ($countries) { + $html .= "
Страны: {$countries}
\n"; + } + + $genres = implode(', ', array_map('strval', $premiere->genres)); + if ($genres) { + $html .= "
Жанры: {$genres}
\n"; + } + + $html .= "
\n"; + } + + $html .= "
\n
\n"; + + // Все премьеры + $html .= "
\n"; + $html .= "
📋 Все премьеры
\n"; + $html .= "
\n"; + + $sortedPremieres = sortPremieresByDate($this->premieres, true); + foreach ($sortedPremieres as $premiere) { + $html .= "
\n"; + $html .= "{$premiere->getDisplayName()}\n"; + $html .= "
{$premiere->getDisplayName()}
\n"; + $html .= "
ID: {$premiere->kinopoiskId}
\n"; + $html .= "
Год: {$premiere->year}
\n"; + $html .= "
Премьера: {$premiere->premiereRu}
\n"; + + if ($premiere->duration) { + $html .= "
Продолжительность: {$premiere->duration} мин.
\n"; + } + + $countries = implode(', ', array_map('strval', $premiere->countries)); + if ($countries) { + $html .= "
Страны: {$countries}
\n"; + } + + $genres = implode(', ', array_map('strval', $premiere->genres)); + if ($genres) { + $html .= "
Жанры: {$genres}
\n"; + } + + $html .= "
\n"; + } + + $html .= "
\n
\n
\n\n"; + + return $html; + } +} + +// Использование +$premieres = $filmService->getPremieres(); +$report = new PremiereReport($premieres); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Отчет по премьерам'); +file_put_contents('premieres_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в premieres_report.html\n"; +``` + +## Анализ премьер + +```php +function analyzePremieres(array $premieres): array { + $analysis = [ + 'totalPremieres' => count($premieres), + 'years' => [], + 'genres' => [], + 'countries' => [], + 'durationDistribution' => [ + 'short' => 0, // <90 мин + 'medium' => 0, // 90-120 мин + 'long' => 0 // >120 мин + ], + 'monthlyDistribution' => [], + 'averageDuration' => 0 + ]; + + $totalDuration = 0; + $durationCount = 0; + + foreach ($premieres as $premiere) { + // Статистика по годам + $year = $premiere->year; + if (!isset($analysis['years'][$year])) { + $analysis['years'][$year] = 0; + } + $analysis['years'][$year]++; + + // Статистика по жанрам + foreach ($premiere->genres as $genre) { + $genreName = $genre->genre; + if (!isset($analysis['genres'][$genreName])) { + $analysis['genres'][$genreName] = 0; + } + $analysis['genres'][$genreName]++; + } + + // Статистика по странам + foreach ($premiere->countries as $country) { + $countryName = $country->country; + if (!isset($analysis['countries'][$countryName])) { + $analysis['countries'][$countryName] = 0; + } + $analysis['countries'][$countryName]++; + } + + // Статистика по продолжительности + if ($premiere->duration) { + $duration = $premiere->duration; + $totalDuration += $duration; + $durationCount++; + + if ($duration < 90) { + $analysis['durationDistribution']['short']++; + } elseif ($duration <= 120) { + $analysis['durationDistribution']['medium']++; + } else { + $analysis['durationDistribution']['long']++; + } + } + + // Статистика по месяцам + $month = date('F', strtotime($premiere->premiereRu)); + if (!isset($analysis['monthlyDistribution'][$month])) { + $analysis['monthlyDistribution'][$month] = 0; + } + $analysis['monthlyDistribution'][$month]++; + } + + // Средняя продолжительность + if ($durationCount > 0) { + $analysis['averageDuration'] = round($totalDuration / $durationCount); + } + + return $analysis; +} + +// Использование +$premieres = $filmService->getPremieres(); +$analysis = analyzePremieres($premieres); + +echo "=== Анализ премьер ===\n"; +echo "Всего премьер: {$analysis['totalPremieres']}\n"; +echo "Средняя продолжительность: {$analysis['averageDuration']} мин.\n"; + +echo "\nРаспределение по продолжительности:\n"; +echo "- Короткие (<90 мин): {$analysis['durationDistribution']['short']}\n"; +echo "- Средние (90-120 мин): {$analysis['durationDistribution']['medium']}\n"; +echo "- Длинные (>120 мин): {$analysis['durationDistribution']['long']}\n"; + +echo "\nТоп жанров:\n"; +arsort($analysis['genres']); +foreach (array_slice($analysis['genres'], 0, 5) as $genre => $count) { + echo "- {$genre}: {$count} премьер\n"; +} + +echo "\nТоп стран:\n"; +arsort($analysis['countries']); +foreach (array_slice($analysis['countries'], 0, 5) as $country => $count) { + echo "- {$country}: {$count} премьер\n"; +} + +echo "\nРаспределение по месяцам:\n"; +ksort($analysis['monthlyDistribution']); +foreach ($analysis['monthlyDistribution'] as $month => $count) { + echo "- {$month}: {$count} премьер\n"; +} +``` + +## Связанные классы + +- [`Country`](country.md) - Модель страны +- [`Genre`](genre.md) - Модель жанра +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/models/related-film.md b/docs/dev/notkinopoiskphp/models/related-film.md new file mode 100644 index 0000000..4746183 --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/related-film.md @@ -0,0 +1,450 @@ +# RelatedFilm + +Модель связанного фильма из Kinopoisk API. + +## Описание + +`RelatedFilm` представляет информацию о фильме, связанном с основным фильмом (похожие фильмы, сиквелы, приквелы и т.д.). + +### Основные возможности + +- Хранение информации о связанном фильме в неизменяемом виде +- Создание объекта из массива данных API +- Получение отображаемого названия фильма +- Определение типа связи между фильмами + +**API Endpoint:** `/api/v2.2/films/{id}/similars` + +## Свойства + +### Основная информация + +- `$filmId` (int) - Уникальный идентификатор фильма +- `$nameRu` (string|null) - Название на русском языке +- `$nameEn` (string|null) - Название на английском языке +- `$nameOriginal` (string|null) - Оригинальное название +- `$posterUrl` (string) - URL постера +- `$posterUrlPreview` (string) - URL превью постера +- `$relationType` (RelationType) - Тип связи + +## Конструктор + +```php +public function __construct( + public readonly int $filmId, + public readonly ?string $nameRu, + public readonly ?string $nameEn, + public readonly ?string $nameOriginal, + public readonly string $posterUrl, + public readonly string $posterUrlPreview, + public readonly RelationType $relationType, +) +``` + +### Пример создания + +```php +$relatedFilm = new RelatedFilm( + filmId: 301, + nameRu: 'Матрица', + nameEn: 'The Matrix', + nameOriginal: 'The Matrix', + posterUrl: 'https://example.com/poster.jpg', + posterUrlPreview: 'https://example.com/poster_small.jpg', + relationType: RelationType::SIMILAR +); +``` + +## Методы + +### fromArray() + +Создает экземпляр модели из массива данных API. + +```php +public static function fromArray(array $data): self +``` + +#### Параметры + +- `$data` (array) - Массив данных от API + +#### Возвращаемое значение + +- `self` - Экземпляр модели + +#### Исключения + +- `\InvalidArgumentException` - При некорректных данных + +#### Пример использования + +```php +$filmData = [ + 'filmId' => 301, + 'nameRu' => 'Матрица', + 'nameEn' => 'The Matrix', + 'nameOriginal' => 'The Matrix', + 'posterUrl' => 'https://example.com/poster.jpg', + 'posterUrlPreview' => 'https://example.com/poster_small.jpg', + 'relationType' => 'SIMILAR' +]; +$film = RelatedFilm::fromArray($filmData); +``` + +### getDisplayName() + +Получает отображаемое название фильма. + +```php +public function getDisplayName(): string +``` + +#### Возвращаемое значение + +- `string` - Отображаемое название фильма + +#### Описание + +Возвращает название фильма в приоритетном порядке: русское название → английское название → оригинальное название + +#### Пример использования + +```php +$displayName = $film->getDisplayName(); +echo "Название: $displayName"; +``` + +### toArray() + +Преобразует модель в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Массив с данными модели + +## Полный пример использования + +```php +films; +$relatedFilms = $filmService->getSimilarFilms(301); // ID фильма "Матрица" + +echo "=== Связанные фильмы ===\n"; + +// Общая статистика +echo "Всего связанных фильмов: " . count($relatedFilms) . "\n\n"; + +// Группировка по типам связи +$groupedByType = []; +foreach ($relatedFilms as $film) { + $type = $film->relationType->value; + if (!isset($groupedByType[$type])) { + $groupedByType[$type] = []; + } + $groupedByType[$type][] = $film; +} + +// Вывод по группам +foreach ($groupedByType as $type => $films) { + echo "📋 {$type} (" . count($films) . " фильмов):\n"; + foreach ($films as $film) { + echo " • {$film->getDisplayName()} (ID: {$film->filmId})\n"; + } + echo "\n"; +} +``` + +## Работа со связанными фильмами + +```php +// Функция для фильтрации по типу связи +function filterByRelationType(array $relatedFilms, RelationType $type): array { + return array_filter($relatedFilms, fn($film) => $film->relationType === $type); +} + +// Функция для получения фильмов по названию +function findFilmsByName(array $relatedFilms, string $name): array { + return array_filter($relatedFilms, function($film) use ($name) { + $displayName = strtolower($film->getDisplayName()); + $searchName = strtolower($name); + return strpos($displayName, $searchName) !== false; + }); +} + +// Функция для группировки по типу связи +function groupByRelationType(array $relatedFilms): array { + $grouped = []; + foreach ($relatedFilms as $film) { + $type = $film->relationType->value; + if (!isset($grouped[$type])) { + $grouped[$type] = []; + } + $grouped[$type][] = $film; + } + return $grouped; +} + +// Функция для получения статистики по типам связи +function getRelationTypeStats(array $relatedFilms): array { + $stats = []; + foreach ($relatedFilms as $film) { + $type = $film->relationType->value; + if (!isset($stats[$type])) { + $stats[$type] = 0; + } + $stats[$type]++; + } + return $stats; +} + +// Использование +$relatedFilms = $filmService->getSimilarFilms(301); + +// Фильтрация +$similarFilms = filterByRelationType($relatedFilms, RelationType::SIMILAR); +$sequels = filterByRelationType($relatedFilms, RelationType::SEQUEL); +$prequels = filterByRelationType($relatedFilms, RelationType::PREQUEL); + +echo "Похожих фильмов: " . count($similarFilms) . "\n"; +echo "Сиквелов: " . count($sequels) . "\n"; +echo "Приквелов: " . count($prequels) . "\n"; + +// Поиск по названию +$matrixFilms = findFilmsByName($relatedFilms, 'матрица'); +echo "Фильмов с 'матрица' в названии: " . count($matrixFilms) . "\n"; + +// Статистика +$stats = getRelationTypeStats($relatedFilms); +echo "\nСтатистика по типам связи:\n"; +foreach ($stats as $type => $count) { + echo "- {$type}: {$count} фильмов\n"; +} +``` + +## Создание отчета по связанным фильмам + +```php +class RelatedFilmReport { + private array $relatedFilms; + + public function __construct(array $relatedFilms) { + $this->relatedFilms = $relatedFilms; + } + + public function getRelatedFilms(): array { + return $this->relatedFilms; + } + + public function getByRelationType(RelationType $type): array { + return filterByRelationType($this->relatedFilms, $type); + } + + public function getGroupedByType(): array { + return groupByRelationType($this->relatedFilms); + } + + public function getStats(): array { + return getRelationTypeStats($this->relatedFilms); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО СВЯЗАННЫМ ФИЛЬМАМ ===\n\n"; + + $stats = $this->getStats(); + $grouped = $this->getGroupedByType(); + + // Общая статистика + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего связанных фильмов: " . count($this->relatedFilms) . "\n\n"; + + // Статистика по типам связи + $report .= "📋 СТАТИСТИКА ПО ТИПАМ СВЯЗИ:\n"; + foreach ($stats as $type => $count) { + $percentage = round(($count / count($this->relatedFilms)) * 100, 1); + $report .= "• {$type}: {$count} фильмов ({$percentage}%)\n"; + } + + $report .= "\n"; + + // Детали по типам связи + foreach ($grouped as $type => $films) { + $report .= "🎬 {$type} (" . count($films) . " фильмов):\n"; + foreach ($films as $film) { + $report .= " • {$film->getDisplayName()}\n"; + $report .= " ID: {$film->filmId}\n"; + if ($film->nameOriginal && $film->nameOriginal !== $film->getDisplayName()) { + $report .= " Оригинальное название: {$film->nameOriginal}\n"; + } + } + $report .= "\n"; + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $stats = $this->getStats(); + $grouped = $this->getGroupedByType(); + + // Статистика + $html .= "
\n"; + $html .= "

Общая статистика

\n"; + $html .= "

Всего связанных фильмов: " . count($this->relatedFilms) . "

\n"; + $html .= "
\n"; + + // Статистика по типам связи + $html .= "
\n"; + $html .= "
📋 Статистика по типам связи
\n"; + foreach ($stats as $type => $count) { + $percentage = round(($count / count($this->relatedFilms)) * 100, 1); + $html .= "

{$type}: {$count} фильмов ({$percentage}%)

\n"; + } + $html .= "
\n"; + + // Фильмы по типам связи + foreach ($grouped as $type => $films) { + $cssClass = strtolower($type); + + $html .= "
\n"; + $html .= "
🎬 {$type} (" . count($films) . " фильмов)
\n"; + $html .= "
\n"; + + foreach ($films as $film) { + $html .= "
\n"; + $html .= "{$film->getDisplayName()}\n"; + $html .= "
{$film->getDisplayName()}
\n"; + $html .= "
ID: {$film->filmId}
\n"; + if ($film->nameOriginal && $film->nameOriginal !== $film->getDisplayName()) { + $html .= "
Оригинальное название: {$film->nameOriginal}
\n"; + } + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + $html .= "
\n\n"; + + return $html; + } +} + +// Использование +$relatedFilms = $filmService->getSimilarFilms(301); +$report = new RelatedFilmReport($relatedFilms); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Отчет по связанным фильмам'); +file_put_contents('related_films_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в related_films_report.html\n"; +``` + +## Анализ связанных фильмов + +```php +function analyzeRelatedFilms(array $relatedFilms): array { + $analysis = [ + 'totalFilms' => count($relatedFilms), + 'relationTypeDistribution' => [], + 'nameAnalysis' => [ + 'hasRussianName' => 0, + 'hasEnglishName' => 0, + 'hasOriginalName' => 0, + 'nameLengths' => [] + ] + ]; + + foreach ($relatedFilms as $film) { + // Статистика по типам связи + $type = $film->relationType->value; + if (!isset($analysis['relationTypeDistribution'][$type])) { + $analysis['relationTypeDistribution'][$type] = 0; + } + $analysis['relationTypeDistribution'][$type]++; + + // Анализ названий + if ($film->nameRu) { + $analysis['nameAnalysis']['hasRussianName']++; + } + if ($film->nameEn) { + $analysis['nameAnalysis']['hasEnglishName']++; + } + if ($film->nameOriginal) { + $analysis['nameAnalysis']['hasOriginalName']++; + } + + $displayName = $film->getDisplayName(); + $analysis['nameAnalysis']['nameLengths'][] = strlen($displayName); + } + + // Средняя длина названия + if (!empty($analysis['nameAnalysis']['nameLengths'])) { + $analysis['nameAnalysis']['averageNameLength'] = round(array_sum($analysis['nameAnalysis']['nameLengths']) / count($analysis['nameAnalysis']['nameLengths'])); + } + + return $analysis; +} + +// Использование +$relatedFilms = $filmService->getSimilarFilms(301); +$analysis = analyzeRelatedFilms($relatedFilms); + +echo "=== Анализ связанных фильмов ===\n"; +echo "Всего фильмов: {$analysis['totalFilms']}\n"; + +echo "\nРаспределение по типам связи:\n"; +foreach ($analysis['relationTypeDistribution'] as $type => $count) { + $percentage = round(($count / $analysis['totalFilms']) * 100, 1); + echo "- {$type}: {$count} фильмов ({$percentage}%)\n"; +} + +echo "\nАнализ названий:\n"; +echo "- С русским названием: {$analysis['nameAnalysis']['hasRussianName']}\n"; +echo "- С английским названием: {$analysis['nameAnalysis']['hasEnglishName']}\n"; +echo "- С оригинальным названием: {$analysis['nameAnalysis']['hasOriginalName']}\n"; +echo "- Средняя длина названия: {$analysis['nameAnalysis']['averageNameLength']} символов\n"; +``` + +## Связанные классы + +- [`RelationType`](../enums/relation-type.md) - Типы связи между фильмами +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/models/review.md b/docs/dev/notkinopoiskphp/models/review.md new file mode 100644 index 0000000..d162271 --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/review.md @@ -0,0 +1,323 @@ +# Review + +Модель рецензии из Kinopoisk API. + +## Описание + +`Review` представляет информацию о рецензии на фильм, включая автора, дату, рейтинг и содержание рецензии. + +### Основные возможности + +- Хранение информации о рецензии в неизменяемом виде +- Создание объекта из массива данных API +- Доступ к рейтингу и метаданным рецензии + +**API Endpoint:** `/api/v2.2/films/{id}/reviews` + +## Свойства + +### Основная информация + +- `$kinopoiskId` (int) - Уникальный идентификатор рецензии в Кинопоиске +- `$type` (ReviewType) - Тип рецензии (POSITIVE, NEGATIVE, NEUTRAL) +- `$date` (string) - Дата публикации рецензии +- `$author` (string) - Автор рецензии + +### Содержание рецензии + +- `$title` (string|null) - Заголовок рецензии +- `$description` (string) - Содержание рецензии + +### Рейтинги + +- `$positiveRating` (int) - Количество положительных оценок +- `$negativeRating` (int) - Количество отрицательных оценок + +## Конструктор + +```php +public function __construct( + public readonly int $kinopoiskId, + public readonly ReviewType $type, + public readonly string $date, + public readonly int $positiveRating, + public readonly int $negativeRating, + public readonly string $author, + public readonly ?string $title, + public readonly string $description, +) +``` + +### Пример создания + +```php +$review = new Review( + kinopoiskId: 12345, + type: ReviewType::POSITIVE, + date: '2023-01-15', + positiveRating: 85, + negativeRating: 15, + author: 'Кинокритик', + title: 'Отличный фильм', + description: 'Подробный анализ фильма...' +); +``` + +## Методы + +### fromArray() + +Создает экземпляр рецензии из массива данных API. + +```php +public static function fromArray(array $data): self +``` + +#### Параметры + +- `$data` (array) - Массив данных рецензии от API + +#### Возвращаемое значение + +- `self` - Новый экземпляр рецензии + +#### Исключения + +- `\InvalidArgumentException` - Если данные имеют неверный формат + +#### Пример использования + +```php +$apiData = [ + 'kinopoiskId' => 12345, + 'type' => 'POSITIVE', + 'date' => '2023-01-15', + 'positiveRating' => 85, + 'negativeRating' => 15, + 'author' => 'Кинокритик', + 'title' => 'Отличный фильм', + 'description' => 'Подробный анализ фильма...' +]; + +$review = Review::fromArray($apiData); +``` + +### toArray() + +Преобразует объект рецензии в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Массив с данными рецензии + +#### Пример использования + +```php +$reviewArray = $review->toArray(); +echo json_encode($reviewArray); // JSON представление рецензии +``` + +## Полный пример использования + +```php +films; +$reviews = $filmService->getReviews(301); // Матрица + +echo "=== Рецензии на фильм 'Матрица' ===\n"; + +// Группировка по типам +$positiveReviews = []; +$negativeReviews = []; +$neutralReviews = []; + +foreach ($reviews as $review) { + switch ($review->type) { + case ReviewType::POSITIVE: + $positiveReviews[] = $review; + break; + case ReviewType::NEGATIVE: + $negativeReviews[] = $review; + break; + case ReviewType::NEUTRAL: + $neutralReviews[] = $review; + break; + } +} + +// Вывод положительных рецензий +if (!empty($positiveReviews)) { + echo "\n👍 Положительные рецензии (" . count($positiveReviews) . "):\n"; + foreach (array_slice($positiveReviews, 0, 3) as $review) { + echo "📝 {$review->title}\n"; + echo "👤 Автор: {$review->author}\n"; + echo "📅 Дата: {$review->date}\n"; + echo "📊 Рейтинг: {$review->positiveRating}/{$review->negativeRating}\n"; + echo "📄 " . substr($review->description, 0, 200) . "...\n"; + echo "---\n"; + } +} + +// Вывод отрицательных рецензий +if (!empty($negativeReviews)) { + echo "\n👎 Отрицательные рецензии (" . count($negativeReviews) . "):\n"; + foreach (array_slice($negativeReviews, 0, 2) as $review) { + echo "📝 {$review->title}\n"; + echo "👤 Автор: {$review->author}\n"; + echo "📅 Дата: {$review->date}\n"; + echo "📊 Рейтинг: {$review->positiveRating}/{$review->negativeRating}\n"; + echo "📄 " . substr($review->description, 0, 200) . "...\n"; + echo "---\n"; + } +} + +// Вывод нейтральных рецензий +if (!empty($neutralReviews)) { + echo "\n😐 Нейтральные рецензии (" . count($neutralReviews) . "):\n"; + foreach (array_slice($neutralReviews, 0, 2) as $review) { + echo "📝 {$review->title}\n"; + echo "👤 Автор: {$review->author}\n"; + echo "📅 Дата: {$review->date}\n"; + echo "📊 Рейтинг: {$review->positiveRating}/{$review->negativeRating}\n"; + echo "📄 " . substr($review->description, 0, 200) . "...\n"; + echo "---\n"; + } +} +``` + +## Анализ рецензий + +```php +function analyzeReviews(array $reviews): array { + $analysis = [ + 'total' => count($reviews), + 'positive' => 0, + 'negative' => 0, + 'neutral' => 0, + 'avgPositiveRating' => 0, + 'avgNegativeRating' => 0, + 'topAuthors' => [] + ]; + + $authorStats = []; + $totalPositive = 0; + $totalNegative = 0; + + foreach ($reviews as $review) { + // Подсчет типов + switch ($review->type) { + case ReviewType::POSITIVE: + $analysis['positive']++; + break; + case ReviewType::NEGATIVE: + $analysis['negative']++; + break; + case ReviewType::NEUTRAL: + $analysis['neutral']++; + break; + } + + // Подсчет рейтингов + $totalPositive += $review->positiveRating; + $totalNegative += $review->negativeRating; + + // Статистика авторов + if (!isset($authorStats[$review->author])) { + $authorStats[$review->author] = 0; + } + $authorStats[$review->author]++; + } + + // Средние рейтинги + if ($analysis['total'] > 0) { + $analysis['avgPositiveRating'] = round($totalPositive / $analysis['total'], 1); + $analysis['avgNegativeRating'] = round($totalNegative / $analysis['total'], 1); + } + + // Топ авторов + arsort($authorStats); + $analysis['topAuthors'] = array_slice($authorStats, 0, 5, true); + + return $analysis; +} + +// Использование +$reviews = $filmService->getReviews(301); +$analysis = analyzeReviews($reviews); + +echo "=== Анализ рецензий ===\n"; +echo "Всего рецензий: {$analysis['total']}\n"; +echo "Положительных: {$analysis['positive']}\n"; +echo "Отрицательных: {$analysis['negative']}\n"; +echo "Нейтральных: {$analysis['neutral']}\n"; +echo "Средний положительный рейтинг: {$analysis['avgPositiveRating']}\n"; +echo "Средний отрицательный рейтинг: {$analysis['avgNegativeRating']}\n"; + +echo "\nТоп авторов:\n"; +foreach ($analysis['topAuthors'] as $author => $count) { + echo "- {$author}: {$count} рецензий\n"; +} +``` + +## Фильтрация и поиск рецензий + +```php +function filterReviews(array $reviews, ReviewType $type = null, string $author = null): array { + return array_filter($reviews, function($review) use ($type, $author) { + if ($type && $review->type !== $type) { + return false; + } + + if ($author && stripos($review->author, $author) === false) { + return false; + } + + return true; + }); +} + +function searchReviewsByContent(array $reviews, string $keyword): array { + $keyword = strtolower($keyword); + + return array_filter($reviews, function($review) use ($keyword) { + return strpos(strtolower($review->description), $keyword) !== false || + strpos(strtolower($review->title ?? ''), $keyword) !== false; + }); +} + +// Использование +$reviews = $filmService->getReviews(301); + +// Фильтрация по типу +$positiveOnly = filterReviews($reviews, ReviewType::POSITIVE); +echo "Только положительные рецензии: " . count($positiveOnly) . "\n"; + +// Фильтрация по автору +$criticReviews = filterReviews($reviews, null, 'Критик'); +echo "Рецензии критиков: " . count($criticReviews) . "\n"; + +// Поиск по содержанию +$matrixReviews = searchReviewsByContent($reviews, 'матрица'); +echo "Рецензии со словом 'матрица': " . count($matrixReviews) . "\n"; +``` + +## Связанные классы + +- [`ReviewType`](../enums/review-type.md) - Типы рецензий +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами +- [`Film`](film.md) - Модель фильма diff --git a/docs/dev/notkinopoiskphp/models/season.md b/docs/dev/notkinopoiskphp/models/season.md new file mode 100644 index 0000000..55aa6a8 --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/season.md @@ -0,0 +1,508 @@ +# Season + +Модель сезона сериала из Kinopoisk API. + +## Описание + +`Season` представляет информацию о сезоне сериала, включая номер сезона и массив эпизодов, входящих в этот сезон. + +### Основные возможности + +- Хранение информации о сезоне в неизменяемом виде +- Создание объекта из массива данных API +- Доступ к эпизодам сезона + +**API Endpoint:** `/api/v2.2/films/{id}/seasons` + +## Свойства + +### Основная информация + +- `$number` (int) - Номер сезона +- `$episodes` (array) - Массив эпизодов сезона + +## Конструктор + +```php +public function __construct( + public readonly int $number, + public readonly array $episodes, +) +``` + +### Пример создания + +```php +$season = new Season( + number: 1, + episodes: [$episode1, $episode2, $episode3] +); +``` + +## Методы + +### fromArray() + +Создает экземпляр сезона из массива данных API. + +```php +public static function fromArray(array $data): self +``` + +#### Параметры + +- `$data` (array) - Массив данных сезона от API + +#### Возвращаемое значение + +- `self` - Новый экземпляр сезона + +#### Исключения + +- `\InvalidArgumentException` - Если данные имеют неверный формат + +#### Описание + +Статический метод для удобного создания объекта Season из данных, полученных от Kinopoisk API. Автоматически создает объекты Episode для каждого эпизода в сезоне. + +#### Пример использования + +```php +$apiData = [ + 'number' => 1, + 'episodes' => [ + ['number' => 1, 'name' => 'Пилот', ...], + ['number' => 2, 'name' => 'Второй эпизод', ...] + ] +]; + +$season = Season::fromArray($apiData); +``` + +### toArray() + +Преобразует объект сезона в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Массив с данными сезона + +#### Пример использования + +```php +$seasonArray = $season->toArray(); +echo json_encode($seasonArray); // JSON представление сезона +``` + +## Полный пример использования + +```php +films; +$seasons = $filmService->getSeasons(123); // ID сериала + +echo "=== Сезоны сериала ===\n"; + +// Обработка всех сезонов +foreach ($seasons as $season) { + echo "\n📺 Сезон {$season->number} (" . count($season->episodes) . " эпизодов):\n"; + + if (!empty($season->episodes)) { + foreach ($season->episodes as $episode) { + echo " • S{$episode->seasonNumber}E{$episode->episodeNumber}: {$episode->getDisplayName()}\n"; + + if ($episode->releaseDate) { + echo " Дата выхода: {$episode->releaseDate}\n"; + } + } + } else { + echo " Нет информации об эпизодах\n"; + } +} +``` + +## Работа с сезонами + +```php +// Функция для получения сезона по номеру +function getSeasonByNumber(array $seasons, int $seasonNumber): ?Season { + foreach ($seasons as $season) { + if ($season->number === $seasonNumber) { + return $season; + } + } + return null; +} + +// Функция для получения статистики по сезонам +function getSeasonsStatistics(array $seasons): array { + $stats = [ + 'totalSeasons' => count($seasons), + 'totalEpisodes' => 0, + 'episodesPerSeason' => [], + 'averageEpisodesPerSeason' => 0, + 'seasonsWithEpisodes' => 0, + 'seasonsWithoutEpisodes' => 0 + ]; + + foreach ($seasons as $season) { + $episodeCount = count($season->episodes); + $stats['totalEpisodes'] += $episodeCount; + $stats['episodesPerSeason'][$season->number] = $episodeCount; + + if ($episodeCount > 0) { + $stats['seasonsWithEpisodes']++; + } else { + $stats['seasonsWithoutEpisodes']++; + } + } + + if ($stats['totalSeasons'] > 0) { + $stats['averageEpisodesPerSeason'] = round($stats['totalEpisodes'] / $stats['totalSeasons'], 1); + } + + return $stats; +} + +// Функция для получения самого длинного сезона +function getLongestSeason(array $seasons): ?Season { + $longestSeason = null; + $maxEpisodes = 0; + + foreach ($seasons as $season) { + $episodeCount = count($season->episodes); + if ($episodeCount > $maxEpisodes) { + $maxEpisodes = $episodeCount; + $longestSeason = $season; + } + } + + return $longestSeason; +} + +// Функция для получения сезонов с эпизодами +function getSeasonsWithEpisodes(array $seasons): array { + return array_filter($seasons, fn($season) => !empty($season->episodes)); +} + +// Функция для получения всех эпизодов из всех сезонов +function getAllEpisodes(array $seasons): array { + $allEpisodes = []; + + foreach ($seasons as $season) { + $allEpisodes = array_merge($allEpisodes, $season->episodes); + } + + return $allEpisodes; +} + +// Использование +$seasons = $filmService->getSeasons(123); + +// Получение конкретного сезона +$season1 = getSeasonByNumber($seasons, 1); +if ($season1) { + echo "Сезон 1 содержит " . count($season1->episodes) . " эпизодов\n"; +} + +// Статистика по сезонам +$stats = getSeasonsStatistics($seasons); +echo "Всего сезонов: {$stats['totalSeasons']}\n"; +echo "Всего эпизодов: {$stats['totalEpisodes']}\n"; +echo "Среднее количество эпизодов на сезон: {$stats['averageEpisodesPerSeason']}\n"; +echo "Сезонов с эпизодами: {$stats['seasonsWithEpisodes']}\n"; +echo "Сезонов без эпизодов: {$stats['seasonsWithoutEpisodes']}\n"; + +// Самый длинный сезон +$longestSeason = getLongestSeason($seasons); +if ($longestSeason) { + echo "Самый длинный сезон: {$longestSeason->number} (" . count($longestSeason->episodes) . " эпизодов)\n"; +} + +// Все эпизоды +$allEpisodes = getAllEpisodes($seasons); +echo "Всего эпизодов во всех сезонах: " . count($allEpisodes) . "\n"; +``` + +## Создание отчета по сезонам + +```php +class SeasonsReport { + private array $seasons; + + public function __construct(array $seasons) { + $this->seasons = $seasons; + } + + public function getSeasonByNumber(int $seasonNumber): ?Season { + return getSeasonByNumber($this->seasons, $seasonNumber); + } + + public function getStatistics(): array { + return getSeasonsStatistics($this->seasons); + } + + public function getLongestSeason(): ?Season { + return getLongestSeason($this->seasons); + } + + public function getSeasonsWithEpisodes(): array { + return getSeasonsWithEpisodes($this->seasons); + } + + public function getAllEpisodes(): array { + return getAllEpisodes($this->seasons); + } + + public function getEpisodesBySeason(int $seasonNumber): array { + $season = $this->getSeasonByNumber($seasonNumber); + return $season ? $season->episodes : []; + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО СЕЗОНАМ ===\n\n"; + + $stats = $this->getStatistics(); + + // Общая статистика + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего сезонов: {$stats['totalSeasons']}\n"; + $report .= "Всего эпизодов: {$stats['totalEpisodes']}\n"; + $report .= "Среднее количество эпизодов на сезон: {$stats['averageEpisodesPerSeason']}\n"; + $report .= "Сезонов с эпизодами: {$stats['seasonsWithEpisodes']}\n"; + $report .= "Сезонов без эпизодов: {$stats['seasonsWithoutEpisodes']}\n\n"; + + // Статистика по сезонам + $report .= "📺 СТАТИСТИКА ПО СЕЗОНАМ:\n"; + foreach ($stats['episodesPerSeason'] as $seasonNumber => $episodeCount) { + $report .= "• Сезон {$seasonNumber}: {$episodeCount} эпизодов\n"; + } + + // Самый длинный сезон + $longestSeason = $this->getLongestSeason(); + if ($longestSeason) { + $report .= "\n🏆 САМЫЙ ДЛИННЫЙ СЕЗОН:\n"; + $report .= "Сезон {$longestSeason->number}: " . count($longestSeason->episodes) . " эпизодов\n"; + } + + // Детали по сезонам + $report .= "\n📋 ДЕТАЛИ ПО СЕЗОНАМ:\n"; + foreach ($this->seasons as $season) { + $report .= "\nСезон {$season->number}:\n"; + $report .= " Количество эпизодов: " . count($season->episodes) . "\n"; + + if (!empty($season->episodes)) { + $report .= " Эпизоды:\n"; + foreach (array_slice($season->episodes, 0, 5) as $episode) { + $report .= " • S{$episode->seasonNumber}E{$episode->episodeNumber}: {$episode->getDisplayName()}\n"; + } + + if (count($season->episodes) > 5) { + $report .= " ... и еще " . (count($season->episodes) - 5) . " эпизодов\n"; + } + } + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $stats = $this->getStatistics(); + $longestSeason = $this->getLongestSeason(); + + // Общая статистика + $html .= "
\n"; + $html .= "

Общая статистика

\n"; + $html .= "

Всего сезонов: {$stats['totalSeasons']}

\n"; + $html .= "

Всего эпизодов: {$stats['totalEpisodes']}

\n"; + $html .= "

Среднее количество эпизодов на сезон: {$stats['averageEpisodesPerSeason']}

\n"; + $html .= "

Сезонов с эпизодами: {$stats['seasonsWithEpisodes']}

\n"; + $html .= "

Сезонов без эпизодов: {$stats['seasonsWithoutEpisodes']}

\n"; + $html .= "
\n"; + + // Самый длинный сезон + if ($longestSeason) { + $html .= "
\n"; + $html .= "
🏆 Самый длинный сезон: {$longestSeason->number}
\n"; + $html .= "

Количество эпизодов: " . count($longestSeason->episodes) . "

\n"; + $html .= "
\n"; + } + + // Детали по сезонам + $html .= "
\n"; + $html .= "
Детали по сезонам
\n"; + + foreach ($this->seasons as $season) { + $isLongest = $longestSeason && $season->number === $longestSeason->number; + $cssClass = $isLongest ? 'highlight' : ''; + + $html .= "
\n"; + $html .= "
Сезон {$season->number} (" . count($season->episodes) . " эпизодов)
\n"; + + if (!empty($season->episodes)) { + foreach (array_slice($season->episodes, 0, 10) as $episode) { + $html .= "
\n"; + $html .= "
S{$episode->seasonNumber}E{$episode->episodeNumber}: {$episode->getDisplayName()}
\n"; + + if ($episode->releaseDate) { + $html .= "
Дата выхода: {$episode->releaseDate}
\n"; + } + + if ($episode->synopsis) { + $html .= "
Синопсис: " . htmlspecialchars(substr($episode->synopsis, 0, 100)) . "...
\n"; + } + + $html .= "
\n"; + } + + if (count($season->episodes) > 10) { + $html .= "
\n"; + $html .= "
... и еще " . (count($season->episodes) - 10) . " эпизодов
\n"; + $html .= "
\n"; + } + } else { + $html .= "

Нет информации об эпизодах

\n"; + } + + $html .= "
\n"; + } + + $html .= "
\n
\n\n"; + + return $html; + } +} + +// Использование +$seasons = $filmService->getSeasons(123); +$report = new SeasonsReport($seasons); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Отчет по сезонам сериала'); +file_put_contents('seasons_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в seasons_report.html\n"; +``` + +## Анализ сезонов + +```php +function analyzeSeasons(array $seasons): array { + $analysis = [ + 'totalSeasons' => count($seasons), + 'totalEpisodes' => 0, + 'seasonsWithEpisodes' => 0, + 'seasonsWithoutEpisodes' => 0, + 'episodesPerSeason' => [], + 'averageEpisodesPerSeason' => 0, + 'longestSeason' => null, + 'shortestSeason' => null, + 'seasonDistribution' => [] + ]; + + $episodeCounts = []; + + foreach ($seasons as $season) { + $episodeCount = count($season->episodes); + $analysis['totalEpisodes'] += $episodeCount; + $analysis['episodesPerSeason'][$season->number] = $episodeCount; + $episodeCounts[$season->number] = $episodeCount; + + if ($episodeCount > 0) { + $analysis['seasonsWithEpisodes']++; + } else { + $analysis['seasonsWithoutEpisodes']++; + } + } + + // Вычисление среднего + if ($analysis['totalSeasons'] > 0) { + $analysis['averageEpisodesPerSeason'] = round($analysis['totalEpisodes'] / $analysis['totalSeasons'], 1); + } + + // Самый длинный и короткий сезоны + if (!empty($episodeCounts)) { + $maxEpisodes = max($episodeCounts); + $minEpisodes = min($episodeCounts); + + foreach ($seasons as $season) { + if (count($season->episodes) === $maxEpisodes) { + $analysis['longestSeason'] = $season; + } + if (count($season->episodes) === $minEpisodes) { + $analysis['shortestSeason'] = $season; + } + } + } + + // Распределение по сезонам + foreach ($episodeCounts as $seasonNumber => $count) { + $analysis['seasonDistribution'][$seasonNumber] = round(($count / $analysis['totalEpisodes']) * 100, 1); + } + + return $analysis; +} + +// Использование +$seasons = $filmService->getSeasons(123); +$analysis = analyzeSeasons($seasons); + +echo "=== Анализ сезонов ===\n"; +echo "Всего сезонов: {$analysis['totalSeasons']}\n"; +echo "Всего эпизодов: {$analysis['totalEpisodes']}\n"; +echo "Среднее количество эпизодов на сезон: {$analysis['averageEpisodesPerSeason']}\n"; +echo "Сезонов с эпизодами: {$analysis['seasonsWithEpisodes']}\n"; +echo "Сезонов без эпизодов: {$analysis['seasonsWithoutEpisodes']}\n"; + +if ($analysis['longestSeason']) { + echo "Самый длинный сезон: {$analysis['longestSeason']->number} (" . count($analysis['longestSeason']->episodes) . " эпизодов)\n"; +} + +if ($analysis['shortestSeason']) { + echo "Самый короткий сезон: {$analysis['shortestSeason']->number} (" . count($analysis['shortestSeason']->episodes) . " эпизодов)\n"; +} + +echo "\nРаспределение эпизодов по сезонам:\n"; +foreach ($analysis['seasonDistribution'] as $season => $percentage) { + echo "- Сезон {$season}: {$percentage}%\n"; +} +``` + +## Связанные классы + +- [`Episode`](episode.md) - Модель эпизода +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/models/staff.md b/docs/dev/notkinopoiskphp/models/staff.md new file mode 100644 index 0000000..959eea6 --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/staff.md @@ -0,0 +1,340 @@ +# Staff + +Модель персонала фильма из Kinopoisk API. + +## Описание + +`Staff` представляет информацию об участнике съемочной группы фильма: актерах, режиссерах, сценаристах, операторах и других специалистах. Содержит данные о роли, профессии и описании работы в фильме. + +### Основные возможности + +- Хранение информации о персонале в неизменяемом виде +- Создание объекта из массива данных API +- Удобные методы для определения профессии +- Получение отображаемого имени + +**API Endpoint:** `/api/v1/staff` + +## Свойства + +### Основная информация + +- `$staffId` (int) - Уникальный идентификатор персонала в Кинопоиске +- `$nameRu` (string|null) - Имя персонала на русском языке +- `$nameEn` (string|null) - Имя персонала на английском языке +- `$description` (string|null) - Описание роли или работы в фильме +- `$posterUrl` (string) - URL фотографии персонала + +### Профессиональная информация + +- `$professionText` (string) - Текстовое описание профессии +- `$professionKey` (ProfessionKey) - Ключ профессии (ACTOR, DIRECTOR, WRITER и т.д.) + +## Конструктор + +```php +public function __construct( + public readonly int $staffId, + public readonly ?string $nameRu, + public readonly ?string $nameEn, + public readonly ?string $description, + public readonly string $posterUrl, + public readonly string $professionText, + public readonly ProfessionKey $professionKey, +) +``` + +### Пример создания + +```php +$staff = new Staff( + staffId: 12345, + nameRu: 'Том Круз', + nameEn: 'Tom Cruise', + description: 'Нео', + posterUrl: 'https://...', + professionText: 'Актер', + professionKey: ProfessionKey::ACTOR +); +``` + +## Методы + +### fromArray() + +Создает экземпляр персонала из массива данных API. + +```php +public static function fromArray(array $data): self +``` + +#### Параметры + +- `$data` (array) - Массив данных персонала от API + +#### Возвращаемое значение + +- `self` - Новый экземпляр персонала + +#### Исключения + +- `\InvalidArgumentException` - Если данные имеют неверный формат + +#### Пример использования + +```php +$apiData = [ + 'staffId' => 12345, + 'nameRu' => 'Том Круз', + 'nameEn' => 'Tom Cruise', + 'description' => 'Нео', + 'posterUrl' => 'https://...', + 'professionText' => 'Актер', + 'professionKey' => 'ACTOR' +]; + +$staff = Staff::fromArray($apiData); +``` + +### getDisplayName() + +Получает отображаемое имя персонала. + +```php +public function getDisplayName(): string +``` + +#### Возвращаемое значение + +- `string` - Отображаемое имя персонала (приоритет: русское имя → английское имя → "Без имени") + +#### Пример использования + +```php +echo $staff->getDisplayName(); // "Том Круз" или "Tom Cruise" или "Без имени" +``` + +### isActor() + +Проверяет, является ли персонал актером. + +```php +public function isActor(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если это актер, `false` в противном случае + +#### Пример использования + +```php +if ($staff->isActor()) { + echo "Актер: {$staff->description}"; +} +``` + +### isDirector() + +Проверяет, является ли персонал режиссером. + +```php +public function isDirector(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если это режиссер, `false` в противном случае + +#### Пример использования + +```php +if ($staff->isDirector()) { + echo "Режиссер"; +} +``` + +### isWriter() + +Проверяет, является ли персонал сценаристом. + +```php +public function isWriter(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true` если это сценарист, `false` в противном случае + +#### Пример использования + +```php +if ($staff->isWriter()) { + echo "Сценарист"; +} +``` + +### toArray() + +Преобразует объект персонала в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Массив с данными персонала + +#### Пример использования + +```php +$staffArray = $staff->toArray(); +echo json_encode($staffArray); // JSON представление персонала +``` + +## Полный пример использования + +```php +persons; +$staff = $personService->getFilmStaff(301); // Матрица + +echo "=== Персонал фильма 'Матрица' ===\n"; + +// Группировка по профессиям +$actors = []; +$directors = []; +$writers = []; +$others = []; + +foreach ($staff as $member) { + if ($member->isActor()) { + $actors[] = $member; + } elseif ($member->isDirector()) { + $directors[] = $member; + } elseif ($member->isWriter()) { + $writers[] = $member; + } else { + $others[] = $member; + } +} + +// Вывод актеров +if (!empty($actors)) { + echo "\n🎭 Актеры:\n"; + foreach ($actors as $actor) { + echo "- {$actor->getDisplayName()}"; + if ($actor->description) { + echo " ({$actor->description})"; + } + echo "\n"; + } +} + +// Вывод режиссеров +if (!empty($directors)) { + echo "\n🎬 Режиссеры:\n"; + foreach ($directors as $director) { + echo "- {$director->getDisplayName()}\n"; + } +} + +// Вывод сценаристов +if (!empty($writers)) { + echo "\n✍️ Сценаристы:\n"; + foreach ($writers as $writer) { + echo "- {$writer->getDisplayName()}\n"; + } +} + +// Вывод остальных участников +if (!empty($others)) { + echo "\n🔧 Остальные участники:\n"; + foreach ($others as $other) { + echo "- {$other->getDisplayName()} ({$other->professionText})\n"; + } +} +``` + +## Работа с персоналом + +```php +// Функция для получения персонала по профессии +function getStaffByProfession(array $staff, ProfessionKey $professionKey): array { + return array_filter($staff, function($member) use ($professionKey) { + return $member->professionKey === $professionKey; + }); +} + +// Функция для поиска персонала по имени +function findStaffByName(array $staff, string $name): ?Staff { + $name = strtolower($name); + + foreach ($staff as $member) { + if (strpos(strtolower($member->getDisplayName()), $name) !== false) { + return $member; + } + } + + return null; +} + +// Использование +$staff = $personService->getFilmStaff(301); + +// Получение всех актеров +$actors = getStaffByProfession($staff, ProfessionKey::ACTOR); +echo "Актеров в фильме: " . count($actors) . "\n"; + +// Поиск конкретного актера +$neo = findStaffByName($staff, 'Киану'); +if ($neo) { + echo "Найден актер: {$neo->getDisplayName()} - {$neo->description}\n"; +} +``` + +## Статистика персонала + +```php +function getStaffStatistics(array $staff): array { + $stats = []; + + foreach ($staff as $member) { + $profession = $member->professionText; + if (!isset($stats[$profession])) { + $stats[$profession] = 0; + } + $stats[$profession]++; + } + + return $stats; +} + +// Использование +$staff = $personService->getFilmStaff(301); +$stats = getStaffStatistics($staff); + +echo "=== Статистика персонала ===\n"; +foreach ($stats as $profession => $count) { + echo "{$profession}: {$count} человек\n"; +} +``` + +## Связанные классы + +- [`Person`](person.md) - Модель персоны +- [`ProfessionKey`](../enums/profession-key.md) - Ключи профессий +- [`PersonService`](../services/person-service.md) - Сервис для работы с персонами +- [`MovieStaffResponse`](../responses/movie-staff-response.md) - Ответ с персоналом фильма diff --git a/docs/dev/notkinopoiskphp/models/user-vote.md b/docs/dev/notkinopoiskphp/models/user-vote.md new file mode 100644 index 0000000..1647f84 --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/user-vote.md @@ -0,0 +1,819 @@ +# UserVote + +Модель пользовательского голоса. + +## Описание + +`UserVote` представляет голос пользователя за фильм в Kinopoisk API. Содержит информацию о фильме и оценке пользователя. + +**API Endpoint:** `/api/v1/kp_users/{id}/votes` + +## Свойства + +### Основная информация + +- `$kinopoiskId` (int) - Уникальный идентификатор фильма +- `$nameRu` (string|null) - Название на русском +- `$nameEn` (string|null) - Название на английском +- `$nameOriginal` (string|null) - Оригинальное название +- `$countries` (Country[]) - Массив стран +- `$genres` (Genre[]) - Массив жанров +- `$ratingKinopoisk` (float|null) - Рейтинг Кинопоиска +- `$ratingImbd` (float|null) - Рейтинг IMDb +- `$year` (string|null) - Год выпуска +- `$type` (ContentType) - Тип контента +- `$posterUrl` (string) - URL постера +- `$posterUrlPreview` (string) - URL превью постера +- `$userRating` (int) - Оценка пользователя + +## Конструктор + +```php +public function __construct( + public readonly int $kinopoiskId, + public readonly ?string $nameRu, + public readonly ?string $nameEn, + public readonly ?string $nameOriginal, + public readonly array $countries, + public readonly array $genres, + public readonly ?float $ratingKinopoisk, + public readonly ?float $ratingImbd, + public readonly ?string $year, + public readonly ContentType $type, + public readonly string $posterUrl, + public readonly string $posterUrlPreview, + public readonly int $userRating, +) +``` + +### Пример создания + +```php +$vote = new UserVote( + kinopoiskId: 301, + nameRu: 'Матрица', + nameEn: 'The Matrix', + nameOriginal: 'The Matrix', + countries: [new Country('США')], + genres: [new Genre('боевик')], + ratingKinopoisk: 8.5, + ratingImbd: 8.7, + year: '1999', + type: ContentType::FILM, + posterUrl: 'https://example.com/poster.jpg', + posterUrlPreview: 'https://example.com/poster_small.jpg', + userRating: 9 +); +``` + +## Методы + +### fromArray() + +Создает экземпляр модели из массива данных API. + +```php +public static function fromArray(array $data): self +``` + +#### Параметры + +- `$data` (array) - Массив данных от API + +#### Возвращаемое значение + +- `self` - Экземпляр модели + +#### Исключения + +- `\InvalidArgumentException` - При некорректных данных + +#### Пример использования + +```php +$voteData = [ + 'kinopoiskId' => 301, + 'nameRu' => 'Матрица', + 'nameEn' => 'The Matrix', + 'nameOriginal' => 'The Matrix', + 'countries' => [['country' => 'США']], + 'genres' => [['genre' => 'боевик']], + 'ratingKinopoisk' => 8.5, + 'ratingImbd' => 8.7, + 'year' => '1999', + 'type' => 'FILM', + 'posterUrl' => 'https://example.com/poster.jpg', + 'posterUrlPreview' => 'https://example.com/poster_small.jpg', + 'userRating' => 9 +]; +$vote = UserVote::fromArray($voteData); +``` + +### getDisplayName() + +Получает отображаемое название фильма. + +```php +public function getDisplayName(): string +``` + +#### Возвращаемое значение + +- `string` - Отображаемое название фильма + +#### Описание + +Возвращает название фильма в приоритетном порядке: русское название → английское название → оригинальное название + +#### Пример использования + +```php +$displayName = $vote->getDisplayName(); +echo "Название: $displayName"; +``` + +### toArray() + +Преобразует объект пользовательского голоса в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Массив данных пользовательского голоса + +#### Описание + +Возвращает массив со всеми свойствами пользовательского голоса, включая преобразованные в массивы объекты стран и жанров. + +#### Пример использования + +```php +$array = $vote->toArray(); +// [ +// 'kinopoiskId' => 301, +// 'nameRu' => 'Матрица', +// 'nameEn' => 'The Matrix', +// 'nameOriginal' => 'The Matrix', +// 'countries' => [['country' => 'США']], +// 'genres' => [['genre' => 'боевик']], +// 'ratingKinopoisk' => 8.5, +// 'ratingImbd' => 8.7, +// 'year' => '1999', +// 'type' => 'FILM', +// 'posterUrl' => 'https://example.com/poster.jpg', +// 'posterUrlPreview' => 'https://example.com/poster_small.jpg', +// 'userRating' => 9 +// ] +``` + +## Полный пример использования + +```php +users; +$votes = $userService->getUserVotes(12345); // ID пользователя + +echo "=== Анализ голосов пользователя ===\n"; + +if (!empty($votes)) { + echo "Всего голосов: " . count($votes) . "\n\n"; + + foreach ($votes as $index => $vote) { + echo ($index + 1) . ". {$vote->getDisplayName()}\n"; + echo " ID: {$vote->kinopoiskId}\n"; + echo " Оценка пользователя: {$vote->userRating}/10\n"; + echo " Рейтинг Кинопоиска: " . ($vote->ratingKinopoisk ?? 'Нет') . "\n"; + echo " Рейтинг IMDb: " . ($vote->ratingImbd ?? 'Нет') . "\n"; + echo " Год: " . ($vote->year ?? 'Неизвестно') . "\n"; + echo " Тип: {$vote->type->getDisplayName()}\n"; + + if (!empty($vote->countries)) { + $countries = array_map(fn($c) => $c->country, $vote->countries); + echo " Страны: " . implode(', ', $countries) . "\n"; + } + + if (!empty($vote->genres)) { + $genres = array_map(fn($g) => $g->genre, $vote->genres); + echo " Жанры: " . implode(', ', $genres) . "\n"; + } + + echo "\n"; + } +} else { + echo "Голоса не найдены\n"; +} +``` + +## Работа с пользовательскими голосами + +```php +// Функция для фильтрации голосов по оценке +function filterVotesByRating(array $votes, int $minRating): array { + return array_filter($votes, fn($vote) => $vote->userRating >= $minRating); +} + +// Функция для фильтрации голосов по типу контента +function filterVotesByType(array $votes, ContentType $type): array { + return array_filter($votes, fn($vote) => $vote->type === $type); +} + +// Функция для фильтрации голосов по году +function filterVotesByYear(array $votes, int $year): array { + return array_filter($votes, fn($vote) => $vote->year == $year); +} + +// Функция для получения статистики по голосам +function getVoteStats(array $votes): array { + $stats = [ + 'total' => count($votes), + 'averageUserRating' => 0, + 'averageKinopoiskRating' => 0, + 'averageImdbRating' => 0, + 'ratingDistribution' => array_fill(1, 10, 0), + 'typeDistribution' => [], + 'yearDistribution' => [], + 'genreDistribution' => [], + 'countryDistribution' => [] + ]; + + $totalUserRating = 0; + $totalKinopoiskRating = 0; + $totalImdbRating = 0; + $ratedKinopoisk = 0; + $ratedImdb = 0; + + foreach ($votes as $vote) { + // Пользовательские оценки + $totalUserRating += $vote->userRating; + $stats['ratingDistribution'][$vote->userRating]++; + + // Рейтинги Кинопоиска + if ($vote->ratingKinopoisk) { + $totalKinopoiskRating += $vote->ratingKinopoisk; + $ratedKinopoisk++; + } + + // Рейтинги IMDb + if ($vote->ratingImbd) { + $totalImdbRating += $vote->ratingImbd; + $ratedImdb++; + } + + // Распределение по типам + $typeKey = $vote->type->value; + if (!isset($stats['typeDistribution'][$typeKey])) { + $stats['typeDistribution'][$typeKey] = 0; + } + $stats['typeDistribution'][$typeKey]++; + + // Распределение по годам + if ($vote->year) { + if (!isset($stats['yearDistribution'][$vote->year])) { + $stats['yearDistribution'][$vote->year] = 0; + } + $stats['yearDistribution'][$vote->year]++; + } + + // Распределение по жанрам + foreach ($vote->genres as $genre) { + if (!isset($stats['genreDistribution'][$genre->genre])) { + $stats['genreDistribution'][$genre->genre] = 0; + } + $stats['genreDistribution'][$genre->genre]++; + } + + // Распределение по странам + foreach ($vote->countries as $country) { + if (!isset($stats['countryDistribution'][$country->country])) { + $stats['countryDistribution'][$country->country] = 0; + } + $stats['countryDistribution'][$country->country]++; + } + } + + // Вычисление средних значений + if ($stats['total'] > 0) { + $stats['averageUserRating'] = round($totalUserRating / $stats['total'], 2); + } + + if ($ratedKinopoisk > 0) { + $stats['averageKinopoiskRating'] = round($totalKinopoiskRating / $ratedKinopoisk, 2); + } + + if ($ratedImdb > 0) { + $stats['averageImdbRating'] = round($totalImdbRating / $ratedImdb, 2); + } + + return $stats; +} + +// Функция для получения топ голосов по оценке +function getTopRatedVotes(array $votes, int $limit = 10): array { + usort($votes, function($a, $b) { + return $b->userRating <=> $a->userRating; + }); + + return array_slice($votes, 0, $limit); +} + +// Функция для получения голосов с наибольшей разницей в оценках +function getVotesWithRatingDifference(array $votes, int $limit = 10): array { + $votesWithDiff = array_map(function($vote) { + $diff = 0; + if ($vote->ratingKinopoisk) { + $diff = abs($vote->userRating - $vote->ratingKinopoisk); + } + return ['vote' => $vote, 'difference' => $diff]; + }, $votes); + + usort($votesWithDiff, function($a, $b) { + return $b['difference'] <=> $a['difference']; + }); + + return array_slice($votesWithDiff, 0, $limit); +} + +// Функция для получения любимых жанров пользователя +function getUserFavoriteGenres(array $votes): array { + $genreStats = []; + + foreach ($votes as $vote) { + foreach ($vote->genres as $genre) { + if (!isset($genreStats[$genre->genre])) { + $genreStats[$genre->genre] = [ + 'count' => 0, + 'totalRating' => 0 + ]; + } + $genreStats[$genre->genre]['count']++; + $genreStats[$genre->genre]['totalRating'] += $vote->userRating; + } + } + + // Вычисление средних оценок + foreach ($genreStats as $genre => &$stats) { + $stats['averageRating'] = round($stats['totalRating'] / $stats['count'], 2); + } + + // Сортировка по количеству фильмов + uasort($genreStats, function($a, $b) { + return $b['count'] <=> $a['count']; + }); + + return $genreStats; +} + +// Использование +$votes = $userService->getUserVotes(12345); + +// Фильтрация +$highRatedVotes = filterVotesByRating($votes, 8); +$filmVotes = filterVotesByType($votes, ContentType::FILM); +$recentVotes = filterVotesByYear($votes, 2020); + +echo "Фильтрация голосов:\n"; +echo "Высоко оцененные (8+): " . count($highRatedVotes) . "\n"; +echo "Фильмы: " . count($filmVotes) . "\n"; +echo "Фильмы 2020 года: " . count($recentVotes) . "\n"; + +// Статистика +$stats = getVoteStats($votes); +echo "\nСтатистика:\n"; +echo "Всего голосов: {$stats['total']}\n"; +echo "Средняя оценка пользователя: {$stats['averageUserRating']}\n"; +echo "Средний рейтинг Кинопоиска: {$stats['averageKinopoiskRating']}\n"; +echo "Средний рейтинг IMDb: {$stats['averageImdbRating']}\n"; + +// Топ голосов +$topVotes = getTopRatedVotes($votes, 5); +echo "\nТоп-5 по оценке:\n"; +foreach ($topVotes as $index => $vote) { + echo ($index + 1) . ". {$vote->getDisplayName()} - {$vote->userRating}/10\n"; +} + +// Любимые жанры +$favoriteGenres = getUserFavoriteGenres($votes); +echo "\nЛюбимые жанры:\n"; +foreach (array_slice($favoriteGenres, 0, 5, true) as $genre => $stats) { + echo "• {$genre}: {$stats['count']} фильмов, средняя оценка {$stats['averageRating']}\n"; +} +``` + +## Создание отчета по голосам пользователя + +```php +class UserVoteReport { + private array $votes; + + public function __construct(array $votes) { + $this->votes = $votes; + } + + public function getVotes(): array { + return $this->votes; + } + + public function getVoteStats(): array { + return getVoteStats($this->votes); + } + + public function getTopRatedVotes(int $limit = 10): array { + return getTopRatedVotes($this->votes, $limit); + } + + public function getFavoriteGenres(): array { + return getUserFavoriteGenres($this->votes); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО ГОЛОСАМ ПОЛЬЗОВАТЕЛЯ ===\n\n"; + + $stats = $this->getVoteStats(); + + // Общая статистика + $report .= "📊 ОБЩАЯ СТАТИСТИКА:\n"; + $report .= "Всего голосов: {$stats['total']}\n"; + $report .= "Средняя оценка пользователя: {$stats['averageUserRating']}\n"; + $report .= "Средний рейтинг Кинопоиска: {$stats['averageKinopoiskRating']}\n"; + $report .= "Средний рейтинг IMDb: {$stats['averageImdbRating']}\n\n"; + + // Распределение оценок + $report .= "📈 РАСПРЕДЕЛЕНИЕ ОЦЕНОК:\n"; + for ($i = 10; $i >= 1; $i--) { + $count = $stats['ratingDistribution'][$i]; + $percent = round(($count / $stats['total']) * 100, 1); + $stars = str_repeat('⭐', $i); + $report .= "{$stars} {$i}/10: {$count} голосов ({$percent}%)\n"; + } + $report .= "\n"; + + // Распределение по типам + $report .= "🎬 РАСПРЕДЕЛЕНИЕ ПО ТИПАМ:\n"; + foreach ($stats['typeDistribution'] as $typeValue => $count) { + $type = ContentType::from($typeValue); + $percent = round(($count / $stats['total']) * 100, 1); + $report .= "• {$type->getDisplayName()}: {$count} ({$percent}%)\n"; + } + $report .= "\n"; + + // Топ жанров + $favoriteGenres = $this->getFavoriteGenres(); + $report .= "🎭 ЛЮБИМЫЕ ЖАНРЫ:\n"; + foreach (array_slice($favoriteGenres, 0, 10, true) as $genre => $genreStats) { + $report .= "• {$genre}: {$genreStats['count']} фильмов, средняя оценка {$genreStats['averageRating']}\n"; + } + $report .= "\n"; + + // Топ стран + arsort($stats['countryDistribution']); + $report .= "🌍 ЛЮБИМЫЕ СТРАНЫ:\n"; + foreach (array_slice($stats['countryDistribution'], 0, 10, true) as $country => $count) { + $percent = round(($count / $stats['total']) * 100, 1); + $report .= "• {$country}: {$count} фильмов ({$percent}%)\n"; + } + $report .= "\n"; + + // Топ голосов + $topVotes = $this->getTopRatedVotes(10); + $report .= "🏆 ТОП-10 ПО ОЦЕНКЕ:\n"; + foreach ($topVotes as $index => $vote) { + $stars = str_repeat('⭐', $vote->userRating); + $report .= ($index + 1) . ". {$vote->getDisplayName()} {$stars} ({$vote->userRating}/10)\n"; + if ($vote->year) { + $report .= " Год: {$vote->year}\n"; + } + if (!empty($vote->genres)) { + $genres = array_map(fn($g) => $g->genre, $vote->genres); + $report .= " Жанры: " . implode(', ', $genres) . "\n"; + } + $report .= "\n"; + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $stats = $this->getVoteStats(); + $topVotes = $this->getTopRatedVotes(12); + $favoriteGenres = $this->getFavoriteGenres(); + + // Статистика + $html .= "
\n"; + $html .= "

Общая статистика

\n"; + $html .= "

Всего голосов: {$stats['total']}

\n"; + $html .= "

Средняя оценка пользователя: {$stats['averageUserRating']}

\n"; + $html .= "

Средний рейтинг Кинопоиска: {$stats['averageKinopoiskRating']}

\n"; + $html .= "

Средний рейтинг IMDb: {$stats['averageImdbRating']}

\n"; + + // Распределение оценок + $html .= "

Распределение оценок

\n"; + for ($i = 10; $i >= 1; $i--) { + $count = $stats['ratingDistribution'][$i]; + $percent = round(($count / $stats['total']) * 100, 1); + + $html .= "
\n"; + $html .= "
\n"; + $html .= "
\n"; + $html .= "

{$i}/10: {$count} голосов ({$percent}%)

\n"; + } + + $html .= "
\n"; + + // Топ голосов + if (!empty($topVotes)) { + $html .= "
\n"; + $html .= "
🏆 Топ голосов по оценке
\n"; + $html .= "
\n"; + + foreach ($topVotes as $vote) { + $cssClass = 'rating-' . $vote->userRating; + if ($vote->userRating < 6) $cssClass = 'rating-low'; + + $html .= "
\n"; + $html .= "
{$vote->getDisplayName()}
\n"; + $html .= "
Оценка: {$vote->userRating}/10
\n"; + if ($vote->ratingKinopoisk) { + $html .= "
Кинопоиск: {$vote->ratingKinopoisk}
\n"; + } + if ($vote->ratingImbd) { + $html .= "
IMDb: {$vote->ratingImbd}
\n"; + } + if ($vote->year) { + $html .= "
Год: {$vote->year}
\n"; + } + if (!empty($vote->genres)) { + $genres = array_map(fn($g) => $g->genre, $vote->genres); + $html .= "
Жанры: " . implode(', ', $genres) . "
\n"; + } + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + // Любимые жанры + if (!empty($favoriteGenres)) { + $html .= "
\n"; + $html .= "
🎭 Любимые жанры
\n"; + $html .= "
\n"; + + foreach (array_slice($favoriteGenres, 0, 12, true) as $genre => $genreStats) { + $html .= "
\n"; + $html .= "
{$genre}
\n"; + $html .= "
Фильмов: {$genreStats['count']}
\n"; + $html .= "
Средняя оценка: {$genreStats['averageRating']}
\n"; + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + $html .= "
\n\n"; + + return $html; + } +} + +// Использование +$votes = $userService->getUserVotes(12345); +$report = new UserVoteReport($votes); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Отчет по голосам пользователя'); +file_put_contents('user_votes_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в user_votes_report.html\n"; +``` + +## Анализ голосов пользователя + +```php +function analyzeUserVotes(array $votes): array { + $analysis = [ + 'totalVotes' => count($votes), + 'ratingAnalysis' => [ + 'averageUserRating' => 0, + 'averageKinopoiskRating' => 0, + 'averageImdbRating' => 0, + 'ratingDistribution' => array_fill(1, 10, 0), + 'ratingCorrelation' => 0 + ], + 'contentTypeAnalysis' => [ + 'typeDistribution' => [], + 'averageRatingByType' => [] + ], + 'yearAnalysis' => [ + 'yearDistribution' => [], + 'averageRatingByYear' => [] + ], + 'genreAnalysis' => [ + 'genreDistribution' => [], + 'averageRatingByGenre' => [] + ], + 'countryAnalysis' => [ + 'countryDistribution' => [], + 'averageRatingByCountry' => [] + ] + ]; + + $totalUserRating = 0; + $totalKinopoiskRating = 0; + $totalImdbRating = 0; + $ratedKinopoisk = 0; + $ratedImdb = 0; + $ratingDifferences = []; + + foreach ($votes as $vote) { + // Пользовательские оценки + $totalUserRating += $vote->userRating; + $analysis['ratingAnalysis']['ratingDistribution'][$vote->userRating]++; + + // Рейтинги Кинопоиска + if ($vote->ratingKinopoisk) { + $totalKinopoiskRating += $vote->ratingKinopoisk; + $ratedKinopoisk++; + $ratingDifferences[] = abs($vote->userRating - $vote->ratingKinopoisk); + } + + // Рейтинги IMDb + if ($vote->ratingImbd) { + $totalImdbRating += $vote->ratingImbd; + $ratedImdb++; + } + + // Анализ по типам контента + $typeKey = $vote->type->value; + if (!isset($analysis['contentTypeAnalysis']['typeDistribution'][$typeKey])) { + $analysis['contentTypeAnalysis']['typeDistribution'][$typeKey] = 0; + $analysis['contentTypeAnalysis']['averageRatingByType'][$typeKey] = ['total' => 0, 'count' => 0]; + } + $analysis['contentTypeAnalysis']['typeDistribution'][$typeKey]++; + $analysis['contentTypeAnalysis']['averageRatingByType'][$typeKey]['total'] += $vote->userRating; + $analysis['contentTypeAnalysis']['averageRatingByType'][$typeKey]['count']++; + + // Анализ по годам + if ($vote->year) { + if (!isset($analysis['yearAnalysis']['yearDistribution'][$vote->year])) { + $analysis['yearAnalysis']['yearDistribution'][$vote->year] = 0; + $analysis['yearAnalysis']['averageRatingByYear'][$vote->year] = ['total' => 0, 'count' => 0]; + } + $analysis['yearAnalysis']['yearDistribution'][$vote->year]++; + $analysis['yearAnalysis']['averageRatingByYear'][$vote->year]['total'] += $vote->userRating; + $analysis['yearAnalysis']['averageRatingByYear'][$vote->year]['count']++; + } + + // Анализ по жанрам + foreach ($vote->genres as $genre) { + if (!isset($analysis['genreAnalysis']['genreDistribution'][$genre->genre])) { + $analysis['genreAnalysis']['genreDistribution'][$genre->genre] = 0; + $analysis['genreAnalysis']['averageRatingByGenre'][$genre->genre] = ['total' => 0, 'count' => 0]; + } + $analysis['genreAnalysis']['genreDistribution'][$genre->genre]++; + $analysis['genreAnalysis']['averageRatingByGenre'][$genre->genre]['total'] += $vote->userRating; + $analysis['genreAnalysis']['averageRatingByGenre'][$genre->genre]['count']++; + } + + // Анализ по странам + foreach ($vote->countries as $country) { + if (!isset($analysis['countryAnalysis']['countryDistribution'][$country->country])) { + $analysis['countryAnalysis']['countryDistribution'][$country->country] = 0; + $analysis['countryAnalysis']['averageRatingByCountry'][$country->country] = ['total' => 0, 'count' => 0]; + } + $analysis['countryAnalysis']['countryDistribution'][$country->country]++; + $analysis['countryAnalysis']['averageRatingByCountry'][$country->country]['total'] += $vote->userRating; + $analysis['countryAnalysis']['averageRatingByCountry'][$country->country]['count']++; + } + } + + // Вычисление средних значений + if ($analysis['totalVotes'] > 0) { + $analysis['ratingAnalysis']['averageUserRating'] = round($totalUserRating / $analysis['totalVotes'], 2); + } + + if ($ratedKinopoisk > 0) { + $analysis['ratingAnalysis']['averageKinopoiskRating'] = round($totalKinopoiskRating / $ratedKinopoisk, 2); + } + + if ($ratedImdb > 0) { + $analysis['ratingAnalysis']['averageImdbRating'] = round($totalImdbRating / $ratedImdb, 2); + } + + // Корреляция оценок + if (!empty($ratingDifferences)) { + $analysis['ratingAnalysis']['ratingCorrelation'] = round(array_sum($ratingDifferences) / count($ratingDifferences), 2); + } + + // Вычисление средних оценок по категориям + foreach ($analysis['contentTypeAnalysis']['averageRatingByType'] as $type => &$data) { + if ($data['count'] > 0) { + $data['average'] = round($data['total'] / $data['count'], 2); + } + } + + foreach ($analysis['yearAnalysis']['averageRatingByYear'] as $year => &$data) { + if ($data['count'] > 0) { + $data['average'] = round($data['total'] / $data['count'], 2); + } + } + + foreach ($analysis['genreAnalysis']['averageRatingByGenre'] as $genre => &$data) { + if ($data['count'] > 0) { + $data['average'] = round($data['total'] / $data['count'], 2); + } + } + + foreach ($analysis['countryAnalysis']['averageRatingByCountry'] as $country => &$data) { + if ($data['count'] > 0) { + $data['average'] = round($data['total'] / $data['count'], 2); + } + } + + return $analysis; +} + +// Использование +$votes = $userService->getUserVotes(12345); +$analysis = analyzeUserVotes($votes); + +echo "=== Анализ голосов пользователя ===\n"; +echo "Всего голосов: {$analysis['totalVotes']}\n"; + +echo "\nАнализ оценок:\n"; +echo "- Средняя оценка пользователя: {$analysis['ratingAnalysis']['averageUserRating']}\n"; +echo "- Средний рейтинг Кинопоиска: {$analysis['ratingAnalysis']['averageKinopoiskRating']}\n"; +echo "- Средний рейтинг IMDb: {$analysis['ratingAnalysis']['averageImdbRating']}\n"; +echo "- Средняя разница с Кинопоиском: {$analysis['ratingAnalysis']['ratingCorrelation']}\n"; + +echo "\nРаспределение оценок:\n"; +for ($i = 10; $i >= 1; $i--) { + $count = $analysis['ratingAnalysis']['ratingDistribution'][$i]; + $percent = round(($count / $analysis['totalVotes']) * 100, 1); + echo "- {$i}/10: {$count} голосов ({$percent}%)\n"; +} + +echo "\nСредние оценки по типам контента:\n"; +foreach ($analysis['contentTypeAnalysis']['averageRatingByType'] as $type => $data) { + if (isset($data['average'])) { + $contentType = ContentType::from($type); + echo "- {$contentType->getDisplayName()}: {$data['average']}\n"; + } +} + +echo "\nТоп жанров по средней оценке:\n"; +$genreAverages = []; +foreach ($analysis['genreAnalysis']['averageRatingByGenre'] as $genre => $data) { + if (isset($data['average'])) { + $genreAverages[$genre] = $data['average']; + } +} +arsort($genreAverages); +foreach (array_slice($genreAverages, 0, 10, true) as $genre => $average) { + echo "- {$genre}: {$average}\n"; +} +``` + +## Связанные классы + +- [`Country`](country.md) - Модель страны +- [`Genre`](genre.md) - Модель жанра +- [`ContentType`](../enums/content-type.md) - Enum типов контента +- [`UserService`](../services/user-service.md) - Сервис для работы с пользователями diff --git a/docs/dev/notkinopoiskphp/models/video.md b/docs/dev/notkinopoiskphp/models/video.md new file mode 100644 index 0000000..360f362 --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/video.md @@ -0,0 +1,460 @@ +# Video + +Модель видео из Kinopoisk API. + +## Описание + +`Video` представляет информацию о видео материале, связанном с фильмом: трейлеры, клипы, закулисные материалы и другие видео. + +### Основные возможности + +- Хранение информации о видео в неизменяемом виде +- Создание объекта из массива данных API +- Доступ к URL, названию и платформе видео + +## Свойства + +### Основная информация + +- `$url` (string) - URL видео +- `$name` (string) - Название видео +- `$site` (VideoSite) - Платформа или сайт, где размещено видео + +## Конструктор + +```php +public function __construct( + public readonly string $url, + public readonly string $name, + public readonly VideoSite $site, +) +``` + +### Пример создания + +```php +$video = new Video( + url: 'https://youtube.com/watch?v=...', + name: 'Трейлер фильма', + site: VideoSite::YOUTUBE +); +``` + +## Методы + +### fromArray() + +Создает экземпляр видео из массива данных API. + +```php +public static function fromArray(array $data): self +``` + +#### Параметры + +- `$data` (array) - Массив данных видео от API + +#### Возвращаемое значение + +- `self` - Новый экземпляр видео + +#### Исключения + +- `\InvalidArgumentException` - Если данные имеют неверный формат + +#### Пример использования + +```php +$apiData = [ + 'url' => 'https://youtube.com/watch?v=...', + 'name' => 'Трейлер фильма', + 'site' => 'YOUTUBE' +]; + +$video = Video::fromArray($apiData); +``` + +### toArray() + +Преобразует объект видео в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Массив с данными видео + +#### Пример использования + +```php +$videoArray = $video->toArray(); +echo json_encode($videoArray); // JSON представление видео +``` + +## Полный пример использования + +```php +films; +$videos = $filmService->getVideos(301); // Матрица + +echo "=== Видео фильма 'Матрица' ===\n"; + +// Группировка по платформам +$videosBySite = []; +foreach ($videos as $video) { + $siteKey = $video->site->value; + if (!isset($videosBySite[$siteKey])) { + $videosBySite[$siteKey] = []; + } + $videosBySite[$siteKey][] = $video; +} + +// Вывод видео по платформам +foreach ($videosBySite as $site => $siteVideos) { + echo "\n🎬 {$video->site->getDisplayName()} (" . count($siteVideos) . " видео):\n"; + + foreach (array_slice($siteVideos, 0, 5) as $index => $video) { + echo ($index + 1) . ". {$video->name}\n"; + echo " URL: {$video->url}\n"; + echo " ---\n"; + } + + if (count($siteVideos) > 5) { + echo "... и еще " . (count($siteVideos) - 5) . " видео\n"; + } +} +``` + +## Работа с видео + +```php +// Функция для фильтрации видео по платформе +function filterVideosBySite(array $videos, VideoSite $site): array { + return array_filter($videos, fn($video) => $video->site === $site); +} + +// Функция для поиска видео по названию +function searchVideosByName(array $videos, string $keyword): array { + $keyword = strtolower($keyword); + + return array_filter($videos, function($video) use ($keyword) { + return strpos(strtolower($video->name), $keyword) !== false; + }); +} + +// Функция для получения всех платформ +function getVideoSites(array $videos): array { + $sites = []; + foreach ($videos as $video) { + $siteKey = $video->site->value; + if (!isset($sites[$siteKey])) { + $sites[$siteKey] = [ + 'site' => $video->site, + 'count' => 0, + 'videos' => [] + ]; + } + $sites[$siteKey]['count']++; + $sites[$siteKey]['videos'][] = $video; + } + + return $sites; +} + +// Использование +$videos = $filmService->getVideos(301); + +// Фильтрация по платформе +$youtubeVideos = filterVideosBySite($videos, VideoSite::YOUTUBE); +echo "Видео на YouTube: " . count($youtubeVideos) . "\n"; + +// Поиск по названию +$trailerVideos = searchVideosByName($videos, 'трейлер'); +echo "Видео с 'трейлер' в названии: " . count($trailerVideos) . "\n"; + +// Получение всех платформ +$sites = getVideoSites($videos); +echo "Платформы с видео:\n"; +foreach ($sites as $siteKey => $siteData) { + echo "- {$siteData['site']->getDisplayName()}: {$siteData['count']} видео\n"; +} +``` + +## Создание плейлиста + +```php +class VideoPlaylist { + private array $videos; + + public function __construct(array $videos) { + $this->videos = $videos; + } + + public function getBySite(VideoSite $site): array { + return filterVideosBySite($this->videos, $site); + } + + public function getTrailers(): array { + return searchVideosByName($this->videos, 'трейлер'); + } + + public function getClips(): array { + return searchVideosByName($this->videos, 'клип'); + } + + public function getBehindTheScenes(): array { + return searchVideosByName($this->videos, 'закулисье'); + } + + public function createHtmlPlaylist(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + foreach ($this->videos as $index => $video) { + $html .= "
\n"; + $html .= "
" . ($index + 1) . ". {$video->name}
\n"; + $html .= "
Платформа: {$video->site->getDisplayName()}
\n"; + $html .= "Смотреть видео\n"; + $html .= "
\n"; + } + + $html .= "
\n\n"; + + return $html; + } + + public function getStatistics(): array { + $stats = [ + 'total' => count($this->videos), + 'sites' => [], + 'trailers' => 0, + 'clips' => 0, + 'behindScenes' => 0 + ]; + + $sites = getVideoSites($this->videos); + foreach ($sites as $siteKey => $siteData) { + $stats['sites'][$siteKey] = $siteData['count']; + } + + $stats['trailers'] = count($this->getTrailers()); + $stats['clips'] = count($this->getClips()); + $stats['behindScenes'] = count($this->getBehindTheScenes()); + + return $stats; + } +} + +// Использование +$videos = $filmService->getVideos(301); +$playlist = new VideoPlaylist($videos); + +// Получение статистики +$stats = $playlist->getStatistics(); +echo "=== Статистика видео ===\n"; +echo "Всего видео: {$stats['total']}\n"; +echo "Трейлеров: {$stats['trailers']}\n"; +echo "Клипов: {$stats['clips']}\n"; +echo "Закулисных материалов: {$stats['behindScenes']}\n"; + +echo "\nПо платформам:\n"; +foreach ($stats['sites'] as $site => $count) { + echo "- {$site}: {$count} видео\n"; +} + +// Создание HTML плейлиста +$html = $playlist->createHtmlPlaylist('Видео фильма "Матрица"'); +file_put_contents('matrix_videos.html', $html); +echo "\n✅ HTML плейлист сохранен в matrix_videos.html\n"; +``` + +## Анализ видео контента + +```php +function analyzeVideoContent(array $videos): array { + $analysis = [ + 'total' => count($videos), + 'sites' => [], + 'namePatterns' => [], + 'avgNameLength' => 0, + 'mostCommonWords' => [] + ]; + + $totalNameLength = 0; + $allWords = []; + $siteStats = []; + + foreach ($videos as $video) { + // Статистика по платформам + $siteKey = $video->site->value; + if (!isset($siteStats[$siteKey])) { + $siteStats[$siteKey] = 0; + } + $siteStats[$siteKey]++; + + // Анализ названий + $totalNameLength += strlen($video->name); + + // Разбор слов в названии + $words = preg_split('/\s+/', strtolower($video->name)); + foreach ($words as $word) { + $word = trim($word, '.,!?()[]{}"\'-'); + if (strlen($word) > 2) { + if (!isset($allWords[$word])) { + $allWords[$word] = 0; + } + $allWords[$word]++; + } + } + + // Паттерны в названиях + $patterns = [ + 'трейлер' => 'Трейлеры', + 'клип' => 'Клипы', + 'закулисье' => 'Закулисные материалы', + 'интервью' => 'Интервью', + 'превью' => 'Превью', + 'съёмки' => 'Материалы со съёмок' + ]; + + foreach ($patterns as $pattern => $category) { + if (stripos($video->name, $pattern) !== false) { + if (!isset($analysis['namePatterns'][$category])) { + $analysis['namePatterns'][$category] = 0; + } + $analysis['namePatterns'][$category]++; + } + } + } + + // Средняя длина названия + if ($analysis['total'] > 0) { + $analysis['avgNameLength'] = round($totalNameLength / $analysis['total']); + } + + // Статистика по платформам + $analysis['sites'] = $siteStats; + + // Самые частые слова + arsort($allWords); + $analysis['mostCommonWords'] = array_slice($allWords, 0, 10, true); + + return $analysis; +} + +// Использование +$videos = $filmService->getVideos(301); +$analysis = analyzeVideoContent($videos); + +echo "=== Анализ видео контента ===\n"; +echo "Всего видео: {$analysis['total']}\n"; +echo "Средняя длина названия: {$analysis['avgNameLength']} символов\n"; + +echo "\nПо типам контента:\n"; +foreach ($analysis['namePatterns'] as $type => $count) { + echo "- {$type}: {$count}\n"; +} + +echo "\nПо платформам:\n"; +foreach ($analysis['sites'] as $site => $count) { + echo "- {$site}: {$count} видео\n"; +} + +echo "\nСамые частые слова в названиях:\n"; +foreach ($analysis['mostCommonWords'] as $word => $count) { + echo "- '{$word}': {$count} раз\n"; +} +``` + +## Валидация видео + +```php +function validateVideo(Video $video): array { + $validation = [ + 'valid' => true, + 'errors' => [], + 'warnings' => [] + ]; + + // Проверка URL + if (empty($video->url)) { + $validation['valid'] = false; + $validation['errors'][] = 'URL видео пустой'; + } elseif (!filter_var($video->url, FILTER_VALIDATE_URL)) { + $validation['valid'] = false; + $validation['errors'][] = 'Некорректный URL видео'; + } + + // Проверка названия + if (empty($video->name)) { + $validation['valid'] = false; + $validation['errors'][] = 'Название видео пустое'; + } + + // Проверка платформы + if (!in_array($video->site, VideoSite::cases())) { + $validation['warnings'][] = 'Неизвестная платформа: ' . $video->site->value; + } + + // Проверка доступности видео + if ($validation['valid']) { + $headers = @get_headers($video->url); + if (!$headers || strpos($headers[0], '200') === false) { + $validation['warnings'][] = 'Видео недоступно по указанному URL'; + } + } + + return $validation; +} + +// Использование +$videos = $filmService->getVideos(301); + +foreach ($videos as $index => $video) { + $validation = validateVideo($video); + + if (!$validation['valid']) { + echo "❌ Видео " . ($index + 1) . " невалидно:\n"; + foreach ($validation['errors'] as $error) { + echo " - {$error}\n"; + } + } elseif (!empty($validation['warnings'])) { + echo "⚠️ Видео " . ($index + 1) . " имеет предупреждения:\n"; + foreach ($validation['warnings'] as $warning) { + echo " - {$warning}\n"; + } + } else { + echo "✅ Видео " . ($index + 1) . " валидно\n"; + } +} +``` + +## Связанные классы + +- [`VideoSite`](../enums/video-site.md) - Платформы для видео +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами +- [`Film`](film.md) - Модель фильма diff --git a/docs/dev/notkinopoiskphp/navigation-map.md b/docs/dev/notkinopoiskphp/navigation-map.md new file mode 100644 index 0000000..ca1c0dc --- /dev/null +++ b/docs/dev/notkinopoiskphp/navigation-map.md @@ -0,0 +1,325 @@ +# Карта навигации документации + +Интерактивная карта навигации по всей документации NotKinopoisk PHP Library. + +--- + +**📚 Навигация:** [Главная](index.md) → Карта навигации + +--- + +## 🗺️ Структура документации + +``` +📁 docs/ +├── 📄 index.md # Главная страница +├── 📄 navigation-map.md # Карта навигации (этот файл) +├── 📄 client.md # Основной клиент +│ +├── 📁 services/ # Сервисы API +│ ├── 📄 index.md # Обзор сервисов +│ ├── 📄 film-service.md # Сервис фильмов +│ ├── 📄 person-service.md # Сервис персон +│ ├── 📄 media-service.md # Сервис медиа +│ └── 📄 user-service.md # Сервис пользователей +│ +├── 📁 models/ # Модели данных +│ ├── 📄 index.md # Обзор моделей +│ ├── 📄 film.md # Модель фильма +│ ├── 📄 person.md # Модель персоны +│ ├── 📄 staff.md # Модель съемочной группы +│ ├── 📄 review.md # Модель отзыва +│ ├── 📄 fact.md # Модель факта +│ ├── 📄 image.md # Модель изображения +│ ├── 📄 video.md # Модель видео +│ ├── 📄 award.md # Модель награды +│ ├── 📄 box-office.md # Модель кассовых сборов +│ ├── 📄 country.md # Модель страны +│ ├── 📄 genre.md # Модель жанра +│ ├── 📄 episode.md # Модель эпизода +│ ├── 📄 season.md # Модель сезона +│ ├── 📄 external-source.md # Модель внешнего источника +│ ├── 📄 distribution.md # Модель дистрибуции +│ ├── 📄 film-search-result.md # Модель результата поиска +│ ├── 📄 person-spouse.md # Модель супруга +│ ├── 📄 person-film.md # Модель фильма персоны +│ ├── 📄 user-vote.md # Модель голоса пользователя +│ ├── 📄 film-collection.md # Модель коллекции фильмов +│ ├── 📄 filters.md # Модель фильтров +│ ├── 📄 related-film.md # Модель связанного фильма +│ ├── 📄 api-key-info.md # Модель информации об API ключе +│ ├── 📄 api-key-qouta.md # Модель квоты API ключа +│ └── 📄 media-post.md # Модель медиа поста +│ +├── 📁 enums/ # Перечисления +│ ├── 📄 index.md # Обзор перечислений +│ ├── 📄 image-type.md # Типы изображений +│ ├── 📄 review-order.md # Порядок сортировки отзывов +│ ├── 📄 review-type.md # Типы отзывов +│ ├── 📄 fact-type.md # Типы фактов +│ ├── 📄 profession-key.md # Ключи профессий +│ ├── 📄 video-site.md # Сайты видео +│ ├── 📄 box-office-type.md # Типы кассовых сборов +│ ├── 📄 distribution-type.md # Типы дистрибуции +│ ├── 📄 relation-type.md # Типы связей +│ ├── 📄 sex.md # Пол +│ ├── 📄 api-version.md # Версии API +│ ├── 📄 month.md # Месяцы +│ ├── 📄 film-order.md # Порядок сортировки фильмов +│ ├── 📄 content-type.md # Типы контента +│ ├── 📄 collection-type.md # Типы коллекций +│ ├── 📄 distribution-sub-type.md # Подтипы дистрибуции +│ └── 📄 account-type.md # Типы аккаунтов +│ +├── 📁 responses/ # Ответы API +│ ├── 📄 index.md # Обзор ответов +│ ├── 📄 default-response.md # Базовый ответ +│ ├── 📄 paginated-response.md # Пагинированный ответ +│ └── 📄 keyword-search-response.md # Ответ поиска +│ +├── 📁 exceptions/ # Исключения +│ ├── 📄 index.md # Обзор исключений +│ ├── 📄 api-exception.md # Базовое исключение API +│ ├── 📄 invalid-api-key-exception.md # Неверный API ключ +│ ├── 📄 rate-limit-exception.md # Превышение лимита +│ ├── 📄 resource-not-found-exception.md # Ресурс не найден +│ └── 📄 kp-validation-exception.md # Ошибка валидации +│ +└── 📁 interfaces/ # Интерфейсы + ├── 📄 index.md # Обзор интерфейсов + ├── 📄 model-interface.md # Интерфейс модели + └── 📄 response-interface.md # Интерфейс ответа +``` + +## 🔗 Быстрые ссылки + +### 🚀 Начало работы + +- **[Главная страница](index.md)** - Обзор библиотеки и быстрый старт +- **[Основной клиент](client.md)** - Главный класс для работы с API +- **[Примеры использования](../examples/)** - Готовые примеры кода + +### 📦 Основные компоненты + +- **[Сервисы](services/index.md)** - Работа с различными типами данных +- **[Модели](models/index.md)** - Структуры данных API +- **[Перечисления](enums/index.md)** - Константы и типы +- **[Ответы](responses/index.md)** - Классы ответов API +- **[Исключения](exceptions/index.md)** - Обработка ошибок +- **[Интерфейсы](interfaces/index.md)** - Базовые контракты + +## 🎯 Популярные разделы + +### 🎬 Работа с фильмами + +1. **[FilmService](services/film-service.md)** - Основной сервис для работы с фильмами +2. **[Film](models/film.md)** - Модель фильма с полной информацией +3. **[FilmSearchResult](models/film-search-result.md)** - Результаты поиска фильмов +4. **[ContentType](enums/content-type.md)** - Типы контента (фильм, сериал, etc.) +5. **[FilmOrder](enums/film-order.md)** - Сортировка фильмов + +### 👥 Работа с персонами + +1. **[PersonService](services/person-service.md)** - Сервис для работы с персонами +2. **[Person](models/person.md)** - Модель персоны +3. **[Staff](models/staff.md)** - Съемочная группа +4. **[ProfessionKey](enums/profession-key.md)** - Профессии в кино +5. **[PersonFilm](models/person-film.md)** - Фильмография персоны + +### 🎥 Работа с медиа + +1. **[MediaService](services/media-service.md)** - Сервис для работы с медиа +2. **[Image](models/image.md)** - Изображения фильмов +3. **[Video](models/video.md)** - Видео контент +4. **[ImageType](enums/image-type.md)** - Типы изображений +5. **[VideoSite](enums/video-site.md)** - Сайты видео + +### 📝 Работа с отзывами и фактами + +1. **[Review](models/review.md)** - Отзывы пользователей +2. **[Fact](models/fact.md)** - Интересные факты +3. **[ReviewType](enums/review-type.md)** - Типы отзывов +4. **[ReviewOrder](enums/review-order.md)** - Сортировка отзывов +5. **[FactType](enums/fact-type.md)** - Типы фактов + +## 🔍 Поиск по функциональности + +### 🔍 Поиск и фильтрация + +- **[FilmService::searchByKeyword()](services/film-service.md#searchbykeyword)** - Поиск фильмов +- **[PersonService::searchByName()](services/person-service.md#searchbyname)** - Поиск персон +- **[FilmService::getTop()](services/film-service.md#gettop)** - Топ фильмов +- **[Filters](models/filters.md)** - Фильтры для поиска + +### 📊 Статистика и рейтинги + +- **[BoxOffice](models/box-office.md)** - Кассовые сборы +- **[Award](models/award.md)** - Награды и номинации +- **[UserVote](models/user-vote.md)** - Пользовательские голоса +- **[ExternalSource](models/external-source.md)** - Внешние источники рейтингов + +### 🎬 Сериалы и эпизоды + +- **[Episode](models/episode.md)** - Эпизоды сериалов +- **[Season](models/season.md)** - Сезоны сериалов +- **[FilmService::getSeasons()](services/film-service.md#getseasons)** - Получение сезонов + +### 🔗 Связанный контент + +- **[RelatedFilm](models/related-film.md)** - Связанные фильмы +- **[FilmService::getSequelsAndPrequels()](services/film-service.md#getsequelsandprequels)** - Сиквелы и приквелы +- **[FilmCollection](models/film-collection.md)** - Коллекции фильмов + +## ⚠️ Обработка ошибок + +### 🔑 Аутентификация + +- **[InvalidApiKeyException](exceptions/invalid-api-key-exception.md)** - Неверный API ключ +- **[ApiKeyInfo](models/api-key-info.md)** - Информация об API ключе +- **[ApiKeyQouta](models/api-key-qouta.md)** - Квоты запросов + +### ⏱️ Лимиты и ограничения + +- **[RateLimitException](exceptions/rate-limit-exception.md)** - Превышение лимита запросов +- **[UserService](services/user-service.md)** - Информация о лимитах + +### 🔍 Ошибки ресурсов + +- **[ResourceNotFoundException](exceptions/resource-not-found-exception.md)** - Ресурс не найден +- **[ApiException](exceptions/api-exception.md)** - Общие ошибки API + +### ✅ Валидация данных + +- **[KpValidationException](exceptions/kp-validation-exception.md)** - Ошибки валидации +- **[ModelInterface](interfaces/model-interface.md)** - Базовый интерфейс моделей + +## 🛠️ Утилиты и помощники + +### 📄 Ответы API + +- **[DefaultResponse](responses/default-response.md)** - Базовый ответ +- **[PaginatedResponse](responses/paginated-response.md)** - Пагинация +- **[KeywordSearchResponse](responses/keyword-search-response.md)** - Ответ поиска + +### 🔧 Интерфейсы + +- **[ModelInterface](interfaces/model-interface.md)** - Интерфейс моделей +- **[ResponseInterface](interfaces/response-interface.md)** - Интерфейс ответов + +### 🌍 Справочные данные + +- **[Country](models/country.md)** - Страны +- **[Genre](models/genre.md)** - Жанры +- **[Distribution](models/distribution.md)** - Дистрибуция +- **[Month](enums/month.md)** - Месяцы + +## 📚 Примеры использования + +### 🚀 Быстрый старт + +```php +// Создание клиента +$client = new Client('your-api-key'); + +// Получение фильма +$film = $client->films->getById(301); + +// Поиск фильмов +$searchResults = $client->films->searchByKeyword('матрица'); + +// Получение съемочной группы +$staff = $client->films->getStaff(301); +``` + +### 🔍 Поиск и фильтрация + +```php +// Поиск персон +$persons = $client->persons->searchByName('Том Круз'); + +// Получение топ фильмов +$topFilms = $client->films->getTop(); + +// Получение изображений +$images = $client->media->getImages(301, ImageType::POSTER); +``` + +### ⚠️ Обработка ошибок + +```php +try { + $film = $client->films->getById(999999); +} catch (ResourceNotFoundException $e) { + echo "Фильм не найден\n"; +} catch (RateLimitException $e) { + echo "Превышен лимит запросов\n"; +} +``` + +## 🎯 Рекомендуемые маршруты изучения + +### 🆕 Для новичков + +1. **[Главная страница](index.md)** - Обзор библиотеки +2. **[Основной клиент](client.md)** - Базовые концепции +3. **[FilmService](services/film-service.md)** - Работа с фильмами +4. **[Film](models/film.md)** - Структура данных фильма +5. **[Примеры](../examples/)** - Практические примеры + +### 🔧 Для разработчиков + +1. **[Интерфейсы](interfaces/index.md)** - Базовые контракты +2. **[Исключения](exceptions/index.md)** - Обработка ошибок +3. **[Ответы](responses/index.md)** - Структура ответов API +4. **[Перечисления](enums/index.md)** - Константы и типы +5. **[Модели](models/index.md)** - Все модели данных + +### 🎬 Для работы с контентом + +1. **[FilmService](services/film-service.md)** - Основные операции с фильмами +2. **[PersonService](services/person-service.md)** - Работа с персонами +3. **[MediaService](services/media-service.md)** - Медиа контент +4. **[UserService](services/user-service.md)** - Пользовательские данные +5. **[Связанные модели](models/index.md)** - Дополнительные данные + +### 🔍 Для поиска и анализа + +1. **[FilmService::searchByKeyword()](services/film-service.md#searchbykeyword)** - Поиск фильмов +2. **[PersonService::searchByName()](services/person-service.md#searchbyname)** - Поиск персон +3. **[FilmService::getTop()](services/film-service.md#gettop)** - Топ фильмов +4. **[Статистические модели](models/index.md)** - Анализ данных +5. **[Перечисления для сортировки](enums/index.md)** - Настройка поиска + +## 📊 Статистика документации + +### 📁 Файлы документации + +- **Всего файлов:** 60+ +- **Главная страница:** 1 +- **Сервисы:** 5 +- **Модели:** 25 +- **Перечисления:** 18 +- **Ответы:** 4 +- **Исключения:** 6 +- **Интерфейсы:** 3 + +### 🔗 Связи между компонентами + +- **Перекрестные ссылки:** 200+ +- **Навигационные элементы:** 60+ +- **Примеры кода:** 100+ +- **Связанные классы:** 300+ + +### 📚 Содержание + +- **Строк документации:** 15,000+ +- **Примеров кода:** 500+ +- **Методов API:** 50+ +- **Моделей данных:** 25+ +- **Перечислений:** 18+ + +--- + +**📚 Навигация:** [Главная](index.md) → Карта навигации + +**🔄 Последнее обновление:** diff --git a/docs/dev/notkinopoiskphp/responses/default-response.md b/docs/dev/notkinopoiskphp/responses/default-response.md new file mode 100644 index 0000000..cb2e3dc --- /dev/null +++ b/docs/dev/notkinopoiskphp/responses/default-response.md @@ -0,0 +1,749 @@ +# DefaultResponse + +Базовый класс для ответов Kinopoisk API. + +## Описание + +`DefaultResponse` предоставляет базовую функциональность для всех типов ответов API, включая создание из массива данных и валидацию целевого класса. Содержит общие свойства: общее количество элементов и массив элементов. + +### Основные возможности + +- Хранение общего количества элементов и массива данных +- Создание экземпляра из массива данных API с валидацией +- Автоматическое преобразование элементов в указанный класс +- Проверка существования и совместимости целевого класса + +## Свойства + +- `$total` (int) - Общее количество элементов в коллекции (включая не загруженные) +- `$items` (array) - Массив элементов данных в текущем ответе + +## Конструктор + +```php +public function __construct( + public int $total, + public array $items, +) +``` + +### Параметры + +- `$total` (int) - Общее количество элементов в коллекции +- `$items` (array) - Массив элементов данных + +### Исключения + +- `\NotKinopoisk\Exception\KpValidationException` - Если данные невалидны + +### Пример использования + +```php +// В дочернем классе +public function __construct(int $total, array $films) { + parent::__construct($total, $films); +} +``` + +## Методы + +### fromArray() + +Создает экземпляр ответа из массива данных API. + +```php +public static function fromArray(array $data, string $cls): self +``` + +#### Параметры + +- `$data` (array) - Массив данных от API, содержащий 'total' и 'items' +- `$cls` (string) - Полное имя класса для преобразования элементов + +#### Возвращаемое значение + +- `self` - Новый экземпляр текущего класса с преобразованными данными + +#### Исключения + +- `KpValidationException` - Если указанный класс не существует +- `KpValidationException` - Если класс не имеет метода fromArray +- `KpValidationException` - Если данные имеют неверный формат + +#### Пример использования + +```php +$apiData = [ + 'total' => 1000, + 'items' => [ + ['id' => 1, 'name' => 'Film 1'], + ['id' => 2, 'name' => 'Film 2'] + ] +]; + +$response = FilmResponse::fromArray($apiData, Film::class); +// $response->items будет содержать массив объектов Film +``` + +### checkClass() + +Валидирует целевой класс для преобразования элементов. + +```php +public static function checkClass(string $cls): void +``` + +#### Параметры + +- `$cls` (string) - Полное имя класса + +#### Исключения + +- `KpValidationException` - Если класс невалиден + +#### Описание + +Проверяет: + +- Существование класса +- Наличие статического метода `fromArray` +- Статичность метода `fromArray` + +### first() + +Возвращает первый элемент или null, если элементов нет. + +```php +public function first(): ?object +``` + +#### Возвращаемое значение + +- `object|null` - Первый элемент или null + +#### Пример использования + +```php +$firstFilm = $response->first(); +if ($firstFilm) { + echo "Первый фильм: " . $firstFilm->getDisplayName(); +} +``` + +### last() + +Возвращает последний элемент или null, если элементов нет. + +```php +public function last(): ?object +``` + +#### Возвращаемое значение + +- `object|null` - Последний элемент или null + +#### Пример использования + +```php +$lastFilm = $response->last(); +if ($lastFilm) { + echo "Последний фильм: " . $lastFilm->getDisplayName(); +} +``` + +### isEmpty() + +Проверяет, пуст ли ответ. + +```php +public function isEmpty(): bool +``` + +#### Возвращаемое значение + +- `bool` - `true`, если нет элементов + +#### Пример использования + +```php +if ($response->isEmpty()) { + echo "Результаты не найдены"; +} else { + echo "Найдено элементов: " . $response->getCount(); +} +``` + +### toArray() + +Преобразует ответ в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array{total: int, items: array}` - Массив с данными ответа + +#### Пример использования + +```php +$array = $response->toArray(); +echo "Всего: {$array['total']}, Загружено: " . count($array['items']); +``` + +### getCount() + +Возвращает количество элементов в текущем ответе. + +```php +public function getCount(): int +``` + +#### Возвращаемое значение + +- `int` - Количество загруженных элементов + +#### Пример использования + +```php +echo "Загружено элементов: " . $response->getCount(); +``` + +## Полный пример использования + +```php +films; +$response = $filmService->getTopFilms(); + +echo "=== Работа с DefaultResponse ===\n"; + +// Основная информация +echo "Общее количество фильмов: {$response->total}\n"; +echo "Загружено в ответе: " . $response->getCount() . "\n"; +echo "Ответ пустой: " . ($response->isEmpty() ? 'Да' : 'Нет') . "\n\n"; + +// Работа с элементами +if (!$response->isEmpty()) { + $firstFilm = $response->first(); + $lastFilm = $response->last(); + + echo "Первый фильм: {$firstFilm->getDisplayName()}\n"; + echo "Последний фильм: {$lastFilm->getDisplayName()}\n\n"; + + // Вывод всех фильмов + echo "Список фильмов:\n"; + foreach ($response->items as $index => $film) { + echo ($index + 1) . ". {$film->getDisplayName()}\n"; + if ($film->rating) { + echo " Рейтинг: {$film->rating}\n"; + } + if ($film->year) { + echo " Год: {$film->year}\n"; + } + echo "\n"; + } +} + +// Преобразование в массив +$arrayData = $response->toArray(); +echo "Данные в виде массива:\n"; +echo "total: {$arrayData['total']}\n"; +echo "items count: " . count($arrayData['items']) . "\n"; +``` + +## Работа с DefaultResponse + +```php +// Функция для фильтрации элементов ответа +function filterResponseItems(DefaultResponse $response, callable $filter): array { + return array_filter($response->items, $filter); +} + +// Функция для сортировки элементов ответа +function sortResponseItems(DefaultResponse $response, callable $comparator): array { + $items = $response->items; + usort($items, $comparator); + return $items; +} + +// Функция для получения статистики ответа +function getResponseStats(DefaultResponse $response): array { + return [ + 'total' => $response->total, + 'loaded' => $response->getCount(), + 'isEmpty' => $response->isEmpty(), + 'hasMore' => $response->total > $response->getCount(), + 'loadPercentage' => $response->total > 0 ? round(($response->getCount() / $response->total) * 100, 1) : 0 + ]; +} + +// Функция для получения элементов по индексам +function getResponseItemsByIndexes(DefaultResponse $response, array $indexes): array { + $result = []; + foreach ($indexes as $index) { + if (isset($response->items[$index])) { + $result[] = $response->items[$index]; + } + } + return $result; +} + +// Функция для получения случайных элементов +function getRandomResponseItems(DefaultResponse $response, int $count = 1): array { + if ($response->isEmpty()) { + return []; + } + + $items = $response->items; + $totalItems = count($items); + + if ($count >= $totalItems) { + return $items; + } + + $randomIndexes = array_rand($items, $count); + if (!is_array($randomIndexes)) { + $randomIndexes = [$randomIndexes]; + } + + return array_map(fn($index) => $items[$index], $randomIndexes); +} + +// Функция для группировки элементов +function groupResponseItems(DefaultResponse $response, callable $groupBy): array { + $groups = []; + + foreach ($response->items as $item) { + $key = $groupBy($item); + if (!isset($groups[$key])) { + $groups[$key] = []; + } + $groups[$key][] = $item; + } + + return $groups; +} + +// Функция для поиска элементов +function searchResponseItems(DefaultResponse $response, callable $search): ?object { + foreach ($response->items as $item) { + if ($search($item)) { + return $item; + } + } + return null; +} + +// Функция для получения уникальных элементов +function getUniqueResponseItems(DefaultResponse $response, callable $uniqueBy): array { + $unique = []; + $seen = []; + + foreach ($response->items as $item) { + $key = $uniqueBy($item); + if (!isset($seen[$key])) { + $seen[$key] = true; + $unique[] = $item; + } + } + + return $unique; +} + +// Использование +$response = $filmService->getTopFilms(); + +// Статистика +$stats = getResponseStats($response); +echo "Статистика ответа:\n"; +echo "- Всего элементов: {$stats['total']}\n"; +echo "- Загружено: {$stats['loaded']}\n"; +echo "- Пустой ответ: " . ($stats['isEmpty'] ? 'Да' : 'Нет') . "\n"; +echo "- Есть еще элементы: " . ($stats['hasMore'] ? 'Да' : 'Нет') . "\n"; +echo "- Процент загрузки: {$stats['loadPercentage']}%\n"; + +// Фильтрация +$highRatedFilms = filterResponseItems($response, fn($film) => $film->rating && $film->rating >= 8.0); +echo "\nВысоко оцененных фильмов: " . count($highRatedFilms) . "\n"; + +// Сортировка +$sortedByRating = sortResponseItems($response, function($a, $b) { + $ratingA = $a->rating ?? 0; + $ratingB = $b->rating ?? 0; + return $ratingB <=> $ratingA; +}); + +echo "\nТоп-5 по рейтингу:\n"; +foreach (array_slice($sortedByRating, 0, 5) as $index => $film) { + echo ($index + 1) . ". {$film->getDisplayName()} - {$film->rating}\n"; +} + +// Группировка по годам +$groupedByYear = groupResponseItems($response, fn($film) => $film->year ?? 'Неизвестно'); +echo "\nГруппировка по годам:\n"; +foreach ($groupedByYear as $year => $films) { + echo "- {$year}: " . count($films) . " фильмов\n"; +} + +// Поиск +$matrixFilm = searchResponseItems($response, fn($film) => + stripos($film->getDisplayName(), 'матрица') !== false +); + +if ($matrixFilm) { + echo "\nНайден фильм 'Матрица': {$matrixFilm->getDisplayName()}\n"; +} + +// Случайные элементы +$randomFilms = getRandomResponseItems($response, 3); +echo "\nСлучайные фильмы:\n"; +foreach ($randomFilms as $film) { + echo "- {$film->getDisplayName()}\n"; +} +``` + +## Создание отчета по ответу + +```php +class ResponseReport { + private DefaultResponse $response; + + public function __construct(DefaultResponse $response) { + $this->response = $response; + } + + public function getResponse(): DefaultResponse { + return $this->response; + } + + public function getStats(): array { + return getResponseStats($this->response); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО ОТВЕТУ API ===\n\n"; + + $stats = $this->getStats(); + + // Основная информация + $report .= "📊 ОСНОВНАЯ ИНФОРМАЦИЯ:\n"; + $report .= "Общее количество элементов: {$stats['total']}\n"; + $report .= "Загружено элементов: {$stats['loaded']}\n"; + $report .= "Ответ пустой: " . ($stats['isEmpty'] ? 'Да' : 'Нет') . "\n"; + $report .= "Есть еще элементы: " . ($stats['hasMore'] ? 'Да' : 'Нет') . "\n"; + $report .= "Процент загрузки: {$stats['loadPercentage']}%\n\n"; + + if (!$this->response->isEmpty()) { + // Первый и последний элементы + $first = $this->response->first(); + $last = $this->response->last(); + + $report .= "🎬 ПЕРВЫЙ ЭЛЕМЕНТ:\n"; + $report .= "• {$first->getDisplayName()}\n"; + if (method_exists($first, 'rating') && $first->rating) { + $report .= "• Рейтинг: {$first->rating}\n"; + } + if (method_exists($first, 'year') && $first->year) { + $report .= "• Год: {$first->year}\n"; + } + $report .= "\n"; + + $report .= "🎬 ПОСЛЕДНИЙ ЭЛЕМЕНТ:\n"; + $report .= "• {$last->getDisplayName()}\n"; + if (method_exists($last, 'rating') && $last->rating) { + $report .= "• Рейтинг: {$last->rating}\n"; + } + if (method_exists($last, 'year') && $last->year) { + $report .= "• Год: {$last->year}\n"; + } + $report .= "\n"; + + // Статистика по рейтингам (если есть) + $ratings = array_filter(array_map(fn($item) => + method_exists($item, 'rating') ? $item->rating : null, + $this->response->items + )); + + if (!empty($ratings)) { + $avgRating = round(array_sum($ratings) / count($ratings), 2); + $maxRating = max($ratings); + $minRating = min($ratings); + + $report .= "📈 СТАТИСТИКА ПО РЕЙТИНГАМ:\n"; + $report .= "Средний рейтинг: {$avgRating}\n"; + $report .= "Максимальный рейтинг: {$maxRating}\n"; + $report .= "Минимальный рейтинг: {$minRating}\n"; + $report .= "Элементов с рейтингом: " . count($ratings) . "\n\n"; + } + + // Статистика по годам (если есть) + $years = array_filter(array_map(fn($item) => + method_exists($item, 'year') ? $item->year : null, + $this->response->items + )); + + if (!empty($years)) { + $yearStats = array_count_values($years); + arsort($yearStats); + + $report .= "📅 СТАТИСТИКА ПО ГОДАМ:\n"; + foreach (array_slice($yearStats, 0, 10, true) as $year => $count) { + $report .= "• {$year}: {$count} элементов\n"; + } + $report .= "\n"; + } + + // Список всех элементов + $report .= "📋 СПИСОК ВСЕХ ЭЛЕМЕНТОВ:\n"; + foreach ($this->response->items as $index => $item) { + $report .= ($index + 1) . ". {$item->getDisplayName()}\n"; + if (method_exists($item, 'rating') && $item->rating) { + $report .= " Рейтинг: {$item->rating}\n"; + } + if (method_exists($item, 'year') && $item->year) { + $report .= " Год: {$item->year}\n"; + } + } + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $stats = $this->getStats(); + + // Статистика + $html .= "
\n"; + $html .= "

Основная информация

\n"; + $html .= "

Общее количество элементов: {$stats['total']}

\n"; + $html .= "

Загружено элементов: {$stats['loaded']}

\n"; + $html .= "

Ответ пустой: " . ($stats['isEmpty'] ? 'Да' : 'Нет') . "

\n"; + $html .= "

Есть еще элементы: " . ($stats['hasMore'] ? 'Да' : 'Нет') . "

\n"; + + // Прогресс-бар загрузки + $html .= "

Процент загрузки

\n"; + $html .= "
\n"; + $html .= "
\n"; + $html .= "
\n"; + $html .= "

{$stats['loadPercentage']}% загружено

\n"; + + $html .= "
\n"; + + // Элементы + if (!$this->response->isEmpty()) { + $html .= "
\n"; + $html .= "
📋 Список элементов
\n"; + $html .= "
\n"; + + foreach ($this->response->items as $item) { + $html .= "
\n"; + $html .= "
{$item->getDisplayName()}
\n"; + if (method_exists($item, 'rating') && $item->rating) { + $html .= "
Рейтинг: {$item->rating}
\n"; + } + if (method_exists($item, 'year') && $item->year) { + $html .= "
Год: {$item->year}
\n"; + } + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + $html .= "
\n\n"; + + return $html; + } +} + +// Использование +$response = $filmService->getTopFilms(); +$report = new ResponseReport($response); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Отчет по ответу API'); +file_put_contents('response_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в response_report.html\n"; +``` + +## Анализ ответов API + +```php +function analyzeResponse(DefaultResponse $response): array { + $analysis = [ + 'basicStats' => [ + 'total' => $response->total, + 'loaded' => $response->getCount(), + 'isEmpty' => $response->isEmpty(), + 'hasMore' => $response->total > $response->getCount(), + 'loadPercentage' => $response->total > 0 ? round(($response->getCount() / $response->total) * 100, 1) : 0 + ], + 'itemAnalysis' => [ + 'firstItem' => null, + 'lastItem' => null, + 'ratingStats' => [], + 'yearStats' => [], + 'typeStats' => [] + ] + ]; + + if (!$response->isEmpty()) { + $analysis['itemAnalysis']['firstItem'] = $response->first(); + $analysis['itemAnalysis']['lastItem'] = $response->last(); + + // Анализ рейтингов + $ratings = array_filter(array_map(fn($item) => + method_exists($item, 'rating') ? $item->rating : null, + $response->items + )); + + if (!empty($ratings)) { + $analysis['itemAnalysis']['ratingStats'] = [ + 'count' => count($ratings), + 'average' => round(array_sum($ratings) / count($ratings), 2), + 'max' => max($ratings), + 'min' => min($ratings), + 'distribution' => array_count_values(array_map(fn($r) => floor($r), $ratings)) + ]; + } + + // Анализ годов + $years = array_filter(array_map(fn($item) => + method_exists($item, 'year') ? $item->year : null, + $response->items + )); + + if (!empty($years)) { + $analysis['itemAnalysis']['yearStats'] = [ + 'count' => count($years), + 'distribution' => array_count_values($years), + 'range' => [min($years), max($years)] + ]; + } + + // Анализ типов + $types = array_filter(array_map(fn($item) => + method_exists($item, 'type') ? $item->type->value : null, + $response->items + )); + + if (!empty($types)) { + $analysis['itemAnalysis']['typeStats'] = [ + 'count' => count($types), + 'distribution' => array_count_values($types) + ]; + } + } + + return $analysis; +} + +// Использование +$response = $filmService->getTopFilms(); +$analysis = analyzeResponse($response); + +echo "=== Анализ ответа API ===\n"; + +// Основная статистика +$basicStats = $analysis['basicStats']; +echo "Основная статистика:\n"; +echo "- Всего элементов: {$basicStats['total']}\n"; +echo "- Загружено: {$basicStats['loaded']}\n"; +echo "- Пустой ответ: " . ($basicStats['isEmpty'] ? 'Да' : 'Нет') . "\n"; +echo "- Есть еще элементы: " . ($basicStats['hasMore'] ? 'Да' : 'Нет') . "\n"; +echo "- Процент загрузки: {$basicStats['loadPercentage']}%\n"; + +// Анализ элементов +$itemAnalysis = $analysis['itemAnalysis']; + +if ($itemAnalysis['firstItem']) { + echo "\nПервый элемент: {$itemAnalysis['firstItem']->getDisplayName()}\n"; +} + +if ($itemAnalysis['lastItem']) { + echo "Последний элемент: {$itemAnalysis['lastItem']->getDisplayName()}\n"; +} + +// Статистика рейтингов +if (!empty($itemAnalysis['ratingStats'])) { + $ratingStats = $itemAnalysis['ratingStats']; + echo "\nСтатистика рейтингов:\n"; + echo "- Элементов с рейтингом: {$ratingStats['count']}\n"; + echo "- Средний рейтинг: {$ratingStats['average']}\n"; + echo "- Максимальный рейтинг: {$ratingStats['max']}\n"; + echo "- Минимальный рейтинг: {$ratingStats['min']}\n"; + + echo "\nРаспределение по рейтингам:\n"; + krsort($ratingStats['distribution']); + foreach ($ratingStats['distribution'] as $rating => $count) { + echo "- {$rating}+: {$count} элементов\n"; + } +} + +// Статистика годов +if (!empty($itemAnalysis['yearStats'])) { + $yearStats = $itemAnalysis['yearStats']; + echo "\nСтатистика годов:\n"; + echo "- Элементов с годом: {$yearStats['count']}\n"; + echo "- Диапазон: {$yearStats['range'][0]} - {$yearStats['range'][1]}\n"; + + echo "\nТоп годов:\n"; + arsort($yearStats['distribution']); + foreach (array_slice($yearStats['distribution'], 0, 10, true) as $year => $count) { + echo "- {$year}: {$count} элементов\n"; + } +} + +// Статистика типов +if (!empty($itemAnalysis['typeStats'])) { + $typeStats = $itemAnalysis['typeStats']; + echo "\nСтатистика типов:\n"; + echo "- Элементов с типом: {$typeStats['count']}\n"; + + echo "\nРаспределение по типам:\n"; + arsort($typeStats['distribution']); + foreach ($typeStats['distribution'] as $type => $count) { + echo "- {$type}: {$count} элементов\n"; + } +} +``` + +## Связанные классы + +- [`ResponseInterface`](../interfaces/response-interface.md) - Интерфейс для ответов API +- [`KpValidationException`](../exceptions/kp-validation-exception.md) - Исключение валидации +- [`Film`](../models/film.md) - Модель фильма +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/responses/index.md b/docs/dev/notkinopoiskphp/responses/index.md new file mode 100644 index 0000000..ed0f38a --- /dev/null +++ b/docs/dev/notkinopoiskphp/responses/index.md @@ -0,0 +1,369 @@ +# Ответы API + +Классы для представления ответов от Kinopoisk API. + +--- + +**📚 Навигация:** [Главная](../index.md) → Ответы + +--- + +## 📋 Список ответов + +### 📄 [DefaultResponse](default-response.md) + +Базовый класс для всех ответов API. + +**Основные возможности:** + +- Хранение общего количества элементов +- Хранение массива элементов +- Валидация данных +- Создание из массива API +- Вспомогательные методы для работы с элементами + +**Свойства:** + +- `$total` (int) - Общее количество элементов +- `$items` (array) - Массив элементов + +**Методы:** + +- `first()` - Получение первого элемента +- `last()` - Получение последнего элемента +- `isEmpty()` - Проверка на пустоту +- `getCount()` - Получение количества элементов + +### 📄 [PaginatedResponse](paginated-response.md) + +Ответ с поддержкой пагинации. + +**Основные возможности:** + +- Наследует функциональность DefaultResponse +- Добавляет информацию о пагинации +- Методы для навигации по страницам +- Валидация параметров пагинации + +**Дополнительные свойства:** + +- `$currentPage` (int) - Текущая страница +- `$totalPages` (int) - Общее количество страниц + +**Дополнительные методы:** + +- `getNextPage()` - Получение следующей страницы +- `hasNextPage()` - Проверка наличия следующей страницы +- `getPreviousPage()` - Получение предыдущей страницы +- `hasPreviousPage()` - Проверка наличия предыдущей страницы +- `getPaginationInfo()` - Получение информации о пагинации + +### 🔍 [KeywordSearchResponse](keyword-search-response.md) + +Ответ на поиск фильмов по ключевому слову. + +**Основные возможности:** + +- Наследует функциональность PaginatedResponse +- Специфичные поля для поиска +- Валидация параметров поиска +- Создание из данных API + +**Специфичные свойства:** + +- `$keyword` (string) - Ключевое слово для поиска +- `$pagesCount` (int) - Общее количество страниц результатов +- `$searchFilmsCountResult` (int) - Общее количество найденных фильмов +- `$films` (array) - Массив найденных фильмов + +## 🔗 Связанные компоненты + +### Модели + +- [Film](../models/film.md) - Используется в KeywordSearchResponse +- [FilmSearchResult](../models/film-search-result.md) - Результаты поиска +- [Staff](../models/staff.md) - Съемочная группа +- [Review](../models/review.md) - Отзывы +- [Fact](../models/fact.md) - Факты +- [Image](../models/image.md) - Изображения +- [Video](../models/video.md) - Видео +- [Person](../models/person.md) - Персоны + +### Сервисы + +- [FilmService](../services/film-service.md) - Возвращает ответы с фильмами +- [PersonService](../services/person-service.md) - Возвращает ответы с персонами +- [MediaService](../services/media-service.md) - Возвращает ответы с медиа +- [UserService](../services/user-service.md) - Возвращает ответы с пользовательскими данными + +### Исключения + +- [KpValidationException](../exceptions/kp-validation-exception.md) - Ошибки валидации ответов + +### Интерфейсы + +- [ResponseInterface](../interfaces/response-interface.md) - Базовый интерфейс ответов + +## 🚀 Быстрый старт + +### Работа с базовым ответом + +```php + 301, 'nameRu' => 'Матрица']), + Film::fromArray(['kinopoiskId' => 302, 'nameRu' => 'Матрица: Перезагрузка']) +]; + +$response = new DefaultResponse(2, $films); + +// Работа с ответом +echo "Всего элементов: {$response->total}\n"; +echo "Количество элементов: {$response->getCount()}\n"; +echo "Пустой ответ: " . ($response->isEmpty() ? 'Да' : 'Нет') . "\n"; + +// Получение элементов +$firstFilm = $response->first(); +if ($firstFilm) { + echo "Первый фильм: {$firstFilm->getDisplayName()}\n"; +} + +$lastFilm = $response->last(); +if ($lastFilm) { + echo "Последний фильм: {$lastFilm->getDisplayName()}\n"; +} + +// Перебор элементов +foreach ($response->items as $index => $film) { + echo ($index + 1) . ". {$film->getDisplayName()}\n"; +} +``` + +### Работа с пагинированным ответом + +```php +use NotKinopoisk\Responses\PaginatedResponse; + +// Создание пагинированного ответа +$response = new PaginatedResponse(100, $films, 1, 10); + +// Информация о пагинации +echo "Текущая страница: {$response->currentPage}\n"; +echo "Всего страниц: {$response->totalPages}\n"; +echo "Есть следующая страница: " . ($response->hasNextPage() ? 'Да' : 'Нет') . "\n"; +echo "Есть предыдущая страница: " . ($response->hasPreviousPage() ? 'Да' : 'Нет') . "\n"; + +// Навигация +if ($response->hasNextPage()) { + $nextPage = $response->getNextPage(); + echo "Следующая страница: {$nextPage}\n"; +} + +if ($response->hasPreviousPage()) { + $previousPage = $response->getPreviousPage(); + echo "Предыдущая страница: {$previousPage}\n"; +} + +// Полная информация о пагинации +$paginationInfo = $response->getPaginationInfo(); +print_r($paginationInfo); +``` + +### Работа с ответом поиска + +```php +use NotKinopoisk\Responses\KeywordSearchResponse; + +// Создание ответа поиска +$response = new KeywordSearchResponse( + keyword: 'матрица', + pagesCount: 5, + searchFilmsCountResult: 50, + films: $films +); + +// Информация о поиске +echo "Ключевое слово: '{$response->keyword}'\n"; +echo "Найдено фильмов: {$response->searchFilmsCountResult}\n"; +echo "Количество страниц: {$response->pagesCount}\n"; + +// Работа с пагинацией (наследуется от PaginatedResponse) +echo "Текущая страница: {$response->currentPage}\n"; +echo "Всего страниц: {$response->totalPages}\n"; + +// Работа с фильмами +foreach ($response->films as $film) { + echo "- {$film->getDisplayName()}\n"; +} +``` + +## 📖 Примеры использования + +### Создание ответа из данных API + +```php +// Данные от API +$apiData = [ + 'total' => 2, + 'items' => [ + ['kinopoiskId' => 301, 'nameRu' => 'Матрица'], + ['kinopoiskId' => 302, 'nameRu' => 'Матрица: Перезагрузка'] + ] +]; + +// Создание ответа +$response = DefaultResponse::fromArray($apiData, Film::class); + +echo "Создан ответ с {$response->total} элементами\n"; +``` + +### Создание пагинированного ответа из данных API + +```php +// Данные от API с пагинацией +$apiData = [ + 'total' => 100, + 'items' => $films, + 'currentPage' => 1, + 'totalPages' => 10 +]; + +// Создание пагинированного ответа +$response = PaginatedResponse::fromArray($apiData, Film::class); + +echo "Страница {$response->currentPage} из {$response->totalPages}\n"; +``` + +### Создание ответа поиска из данных API + +```php +// Данные от API поиска +$apiData = [ + 'keyword' => 'матрица', + 'pagesCount' => 5, + 'searchFilmsCountResult' => 50, + 'films' => $films +]; + +// Создание ответа поиска +$response = KeywordSearchResponse::fromArray($apiData, Film::class); + +echo "По запросу '{$response->keyword}' найдено {$response->searchFilmsCountResult} фильмов\n"; +``` + +### Обработка пустых ответов + +```php +// Пустой ответ +$emptyResponse = new DefaultResponse(0, []); + +if ($emptyResponse->isEmpty()) { + echo "Ответ пустой\n"; +} else { + echo "В ответе есть элементы\n"; +} + +// Проверка первого элемента +$first = $emptyResponse->first(); +if ($first === null) { + echo "Первый элемент отсутствует\n"; +} +``` + +### Работа с большими ответами + +```php +// Симуляция большого ответа +$largeResponse = new PaginatedResponse(1000, $films, 1, 100); + +// Получение статистики +echo "Всего элементов: {$largeResponse->total}\n"; +echo "Элементов на странице: " . count($largeResponse->items) . "\n"; +echo "Всего страниц: {$largeResponse->totalPages}\n"; + +// Навигация по страницам +if ($largeResponse->hasNextPage()) { + echo "Можно перейти на следующую страницу\n"; +} + +if ($largeResponse->isLastPage()) { + echo "Это последняя страница\n"; +} + +if ($largeResponse->isFirstPage()) { + echo "Это первая страница\n"; +} +``` + +## 🔧 Общие методы + +### fromArray() + +```php +public static function fromArray(array $data, string $cls): self +``` + +Создает экземпляр ответа из массива данных API. + +### toArray() + +```php +public function toArray(): array +``` + +Преобразует ответ в массив. + +### getCount() + +```php +public function getCount(): int +``` + +Возвращает количество элементов в ответе. + +### isEmpty() + +```php +public function isEmpty(): bool +``` + +Проверяет, пуст ли ответ. + +## 📊 Статистика ответов + +### DefaultResponse + +- **Свойства:** 2 +- **Методы:** 8+ +- **Использование:** Базовый класс для всех ответов + +### PaginatedResponse + +- **Свойства:** 4 (наследует 2 от DefaultResponse) +- **Методы:** 15+ (наследует 8+ от DefaultResponse) +- **Использование:** Все ответы с пагинацией + +### KeywordSearchResponse + +- **Свойства:** 7 (наследует 4 от PaginatedResponse) +- **Методы:** 20+ (наследует 15+ от PaginatedResponse) +- **Использование:** Ответы поиска фильмов + +## 🔗 Связанные разделы + +- [Модели](../models/index.md) - Элементы ответов +- [Сервисы](../services/index.md) - Возвращают ответы +- [Перечисления](../enums/index.md) - Используются в ответах +- [Исключения](../exceptions/index.md) - Обработка ошибок +- [Интерфейсы](../interfaces/index.md) - Базовые интерфейсы + +--- + +**📚 Навигация:** [Главная](../index.md) → Ответы diff --git a/docs/dev/notkinopoiskphp/responses/keyword-search-response.md b/docs/dev/notkinopoiskphp/responses/keyword-search-response.md new file mode 100644 index 0000000..ff685df --- /dev/null +++ b/docs/dev/notkinopoiskphp/responses/keyword-search-response.md @@ -0,0 +1,669 @@ +# KeywordSearchResponse + +Ответ на поиск фильмов по ключевому слову. + +## Описание + +`KeywordSearchResponse` представляет результат поиска фильмов в Kinopoisk API по заданному ключевому слову. Наследует функциональность пагинации от `PaginatedResponse` и добавляет специфичные для поиска поля: ключевое слово, количество страниц и общее количество найденных фильмов. + +### Основные возможности + +- Хранение результатов поиска с пагинацией +- Валидация входных параметров поиска +- Создание объекта из данных API +- Проверка логической согласованности данных + +## Свойства + +### Специфичные для поиска + +- `$keyword` (string) - Ключевое слово для поиска +- `$pagesCount` (int) - Общее количество страниц результатов +- `$searchFilmsCountResult` (int) - Общее количество найденных фильмов +- `$films` (array) - Массив найденных фильмов + +### Наследуемые от PaginatedResponse + +- `$total` (int) - Общее количество элементов (наследуется) +- `$items` (array) - Массив элементов (наследуется) +- `$currentPage` (int) - Текущая страница (всегда 1 для поиска) +- `$totalPages` (int) - Общее количество страниц (наследуется) + +## Конструктор + +```php +public function __construct( + public string $keyword, + public int $pagesCount, + public int $searchFilmsCountResult, + public array $films = [] +) +``` + +### Параметры + +- `$keyword` (string) - Ключевое слово для поиска (не может быть пустым) +- `$pagesCount` (int) - Общее количество страниц результатов (неотрицательное) +- `$searchFilmsCountResult` (int) - Общее количество найденных фильмов (неотрицательное) +- `$films` (array) - Массив найденных фильмов (по умолчанию пустой) + +### Исключения + +- `\NotKinopoisk\Exception\KpValidationException` - При некорректных параметрах поиска + +### Пример использования + +```php +$response = new KeywordSearchResponse( + keyword: 'драма', + pagesCount: 5, + searchFilmsCountResult: 250, + films: $searchResults +); +``` + +## Методы + +### fromArray() + +Создает экземпляр ответа поиска из массива данных API. + +```php +public static function fromArray(array $data, string $cls): self +``` + +#### Параметры + +- `$data` (array) - Массив данных от API, содержащий результаты поиска +- `$cls` (string) - Имя класса для создания объектов фильмов (обычно Film::class) + +#### Возвращаемое значение + +- `self` - Новый экземпляр ответа поиска + +#### Исключения + +- `KpValidationException` - При ошибках типизации или некорректных данных + +#### Пример использования + +```php +$apiResponse = [ + 'keyword' => 'комедия', + 'pagesCount' => 10, + 'searchFilmsCountResult' => 500, + 'films' => [ + ['kinopoiskId' => 123, 'nameRu' => 'Комедия 1'], + ['kinopoiskId' => 456, 'nameRu' => 'Комедия 2'] + ] +]; + +$response = KeywordSearchResponse::fromArray($apiResponse, Film::class); +``` + +### toArray() + +Преобразует ответ поиска в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Ассоциативный массив с данными ответа поиска + +#### Описание + +Конвертирует все свойства объекта в ассоциативный массив, включая наследованные поля от PaginatedResponse. Преобразует объекты фильмов в массивы через их метод toArray(). + +#### Пример использования + +```php +$responseArray = $searchResponse->toArray(); + +// Результат: +// [ +// 'keyword' => 'драма', +// 'pagesCount' => 5, +// 'searchFilmsCountResult' => 250, +// 'films' => [...], // массив фильмов +// 'total' => 250, +// 'currentPage' => 1, +// 'totalPages' => 5 +// ] +``` + +## Полный пример использования + +```php +films; +$response = $filmService->searchByKeyword('драма'); + +echo "=== Работа с KeywordSearchResponse ===\n"; + +// Основная информация о поиске +echo "Ключевое слово: '{$response->keyword}'\n"; +echo "Найдено фильмов: {$response->searchFilmsCountResult}\n"; +echo "Количество страниц: {$response->pagesCount}\n"; +echo "Текущая страница: {$response->currentPage}\n"; +echo "Всего страниц: {$response->totalPages}\n\n"; + +// Информация о пагинации +echo "Информация о пагинации:\n"; +$paginationInfo = $response->getPaginationInfo(); +echo "Текущая страница: {$paginationInfo['current']}\n"; +echo "Всего страниц: {$paginationInfo['total']}\n"; +echo "Есть следующая страница: " . ($paginationInfo['hasNext'] ? 'Да' : 'Нет') . "\n"; +echo "Есть предыдущая страница: " . ($paginationInfo['hasPrevious'] ? 'Да' : 'Нет') . "\n\n"; + +// Вывод найденных фильмов +if (!$response->isEmpty()) { + echo "Найденные фильмы:\n"; + foreach ($response->films as $index => $film) { + echo ($index + 1) . ". {$film->getDisplayName()}\n"; + if ($film->rating) { + echo " Рейтинг: {$film->rating}\n"; + } + if ($film->year) { + echo " Год: {$film->year}\n"; + } + if ($film->description) { + echo " Описание: " . substr($film->description, 0, 100) . "...\n"; + } + echo "\n"; + } +} else { + echo "По запросу '{$response->keyword}' ничего не найдено.\n"; +} + +// Преобразование в массив +$arrayData = $response->toArray(); +echo "Данные в виде массива:\n"; +echo "keyword: {$arrayData['keyword']}\n"; +echo "pagesCount: {$arrayData['pagesCount']}\n"; +echo "searchFilmsCountResult: {$arrayData['searchFilmsCountResult']}\n"; +echo "films count: " . count($arrayData['films']) . "\n"; +``` + +## Работа с поиском по ключевым словам + +```php +// Функция для анализа результатов поиска +function analyzeSearchResults(KeywordSearchResponse $response): array { + return [ + 'searchInfo' => [ + 'keyword' => $response->keyword, + 'totalFound' => $response->searchFilmsCountResult, + 'pagesCount' => $response->pagesCount, + 'currentPage' => $response->currentPage, + 'hasMorePages' => $response->hasNextPage() + ], + 'filmsAnalysis' => [ + 'totalFilms' => count($response->films), + 'filmsWithRating' => count(array_filter($response->films, fn($f) => $f->rating !== null)), + 'filmsWithYear' => count(array_filter($response->films, fn($f) => $f->year !== null)), + 'averageRating' => 0, + 'yearRange' => ['min' => null, 'max' => null] + ] + ]; +} + +// Функция для фильтрации результатов поиска +function filterSearchResults(KeywordSearchResponse $response, callable $filter): array { + return array_filter($response->films, $filter); +} + +// Функция для сортировки результатов поиска +function sortSearchResults(KeywordSearchResponse $response, callable $comparator): array { + $films = $response->films; + usort($films, $comparator); + return $films; +} + +// Функция для получения статистики по годам +function getYearStats(KeywordSearchResponse $response): array { + $years = array_filter(array_map(fn($film) => $film->year, $response->films)); + + if (empty($years)) { + return ['count' => 0, 'distribution' => []]; + } + + return [ + 'count' => count($years), + 'min' => min($years), + 'max' => max($years), + 'average' => round(array_sum($years) / count($years)), + 'distribution' => array_count_values($years) + ]; +} + +// Функция для получения статистики по рейтингам +function getRatingStats(KeywordSearchResponse $response): array { + $ratings = array_filter(array_map(fn($film) => $film->rating, $response->films)); + + if (empty($ratings)) { + return ['count' => 0, 'average' => 0, 'distribution' => []]; + } + + return [ + 'count' => count($ratings), + 'average' => round(array_sum($ratings) / count($ratings), 2), + 'min' => min($ratings), + 'max' => max($ratings), + 'distribution' => array_count_values(array_map(fn($r) => floor($r), $ratings)) + ]; +} + +// Функция для поиска похожих ключевых слов +function findSimilarKeywords(KeywordSearchResponse $response): array { + $similarKeywords = []; + $baseKeyword = strtolower(trim($response->keyword)); + + // Простая логика поиска похожих слов + $variations = [ + $baseKeyword, + $baseKeyword . 'ы', + $baseKeyword . 'и', + $baseKeyword . 'а', + $baseKeyword . 'я' + ]; + + foreach ($variations as $variation) { + if ($variation !== $baseKeyword) { + $similarKeywords[] = $variation; + } + } + + return $similarKeywords; +} + +// Функция для получения топ результатов +function getTopSearchResults(KeywordSearchResponse $response, int $limit = 10): array { + $sortedFilms = sortSearchResults($response, function($a, $b) { + $ratingA = $a->rating ?? 0; + $ratingB = $b->rating ?? 0; + return $ratingB <=> $ratingA; + }); + + return array_slice($sortedFilms, 0, $limit); +} + +// Использование +$response = $filmService->searchByKeyword('комедия'); + +// Анализ результатов +$analysis = analyzeSearchResults($response); +echo "=== Анализ результатов поиска ===\n"; +echo "Ключевое слово: '{$analysis['searchInfo']['keyword']}'\n"; +echo "Всего найдено: {$analysis['searchInfo']['totalFound']}\n"; +echo "Страниц результатов: {$analysis['searchInfo']['pagesCount']}\n"; +echo "Есть еще страницы: " . ($analysis['searchInfo']['hasMorePages'] ? 'Да' : 'Нет') . "\n"; + +// Статистика фильмов +$filmsAnalysis = $analysis['filmsAnalysis']; +echo "\nСтатистика фильмов:\n"; +echo "Всего фильмов на странице: {$filmsAnalysis['totalFilms']}\n"; +echo "Фильмов с рейтингом: {$filmsAnalysis['filmsWithRating']}\n"; +echo "Фильмов с годом: {$filmsAnalysis['filmsWithYear']}\n"; + +// Статистика по годам +$yearStats = getYearStats($response); +if ($yearStats['count'] > 0) { + echo "\nСтатистика по годам:\n"; + echo "Диапазон: {$yearStats['min']} - {$yearStats['max']}\n"; + echo "Средний год: {$yearStats['average']}\n"; + + echo "\nРаспределение по годам:\n"; + arsort($yearStats['distribution']); + foreach (array_slice($yearStats['distribution'], 0, 10, true) as $year => $count) { + echo "- {$year}: {$count} фильмов\n"; + } +} + +// Статистика по рейтингам +$ratingStats = getRatingStats($response); +if ($ratingStats['count'] > 0) { + echo "\nСтатистика по рейтингам:\n"; + echo "Средний рейтинг: {$ratingStats['average']}\n"; + echo "Диапазон: {$ratingStats['min']} - {$ratingStats['max']}\n"; + + echo "\nРаспределение по рейтингам:\n"; + krsort($ratingStats['distribution']); + foreach ($ratingStats['distribution'] as $rating => $count) { + echo "- {$rating}+: {$count} фильмов\n"; + } +} + +// Топ результатов +$topResults = getTopSearchResults($response, 5); +echo "\nТоп-5 результатов:\n"; +foreach ($topResults as $index => $film) { + echo ($index + 1) . ". {$film->getDisplayName()}"; + if ($film->rating) { + echo " (рейтинг: {$film->rating})"; + } + echo "\n"; +} + +// Похожие ключевые слова +$similarKeywords = findSimilarKeywords($response); +echo "\nПохожие ключевые слова:\n"; +foreach ($similarKeywords as $keyword) { + echo "- {$keyword}\n"; +} +``` + +## Создание отчета по поиску + +```php +class SearchReport { + private KeywordSearchResponse $response; + + public function __construct(KeywordSearchResponse $response) { + $this->response = $response; + } + + public function getResponse(): KeywordSearchResponse { + return $this->response; + } + + public function getAnalysis(): array { + return analyzeSearchResults($this->response); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО ПОИСКУ ===\n\n"; + + $analysis = $this->getAnalysis(); + $searchInfo = $analysis['searchInfo']; + $filmsAnalysis = $analysis['filmsAnalysis']; + + // Информация о поиске + $report .= "🔍 ИНФОРМАЦИЯ О ПОИСКЕ:\n"; + $report .= "Ключевое слово: '{$searchInfo['keyword']}'\n"; + $report .= "Всего найдено: {$searchInfo['totalFound']}\n"; + $report .= "Страниц результатов: {$searchInfo['pagesCount']}\n"; + $report .= "Текущая страница: {$searchInfo['currentPage']}\n"; + $report .= "Есть еще страницы: " . ($searchInfo['hasMorePages'] ? 'Да' : 'Нет') . "\n\n"; + + // Статистика фильмов + $report .= "📊 СТАТИСТИКА ФИЛЬМОВ:\n"; + $report .= "Всего фильмов на странице: {$filmsAnalysis['totalFilms']}\n"; + $report .= "Фильмов с рейтингом: {$filmsAnalysis['filmsWithRating']}\n"; + $report .= "Фильмов с годом: {$filmsAnalysis['filmsWithYear']}\n\n"; + + // Статистика по годам + $yearStats = getYearStats($this->response); + if ($yearStats['count'] > 0) { + $report .= "📅 СТАТИСТИКА ПО ГОДАМ:\n"; + $report .= "Диапазон: {$yearStats['min']} - {$yearStats['max']}\n"; + $report .= "Средний год: {$yearStats['average']}\n"; + + $report .= "\nРаспределение по годам:\n"; + arsort($yearStats['distribution']); + foreach (array_slice($yearStats['distribution'], 0, 10, true) as $year => $count) { + $report .= "• {$year}: {$count} фильмов\n"; + } + $report .= "\n"; + } + + // Статистика по рейтингам + $ratingStats = getRatingStats($this->response); + if ($ratingStats['count'] > 0) { + $report .= "⭐ СТАТИСТИКА ПО РЕЙТИНГАМ:\n"; + $report .= "Средний рейтинг: {$ratingStats['average']}\n"; + $report .= "Диапазон: {$ratingStats['min']} - {$ratingStats['max']}\n"; + + $report .= "\nРаспределение по рейтингам:\n"; + krsort($ratingStats['distribution']); + foreach ($ratingStats['distribution'] as $rating => $count) { + $report .= "• {$rating}+: {$count} фильмов\n"; + } + $report .= "\n"; + } + + // Топ результатов + $topResults = getTopSearchResults($this->response, 10); + $report .= "🏆 ТОП-10 РЕЗУЛЬТАТОВ:\n"; + foreach ($topResults as $index => $film) { + $report .= ($index + 1) . ". {$film->getDisplayName()}"; + if ($film->rating) { + $report .= " (рейтинг: {$film->rating})"; + } + if ($film->year) { + $report .= " ({$film->year})"; + } + $report .= "\n"; + } + $report .= "\n"; + + // Похожие ключевые слова + $similarKeywords = findSimilarKeywords($this->response); + if (!empty($similarKeywords)) { + $report .= "🔗 ПОХОЖИЕ КЛЮЧЕВЫЕ СЛОВА:\n"; + foreach ($similarKeywords as $keyword) { + $report .= "• {$keyword}\n"; + } + $report .= "\n"; + } + + // Список всех фильмов + if (!$this->response->isEmpty()) { + $report .= "📋 ВСЕ НАЙДЕННЫЕ ФИЛЬМЫ:\n"; + foreach ($this->response->films as $index => $film) { + $report .= ($index + 1) . ". {$film->getDisplayName()}\n"; + if ($film->rating) { + $report .= " Рейтинг: {$film->rating}\n"; + } + if ($film->year) { + $report .= " Год: {$film->year}\n"; + } + if ($film->description) { + $report .= " Описание: " . substr($film->description, 0, 150) . "...\n"; + } + } + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $analysis = $this->getAnalysis(); + $searchInfo = $analysis['searchInfo']; + + // Статистика + $html .= "
\n"; + $html .= "

Информация о поиске

\n"; + $html .= "

Ключевое слово: {$searchInfo['keyword']}

\n"; + $html .= "

Всего найдено: {$searchInfo['totalFound']}

\n"; + $html .= "

Страниц результатов: {$searchInfo['pagesCount']}

\n"; + $html .= "

Текущая страница: {$searchInfo['currentPage']}

\n"; + $html .= "

Есть еще страницы: " . ($searchInfo['hasMorePages'] ? 'Да' : 'Нет') . "

\n"; + $html .= "
\n"; + + // Топ результатов + $topResults = getTopSearchResults($this->response, 10); + $html .= "
\n"; + $html .= "
🏆 Топ-10 результатов
\n"; + $html .= "
\n"; + + foreach ($topResults as $item) { + $html .= "
\n"; + $html .= "
{$item->getDisplayName()}
\n"; + if ($item->rating) { + $ratingClass = $item->rating >= 8 ? 'rating-high' : ($item->rating >= 6 ? 'rating-medium' : 'rating-low'); + $html .= "
Рейтинг: {$item->rating}
\n"; + } + if ($item->year) { + $html .= "
Год: {$item->year}
\n"; + } + if ($item->description) { + $html .= "
Описание: " . substr($item->description, 0, 100) . "...
\n"; + } + $html .= "
\n"; + } + + $html .= "
\n
\n"; + + // Все фильмы + if (!$this->response->isEmpty()) { + $html .= "
\n"; + $html .= "
📋 Все найденные фильмы
\n"; + $html .= "
\n"; + + foreach ($this->response->films as $item) { + $html .= "
\n"; + $html .= "
{$item->getDisplayName()}
\n"; + if ($item->rating) { + $ratingClass = $item->rating >= 8 ? 'rating-high' : ($item->rating >= 6 ? 'rating-medium' : 'rating-low'); + $html .= "
Рейтинг: {$item->rating}
\n"; + } + if ($item->year) { + $html .= "
Год: {$item->year}
\n"; + } + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + $html .= "
\n\n"; + + return $html; + } +} + +// Использование +$response = $filmService->searchByKeyword('комедия'); +$report = new SearchReport($response); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Отчет по поиску: ' . $response->keyword); +file_put_contents('search_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в search_report.html\n"; +``` + +## Анализ поисковых запросов + +```php +function analyzeSearchQuery(KeywordSearchResponse $response): array { + $analysis = [ + 'queryInfo' => [ + 'keyword' => $response->keyword, + 'keywordLength' => strlen($response->keyword), + 'isSingleWord' => !str_contains($response->keyword, ' '), + 'wordCount' => count(explode(' ', trim($response->keyword))), + 'hasSpecialChars' => preg_match('/[^a-zA-Zа-яА-Я0-9\s]/', $response->keyword) + ], + 'resultsInfo' => [ + 'totalFound' => $response->searchFilmsCountResult, + 'pagesCount' => $response->pagesCount, + 'currentPage' => $response->currentPage, + 'hasMorePages' => $response->hasNextPage(), + 'resultsPerPage' => count($response->films), + 'averageResultsPerPage' => $response->pagesCount > 0 ? round($response->searchFilmsCountResult / $response->pagesCount, 1) : 0 + ], + 'contentAnalysis' => [ + 'filmsWithRating' => count(array_filter($response->films, fn($f) => $f->rating !== null)), + 'filmsWithYear' => count(array_filter($response->films, fn($f) => $f->year !== null)), + 'filmsWithDescription' => count(array_filter($response->films, fn($f) => !empty($f->description))), + 'averageRating' => 0, + 'yearRange' => ['min' => null, 'max' => null] + ] + ]; + + // Вычисляем средний рейтинг + $ratings = array_filter(array_map(fn($film) => $film->rating, $response->films)); + if (!empty($ratings)) { + $analysis['contentAnalysis']['averageRating'] = round(array_sum($ratings) / count($ratings), 2); + } + + // Вычисляем диапазон годов + $years = array_filter(array_map(fn($film) => $film->year, $response->films)); + if (!empty($years)) { + $analysis['contentAnalysis']['yearRange'] = ['min' => min($years), 'max' => max($years)]; + } + + return $analysis; +} + +// Использование +$response = $filmService->searchByKeyword('комедия'); +$analysis = analyzeSearchQuery($response); + +echo "=== Анализ поискового запроса ===\n"; + +// Информация о запросе +$queryInfo = $analysis['queryInfo']; +echo "Информация о запросе:\n"; +echo "- Ключевое слово: '{$queryInfo['keyword']}'\n"; +echo "- Длина ключевого слова: {$queryInfo['keywordLength']} символов\n"; +echo "- Одно слово: " . ($queryInfo['isSingleWord'] ? 'Да' : 'Нет') . "\n"; +echo "- Количество слов: {$queryInfo['wordCount']}\n"; +echo "- Содержит спецсимволы: " . ($queryInfo['hasSpecialChars'] ? 'Да' : 'Нет') . "\n"; + +// Информация о результатах +$resultsInfo = $analysis['resultsInfo']; +echo "\nИнформация о результатах:\n"; +echo "- Всего найдено: {$resultsInfo['totalFound']}\n"; +echo "- Страниц результатов: {$resultsInfo['pagesCount']}\n"; +echo "- Результатов на странице: {$resultsInfo['resultsPerPage']}\n"; +echo "- Среднее результатов на страницу: {$resultsInfo['averageResultsPerPage']}\n"; +echo "- Есть еще страницы: " . ($resultsInfo['hasMorePages'] ? 'Да' : 'Нет') . "\n"; + +// Анализ контента +$contentAnalysis = $analysis['contentAnalysis']; +echo "\nАнализ контента:\n"; +echo "- Фильмов с рейтингом: {$contentAnalysis['filmsWithRating']}\n"; +echo "- Фильмов с годом: {$contentAnalysis['filmsWithYear']}\n"; +echo "- Фильмов с описанием: {$contentAnalysis['filmsWithDescription']}\n"; + +if ($contentAnalysis['averageRating'] > 0) { + echo "- Средний рейтинг: {$contentAnalysis['averageRating']}\n"; +} + +if ($contentAnalysis['yearRange']['min'] !== null) { + echo "- Диапазон годов: {$contentAnalysis['yearRange']['min']} - {$contentAnalysis['yearRange']['max']}\n"; +} +``` + +## Связанные классы + +- [`PaginatedResponse`](paginated-response.md) - Базовый класс для пагинированных ответов +- [`KpValidationException`](../exceptions/kp-validation-exception.md) - Исключение валидации +- [`Film`](../models/film.md) - Модель фильма +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/responses/paginated-response.md b/docs/dev/notkinopoiskphp/responses/paginated-response.md new file mode 100644 index 0000000..8c1dd74 --- /dev/null +++ b/docs/dev/notkinopoiskphp/responses/paginated-response.md @@ -0,0 +1,839 @@ +# PaginatedResponse + +Пагинированный ответ API с поддержкой навигации по страницам. + +## Описание + +`PaginatedResponse` расширяет `DefaultResponse`, добавляя функциональность пагинации с безопасной навигацией по страницам и валидацией границ. + +### Основные возможности + +- Безопасная навигация по страницам с валидацией границ +- Promoted constructor properties для краткости кода +- Typed properties для строгой типизации +- Улучшенная обработка ошибок + +## Свойства + +### Наследуемые от DefaultResponse + +- `$total` (int) - Общее количество элементов в коллекции +- `$items` (array) - Массив элементов данных в текущем ответе + +### Специфичные для пагинации + +- `$currentPage` (int) - Номер текущей страницы (начиная с 1) +- `$totalPages` (int) - Общее количество страниц + +## Конструктор + +```php +public function __construct( + int $total, + array $items, + public int $currentPage, + public int $totalPages +) +``` + +### Параметры + +- `$total` (int) - Общее количество элементов +- `$items` (array) - Массив элементов текущей страницы +- `$currentPage` (int) - Номер текущей страницы (начиная с 1) +- `$totalPages` (int) - Общее количество страниц + +### Исключения + +- `\NotKinopoisk\Exception\KpValidationException` - При некорректных значениях пагинации + +### Пример использования + +```php +$response = new PaginatedResponse( + total: 1000, + items: $filmsArray, + currentPage: 5, + totalPages: 50 +); +``` + +## Методы + +### fromArray() + +Создает экземпляр из массива данных API. + +```php +public static function fromArray(array $data, string $cls): self +``` + +#### Параметры + +- `$data` (array) - Данные API с обязательными ключами +- `$cls` (string) - Класс для преобразования элементов + +#### Возвращаемое значение + +- `self` - Новый экземпляр с преобразованными данными + +#### Исключения + +- `KpValidationException` - При ошибках валидации или преобразования + +#### Пример использования + +```php +$apiData = [ + 'total' => 1000, + 'total_pages' => 50, + 'current_page' => 5, + 'items' => [...] +]; + +$response = PaginatedResponse::fromArray($apiData, Film::class); +``` + +### getNextPage() + +Получает номер следующей страницы. + +```php +public function getNextPage(): int +``` + +#### Возвращаемое значение + +- `int` - Номер следующей страницы + +#### Исключения + +- `\OutOfBoundsException` - Если следующая страница недоступна + +#### Пример использования + +```php +if ($response->hasNextPage()) { + $nextPage = $response->getNextPage(); + echo "Следующая страница: {$nextPage}"; +} +``` + +### hasNextPage() + +Проверяет наличие следующей страницы. + +```php +public function hasNextPage(): bool +``` + +#### Возвращаемое значение + +- `bool` - Доступна ли следующая страница + +#### Пример использования + +```php +if ($response->hasNextPage()) { + echo "Есть следующая страница"; +} else { + echo "Это последняя страница"; +} +``` + +### getPreviousPage() + +Получает номер предыдущей страницы. + +```php +public function getPreviousPage(): int +``` + +#### Возвращаемое значение + +- `int` - Номер предыдущей страницы + +#### Исключения + +- `\OutOfBoundsException` - Если предыдущая страница недоступна + +#### Пример использования + +```php +if ($response->hasPreviousPage()) { + $prevPage = $response->getPreviousPage(); + echo "Предыдущая страница: {$prevPage}"; +} +``` + +### hasPreviousPage() + +Проверяет наличие предыдущей страницы. + +```php +public function hasPreviousPage(): bool +``` + +#### Возвращаемое значение + +- `bool` - Доступна ли предыдущая страница + +#### Пример использования + +```php +if ($response->hasPreviousPage()) { + echo "Есть предыдущая страница"; +} else { + echo "Это первая страница"; +} +``` + +### getFirstPage() + +Получает номер первой страницы. + +```php +public function getFirstPage(): int +``` + +#### Возвращаемое значение + +- `int` - Всегда возвращает 1 + +#### Пример использования + +```php +$firstPage = $response->getFirstPage(); // Всегда 1 +``` + +### getLastPage() + +Получает номер последней страницы. + +```php +public function getLastPage(): int +``` + +#### Возвращаемое значение + +- `int` - Номер последней страницы + +#### Пример использования + +```php +$lastPage = $response->getLastPage(); +echo "Последняя страница: {$lastPage}"; +``` + +### isFirstPage() + +Проверяет, является ли текущая страница первой. + +```php +public function isFirstPage(): bool +``` + +#### Возвращаемое значение + +- `bool` - Является ли первой страницей + +#### Пример использования + +```php +if ($response->isFirstPage()) { + echo "Это первая страница"; +} +``` + +### isLastPage() + +Проверяет, является ли текущая страница последней. + +```php +public function isLastPage(): bool +``` + +#### Возвращаемое значение + +- `bool` - Является ли последней страницей + +#### Пример использования + +```php +if ($response->isLastPage()) { + echo "Это последняя страница"; +} +``` + +### getPaginationInfo() + +Получает информацию о пагинации в виде массива. + +```php +public function getPaginationInfo(): array +``` + +#### Возвращаемое значение + +- `array{current: int, total: int, hasNext: bool, hasPrevious: bool}` - Информация о пагинации + +#### Пример использования + +```php +$paginationInfo = $response->getPaginationInfo(); +echo "Страница {$paginationInfo['current']} из {$paginationInfo['total']}"; +echo "Следующая страница: " . ($paginationInfo['hasNext'] ? 'Да' : 'Нет'); +``` + +### toArray() + +Преобразует объект пагинированного ответа в массив. + +```php +public function toArray(): array +``` + +#### Возвращаемое значение + +- `array` - Массив с данными пагинации + +#### Описание + +Создает массив со всеми данными о пагинации и элементами результата. Каждый элемент в массиве items также преобразуется в массив с помощью вызова его метода toArray(). + +#### Пример использования + +```php +$paginatedResponse = new PaginatedResponse( + items: [$film1, $film2], + total: 150, + currentPage: 1, + totalPages: 15 +); + +$array = $paginatedResponse->toArray(); +// Результат: +// [ +// 'total' => 150, +// 'items' => [ +// ['id' => 1, 'name' => 'Фильм 1', ...], +// ['id' => 2, 'name' => 'Фильм 2', ...] +// ], +// 'current_page' => 1, +// 'total_pages' => 15 +// ] +``` + +## Полный пример использования + +```php +films; +$response = $filmService->getTopFilms(page: 1); + +echo "=== Работа с PaginatedResponse ===\n"; + +// Основная информация +echo "Общее количество фильмов: {$response->total}\n"; +echo "Загружено на текущей странице: " . $response->getCount() . "\n"; +echo "Текущая страница: {$response->currentPage}\n"; +echo "Всего страниц: {$response->totalPages}\n\n"; + +// Навигация по страницам +echo "Навигация:\n"; +echo "Первая страница: {$response->getFirstPage()}\n"; +echo "Последняя страница: {$response->getLastPage()}\n"; + +if ($response->hasPreviousPage()) { + echo "Предыдущая страница: {$response->getPreviousPage()}\n"; +} else { + echo "Предыдущая страница: недоступна\n"; +} + +if ($response->hasNextPage()) { + echo "Следующая страница: {$response->getNextPage()}\n"; +} else { + echo "Следующая страница: недоступна\n"; +} + +echo "\n"; + +// Проверки страниц +echo "Проверки:\n"; +echo "Это первая страница: " . ($response->isFirstPage() ? 'Да' : 'Нет') . "\n"; +echo "Это последняя страница: " . ($response->isLastPage() ? 'Да' : 'Нет') . "\n\n"; + +// Информация о пагинации +$paginationInfo = $response->getPaginationInfo(); +echo "Информация о пагинации:\n"; +echo "Текущая страница: {$paginationInfo['current']}\n"; +echo "Всего страниц: {$paginationInfo['total']}\n"; +echo "Есть следующая страница: " . ($paginationInfo['hasNext'] ? 'Да' : 'Нет') . "\n"; +echo "Есть предыдущая страница: " . ($paginationInfo['hasPrevious'] ? 'Да' : 'Нет') . "\n\n"; + +// Вывод фильмов +if (!$response->isEmpty()) { + echo "Фильмы на текущей странице:\n"; + foreach ($response->items as $index => $film) { + echo ($index + 1) . ". {$film->getDisplayName()}\n"; + if ($film->rating) { + echo " Рейтинг: {$film->rating}\n"; + } + if ($film->year) { + echo " Год: {$film->year}\n"; + } + echo "\n"; + } +} + +// Преобразование в массив +$arrayData = $response->toArray(); +echo "Данные в виде массива:\n"; +echo "total: {$arrayData['total']}\n"; +echo "current_page: {$arrayData['current_page']}\n"; +echo "total_pages: {$arrayData['total_pages']}\n"; +echo "items count: " . count($arrayData['items']) . "\n"; +``` + +## Работа с пагинацией + +```php +// Функция для навигации по всем страницам +function navigateAllPages(PaginatedResponse $response, callable $pageCallback): void { + $currentPage = $response->currentPage; + $totalPages = $response->totalPages; + + echo "Начинаем навигацию с страницы {$currentPage} из {$totalPages}\n"; + + // Обрабатываем текущую страницу + $pageCallback($response); + + // Переходим к следующим страницам + while ($response->hasNextPage()) { + $nextPage = $response->getNextPage(); + echo "Переходим к странице {$nextPage}\n"; + + // Здесь должен быть запрос к API для получения следующей страницы + // $nextResponse = $filmService->getTopFilms(page: $nextPage); + // $pageCallback($nextResponse); + } +} + +// Функция для получения диапазона страниц +function getPageRange(PaginatedResponse $response, int $startPage, int $endPage): array { + $range = []; + + for ($page = $startPage; $page <= $endPage; $page++) { + if ($page >= 1 && $page <= $response->totalPages) { + $range[] = $page; + } + } + + return $range; +} + +// Функция для получения статистики пагинации +function getPaginationStats(PaginatedResponse $response): array { + return [ + 'total' => $response->total, + 'totalPages' => $response->totalPages, + 'currentPage' => $response->currentPage, + 'itemsPerPage' => $response->getCount(), + 'averageItemsPerPage' => $response->totalPages > 0 ? round($response->total / $response->totalPages, 1) : 0, + 'progress' => $response->totalPages > 0 ? round(($response->currentPage / $response->totalPages) * 100, 1) : 0, + 'hasNext' => $response->hasNextPage(), + 'hasPrevious' => $response->hasPreviousPage(), + 'isFirst' => $response->isFirstPage(), + 'isLast' => $response->isLastPage() + ]; +} + +// Функция для создания навигационного меню +function createPaginationMenu(PaginatedResponse $response, int $maxVisiblePages = 5): array { + $menu = []; + $currentPage = $response->currentPage; + $totalPages = $response->totalPages; + + // Добавляем кнопку "Первая" + if (!$response->isFirstPage()) { + $menu[] = ['type' => 'first', 'page' => 1, 'text' => '« Первая']; + } + + // Добавляем кнопку "Предыдущая" + if ($response->hasPreviousPage()) { + $menu[] = ['type' => 'previous', 'page' => $response->getPreviousPage(), 'text' => '‹ Предыдущая']; + } + + // Вычисляем диапазон страниц для отображения + $startPage = max(1, $currentPage - floor($maxVisiblePages / 2)); + $endPage = min($totalPages, $startPage + $maxVisiblePages - 1); + + // Корректируем начальную страницу, если нужно + if ($endPage - $startPage + 1 < $maxVisiblePages) { + $startPage = max(1, $endPage - $maxVisiblePages + 1); + } + + // Добавляем номера страниц + for ($page = $startPage; $page <= $endPage; $page++) { + $menu[] = [ + 'type' => $page === $currentPage ? 'current' : 'page', + 'page' => $page, + 'text' => (string)$page + ]; + } + + // Добавляем кнопку "Следующая" + if ($response->hasNextPage()) { + $menu[] = ['type' => 'next', 'page' => $response->getNextPage(), 'text' => 'Следующая ›']; + } + + // Добавляем кнопку "Последняя" + if (!$response->isLastPage()) { + $menu[] = ['type' => 'last', 'page' => $totalPages, 'text' => 'Последняя »']; + } + + return $menu; +} + +// Функция для получения элементов с нескольких страниц +function getItemsFromMultiplePages(PaginatedResponse $response, array $pageNumbers): array { + $allItems = []; + + foreach ($pageNumbers as $pageNumber) { + if ($pageNumber >= 1 && $pageNumber <= $response->totalPages) { + // Здесь должен быть запрос к API для получения страницы + // $pageResponse = $filmService->getTopFilms(page: $pageNumber); + // $allItems = array_merge($allItems, $pageResponse->items); + } + } + + return $allItems; +} + +// Использование +$response = $filmService->getTopFilms(page: 1); + +// Статистика пагинации +$stats = getPaginationStats($response); +echo "Статистика пагинации:\n"; +echo "- Всего элементов: {$stats['total']}\n"; +echo "- Всего страниц: {$stats['totalPages']}\n"; +echo "- Текущая страница: {$stats['currentPage']}\n"; +echo "- Элементов на странице: {$stats['itemsPerPage']}\n"; +echo "- Среднее элементов на странице: {$stats['averageItemsPerPage']}\n"; +echo "- Прогресс: {$stats['progress']}%\n"; +echo "- Есть следующая страница: " . ($stats['hasNext'] ? 'Да' : 'Нет') . "\n"; +echo "- Есть предыдущая страница: " . ($stats['hasPrevious'] ? 'Да' : 'Нет') . "\n"; + +// Навигационное меню +$menu = createPaginationMenu($response, 7); +echo "\nНавигационное меню:\n"; +foreach ($menu as $item) { + $type = $item['type']; + $text = $item['text']; + + switch ($type) { + case 'current': + echo "[{$text}] "; + break; + case 'page': + echo "{$text} "; + break; + default: + echo "{$text} "; + } +} +echo "\n"; + +// Диапазон страниц +$pageRange = getPageRange($response, 1, 5); +echo "\nДиапазон страниц 1-5: " . implode(', ', $pageRange) . "\n"; +``` + +## Создание отчета по пагинации + +```php +class PaginationReport { + private PaginatedResponse $response; + + public function __construct(PaginatedResponse $response) { + $this->response = $response; + } + + public function getResponse(): PaginatedResponse { + return $this->response; + } + + public function getStats(): array { + return getPaginationStats($this->response); + } + + public function createDetailedReport(): string { + $report = "=== ОТЧЕТ ПО ПАГИНАЦИИ ===\n\n"; + + $stats = $this->getStats(); + + // Основная информация + $report .= "📊 ОСНОВНАЯ ИНФОРМАЦИЯ:\n"; + $report .= "Общее количество элементов: {$stats['total']}\n"; + $report .= "Всего страниц: {$stats['totalPages']}\n"; + $report .= "Текущая страница: {$stats['currentPage']}\n"; + $report .= "Элементов на текущей странице: {$stats['itemsPerPage']}\n"; + $report .= "Среднее элементов на странице: {$stats['averageItemsPerPage']}\n"; + $report .= "Прогресс: {$stats['progress']}%\n\n"; + + // Навигация + $report .= "🧭 НАВИГАЦИЯ:\n"; + $report .= "Первая страница: {$this->response->getFirstPage()}\n"; + $report .= "Последняя страница: {$this->response->getLastPage()}\n"; + + if ($this->response->hasPreviousPage()) { + $report .= "Предыдущая страница: {$this->response->getPreviousPage()}\n"; + } else { + $report .= "Предыдущая страница: недоступна\n"; + } + + if ($this->response->hasNextPage()) { + $report .= "Следующая страница: {$this->response->getNextPage()}\n"; + } else { + $report .= "Следующая страница: недоступна\n"; + } + $report .= "\n"; + + // Статус страниц + $report .= "📄 СТАТУС СТРАНИЦ:\n"; + $report .= "Это первая страница: " . ($stats['isFirst'] ? 'Да' : 'Нет') . "\n"; + $report .= "Это последняя страница: " . ($stats['isLast'] ? 'Да' : 'Нет') . "\n"; + $report .= "Есть предыдущая страница: " . ($stats['hasPrevious'] ? 'Да' : 'Нет') . "\n"; + $report .= "Есть следующая страница: " . ($stats['hasNext'] ? 'Да' : 'Нет') . "\n\n"; + + // Навигационное меню + $menu = createPaginationMenu($this->response, 7); + $report .= "🔗 НАВИГАЦИОННОЕ МЕНЮ:\n"; + foreach ($menu as $item) { + $type = $item['type']; + $text = $item['text']; + + switch ($type) { + case 'current': + $report .= "[{$text}] "; + break; + case 'page': + $report .= "{$text} "; + break; + default: + $report .= "{$text} "; + } + } + $report .= "\n\n"; + + // Элементы текущей страницы + if (!$this->response->isEmpty()) { + $report .= "📋 ЭЛЕМЕНТЫ ТЕКУЩЕЙ СТРАНИЦЫ:\n"; + foreach ($this->response->items as $index => $item) { + $report .= ($index + 1) . ". {$item->getDisplayName()}\n"; + if (method_exists($item, 'rating') && $item->rating) { + $report .= " Рейтинг: {$item->rating}\n"; + } + if (method_exists($item, 'year') && $item->year) { + $report .= " Год: {$item->year}\n"; + } + } + } + + return $report; + } + + public function createHtmlReport(string $title): string { + $html = "\n\n\n"; + $html .= "{$title}\n"; + $html .= "\n\n\n"; + $html .= "
\n"; + $html .= "

{$title}

\n"; + + $stats = $this->getStats(); + + // Статистика + $html .= "
\n"; + $html .= "

Основная информация

\n"; + $html .= "

Общее количество элементов: {$stats['total']}

\n"; + $html .= "

Всего страниц: {$stats['totalPages']}

\n"; + $html .= "

Текущая страница: {$stats['currentPage']}

\n"; + $html .= "

Элементов на странице: {$stats['itemsPerPage']}

\n"; + $html .= "

Среднее элементов на странице: {$stats['averageItemsPerPage']}

\n"; + + // Прогресс-бар + $html .= "

Прогресс

\n"; + $html .= "
\n"; + $html .= "
\n"; + $html .= "
\n"; + $html .= "

{$stats['progress']}% завершено

\n"; + + $html .= "
\n"; + + // Навигация + $html .= "
\n"; + $html .= "
🧭 Навигация
\n"; + + $menu = createPaginationMenu($this->response, 7); + $html .= "\n"; + $html .= "
\n"; + + // Элементы + if (!$this->response->isEmpty()) { + $html .= "
\n"; + $html .= "
📋 Элементы текущей страницы
\n"; + $html .= "
\n"; + + foreach ($this->response->items as $item) { + $html .= "
\n"; + $html .= "
{$item->getDisplayName()}
\n"; + if (method_exists($item, 'rating') && $item->rating) { + $html .= "
Рейтинг: {$item->rating}
\n"; + } + if (method_exists($item, 'year') && $item->year) { + $html .= "
Год: {$item->year}
\n"; + } + $html .= "
\n"; + } + + $html .= "
\n
\n"; + } + + $html .= "
\n\n"; + + return $html; + } +} + +// Использование +$response = $filmService->getTopFilms(page: 1); +$report = new PaginationReport($response); + +// Создание текстового отчета +$textReport = $report->createDetailedReport(); +echo $textReport; + +// Создание HTML отчета +$htmlReport = $report->createHtmlReport('Отчет по пагинации'); +file_put_contents('pagination_report.html', $htmlReport); +echo "\n✅ HTML отчет сохранен в pagination_report.html\n"; +``` + +## Анализ пагинации + +```php +function analyzePagination(PaginatedResponse $response): array { + $analysis = [ + 'basicStats' => [ + 'total' => $response->total, + 'totalPages' => $response->totalPages, + 'currentPage' => $response->currentPage, + 'itemsPerPage' => $response->getCount(), + 'averageItemsPerPage' => $response->totalPages > 0 ? round($response->total / $response->totalPages, 1) : 0, + 'progress' => $response->totalPages > 0 ? round(($response->currentPage / $response->totalPages) * 100, 1) : 0 + ], + 'navigation' => [ + 'hasNext' => $response->hasNextPage(), + 'hasPrevious' => $response->hasPreviousPage(), + 'isFirst' => $response->isFirstPage(), + 'isLast' => $response->isLastPage(), + 'nextPage' => $response->hasNextPage() ? $response->getNextPage() : null, + 'previousPage' => $response->hasPreviousPage() ? $response->getPreviousPage() : null, + 'firstPage' => $response->getFirstPage(), + 'lastPage' => $response->getLastPage() + ], + 'pageDistribution' => [ + 'pagesBeforeCurrent' => $response->currentPage - 1, + 'pagesAfterCurrent' => $response->totalPages - $response->currentPage, + 'totalPagesInRange' => $response->totalPages, + 'currentPagePosition' => $response->totalPages > 0 ? round(($response->currentPage / $response->totalPages) * 100, 1) : 0 + ] + ]; + + return $analysis; +} + +// Использование +$response = $filmService->getTopFilms(page: 1); +$analysis = analyzePagination($response); + +echo "=== Анализ пагинации ===\n"; + +// Основная статистика +$basicStats = $analysis['basicStats']; +echo "Основная статистика:\n"; +echo "- Всего элементов: {$basicStats['total']}\n"; +echo "- Всего страниц: {$basicStats['totalPages']}\n"; +echo "- Текущая страница: {$basicStats['currentPage']}\n"; +echo "- Элементов на странице: {$basicStats['itemsPerPage']}\n"; +echo "- Среднее элементов на странице: {$basicStats['averageItemsPerPage']}\n"; +echo "- Прогресс: {$basicStats['progress']}%\n"; + +// Навигация +$navigation = $analysis['navigation']; +echo "\nНавигация:\n"; +echo "- Есть следующая страница: " . ($navigation['hasNext'] ? 'Да' : 'Нет') . "\n"; +echo "- Есть предыдущая страница: " . ($navigation['hasPrevious'] ? 'Да' : 'Нет') . "\n"; +echo "- Это первая страница: " . ($navigation['isFirst'] ? 'Да' : 'Нет') . "\n"; +echo "- Это последняя страница: " . ($navigation['isLast'] ? 'Да' : 'Нет') . "\n"; + +if ($navigation['nextPage']) { + echo "- Следующая страница: {$navigation['nextPage']}\n"; +} +if ($navigation['previousPage']) { + echo "- Предыдущая страница: {$navigation['previousPage']}\n"; +} + +// Распределение страниц +$pageDistribution = $analysis['pageDistribution']; +echo "\nРаспределение страниц:\n"; +echo "- Страниц до текущей: {$pageDistribution['pagesBeforeCurrent']}\n"; +echo "- Страниц после текущей: {$pageDistribution['pagesAfterCurrent']}\n"; +echo "- Всего страниц в диапазоне: {$pageDistribution['totalPagesInRange']}\n"; +echo "- Позиция текущей страницы: {$pageDistribution['currentPagePosition']}%\n"; +``` + +## Связанные классы + +- [`DefaultResponse`](default-response.md) - Базовый класс для ответов API +- [`KpValidationException`](../exceptions/kp-validation-exception.md) - Исключение валидации +- [`Film`](../models/film.md) - Модель фильма +- [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/services/film-service.md b/docs/dev/notkinopoiskphp/services/film-service.md new file mode 100644 index 0000000..56e20b7 --- /dev/null +++ b/docs/dev/notkinopoiskphp/services/film-service.md @@ -0,0 +1,418 @@ +# FilmService - Сервис для работы с фильмами + +Сервис для работы с фильмами в Kinopoisk API. + +--- + +**📚 Навигация:** [Главная](../index.md) → [Сервисы](index.md) → FilmService + +**🔗 Связанные классы:** + +- [Client](../client.md) - Основной клиент +- [Film](../models/film.md) - Модель фильма +- [Staff](../models/staff.md) - Модель съемочной группы +- [Review](../models/review.md) - Модель отзыва +- [Fact](../models/fact.md) - Модель факта +- [Image](../models/image.md) - Модель изображения +- [Video](../models/video.md) - Модель видео +- [Award](../models/award.md) - Модель награды +- [BoxOffice](../models/box-office.md) - Модель кассовых сборов +- [Episode](../models/episode.md) - Модель эпизода +- [Season](../models/season.md) - Модель сезона +- [RelatedFilm](../models/related-film.md) - Модель связанного фильма +- [FilmSearchResult](../models/film-search-result.md) - Модель результата поиска +- [KeywordSearchResponse](../responses/keyword-search-response.md) - Ответ поиска +- [ImageType](../enums/image-type.md) - Типы изображений +- [ReviewOrder](../enums/review-order.md) - Порядок сортировки отзывов +- [ReviewType](../enums/review-type.md) - Типы отзывов +- [FactType](../enums/fact-type.md) - Типы фактов +- [VideoSite](../enums/video-site.md) - Сайты видео +- [BoxOfficeType](../enums/box-office-type.md) - Типы кассовых сборов +- [ContentType](../enums/content-type.md) - Типы контента +- [FilmOrder](../enums/film-order.md) - Порядок сортировки фильмов +- [Month](../enums/month.md) - Месяцы +- [ApiException](../exceptions/api-exception.md) - Базовое исключение API +- [ResourceNotFoundException](../exceptions/resource-not-found-exception.md) - Ресурс не найден +- [RateLimitException](../exceptions/rate-limit-exception.md) - Превышение лимита + +--- + +## Описание + +Предоставляет полный набор методов для взаимодействия с фильмами через Kinopoisk API. Реализует CRUD операции: Create (поиск), Read (получение данных), Update (не поддерживается), Delete (не поддерживается). + +## Основные возможности + +- Получение детальной информации о фильмах +- Поиск фильмов по ключевым словам и фильтрам +- Получение связанного контента (сезоны, факты, награды, отзывы) +- Работа с коллекциями фильмов (популярные, топ-250) +- Получение премьер и фильтров для поиска + +## Конструктор + +### `__construct(Client $client)` + +Инициализирует новый экземпляр сервиса с переданным HTTP-клиентом и устанавливает версию API v2.2 для работы с Kinopoisk Unofficial API. + +**Параметры:** + +- `$client` (Client) - HTTP-клиент для выполнения запросов к API + +**Пример:** + +```php +$client = new Client('your-api-key'); +$service = new FilmService($client); +``` + +## Методы + +### `getById(int $id): Film` + +Получает детальную информацию о фильме по его ID. + +**Параметры:** + +- `$id` (int) - ID фильма в базе Kinopoisk + +**Возвращает:** + +- `Film` - Объект с информацией о фильме + +**Пример:** + +```php +$film = $filmService->getById(301); // Матрица +echo $film->getDisplayName(); +``` + +### `getSeasons(int $id): DefaultResponse` + +Получает информацию о сезонах сериала. + +**Параметры:** + +- `$id` (int) - ID сериала + +**Возвращает:** + +- `DefaultResponse` - Ответ с сезонами сериала + +### `getFacts(int $id): DefaultResponse` + +Получает факты и ошибки о фильме. + +**Параметры:** + +- `$id` (int) - ID фильма + +**Возвращает:** + +- `DefaultResponse` - Ответ с фактами о фильме + +### `getDistributions(int $id): DefaultResponse` + +Получает информацию о прокате фильма. + +**Параметры:** + +- `$id` (int) - ID фильма + +**Возвращает:** + +- `DefaultResponse` - Ответ с информацией о прокате + +### `getBoxOffice(int $id): BudgetResponse` + +Получает информацию о кассовых сборах фильма. + +**Параметры:** + +- `$id` (int) - ID фильма + +**Возвращает:** + +- `BudgetResponse` - Ответ с информацией о кассовых сборах + +### `getAwards(int $id): DefaultResponse` + +Получает информацию о наградах фильма. + +**Параметры:** + +- `$id` (int) - ID фильма + +**Возвращает:** + +- `DefaultResponse` - Ответ с информацией о наградах + +### `getVideos(int $id): DefaultResponse` + +Получает видео контент фильма (трейлеры, клипы). + +**Параметры:** + +- `$id` (int) - ID фильма + +**Возвращает:** + +- `DefaultResponse` - Ответ с видео контентом + +### `getSimilar(int $id): DefaultResponse` + +Получает похожие фильмы. + +**Параметры:** + +- `$id` (int) - ID фильма + +**Возвращает:** + +- `DefaultResponse` - Ответ с похожими фильмами + +### `getImages(int $id, ImageType $type = ImageType::STILL, int $page = 1): PaginatedResponse` + +Получает изображения фильма определенного типа. + +**Параметры:** + +- `$id` (int) - ID фильма +- `$type` (ImageType) - Тип изображений (по умолчанию STILL) +- `$page` (int) - Номер страницы (по умолчанию 1) + +**Возвращает:** + +- `PaginatedResponse` - Пагинированный ответ с изображениями + +**Пример:** + +```php +// Получение постеров +$posters = $filmService->getImages(301, ImageType::POSTER); + +// Получение кадров из фильма +$stills = $filmService->getImages(301, ImageType::STILL); +``` + +### `getReviews(int $id, int $page = 1, ReviewOrder $order = ReviewOrder::DATE_DESC): ReviewResponse` + +Получает отзывы о фильме. + +**Параметры:** + +- `$id` (int) - ID фильма +- `$page` (int) - Номер страницы (по умолчанию 1) +- `$order` (ReviewOrder) - Порядок сортировки (по умолчанию DATE_DESC) + +**Возвращает:** + +- `ReviewResponse` - Ответ с отзывами + +### `getExternalSources(int $id, int $page = 1): PaginatedResponse` + +Получает внешние источники фильма. + +**Параметры:** + +- `$id` (int) - ID фильма +- `$page` (int) - Номер страницы (по умолчанию 1) + +**Возвращает:** + +- `PaginatedResponse` - Пагинированный ответ с внешними источниками + +### `getSequelsAndPrequels(int $id): SequelPrequelResponse` + +Получает сиквелы и приквелы фильма. + +**Параметры:** + +- `$id` (int) - ID фильма + +**Возвращает:** + +- `SequelPrequelResponse` - Ответ с сиквелами и приквелами + +### `searchByKeyword(string $keyword, int $page = 1): KeywordSearchResponse` + +Поиск фильмов по ключевому слову. + +**Параметры:** + +- `$keyword` (string) - Ключевое слово для поиска +- `$page` (int) - Номер страницы (по умолчанию 1) + +**Возвращает:** + +- `KeywordSearchResponse` - Ответ с результатами поиска + +**Пример:** + +```php +$results = $filmService->searchByKeyword('матрица'); +foreach ($results->items as $film) { + echo $film->getDisplayName() . "\n"; +} +``` + +### `getPremieres(int $year, Month $month): DefaultResponse` + +Получает премьеры фильмов в указанном месяце и году. + +**Параметры:** + +- `$year` (int) - Год +- `$month` (Month) - Месяц + +**Возвращает:** + +- `DefaultResponse` - Ответ с премьерами + +### `getFilters(): Filters` + +Получает доступные фильтры для поиска. + +**Возвращает:** + +- `Filters` - Объект с доступными фильтрами + +### `getPopular(int $page = 1): PaginatedResponse` + +Получает популярные фильмы. + +**Параметры:** + +- `$page` (int) - Номер страницы (по умолчанию 1) + +**Возвращает:** + +- `PaginatedResponse` - Пагинированный ответ с популярными фильмами + +### `getCollections(CollectionType $type = CollectionType::TOP_POPULAR_ALL, int $page = 1): PaginatedResponse` + +Получает коллекции фильмов. + +**Параметры:** + +- `$type` (CollectionType) - Тип коллекции (по умолчанию TOP_POPULAR_ALL) +- `$page` (int) - Номер страницы (по умолчанию 1) + +**Возвращает:** + +- `PaginatedResponse` - Пагинированный ответ с коллекцией фильмов + +### `getTop250(int $page = 1): PaginatedResponse` + +Получает топ-250 фильмов. + +**Параметры:** + +- `$page` (int) - Номер страницы (по умолчанию 1) + +**Возвращает:** + +- `PaginatedResponse` - Пагинированный ответ с топ-250 фильмами + +### `searchFilmsByFilter(...): PaginatedResponse` + +Поиск фильмов с использованием фильтров. + +**Параметры:** + +- `$country` (array|null) - Массив стран +- `$genre` (array|null) - Массив жанров +- `$order` (FilmOrder) - Порядок сортировки (по умолчанию RATING) +- `$type` (ContentType) - Тип контента (по умолчанию ALL) +- `$ratingFrom` (float) - Минимальный рейтинг (по умолчанию 0) +- `$ratingTo` (float) - Максимальный рейтинг (по умолчанию 10) +- `$yearFrom` (int) - Минимальный год (по умолчанию 1000) +- `$yearTo` (int) - Максимальный год (по умолчанию 3000) +- `$imdbId` (string|null) - IMDB ID +- `$keyword` (string|null) - Ключевое слово +- `$page` (int) - Номер страницы (по умолчанию 1) + +**Возвращает:** + +- `PaginatedResponse` - Пагинированный ответ с результатами поиска + +**Пример:** + +```php +// Поиск боевиков из США с рейтингом выше 7 +$results = $filmService->searchFilmsByFilter( + country: ['США'], + genre: ['боевик'], + order: FilmOrder::RATING, + type: ContentType::FILM, + ratingFrom: 7.0, + yearFrom: 2020 +); +``` + +## Примеры использования + +### Базовое использование + +```php +films; + +// Получение информации о фильме +$film = $filmService->getById(301); +echo $film->getDisplayName(); + +// Поиск фильмов +$results = $filmService->searchByKeyword('матрица'); +foreach ($results->items as $film) { + echo $film->getDisplayName() . "\n"; +} +``` + +### Работа с изображениями + +```php +// Получение постеров +$posters = $filmService->getImages(301, ImageType::POSTER); + +// Получение кадров из фильма +$stills = $filmService->getImages(301, ImageType::STILL); + +// Получение фан-артов +$fanArts = $filmService->getImages(301, ImageType::FAN_ART); +``` + +### Работа с коллекциями + +```php +// Получение популярных фильмов +$popular = $filmService->getPopular(); + +// Получение топ-250 фильмов +$top250 = $filmService->getTop250(); + +// Получение коллекций +$collections = $filmService->getCollections(CollectionType::TOP_POPULAR_MOVIES); +``` + +## Связанные классы + +- `\NotKinopoisk\Models\Film` - Модель фильма +- `\NotKinopoisk\Models\FilmCollection` - Коллекция фильмов +- `\NotKinopoisk\Responses\PaginatedResponse` - Пагинированный ответ +- `\NotKinopoisk\Responses\KeywordSearchResponse` - Ответ поиска по ключевым словам +- `\NotKinopoisk\Enums\ImageType` - Типы изображений +- `\NotKinopoisk\Enums\CollectionType` - Типы коллекций +- `\NotKinopoisk\Enums\FilmOrder` - Порядок сортировки фильмов + +## Информация о пакете + +- **Пакет:** NotKinopoisk\Services +- **Версия:** 1.0.0 +- **Автор:** Maxim Harder +- **API версия:** v2.2 diff --git a/docs/dev/notkinopoiskphp/services/index.md b/docs/dev/notkinopoiskphp/services/index.md new file mode 100644 index 0000000..e1969b7 --- /dev/null +++ b/docs/dev/notkinopoiskphp/services/index.md @@ -0,0 +1,220 @@ +# Сервисы + +Сервисы для работы с различными компонентами Kinopoisk API. + +--- + +**📚 Навигация:** [Главная](../index.md) → Сервисы + +--- + +## 📋 Список сервисов + +### 🎬 [FilmService](film-service.md) + +Сервис для работы с фильмами, сериалами и связанным контентом. + +**Основные возможности:** + +- Получение информации о фильмах +- Поиск фильмов по ключевым словам +- Получение топ фильмов +- Работа со съемочной группой +- Получение отзывов и фактов +- Работа с медиа контентом + +**Связанные модели:** + +- [Film](../models/film.md) +- [Staff](../models/staff.md) +- [Review](../models/review.md) +- [Fact](../models/fact.md) +- [Image](../models/image.md) +- [Video](../models/video.md) + +### 👥 [PersonService](person-service.md) + +Сервис для работы с персонами (актеры, режиссеры, сценаристы). + +**Основные возможности:** + +- Получение информации о персонах +- Поиск персон по имени +- Получение фильмографии +- Работа с биографией и фактами + +**Связанные модели:** + +- [Person](../models/person.md) +- [PersonFilm](../models/person-film.md) +- [PersonSpouse](../models/person-spouse.md) + +### 🎥 [MediaService](media-service.md) + +Сервис для работы с медиа контентом (изображения, видео). + +**Основные возможности:** + +- Получение изображений фильмов +- Работа с видео контентом +- Фильтрация по типам медиа + +**Связанные модели:** + +- [Image](../models/image.md) +- [Video](../models/video.md) +- [ImageType](../enums/image-type.md) +- [VideoSite](../enums/video-site.md) + +### 👤 [UserService](user-service.md) + +Сервис для работы с пользовательскими данными. + +**Основные возможности:** + +- Получение информации об API ключе +- Работа с квотами запросов +- Получение пользовательских голосов + +**Связанные модели:** + +- [ApiKeyInfo](../models/api-key-info.md) +- [ApiKeyQouta](../models/api-key-qouta.md) +- [UserVote](../models/user-vote.md) + +## 🔗 Общие компоненты + +Все сервисы используют общие компоненты: + +### Исключения + +- [ApiException](../exceptions/api-exception.md) - Базовое исключение API +- [InvalidApiKeyException](../exceptions/invalid-api-key-exception.md) - Неверный API ключ +- [RateLimitException](../exceptions/rate-limit-exception.md) - Превышение лимита запросов +- [ResourceNotFoundException](../exceptions/resource-not-found-exception.md) - Ресурс не найден +- [KpValidationException](../exceptions/kp-validation-exception.md) - Ошибка валидации + +### Ответы + +- [DefaultResponse](../responses/default-response.md) - Базовый ответ +- [PaginatedResponse](../responses/paginated-response.md) - Пагинированный ответ +- [KeywordSearchResponse](../responses/keyword-search-response.md) - Ответ поиска + +## 🚀 Быстрый старт + +```php +films->getById(301); +$searchResults = $client->films->searchByKeyword('матрица'); + +// Работа с персонами +$person = $client->persons->getById(123); +$persons = $client->persons->searchByName('Том Круз'); + +// Работа с медиа +$images = $client->media->getImages(301); +$videos = $client->media->getVideos(301); + +// Работа с пользователем +$apiInfo = $client->user->getApiKeyInfo(); +``` + +## 📖 Примеры использования + +### Получение полной информации о фильме + +```php +$filmId = 301; + +// Основная информация +$film = $client->films->getById($filmId); + +// Съемочная группа +$staff = $client->films->getStaff($filmId); + +// Отзывы +$reviews = $client->films->getReviews($filmId); + +// Факты +$facts = $client->films->getFacts($filmId); + +// Изображения +$posters = $client->media->getImages($filmId, ImageType::POSTER); +$screenshots = $client->media->getImages($filmId, ImageType::SCREENSHOT); + +// Видео +$videos = $client->media->getVideos($filmId); +``` + +### Поиск и анализ + +```php +// Поиск фильмов +$searchResults = $client->films->searchByKeyword('драма'); + +// Получение топ фильмов +$topFilms = $client->films->getTop(); + +// Поиск персон +$persons = $client->persons->searchByName('Кристофер Нолан'); + +// Получение фильмографии +$films = $client->persons->getFilms(123); +``` + +## 🔧 Обработка ошибок + +```php +try { + $film = $client->films->getById(999999); +} catch (ResourceNotFoundException $e) { + echo "Фильм не найден: {$e->getMessage()}\n"; +} catch (RateLimitException $e) { + echo "Превышен лимит запросов\n"; + // Ждем и повторяем + sleep(1); +} catch (InvalidApiKeyException $e) { + echo "Неверный API ключ\n"; +} catch (ApiException $e) { + echo "Ошибка API: {$e->getMessage()}\n"; +} +``` + +## 📊 Статистика использования + +### FilmService + +- **Методы:** 15+ +- **Модели:** 10+ +- **Перечисления:** 8+ + +### PersonService + +- **Методы:** 8+ +- **Модели:** 3+ +- **Перечисления:** 2+ + +### MediaService + +- **Методы:** 6+ +- **Модели:** 2+ +- **Перечисления:** 2+ + +### UserService + +- **Методы:** 4+ +- **Модели:** 3+ +- **Перечисления:** 1+ + +--- + +**📚 Навигация:** [Главная](../index.md) → Сервисы diff --git a/docs/dev/notkinopoiskphp/services/media-service.md b/docs/dev/notkinopoiskphp/services/media-service.md new file mode 100644 index 0000000..66eb65d --- /dev/null +++ b/docs/dev/notkinopoiskphp/services/media-service.md @@ -0,0 +1,189 @@ +# MediaService + +Сервис для работы с медиа контентом в Kinopoisk API. + +## Описание + +`MediaService` предоставляет методы для получения медиа материалов, связанных с фильмами: новости, статьи, интервью и другие публикации. + +### Основные возможности + +- Получение медиа постов о фильмах +- Наследование функциональности от AbstractService +- Использование API версии v1 по умолчанию +- Доступ к HTTP-клиенту для выполнения запросов + +## Конструктор + +```php +public function __construct(\NotKinopoisk\Client $client, string $apiVersion = 'v1.4') +``` + +### Параметры + +- `$client` - Экземпляр клиента для выполнения HTTP запросов +- `$apiVersion` - Версия API (по умолчанию 'v1.4') + +## Методы + +### getPosts() + +Получает список медиа-постов с поддержкой пагинации. + +```php +public function getPosts(int $page = 1): PaginatedResponse +``` + +**API Endpoint:** `/api/v1/media_posts` + +#### Параметры + +- `$page` (int) - Номер страницы для загрузки (начиная с 1, по умолчанию первая страница) + +#### Возвращаемое значение + +- `PaginatedResponse` - Пагинированный ответ содержащий коллекцию медиа-постов с метаданными навигации + +#### Исключения + +- `\NotKinopoisk\Exception\ApiException` - При общих ошибках API или проблемах сети +- `\NotKinopoisk\Exception\InvalidApiKeyException` - Если API ключ недействителен, заблокирован или отсутствует +- `\NotKinopoisk\Exception\KpValidationException` - При некорректном номере страницы или других параметрах валидации +- `\NotKinopoisk\Exception\RateLimitException` - При превышении лимитов запросов (дневных или общих) +- `\NotKinopoisk\Exception\ResourceNotFoundException` - Если запрашиваемая страница или ресурс не найден + +#### Пример использования + +```php +// Получение первой страницы медиа-постов +$posts = $mediaService->getPosts(); + +foreach ($posts->items as $post) { + echo "Заголовок: {$post->title}\n"; + echo "Описание: {$post->description}\n"; + echo "URL: {$post->url}\n"; + echo "Дата публикации: {$post->publishedAt}\n"; + echo "Изображение: {$post->imageUrl}\n"; + echo "---\n"; +} + +// Пагинация +if ($posts->hasNextPage()) { + $nextPagePosts = $mediaService->getPosts($posts->getNextPage()); +} + +// Получение конкретной страницы +$secondPagePosts = $mediaService->getPosts(2); +echo "Страница {$secondPagePosts->currentPage} из {$secondPagePosts->totalPages}"; +``` + +## Полный пример использования + +```php +media; + +// Получение медиа постов +try { + $posts = $mediaService->getPosts(1); + + echo "=== Медиа посты ===\n"; + echo "Всего постов: {$posts->total}\n"; + echo "Страница {$posts->currentPage} из {$posts->totalPages}\n\n"; + + foreach ($posts->items as $post) { + echo "📰 {$post->title}\n"; + echo "📝 {$post->description}\n"; + echo "🔗 {$post->url}\n"; + echo "📅 {$post->publishedAt}\n"; + + if ($post->imageUrl) { + echo "🖼️ {$post->imageUrl}\n"; + } + echo "---\n"; + } + + // Навигация по страницам + if ($posts->hasNextPage()) { + echo "Следующая страница: {$posts->getNextPage()}\n"; + } + + if ($posts->hasPreviousPage()) { + echo "Предыдущая страница: {$posts->getPreviousPage()}\n"; + } + +} catch (\NotKinopoisk\Exception\ApiException $e) { + echo "Ошибка API: {$e->getMessage()}\n"; +} catch (\NotKinopoisk\Exception\RateLimitException $e) { + echo "Превышен лимит запросов\n"; +} catch (\Exception $e) { + echo "Ошибка: {$e->getMessage()}\n"; +} +``` + +## Навигация по страницам + +```php +// Функция для получения всех медиа постов +function getAllMediaPosts(MediaService $mediaService): array { + $allPosts = []; + $page = 1; + + do { + try { + $posts = $mediaService->getPosts($page); + $allPosts = array_merge($allPosts, $posts->items); + + echo "Загружена страница {$page} из {$posts->totalPages}\n"; + + $page++; + + } catch (\Exception $e) { + echo "Ошибка при загрузке страницы {$page}: {$e->getMessage()}\n"; + break; + } + + } while ($page <= $posts->totalPages); + + return $allPosts; +} + +// Использование +$allPosts = getAllMediaPosts($mediaService); +echo "Всего загружено постов: " . count($allPosts) . "\n"; +``` + +## Фильтрация и поиск + +```php +// Функция для поиска постов по ключевому слову +function searchMediaPosts(array $posts, string $keyword): array { + $keyword = strtolower($keyword); + + return array_filter($posts, function($post) use ($keyword) { + return strpos(strtolower($post->title), $keyword) !== false || + strpos(strtolower($post->description), $keyword) !== false; + }); +} + +// Использование +$posts = $mediaService->getPosts(1); +$filteredPosts = searchMediaPosts($posts->items, 'интервью'); + +echo "Найдено постов с 'интервью': " . count($filteredPosts) . "\n"; +``` + +## Связанные классы + +- [`MediaPost`](../models/media-post.md) - Модель медиа поста +- [`PaginatedResponse`](../responses/paginated-response.md) - Пагинированный ответ diff --git a/docs/dev/notkinopoiskphp/services/person-service.md b/docs/dev/notkinopoiskphp/services/person-service.md new file mode 100644 index 0000000..9516210 --- /dev/null +++ b/docs/dev/notkinopoiskphp/services/person-service.md @@ -0,0 +1,192 @@ +# PersonService + +Сервис для работы с персонами в Kinopoisk API. + +## Описание + +`PersonService` предоставляет методы для получения информации о персонах (актерах, режиссерах, сценаристах и т.д.) из Kinopoisk API. Поддерживает поиск персон и получение детальной информации о конкретной персоне. + +### Основные возможности + +- Поиск персон по различным критериям +- Получение детальной информации о персоне +- Поддержка пагинации результатов поиска +- Обработка ошибок API + +## Конструктор + +```php +public function __construct(\NotKinopoisk\Client $client, string $apiVersion = 'v1.4') +``` + +### Параметры + +- `$client` - Экземпляр клиента для выполнения HTTP запросов +- `$apiVersion` - Версия API (по умолчанию 'v1.4') + +## Методы + +### searchByName() + +Поиск персон по имени. + +```php +public function searchByName(string $name, int $page = 1): PaginatedResponse +``` + +**API Endpoint:** `/api/v1/persons` + +#### Параметры + +- `$name` (string) - Имя или часть имени для поиска +- `$page` (int) - Номер страницы (по умолчанию 1) + +#### Возвращаемое значение + +- `PaginatedResponse` - Результат поиска персон + +#### Исключения + +- `\NotKinopoisk\Exception\ApiException` - При ошибках API + +#### Пример использования + +```php +$results = $personService->searchByName('Том Круз'); +echo "Найдено персон: {$results->getCount()}\n"; + +foreach ($results->items as $person) { + echo "- {$person->name} (ID: {$person->personId})\n"; +} +``` + +### getById() + +Получает детальную информацию о персоне по ID. + +```php +public function getById(int $id): Person +``` + +**API Endpoint:** `/api/v1/staff/{id}` + +#### Параметры + +- `$id` (int) - Уникальный идентификатор персоны в Кинопоиске + +#### Возвращаемое значение + +- `Person` - Объект персоны с полной информацией + +#### Исключения + +- `\NotKinopoisk\Exception\ResourceNotFoundException` - Если персона с указанным ID не найдена +- `\NotKinopoisk\Exception\ApiException` - При других ошибках API + +#### Пример использования + +```php +$person = $personService->getById(12345); +echo "Имя: " . $person->getDisplayName(); +echo "Профессия: " . $person->profession; +echo "Биография: " . $person->biography; +``` + +### getFilmStaff() + +Получает персонал фильма (актеры, режиссеры и другие участники). + +```php +public function getFilmStaff(int $filmId): MovieStaffResponse +``` + +**API Endpoint:** `/api/v1/staff` + +#### Параметры + +- `$filmId` (int) - Уникальный идентификатор фильма в Кинопоиске + +#### Возвращаемое значение + +- `MovieStaffResponse` - Массив объектов персонала фильма + +#### Исключения + +- `\NotKinopoisk\Exception\ApiException` - При общих ошибках API +- `\NotKinopoisk\Exception\InvalidApiKeyException` - При неверном или недействительном API ключе +- `\NotKinopoisk\Exception\KpValidationException` - При ошибках валидации +- `\NotKinopoisk\Exception\RateLimitException` - При превышении лимита запросов +- `\NotKinopoisk\Exception\ResourceNotFoundException` - При отсутствии фильма с указанным ID + +#### Пример использования + +```php +// Получение персонала фильма "Матрица" (ID: 301) +$staff = $personService->getFilmStaff(301); + +echo "Всего участников: " . count($staff) . "\n"; + +foreach ($staff as $person) { + echo "{$person->getDisplayName()} - {$person->professionText}"; + + if ($person->description) { + echo " ({$person->description})"; + } + echo "\n"; +} + +// Фильтрация по типу профессии +$actors = array_filter($staff, fn($person) => $person->isActor()); +$directors = array_filter($staff, fn($person) => $person->isDirector()); + +echo "Актеров: " . count($actors) . "\n"; +echo "Режиссеров: " . count($directors) . "\n"; +``` + +## Полный пример использования + +```php +persons; + +// Поиск персон +$searchResults = $personService->searchByName('Киану Ривз'); +echo "Найдено персон: {$searchResults->getCount()}\n"; + +// Получение информации о первой найденной персоне +if (!empty($searchResults->items)) { + $firstPerson = $searchResults->items[0]; + $person = $personService->getById($firstPerson->personId); + + echo "Детальная информация:\n"; + echo "Имя: " . $person->getDisplayName() . "\n"; + echo "Профессия: " . $person->profession . "\n"; + echo "Биография: " . substr($person->biography, 0, 200) . "...\n"; +} + +// Получение персонала фильма +$staff = $personService->getFilmStaff(301); // Матрица +echo "Персонал фильма 'Матрица':\n"; + +foreach ($staff as $member) { + echo "- {$member->getDisplayName()} ({$member->professionText})\n"; +} +``` + +## Связанные классы + +- [`Person`](../models/person.md) - Модель персоны +- [`PersonByNameResult`](../models/person-by-name-result.md) - Результат поиска по имени +- [`Staff`](../models/staff.md) - Модель участника съемочной группы +- [`MovieStaffResponse`](../responses/movie-staff-response.md) - Ответ с персоналом фильма +- [`PaginatedResponse`](../responses/paginated-response.md) - Пагинированный ответ diff --git a/docs/dev/notkinopoiskphp/services/user-service.md b/docs/dev/notkinopoiskphp/services/user-service.md new file mode 100644 index 0000000..b913870 --- /dev/null +++ b/docs/dev/notkinopoiskphp/services/user-service.md @@ -0,0 +1,190 @@ +# UserService + +Сервис для работы с пользовательскими данными в Kinopoisk API. + +## Описание + +`UserService` предоставляет методы для получения информации о пользователе и его API ключе, включая статистику использования и лимиты запросов. + +### Основные возможности + +- Получение информации об API ключе +- Просмотр статистики использования +- Проверка лимитов и квот +- Получение оценок пользователя + +## Конструктор + +```php +public function __construct(\NotKinopoisk\Client $client, string $apiVersion = 'v1.4') +``` + +### Параметры + +- `$client` - Экземпляр клиента для выполнения HTTP запросов +- `$apiVersion` - Версия API (по умолчанию 'v1.4') + +## Методы + +### getApiKeyInfo() + +Получает информацию об API ключе. + +```php +public function getApiKeyInfo(string $apiKey): ApiKeyInfo +``` + +**API Endpoint:** `/api/v1/api_keys/{apiKey}` + +#### Параметры + +- `$apiKey` (string) - API ключ для проверки + +#### Возвращаемое значение + +- `ApiKeyInfo` - Информация об API ключе + +#### Исключения + +- `\NotKinopoisk\Exception\InvalidApiKeyException` - Если API ключ неверный +- `\NotKinopoisk\Exception\ApiException` - При других ошибках API + +#### Пример использования + +```php +$keyInfo = $userService->getApiKeyInfo('your-api-key'); + +echo "Тип аккаунта: {$keyInfo->accountType}\n"; +echo "Использовано запросов: {$keyInfo->getTotalQuotaUsed()}\n"; +echo "Лимит запросов: {$keyInfo->getTotalQuotaValue()}\n"; +echo "Осталось запросов: {$keyInfo->getRemainingQuota()}\n"; +``` + +### getVotes() + +Получает оценки пользователя. + +```php +public function getVotes(int $userId, int $page = 1): PaginatedResponse +``` + +**API Endpoint:** `/api/v1/kp_users/{userId}/votes` + +#### Параметры + +- `$userId` (int) - ID пользователя +- `$page` (int) - Номер страницы для пагинации (по умолчанию 1) + +#### Возвращаемое значение + +- `PaginatedResponse` - Пагинированный список оценок пользователя + +#### Исключения + +- `\NotKinopoisk\Exception\ApiException` - При ошибках API +- `\NotKinopoisk\Exception\ResourceNotFoundException` - Если пользователь не найден + +#### Пример использования + +```php +$votes = $userService->getVotes(12345, 1); +echo "Всего оценок: {$votes->total}\n"; + +foreach ($votes->items as $vote) { + echo "Фильм: {$vote->filmName}, Оценка: {$vote->rating}\n"; +} +``` + +## Полный пример использования + +```php +users; + +// Получение информации об API ключе +try { + $keyInfo = $userService->getApiKeyInfo('your-api-key'); + + echo "=== Информация об API ключе ===\n"; + echo "Тип аккаунта: {$keyInfo->accountType}\n"; + echo "Использовано запросов: {$keyInfo->getTotalQuotaUsed()}\n"; + echo "Лимит запросов: {$keyInfo->getTotalQuotaValue()}\n"; + echo "Осталось запросов: {$keyInfo->getRemainingQuota()}\n"; + echo "Использовано сегодня: {$keyInfo->dailyQuota->used}\n"; + echo "Осталось сегодня: {$keyInfo->getRemainingDailyQuota()}\n"; + +} catch (\NotKinopoisk\Exception\InvalidApiKeyException $e) { + echo "Ошибка: Неверный API ключ\n"; +} catch (\NotKinopoisk\Exception\ApiException $e) { + echo "Ошибка API: {$e->getMessage()}\n"; +} + +// Получение оценок пользователя (если известен ID) +try { + $votes = $userService->getVotes(12345, 1); + + echo "\n=== Оценки пользователя ===\n"; + echo "Всего оценок: {$votes->total}\n"; + echo "Страница {$votes->currentPage} из {$votes->totalPages}\n\n"; + + foreach ($votes->items as $vote) { + echo "Фильм: {$vote->filmName}\n"; + echo "Оценка: {$vote->rating}/10\n"; + echo "Дата: {$vote->date}\n"; + echo "---\n"; + } + +} catch (\NotKinopoisk\Exception\ResourceNotFoundException $e) { + echo "Пользователь не найден\n"; +} catch (\NotKinopoisk\Exception\ApiException $e) { + echo "Ошибка API: {$e->getMessage()}\n"; +} +``` + +## Мониторинг использования API + +```php +// Функция для проверки лимитов API +function checkApiLimits(UserService $userService, string $apiKey): void { + try { + $keyInfo = $userService->getApiKeyInfo($apiKey); + + $remainingTotal = $keyInfo->getRemainingQuota(); + $remainingDaily = $keyInfo->getRemainingDailyQuota(); + + echo "Осталось запросов (всего): {$remainingTotal}\n"; + echo "Осталось запросов (сегодня): {$remainingDaily}\n"; + + if ($remainingDaily < 10) { + echo "⚠️ Внимание: Осталось мало запросов на сегодня!\n"; + } + + if ($remainingTotal < 100) { + echo "⚠️ Внимание: Осталось мало запросов в общем!\n"; + } + + } catch (\Exception $e) { + echo "Ошибка при проверке лимитов: {$e->getMessage()}\n"; + } +} + +// Использование +checkApiLimits($userService, 'your-api-key'); +``` + +## Связанные классы + +- [`ApiKeyInfo`](../models/api-key-info.md) - Информация об API ключе +- [`UserVote`](../models/user-vote.md) - Оценка пользователя +- [`PaginatedResponse`](../responses/paginated-response.md) - Пагинированный ответ +- [`AccountType`](../enums/account-type.md) - Типы аккаунтов diff --git a/freeze.sh b/freeze.sh new file mode 100755 index 0000000..e169aba --- /dev/null +++ b/freeze.sh @@ -0,0 +1 @@ +pip-chill --no-version > requirements.txt \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 73da60c..6d12e3a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,57 +1,18 @@ -babel==2.16.0 -cairocffi==1.7.1 -CairoSVG==2.7.1 -certifi==2024.12.14 -cffi==1.17.1 -charset-normalizer==3.4.1 -click==8.1.8 -colorama==0.4.6 -csscompressor==0.9.5 -cssselect2==0.7.0 -defusedxml==0.7.1 -ghp-import==2.1.0 -gitdb==4.0.12 -GitPython==3.1.44 -htmlmin==0.1.12 -htmlmin2==0.1.13 -idna==3.10 -importlib_metadata==8.6.1 -importlib_resources==6.5.2 -Jinja2==3.1.5 -jsmin==3.0.1 -Markdown==3.7 -MarkupSafe==3.0.2 -mergedeep==1.3.4 -mike==2.1.3 -mkdocs==1.6.1 -mkdocs-get-deps==0.2.0 -mkdocs-git-revision-date-localized-plugin==1.3.0 -mkdocs-git-revision-date-plugin==0.3.2 -mkdocs-material==9.5.50 -mkdocs-material-extensions==1.3.1 -mkdocs-minify-plugin==0.8.0 -mkdocs-redirects==1.2.2 -packaging==24.2 -paginate==0.5.7 -pathspec==0.12.1 -pillow==11.1.0 -platformdirs==4.3.6 -pycparser==2.22 -Pygments==2.19.1 -pymdown-extensions==10.14.1 -pyparsing==3.2.1 -python-dateutil==2.9.0.post0 -pytz==2024.2 -PyYAML==6.0.2 -pyyaml_env_tag==0.1 -regex==2024.11.6 -requests==2.32.3 -shtab==1.7.1 -six==1.17.0 -smmap==5.0.2 -tinycss2==1.4.0 -urllib3==2.3.0 -verspec==0.1.0 -watchdog==6.0.0 -webencodings==0.5.1 -zipp==3.21.0 +backports.tarfile +cairosvg +docopt +inflect +jaraco-collections +jaraco-functools +jaraco.collections +mike +mkdocs-git-revision-date-localized-plugin +mkdocs-git-revision-date-plugin +mkdocs-material +mkdocs-minify-plugin +mkdocs-redirects +pip-chill +regex +shtab +tomli +yarg From 64bfef47a125623e84470b947ac39028b488f168 Mon Sep 17 00:00:00 2001 From: Maxim Harder Date: Fri, 25 Jul 2025 10:21:33 +0200 Subject: [PATCH 2/8] =?UTF-8?q?=F0=9F=94=A7=20chore:=20=D0=B4=D0=BE=D0=B1?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D1=82=D1=8C=20CI/CD=20workflow=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлен workflow для проверки безопасности кода с Bandit и Safety - Добавлен workflow для автоматического обновления зависимостей по расписанию - Добавлен workflow для сборки и деплоя документации на Git Pages - Добавлен workflow для генерации changelog при выпуске новых тегов - Настроены автоматические комментарии с результатами проверки безопасности в PR --- .github/dependabot.yml | 45 +++++++++++ .github/workflows/changelog.yml | 121 ++++++++++++++++++++++++++++++ .github/workflows/deploy.yml | 41 ++++++++++ .github/workflows/security.yml | 106 ++++++++++++++++++++++++++ .github/workflows/update-deps.yml | 76 +++++++++++++++++++ 5 files changed, 389 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/changelog.yml create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/security.yml create mode 100644 .github/workflows/update-deps.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..16f77b8 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,45 @@ +version: 2 +updates: + # Python dependencies + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" + day: "sunday" + time: "02:00" + open-pull-requests-limit: 5 + reviewers: + - "DevCraftClub" + assignees: + - "DevCraftClub" + commit-message: + prefix: "chore" + include: "scope" + labels: + - "dependencies" + - "python" + ignore: + # Игнорируем major версии для критических зависимостей + - dependency-name: "mkdocs-material" + update-types: ["version-update:semver-major"] + - dependency-name: "mkdocs" + update-types: ["version-update:semver-major"] + + # GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "03:00" + open-pull-requests-limit: 3 + reviewers: + - "DevCraftClub" + assignees: + - "DevCraftClub" + commit-message: + prefix: "ci" + include: "scope" + labels: + - "dependencies" + - "github-actions" diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml new file mode 100644 index 0000000..118e908 --- /dev/null +++ b/.github/workflows/changelog.yml @@ -0,0 +1,121 @@ +name: Generate Changelog + +on: + push: + tags: + - "v*" + +jobs: + changelog: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Generate changelog + id: changelog + uses: actions/github-script@v6 + with: + script: | + const { data: commits } = await github.rest.repos.compareCommits({ + owner: context.repo.owner, + repo: context.repo.repo, + base: 'main', + head: context.sha + }); + + let changelog = '# 📋 Changelog\n\n'; + changelog += `## Version ${context.ref.replace('refs/tags/', '')}\n\n`; + changelog += `**Release Date:** ${new Date().toISOString().split('T')[0]}\n\n`; + + const categories = { + 'feat': '🚀 Features', + 'fix': '🐛 Bug Fixes', + 'docs': '📚 Documentation', + 'style': '💄 Style', + 'refactor': '♻️ Refactoring', + 'test': '🧪 Tests', + 'chore': '🔧 Chores', + 'ci': '⚙️ CI/CD', + 'perf': '⚡ Performance', + 'build': '📦 Build', + 'revert': '⏪ Reverts' + }; + + const categorizedCommits = {}; + + for (const commit of commits.commits) { + const message = commit.commit.message; + const lines = message.split('\n'); + const firstLine = lines[0]; + + // Парсим conventional commits + const match = firstLine.match(/^(\w+)(?:\(([^)]+)\))?:\s*(.+)$/); + + if (match) { + const [, type, scope, description] = match; + const category = categories[type] || '📝 Other'; + + if (!categorizedCommits[category]) { + categorizedCommits[category] = []; + } + + const scopeText = scope ? `**${scope}:** ` : ''; + categorizedCommits[category].push(`- ${scopeText}${description}`); + } else { + // Не conventional commit + if (!categorizedCommits['📝 Other']) { + categorizedCommits['📝 Other'] = []; + } + categorizedCommits['📝 Other'].push(`- ${firstLine}`); + } + } + + // Добавляем категории в changelog + for (const [category, commits] of Object.entries(categorizedCommits)) { + if (commits.length > 0) { + changelog += `### ${category}\n\n`; + for (const commit of commits) { + changelog += `${commit}\n`; + } + changelog += '\n'; + } + } + + // Добавляем статистику + changelog += '## 📊 Statistics\n\n'; + changelog += `- **Total commits:** ${commits.commits.length}\n`; + changelog += `- **Files changed:** ${commits.files ? commits.files.length : 'N/A'}\n`; + changelog += `- **Additions:** ${commits.stats ? commits.stats.additions : 'N/A'}\n`; + changelog += `- **Deletions:** ${commits.stats ? commits.stats.deletions : 'N/A'}\n\n`; + + // Добавляем ссылки + changelog += '## 🔗 Links\n\n'; + changelog += `- [Full diff](https://github.com/${context.repo.owner}/${context.repo.repo}/compare/main...${context.ref})\n`; + changelog += `- [Documentation](https://devcraftclub.github.io/mhdocs/)\n`; + changelog += `- [Readme DevCraft](https://readme.devcraft.club)\n`; + + core.setOutput('changelog', changelog); + console.log('Generated changelog:', changelog); + + - name: Create changelog file + run: | + echo "${{ steps.changelog.outputs.changelog }}" > CHANGELOG.md + + - name: Commit changelog + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git add CHANGELOG.md + git commit -m "docs: update changelog for ${{ github.ref_name }}" || echo "No changes to commit" + git push + + - name: Upload changelog as artifact + uses: actions/upload-artifact@v3 + with: + name: changelog-${{ github.ref_name }} + path: CHANGELOG.md + retention-days: 90 diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..e3bd2a3 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,41 @@ +name: Deploy Documentation + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Получаем полную историю для git-revision-date плагина + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.13" + cache: "pip" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Build documentation + run: | + mkdocs build --strict + mkdocs gh-deploy --force + + - name: Deploy to GitHub Pages + if: github.ref == 'refs/heads/main' + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./site + force_orphan: true diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..5ac7e85 --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,106 @@ +name: Security Check + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + # Еженедельная проверка безопасности + - cron: "0 4 * * 1" + +jobs: + security: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.11" + cache: "pip" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install bandit safety + + - name: Run Bandit security check + run: | + echo "Running Bandit security check..." + bandit -r . -f json -o bandit-report.json || true + bandit -r . -f txt -o bandit-report.txt || true + + # Показываем результаты + if [ -f bandit-report.txt ]; then + echo "=== Bandit Security Report ===" + cat bandit-report.txt + fi + + - name: Check for known vulnerabilities + run: | + echo "Checking for known vulnerabilities..." + safety check --json --output safety-report.json || true + safety check --output safety-report.txt || true + + # Показываем результаты + if [ -f safety-report.txt ]; then + echo "=== Safety Vulnerability Report ===" + cat safety-report.txt + fi + + - name: Upload security reports + uses: actions/upload-artifact@v3 + if: always() + with: + name: security-reports + path: | + bandit-report.json + bandit-report.txt + safety-report.json + safety-report.txt + retention-days: 30 + + - name: Comment PR with security findings + if: github.event_name == 'pull_request' + uses: actions/github-script@v6 + with: + script: | + const fs = require('fs'); + + let comment = '## 🔒 Security Check Results\n\n'; + + // Bandit results + if (fs.existsSync('bandit-report.txt')) { + const banditContent = fs.readFileSync('bandit-report.txt', 'utf8'); + if (banditContent.trim()) { + comment += '### 🚨 Bandit Security Issues\n\n'; + comment += '```\n' + banditContent + '\n```\n\n'; + } else { + comment += '✅ No Bandit security issues found\n\n'; + } + } + + // Safety results + if (fs.existsSync('safety-report.txt')) { + const safetyContent = fs.readFileSync('safety-report.txt', 'utf8'); + if (safetyContent.trim()) { + comment += '### ⚠️ Known Vulnerabilities\n\n'; + comment += '```\n' + safetyContent + '\n```\n\n'; + } else { + comment += '✅ No known vulnerabilities found\n\n'; + } + } + + comment += '---\n*This report was generated automatically by GitHub Actions*'; + + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: comment + }); diff --git a/.github/workflows/update-deps.yml b/.github/workflows/update-deps.yml new file mode 100644 index 0000000..3eec41c --- /dev/null +++ b/.github/workflows/update-deps.yml @@ -0,0 +1,76 @@ +name: Update Dependencies + +on: + schedule: + # Запускается каждое воскресенье в 2:00 UTC + - cron: "0 2 * * 0" + workflow_dispatch: # Позволяет запускать вручную + +jobs: + update-deps: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.11" + cache: "pip" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install pip-chill + + - name: Update dependencies + run: | + echo "Updating dependencies..." + pip install --upgrade $(pip list --outdated --format=freeze | grep -v '^\-e' | cut -d = -f 1) + + - name: Update requirements.txt + run: | + echo "Updating requirements.txt..." + pip-chill --no-version > requirements.txt.new + if ! cmp -s requirements.txt requirements.txt.new; then + mv requirements.txt.new requirements.txt + echo "Requirements updated" + else + echo "No updates needed" + rm requirements.txt.new + fi + + - name: Test build + run: | + echo "Testing build with updated dependencies..." + mkdocs build --strict + + - name: Create Pull Request + if: success() + uses: peter-evans/create-pull-request@v5 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "chore: update dependencies" + title: "🤖 Automated dependency update" + body: | + ## 🔄 Automated Dependency Update + + This PR was automatically created to update project dependencies. + + ### 📋 Changes + - Updated Python package dependencies + - Tested build with new dependencies + + ### ✅ Checks + - [x] Dependencies updated + - [x] Build tested successfully + + ### 🚀 Ready to merge + This PR is safe to merge as all tests pass. + branch: update-dependencies + delete-branch: true From f0fc30e9ca3e626b05aef42b484612cbdb008244 Mon Sep 17 00:00:00 2001 From: Maxim Harder Date: Fri, 25 Jul 2025 10:21:50 +0200 Subject: [PATCH 3/8] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20UPDATE=20=D0=A3=D0=B4?= =?UTF-8?q?=D0=B0=D0=BB=D1=8F=D1=8E=20=D1=83=D1=81=D1=82=D0=B0=D1=80=D0=B5?= =?UTF-8?q?=D0=B2=D1=88=D1=83=D1=8E=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=B0=D1=86=D0=B8=D1=8E=20NotKinopoisk=20PHP=20Wra?= =?UTF-8?q?pper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Полностью удалена старая документация из docs/dev/notkinopoiskphp/README.md - Удаление связано с обновлением структуры проекта или переносом документации - Обновлен requirements.txt с добавлением новых плагинов mkdocs для улучшения сборки документации - Добавлены mkdocs-awesome-pages-plugin и mkdocs-section-index для расширения функционала документации --- docs/dev/notkinopoiskphp/README.md | 124 ----------------------------- requirements.txt | 2 + 2 files changed, 2 insertions(+), 124 deletions(-) delete mode 100644 docs/dev/notkinopoiskphp/README.md diff --git a/docs/dev/notkinopoiskphp/README.md b/docs/dev/notkinopoiskphp/README.md deleted file mode 100644 index 5a417d2..0000000 --- a/docs/dev/notkinopoiskphp/README.md +++ /dev/null @@ -1,124 +0,0 @@ -# NotKinopoisk PHP Wrapper - Документация - -Полная документация библиотеки NotKinopoisk PHP Wrapper для работы с Kinopoisk API. - -## 📚 Содержание - -### 🏗️ Архитектура - -- [Клиент (Client)](client.md) - Основной класс для работы с API -- [Интерфейсы (Interfaces)](interfaces.md) - Интерфейсы библиотеки -- [Абстрактные классы](abstract-classes.md) - Базовые классы - -### 🎬 Сервисы - -- [FilmService](services/film-service.md) - Работа с фильмами и сериалами -- [PersonService](services/person-service.md) - Работа с персонами -- [MediaService](services/media-service.md) - Работа с медиа-контентом -- [UserService](services/user-service.md) - Работа с пользователем - -### 📊 Модели данных - -- [Film](models/film.md) - Модель фильма/сериала -- [Person](models/person.md) - Модель персоны -- [Staff](models/staff.md) - Модель персонала фильма -- [Review](models/review.md) - Модель отзыва -- [Fact](models/fact.md) - Модель факта -- [Image](models/image.md) - Модель изображения -- [Video](models/video.md) - Модель видео -- [BoxOffice](models/box-office.md) - Модель кассовых сборов -- [Distribution](models/distribution.md) - Модель проката -- [Award](models/award.md) - Модель награды -- [Genre](models/genre.md) - Модель жанра -- [Country](models/country.md) - Модель страны -- [Season](models/season.md) - Модель сезона -- [Episode](models/episode.md) - Модель эпизода -- [PersonFilm](models/person-film.md) - Модель фильма персоны -- [PersonSpouse](models/person-spouse.md) - Модель супруга персоны -- [PersonByNameResult](models/person-by-name-result.md) - Результат поиска персоны -- [FilmSearchResult](models/film-search-result.md) - Результат поиска фильма -- [FilmCollection](models/film-collection.md) - Коллекция фильмов -- [RelatedFilm](models/related-film.md) - Связанный фильм -- [Premiere](models/premiere.md) - Модель премьеры -- [UserVote](models/user-vote.md) - Модель голоса пользователя -- [ExternalSource](models/external-source.md) - Модель внешнего источника -- [MediaPost](models/media-post.md) - Модель медиа-поста -- [ApiKeyInfo](models/api-key-info.md) - Информация об API ключе -- [ApiKeyQuota](models/api-key-quota.md) - Квоты API ключа -- [Filters](models/filters.md) - Фильтры для поиска - -### 🔧 Перечисления (Enums) - -- [ContentType](enums/content-type.md) - Типы контента -- [ImageType](enums/image-type.md) - Типы изображений -- [VideoSite](enums/video-site.md) - Сайты видео -- [BoxOfficeType](enums/box-office-type.md) - Типы кассовых сборов -- [DistributionType](enums/distribution-type.md) - Типы проката -- [DistributionSubType](enums/distribution-sub-type.md) - Подтипы проката -- [FactType](enums/fact-type.md) - Типы фактов -- [ReviewType](enums/review-type.md) - Типы отзывов -- [ReviewOrder](enums/review-order.md) - Порядок сортировки отзывов -- [FilmOrder](enums/film-order.md) - Порядок сортировки фильмов -- [CollectionType](enums/collection-type.md) - Типы коллекций -- [RelationType](enums/relation-type.md) - Типы связей -- [ProductionStatus](enums/production-status.md) - Статусы производства -- [ProfessionKey](enums/profession-key.md) - Ключи профессий -- [AccountType](enums/account-type.md) - Типы аккаунтов -- [Sex](enums/sex.md) - Пол -- [Month](enums/month.md) - Месяцы -- [ApiVersion](enums/api-version.md) - Версии API - -### 📤 Ответы API - -- [DefaultResponse](responses/default-response.md) - Базовый ответ -- [PaginatedResponse](responses/paginated-response.md) - Пагинированный ответ -- [SimpleResponse](responses/simple-response.md) - Простой ответ -- [BudgetResponse](responses/budget-response.md) - Ответ с бюджетом -- [KeywordSearchResponse](responses/keyword-search-response.md) - Ответ поиска по ключевым словам -- [MovieStaffResponse](responses/movie-staff-response.md) - Ответ с персоналом фильма -- [SequelPrequelResponse](responses/sequel-prequel-response.md) - Ответ с сиквелами и приквелами - -### ⚠️ Исключения - -- [ApiException](exceptions/api-exception.md) - Базовое исключение API -- [InvalidApiKeyException](exceptions/invalid-api-key-exception.md) - Неверный API ключ -- [RateLimitException](exceptions/rate-limit-exception.md) - Превышен лимит запросов -- [ResourceNotFoundException](exceptions/resource-not-found-exception.md) - Ресурс не найден -- [KpValidationException](exceptions/kp-validation-exception.md) - Ошибка валидации - -## 🚀 Быстрый старт - -```php -films->getById(301); -echo $film->getDisplayName(); -``` - -## 📋 Требования - -- PHP 8.3+ -- Composer -- API ключ от Kinopoisk Unofficial API - -## 🔗 Полезные ссылки - -- [GitHub репозиторий](https://github.com/DevCraftClub/NotKinopoiskPHP) -- [Issues](https://github.com/DevCraftClub/NotKinopoiskPHP/issues) -- [Примеры использования](../examples/) -- [Kinopoisk Unofficial API](https://kinopoiskapiunofficial.tech) - -## ⚠️ Важные предостережения - -- **Структура API** в последний раз обновлялась **16.10.2023** -- **НЕ путать** с kinopoisk.dev - это другой API -- Работает с **kinopoiskapiunofficial.tech** -- Некоторые методы могут не возвращать актуальные данные diff --git a/requirements.txt b/requirements.txt index 6d12e3a..19daf62 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,11 +6,13 @@ jaraco-collections jaraco-functools jaraco.collections mike +mkdocs-awesome-pages-plugin mkdocs-git-revision-date-localized-plugin mkdocs-git-revision-date-plugin mkdocs-material mkdocs-minify-plugin mkdocs-redirects +mkdocs-section-index pip-chill regex shtab From b1e6cbde21967fa397ee27245f232cbc196188f4 Mon Sep 17 00:00:00 2001 From: Maxim Harder Date: Fri, 25 Jul 2025 13:46:37 +0200 Subject: [PATCH 4/8] =?UTF-8?q?=F0=9F=93=9D=20DOCS=20=D0=9E=D0=B1=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=BB=D1=8F=D0=B5=D1=82=20=D0=B4=D0=BE=D0=BA=D1=83?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=86=D0=B8=D1=8E=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20ReviewResponse=20=D0=B8=20=D0=BD=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=B3=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавляет подробное описание класса ReviewResponse с примерами использования - Описывает структуру, методы, свойства и обработку ошибок ReviewResponse - Обновляет ссылки в документации исключений и моделей для корректной навигации - Исправляет и улучшает карту навигации в документации - Обновляет зависимости для генерации документации (mkdocs) --- docs/.nav.yml | 0 docs/dev/dle_faker/gen_news.md~ | 15 - docs/dev/dle_faker/gen_users.md~ | 10 - docs/dev/dle_faker/parser.md~ | 362 ---------- docs/dev/dle_faker/settings.md~ | 6 - docs/dev/dle_faker/tag_for_all.md~ | 21 - docs/dev/dle_faker/tag_for_users.md~ | 19 - docs/dev/notkinopoiskphp/abstract-classes.md | 7 - docs/dev/notkinopoiskphp/client.md | 25 +- .../dev/notkinopoiskphp/enums/account-type.md | 334 ++++++++++ .../dev/notkinopoiskphp/enums/content-type.md | 2 +- .../enums/distribution-sub-type.md | 283 ++++++++ docs/dev/notkinopoiskphp/enums/index.md | 38 +- .../enums/production-status.md | 389 +++++++++++ .../exceptions/api-exception.md | 8 +- docs/dev/notkinopoiskphp/exceptions/index.md | 14 +- .../exceptions/invalid-api-key-exception.md | 6 +- .../exceptions/kp-validation-exception.md | 410 ++++++++++++ .../exceptions/rate-limit-exception.md | 4 +- .../resource-not-found-exception.md | 5 +- docs/dev/notkinopoiskphp/index.md | 150 ++--- docs/dev/notkinopoiskphp/interfaces.md | 192 ------ docs/dev/notkinopoiskphp/interfaces/index.md | 4 +- .../interfaces/model-interface.md | 97 +++ .../interfaces/response-interface.md | 96 +++ .../notkinopoiskphp/models/api-key-info.md | 416 ++++++++++++ .../notkinopoiskphp/models/api-key-qouta.md | 463 +++++++++++++ docs/dev/notkinopoiskphp/models/award.md | 2 +- docs/dev/notkinopoiskphp/models/box-office.md | 2 +- docs/dev/notkinopoiskphp/models/country.md | 2 +- .../notkinopoiskphp/models/distribution.md | 2 +- docs/dev/notkinopoiskphp/models/episode.md | 2 +- docs/dev/notkinopoiskphp/models/fact.md | 2 +- .../notkinopoiskphp/models/film-collection.md | 4 +- .../models/film-search-result.md | 4 +- docs/dev/notkinopoiskphp/models/film.md | 7 - docs/dev/notkinopoiskphp/models/filters.md | 4 +- docs/dev/notkinopoiskphp/models/genre.md | 2 +- docs/dev/notkinopoiskphp/models/image.md | 2 +- docs/dev/notkinopoiskphp/models/index.md | 155 ++++- docs/dev/notkinopoiskphp/models/media-post.md | 452 +++++++++++++ .../models/person-by-name-result.md | 627 ++++++++++++++++++ .../dev/notkinopoiskphp/models/person-film.md | 2 +- .../notkinopoiskphp/models/person-spouse.md | 2 +- docs/dev/notkinopoiskphp/models/premiere.md | 4 +- docs/dev/notkinopoiskphp/models/review.md | 2 +- docs/dev/notkinopoiskphp/models/season.md | 2 +- docs/dev/notkinopoiskphp/models/staff.md | 2 +- docs/dev/notkinopoiskphp/models/user-vote.md | 4 +- docs/dev/notkinopoiskphp/models/video.md | 2 +- docs/dev/notkinopoiskphp/navigation-map.md | 315 ++++----- .../responses/budget-response.md | 179 +++++ docs/dev/notkinopoiskphp/responses/index.md | 246 +++++++ .../responses/movie-staff-response.md | 292 ++++++++ .../responses/review-response.md | 255 +++++++ .../responses/sequel-prequel-response.md | 442 ++++++++++++ .../responses/simple-response.md | 205 ++++++ .../services/abstract-service.md | 482 ++++++++++++++ docs/dev/notkinopoiskphp/services/index.md | 12 +- docs/extras/css/search-enhancement.css | 379 +++++++++++ docs/extras/js/search-enhancement.js | 450 +++++++++++++ docs/extras/js/search-metadata-loader.js | 316 +++++++++ docs/extras/search-metadata.yml | 623 +++++++++++++++++ docs/search-metadata.yml | 623 +++++++++++++++++ extras/search-metadata.yml | 623 +++++++++++++++++ mkdocs.yml | 459 ++++++++----- requirements.txt | 3 +- 67 files changed, 9397 insertions(+), 1172 deletions(-) create mode 100644 docs/.nav.yml delete mode 100644 docs/dev/dle_faker/gen_news.md~ delete mode 100644 docs/dev/dle_faker/gen_users.md~ delete mode 100644 docs/dev/dle_faker/parser.md~ delete mode 100644 docs/dev/dle_faker/settings.md~ delete mode 100644 docs/dev/dle_faker/tag_for_all.md~ delete mode 100644 docs/dev/dle_faker/tag_for_users.md~ create mode 100644 docs/dev/notkinopoiskphp/enums/account-type.md create mode 100644 docs/dev/notkinopoiskphp/enums/distribution-sub-type.md create mode 100644 docs/dev/notkinopoiskphp/enums/production-status.md create mode 100644 docs/dev/notkinopoiskphp/exceptions/kp-validation-exception.md delete mode 100644 docs/dev/notkinopoiskphp/interfaces.md create mode 100644 docs/dev/notkinopoiskphp/interfaces/model-interface.md create mode 100644 docs/dev/notkinopoiskphp/interfaces/response-interface.md create mode 100644 docs/dev/notkinopoiskphp/models/api-key-info.md create mode 100644 docs/dev/notkinopoiskphp/models/api-key-qouta.md create mode 100644 docs/dev/notkinopoiskphp/models/media-post.md create mode 100644 docs/dev/notkinopoiskphp/models/person-by-name-result.md create mode 100644 docs/dev/notkinopoiskphp/responses/budget-response.md create mode 100644 docs/dev/notkinopoiskphp/responses/movie-staff-response.md create mode 100644 docs/dev/notkinopoiskphp/responses/review-response.md create mode 100644 docs/dev/notkinopoiskphp/responses/sequel-prequel-response.md create mode 100644 docs/dev/notkinopoiskphp/responses/simple-response.md create mode 100644 docs/dev/notkinopoiskphp/services/abstract-service.md create mode 100644 docs/extras/css/search-enhancement.css create mode 100644 docs/extras/js/search-enhancement.js create mode 100644 docs/extras/js/search-metadata-loader.js create mode 100644 docs/extras/search-metadata.yml create mode 100644 docs/search-metadata.yml create mode 100644 extras/search-metadata.yml diff --git a/docs/.nav.yml b/docs/.nav.yml new file mode 100644 index 0000000..e69de29 diff --git a/docs/dev/dle_faker/gen_news.md~ b/docs/dev/dle_faker/gen_news.md~ deleted file mode 100644 index 54f5621..0000000 --- a/docs/dev/dle_faker/gen_news.md~ +++ /dev/null @@ -1,15 +0,0 @@ -# Генератор новостей - -Генератор новостей работает по следующему принципу: -1. Создаётся шаблон (`/admin.php?mod=dle_faker&sites=template&action=create`) -2. Настраивается вывод информации -3. В генераторе выбирается нужный шаблон (`/admin.php?mod=dle_faker&sites=generator&action=news`) -4. Генерируется новость - -![img.png](assets/gen_news.png) - -Успешную генерацию новостей можно увидеть в списке во всплывшем окне. -![Модальное окно статус](assets/gen_users_modal.png) -![Процесс обработки](assets/gen_users_result.png) - -Если в процессе произошла ошибка, то во всплывшем окне будет сообщение об этом. Их можно будет прочесть в логах, если данная функция MH Admin включена. А так-же процесс не достигнет 100%. \ No newline at end of file diff --git a/docs/dev/dle_faker/gen_users.md~ b/docs/dev/dle_faker/gen_users.md~ deleted file mode 100644 index ba1c50e..0000000 --- a/docs/dev/dle_faker/gen_users.md~ +++ /dev/null @@ -1,10 +0,0 @@ -# Генератор пользователей - -В отличие от генератора новостей, генератор пользователей не имеет шаблонов. И операция генерации настраивается с нуля. - -![Генератор Пользователей](assets/gen_users.png) - -В отличие от настроек, в этом генераторе значения по умолчанию проставлены. - -Успешную генерацию пользователй можно увидеть в списке во всплывшем окне. -![img.png](img.png) \ No newline at end of file diff --git a/docs/dev/dle_faker/parser.md~ b/docs/dev/dle_faker/parser.md~ deleted file mode 100644 index bf5d488..0000000 --- a/docs/dev/dle_faker/parser.md~ +++ /dev/null @@ -1,362 +0,0 @@ -# Парсинг данных с шаблонов - -Процесс обработки шаблонов происходит при AJAX запросе. В данном случае за этот процесс отвечает файл: `upload/engine/ajax/maharder/dle_faker/parse_content.php`. - -# Документация для parse_content.php - -## Глобальные переменные -- `$fakerConfig`: Конфигурация для генерации данных. -- `$faker`: Объект генератора данных Faker. - ---- - -## Константы -- `DEFAULT_NEWS_FORMAT`: `'Y-m-d H:m:s'`. - ---- - -## Пространства имен -- `use Faker\Factory` -- `use JetBrains\PhpStorm\ExpectedValues` - ---- - -## Функции - -### Функция: `extractNumsAndStrict` - -**Описание:** -Извлекает и заменяет шаблоны формата `{{ randomNumber nums=X strict=Y }}` на случайные числа, используя глобальную зависимость `$faker`. - -**Краткое описание:** -Шаблоны имеют следующий формат: -- `nums=X` - задаёт количество цифр в числе. -- `strict=Y` - указывает, строго ли соблюдать количество цифр. (необязательный) - -**Параметры:** -- `$text` (string): Текст, содержащий шаблоны для замены. - -**Возвращаемый тип:** `string` - -**Теги:** -- `@global \Faker\Generator $faker` -- `@see \Faker\Generator::randomNumber()` -- `@see \Faker\Extension\Extension::numberBetween()` - ---- - -### Функция: `extractRandomDigit` - -**Описание:** -Заменяет шаблоны вида `{{ randomDigit not=X }}` случайными числами, не равными указанному `X`. - -**Параметры:** -- `$text` (string): Входной текст с шаблонами. - -**Возвращаемый тип:** `string` - -**Теги:** -- `@global \Faker\Generator $faker` -- `@see \Faker\Generator::randomDigitNot()` -- `@throws \RuntimeException` - ---- - -### Функция: `extractRandomFloatParams` - -**Описание:** -Обрабатывает шаблоны вида `{{ randomFloat ... }}` и заменяет их случайными числами с плавающей запятой. - -**Параметры:** -- `$text` (string): Текст, содержащий шаблоны. - -**Возвращаемый тип:** `string` - -**Теги:** -- `@global \Faker\Generator $faker` -- `@see \Faker\Generator::randomFloat()` - ---- - -### Функция: `extractNubmerBetween` - -**Описание:** -Заменяет шаблоны вида `{{ numberBetween min=X max=Y }}` случайными числами из диапазона. - -Регулярное выражение ищет шаблон вида {{ numberBetween min=X max=Y }}, где параметры min и max являются необязательными. -Если параметр min не указан, используется значение PHP_INT_MIN, если max не указан - используется значение 2147483647. - -При каждом совпадении выполняется генерация случайного числа с использованием функции numberBetween глобального объекта $faker. - -**Параметры:** -- `$text` (string): Текст с шаблонами. - -**Возвращаемый тип:** `string` - -**Теги:** -- `@global object $faker` - ---- - -### Функция: `extractRandomElementsParams` - -**Описание:** -Извлекает параметры шаблона `{{ randomElements }}` и заменяет их случайными элементами. - -Следующие параметры могут быть переданы этому тегу: -- `items=[]` - Обязательный параметр с перечислением элементов через запятую (,) -- `count=X` - Необязательный параметр, устанавливает сколько элементов выводить. По умолчанию: 1. Если указано большее кол-во, нежели кол-во элементов, то возвращает всё -- `connector=_` - Необязательный параметр соединения элементов. По умолчанию: _. - -**Параметры:** -- `$text` (string): Текст с шаблонами. - -**Возвращаемый тип:** `string` - -**Теги:** -- `@global \Faker\Generator $faker` -- `@see randomElements()` - ---- - -### Функция: `createWords` - -**Описание:** -Заменяет шаблоны вида `{{ words max=N }}` на случайные слова. Выводятся через запятую -- `max=X` - Параметр устанавливающий кол-во выводимых слов. По умолчанию: 3 - -**Параметры:** -- `$text` (string): Текст с шаблонами. - -**Возвращаемый тип:** `string` - -**Теги:** -- `@global \Faker\Generator $faker` -- `@see \Faker\Provider\Base::words()` - ---- - -### Функция: `generateSentences` - -**Описание:** -Генерирует текст с заменой всех вхождений шаблона {{ sentences max=N }} на сгенерированные предложения с использованием глобальной переменной $faker. - -Ищет совпадения с шаблоном {{ sentences max=N }}, где N указывает максимальное количество предложений, затем заменяет их сгенерированными предложениями с помощью метода sentences из $faker. -Если параметр max отсутствует или указан некорректно, используется значение по умолчанию — 3. - -**Параметры:** -- `$text` (string): Текст с шаблонами. - -**Возвращаемый тип:** `string` - -**Теги:** -- `@global \Faker\Generator $faker` -- `@see \Faker\Generator::sentences()` - ---- - -### Функция: `generateParagraph` - -**Описание:** -Генерирует текст с заменой шаблонов в виде {{ paragraph max=N }} на случайные абзацы текста. - -Шаблоны в тексте, которые соответствуют указанному формату, будут заменены на случайно сгенерированные абзацы текста, где количество абзацев указывается через параметр `max`. -По умолчанию используется 3 абзаца. - -**Параметры:** -- `$text` (string): Текст с шаблонами. - -**Возвращаемый тип:** `string` - -**Теги:** -- `@global \Faker\Generator $faker` -- `@see \Faker\Generator::paragraphs()` - ---- - -### Функция: `generateText` - -**Описание:** -Генерирует текст, заменяя шаблонные маркеры {{ text max=X }} случайным текстом. - -Маркеры в тексте имеют формат {{ text max=X }}. Если параметр `max` указан, он задаёт максимальную длину сгенерированного текста. Если `max` не указан, используется значение по умолчанию - 200 символов. - -**Параметры:** -- `$text` (string): Текст с шаблонами. - -**Возвращаемый тип:** `string` - -**Теги:** -- `@global object $faker` -- `@see text()` - ---- - -### Функция: `generateDateTime` - -**Описание:** -Генерирует строку с заменой шаблонов даты и времени на соответствующие значения. - -Шаблоны даты и времени в строке задаются в виде: -- `{{ datetime format="формат" }}` - -Пример шаблона: -- `{{ datetime format="Y-m-d H:i:s" }}` - -Если формат даты не указан, используется значение константы DEFAULT_NEWS_FORMAT. - -**Параметры:** -- `$text` (string): Текст с шаблонами. - -**Возвращаемый тип:** `string` - -**Теги:** -- `@global \Faker\Generator $faker` -- `@see dateTime()` -- `@see getTimezone()` - ---- - -### Функция: `getRandomValue` - -**Описание:** -Возвращает случайное значение или значения из массива с возможностью исключения определённых элементов. - -**Параметры:** -- `$array` (array): Массив для выборки. -- `$count` (int): Количество значений (по умолчанию 1). -- `$exclude` (array|string|null): Элементы для исключения. - -**Возвращаемый тип:** `mixed` - -**Теги:** -- `@global \Faker\Generator $faker` -- `@see \Faker\Generator::numberBetween()` - ---- - -### Функция: `getTimezone` - -**Описание:** -Возвращает временную зону как объект или строку. - -**Параметры:** -- `$parse` (bool): Если `true`, возвращает объект; иначе строку. - -**Возвращаемый тип:** `DateTimeZone|string` - -**Теги:** -- `@global array $config` - ---- - -### Функция: `getRandomDateBetween` - -**Описание:** -Генерирует случайную дату в пределах заданного диапазона. - -Если начальная и конечная даты идентичны, возвращает форматированную дату начала. -В случае, если конечная дата не указана, диапазон устанавливается от начальной даты до текущей. -Если начальная дата позже конечной, параметры меняются местами. - -**Параметры:** -- `$start` (string|DateTime): Начальная дата. -- `$end` (string|DateTime|null): Конечная дата. - -**Возвращаемый тип:** `string` - -**Теги:** -- `@see DateTime` -- `@see getTimezone` - ---- - -### Функция: `parseBoolValue` - -**Описание:** -Преобразует строковое значение в логическое. - -Значение `'on'` будет преобразовано в `true`, `'off'` — в `false`. -Для всех остальных значений используется случайное логическое значение, сгенерированное объектом `$faker`. - -**Параметры:** -- `$text` (string): Значение `'on'`, `'off'` или `'random'`. - -**Возвращаемый тип:** `bool` - -**Теги:** -- `@global \Faker\Generator $faker` -- `@see \Faker\Generator::boolean()` - ---- - -### Функция: `create_metatags` - -**Описание:** -Создаёт метатеги на основе текста и входящих параметров. - -**Параметры:** -- `$story` (string): Текст для анализа. -- `$ajax` (bool): Режим AJAX. - -**Возвращаемый тип:** `array` - -**Теги:** -- `@global object $db` -- `@global array $config` -- `@see create_metatags engine/inc/include/functions.inc.php` - ---- - -### Функция: `parseFaker` - -**Описание:** -Обрабатывает и заменяет указанный тег в зависимости от заданного типа. - -**Параметры:** -- `$tag` (string): Тег для обработки. -- `$type` (string): Тип данных (`'user'` или `'post'`). - -**Возвращаемый тип:** `mixed` - -**Теги:** -- `@global object $faker` -- `@global array $fakerConfig` -- `@see extractNumsAndStrict()` -- `@see extractRandomDigit()` -- `@see extractRandomFloatParams()` -- `@see extractRandomElementsParams()` -- `@see extractNubmerBetween()` -- `@see getRandomValue()` - ---- - -### Функция: `parseUserValues` - -**Описание:** -Обрабатывает шаблоны типа `user`. - -**Параметры:** -- `$tag` (string): Шаблон для обработки. - -**Возвращаемый тип:** `mixed` - -**Теги:** -- `@see parseFaker()` - ---- - -### Функция: `parseNewsValues` - -**Описание:** -Обрабатывает значения новостей. - -**Параметры:** -- `$tag` (string): Тег для обработки. - -**Возвращаемый тип:** `mixed` - -**Теги:** -- `@see parseFaker()` - ---- diff --git a/docs/dev/dle_faker/settings.md~ b/docs/dev/dle_faker/settings.md~ deleted file mode 100644 index 7d37e17..0000000 --- a/docs/dev/dle_faker/settings.md~ +++ /dev/null @@ -1,6 +0,0 @@ -# Настройки - -Настройки плагина являются главной страницей. Они обязательны к заполнению, ибо значений по умолчанию нет! - -![Настройки (пример)](assets/setting.png) - diff --git a/docs/dev/dle_faker/tag_for_all.md~ b/docs/dev/dle_faker/tag_for_all.md~ deleted file mode 100644 index efb8e26..0000000 --- a/docs/dev/dle_faker/tag_for_all.md~ +++ /dev/null @@ -1,21 +0,0 @@ -# Теги для всех шаблонов - -Данные теги будут доступны для всех шаблонов, поскольку они универсальны. - -| Тег | Описание | -|---------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| {{ yesNo }} | Генерирует случайное значение да или нет (true / false). | -| {{ emoji }} | Генерирует случайный эмодзи. | -| {{ randomNumber nums=0 }} | Генерирует случайное целое число, содержащее от 0 до nums цифр. | -| {{ randomNumber nums=0 strict=true }} | Когда параметр strict установлен в значение true, функция будет возвращать только целые числа, содержащие ровно nums цифр. | -| {{ randomDigit }} | Генерирует случайное целое число от 0 до 9. | -| {{ randomDigit not=0 }} | Генерирует случайное целое число от 0 до 9, исключая заданное число. | -| {{ randomFloat }} | Генерирует случайное число с плавающей запятой. | -| {{ randomFloat float=0 }} | Параметр float отвечает за количество десятичных знаков. | -| {{ randomFloat float=0 min=0 }} | Параметр min задаёт нижнюю границу. | -| {{ randomFloat float=0 min=0 max=0 }} | Параметр max задаёт верхнюю границу. | -| {{ numberBetween min=0 max=0 }} | Генерирует случайное целое число в диапазоне от min до max. По умолчанию генерируется число в диапазоне от 0 до 2 147 483 647. | -| {{ randomLetter }} | Генерирует случайный символ из алфавита. | -| {{ randomElements items=[] }} | Возвращает случайный элемент из заданного массива, итерируемого объекта или перечисления. По умолчанию параметр $count установлен в 1, а если передано значение null, возвращается случайное количество элементов.
Массив должен быть простым без дополнительных ковычек. | -| {{ randomElements items=[] count=0 }} | Параметр count установлен в 1, а если передано значение null, возвращается случайное количество элементов. | -| {{ randomElements items=[] count=0 connector=_ }} | Параметр connector установлен на _. Он отвечает за объединение элементов списка. | diff --git a/docs/dev/dle_faker/tag_for_users.md~ b/docs/dev/dle_faker/tag_for_users.md~ deleted file mode 100644 index 55438ee..0000000 --- a/docs/dev/dle_faker/tag_for_users.md~ +++ /dev/null @@ -1,19 +0,0 @@ -# Теги для шаблона пользователей - -Эти теги используются в шаблоне генерации пользователей. Любые другие теги будут игнорироваться. - -## Таблица тегов - -| Тэг | Описание | -|-----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| {{ userName }} | Случайный псевдоним, пример: coolcat123 | -| {{ name }} | Полное имя, пример: Проф. Артём Волков | -| {{ firstName }} | Имя, пример: Артём | -| {{ firstNameMale }} | Мужское имя, пример: Артём | -| {{ firstNameFemale }} | Женское имя, пример: Анна | -| {{ lastName }} | Фамилия, пример: Волков | -| {{ suffix }} | Суффикс, пример: Мл. | -| {{ title }} | Обращение, пример: Госпожа или Девушка. В русском нет аналога для Ms. или Mrs. | -| {{ titleMale }} | Мужское обращение, пример: Господин. В русском нет аналога для Mr. | -| {{ titleFemale }} | Женское обращение, пример: Госпожа или Девушка. В русском нет аналога для Ms. или Mrs. | -| {{ email }} | Случайная электронная почта, пример: walter.sophia@hotmail.com. Связи с именем никакой нет! | diff --git a/docs/dev/notkinopoiskphp/abstract-classes.md b/docs/dev/notkinopoiskphp/abstract-classes.md index c377687..c02f025 100644 --- a/docs/dev/notkinopoiskphp/abstract-classes.md +++ b/docs/dev/notkinopoiskphp/abstract-classes.md @@ -118,10 +118,3 @@ class MyService extends AbstractService - `\NotKinopoisk\Client` - Основной клиент API - `\NotKinopoisk\Enums\ApiVersion` - Перечисление версий API -## Информация о пакете - -- **Пакет:** NotKinopoisk\Services -- **API:** https://kinopoiskapiunofficial.tech -- **Документация API:** https://kinopoiskapiunofficial.tech/documentation/api -- **Автор:** Maxim Harder -- **Версия:** 1.0.0 diff --git a/docs/dev/notkinopoiskphp/client.md b/docs/dev/notkinopoiskphp/client.md index fed0f6f..1c8afa3 100644 --- a/docs/dev/notkinopoiskphp/client.md +++ b/docs/dev/notkinopoiskphp/client.md @@ -8,15 +8,15 @@ **🔗 Связанные классы:** -- [FilmService](services/film-service.md) - Сервис для работы с фильмами -- [PersonService](services/person-service.md) - Сервис для работы с персонами -- [MediaService](services/media-service.md) - Сервис для работы с медиа -- [UserService](services/user-service.md) - Сервис для работы с пользователями -- [ApiVersion](enums/api-version.md) - Версии API -- [ApiException](exceptions/api-exception.md) - Базовое исключение API -- [InvalidApiKeyException](exceptions/invalid-api-key-exception.md) - Неверный API ключ -- [RateLimitException](exceptions/rate-limit-exception.md) - Превышение лимита -- [ResourceNotFoundException](exceptions/resource-not-found-exception.md) - Ресурс не найден +- [FilmService](./services/film-service.md) - Сервис для работы с фильмами +- [PersonService](./services/person-service.md) - Сервис для работы с персонами +- [MediaService](./services/media-service.md) - Сервис для работы с медиа +- [UserService](./services/user-service.md) - Сервис для работы с пользователями +- [ApiVersion](./enums/api-version.md) - Версии API +- [ApiException](./exceptions/api-exception.md) - Базовое исключение API +- [InvalidApiKeyException](./exceptions/invalid-api-key-exception.md) - Неверный API ключ +- [RateLimitException](./exceptions/rate-limit-exception.md) - Превышение лимита +- [ResourceNotFoundException](./exceptions/resource-not-found-exception.md) - Ресурс не найден --- @@ -235,10 +235,3 @@ try { } ``` -## Информация о пакете - -- **Пакет:** NotKinopoisk -- **API:** https://kinopoiskapiunofficial.tech -- **Документация API:** https://kinopoiskapiunofficial.tech/documentation/api -- **Автор:** Maxim Harder -- **Версия:** 1.0.0 diff --git a/docs/dev/notkinopoiskphp/enums/account-type.md b/docs/dev/notkinopoiskphp/enums/account-type.md new file mode 100644 index 0000000..aa4d33b --- /dev/null +++ b/docs/dev/notkinopoiskphp/enums/account-type.md @@ -0,0 +1,334 @@ +# AccountType + +## Описание + +`AccountType` - это перечисление типов аккаунтов в Kinopoisk API. Определяет различные типы аккаунтов API, которые влияют на лимиты и возможности использования API. + +## Основные возможности + +- Определение типов аккаунтов API +- Проверка бесплатности аккаунта +- Проверка безлимитности аккаунта +- Получение человекочитаемых названий типов +- Совместимость с Kinopoisk API + +## Значения перечисления + +### FREE + +```php +case FREE = 'FREE'; +``` + +**Описание:** Бесплатный аккаунт + +**Использование:** Базовый аккаунт с ограниченными возможностями + +**Пример:** + +```php +$accountType = AccountType::FREE; +echo $accountType->getDisplayName(); // "Бесплатный" +echo $accountType->isFree(); // true +echo $accountType->isUnlimited(); // false +``` + +### PAID + +```php +case PAID = 'PAID'; +``` + +**Описание:** Платный аккаунт + +**Использование:** Аккаунт с расширенными возможностями за плату + +**Пример:** + +```php +$accountType = AccountType::PAID; +echo $accountType->getDisplayName(); // "Платный" +echo $accountType->isFree(); // false +echo $accountType->isUnlimited(); // false +``` + +### UNLIMITED + +```php +case UNLIMITED = 'UNLIMITED'; +``` + +**Описание:** Безлимитный аккаунт + +**Использование:** Аккаунт с максимальными возможностями без ограничений + +**Пример:** + +```php +$accountType = AccountType::UNLIMITED; +echo $accountType->getDisplayName(); // "Безлимитный" +echo $accountType->isFree(); // false +echo $accountType->isUnlimited(); // true +``` + +## Методы + +### isFree() + +```php +public function isFree(): bool +``` + +Проверяет, является ли аккаунт бесплатным. + +#### Возвращает + +- `bool` - true если аккаунт бесплатный, false в противном случае + +#### Пример использования + +```php +use NotKinopoisk\Enums\AccountType; + +$accountType = AccountType::FREE; + +if ($accountType->isFree()) { + echo "Бесплатный аккаунт"; +} else { + echo "Платный аккаунт"; +} +``` + +### isUnlimited() + +```php +public function isUnlimited(): bool +``` + +Проверяет, является ли аккаунт безлимитным. + +#### Возвращает + +- `bool` - true если аккаунт безлимитный, false в противном случае + +#### Пример использования + +```php +use NotKinopoisk\Enums\AccountType; + +$accountType = AccountType::UNLIMITED; + +if ($accountType->isUnlimited()) { + echo "Безлимитный аккаунт"; +} else { + echo "Аккаунт с ограничениями"; +} +``` + +### getDisplayName() + +```php +public function getDisplayName(): string +``` + +Получает человекочитаемое название типа аккаунта. + +#### Возвращает + +- `string` - Название типа аккаунта на русском языке + +#### Пример использования + +```php +use NotKinopoisk\Enums\AccountType; + +echo AccountType::FREE->getDisplayName(); // "Бесплатный" +echo AccountType::PAID->getDisplayName(); // "Платный" +echo AccountType::UNLIMITED->getDisplayName(); // "Безлимитный" +``` + +## Сравнение типов аккаунтов + +```php +use NotKinopoisk\Enums\AccountType; + +// Проверка типа аккаунта +$accountType = AccountType::FREE; + +if ($accountType === AccountType::FREE) { + echo "Это бесплатный аккаунт"; +} + +// Использование в switch/match +$message = match ($accountType) { + AccountType::FREE => 'Бесплатный аккаунт с ограничениями', + AccountType::PAID => 'Платный аккаунт с расширенными возможностями', + AccountType::UNLIMITED => 'Безлимитный аккаунт без ограничений', +}; +``` + +## Использование в моделях + +Типы аккаунтов используются в моделях, связанных с API ключами: + +```php +use NotKinopoisk\Enums\AccountType; +use NotKinopoisk\Models\ApiKeyInfo; + +// Создание информации об API ключе +$apiKeyInfo = new ApiKeyInfo( + type: AccountType::PAID, + current: 100, + limit: 1000 +); + +// Проверка типа аккаунта +if ($apiKeyInfo->type->isFree()) { + echo "Используется бесплатный аккаунт"; +} elseif ($apiKeyInfo->type->isUnlimited()) { + echo "Используется безлимитный аккаунт"; +} else { + echo "Используется платный аккаунт"; +} +``` + +## Логика работы методов + +### isFree() + +Метод `isFree()` возвращает `true` только для значения `FREE`: + +```php +AccountType::FREE->isFree() // true +AccountType::PAID->isFree() // false +AccountType::UNLIMITED->isFree() // false +``` + +### isUnlimited() + +Метод `isUnlimited()` возвращает `true` только для значения `UNLIMITED`: + +```php +AccountType::FREE->isUnlimited() // false +AccountType::PAID->isUnlimited() // false +AccountType::UNLIMITED->isUnlimited() // true +``` + +## Валидация значений + +```php +use NotKinopoisk\Enums\AccountType; + +// Проверка существования значения +$value = 'FREE'; +if (AccountType::tryFrom($value)) { + $accountType = AccountType::from($value); + echo "Тип аккаунта: {$accountType->getDisplayName()}\n"; +} else { + echo "Неизвестный тип аккаунта: {$value}\n"; +} +``` + +## Получение всех значений + +```php +use NotKinopoisk\Enums\AccountType; + +// Получение всех типов аккаунтов +$allTypes = AccountType::cases(); + +foreach ($allTypes as $type) { + echo "{$type->value} => {$type->getDisplayName()}\n"; + echo " Бесплатный: " . ($type->isFree() ? 'Да' : 'Нет') . "\n"; + echo " Безлимитный: " . ($type->isUnlimited() ? 'Да' : 'Нет') . "\n"; + echo "\n"; +} +``` + +## Обработка ошибок + +```php +use NotKinopoisk\Enums\AccountType; + +try { + // Попытка создания из несуществующего значения + $accountType = AccountType::from('INVALID'); +} catch (\ValueError $e) { + echo "Ошибка: Неизвестный тип аккаунта\n"; +} + +// Безопасное получение значения +$accountType = AccountType::tryFrom('FREE'); +if ($accountType) { + echo "Тип аккаунта найден: {$accountType->getDisplayName()}\n"; +} else { + echo "Тип аккаунта не найден\n"; +} +``` + +## Пример полного использования + +```php +value}\n"; +echo "Название: {$accountType->getDisplayName()}\n"; +echo "Бесплатный: " . ($accountType->isFree() ? 'Да' : 'Нет') . "\n"; +echo "Безлимитный: " . ($accountType->isUnlimited() ? 'Да' : 'Нет') . "\n"; + +// Сравнение +if ($accountType === AccountType::PAID) { + echo "Это платный аккаунт\n"; +} + +// Использование в массиве +$accountTypes = [ + AccountType::FREE, + AccountType::PAID, + AccountType::UNLIMITED +]; + +echo "Типы аккаунтов:\n"; +foreach ($accountTypes as $type) { + echo "- {$type->getDisplayName()}\n"; +} + +// Фильтрация по типам +$freeAccounts = array_filter($accountTypes, fn($t) => $t->isFree()); +$unlimitedAccounts = array_filter($accountTypes, fn($t) => $t->isUnlimited()); + +echo "Бесплатные аккаунты: " . count($freeAccounts) . "\n"; +echo "Безлимитные аккаунты: " . count($unlimitedAccounts) . "\n"; + +// Проверка типа +$checkType = 'UNLIMITED'; +$foundType = AccountType::tryFrom($checkType); + +if ($foundType) { + echo "Найден тип аккаунта: {$foundType->getDisplayName()}\n"; +} else { + echo "Тип аккаунта '{$checkType}' не найден\n"; +} +``` + +## Связанные классы + +- [`ApiKeyInfo`](../models/api-key-info.md) - Модель информации об API ключе +- [`ApiKeyQouta`](../models/api-key-qouta.md) - Модель квоты API ключа +- [`UserService`](../services/user-service.md) - Сервис для работы с пользовательскими данными + +## API Endpoints + +Типы аккаунтов используются в следующих API endpoints: + +- `/api/v1/user/info` - Информация о пользователе +- `/api/v1/user/quota` - Квота пользователя +- `/api/v1/user/limits` - Лимиты пользователя diff --git a/docs/dev/notkinopoiskphp/enums/content-type.md b/docs/dev/notkinopoiskphp/enums/content-type.md index 4e8fca2..079e276 100644 --- a/docs/dev/notkinopoiskphp/enums/content-type.md +++ b/docs/dev/notkinopoiskphp/enums/content-type.md @@ -689,5 +689,5 @@ if ($analysis['ratingAnalysis']['lowestRatedType']) { ## Связанные классы -- [`Film`](film.md) - Модель фильма +- [`Film`](../models/film.md) - Модель фильма - [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/enums/distribution-sub-type.md b/docs/dev/notkinopoiskphp/enums/distribution-sub-type.md new file mode 100644 index 0000000..05e3389 --- /dev/null +++ b/docs/dev/notkinopoiskphp/enums/distribution-sub-type.md @@ -0,0 +1,283 @@ +# DistributionSubType + +## Описание + +`DistributionSubType` - это перечисление подтипов проката фильмов в Kinopoisk API. Определяет различные способы распространения и показа фильмов, включая традиционные кинотеатры и современные цифровые форматы. + +## Основные возможности + +- Определение подтипов проката фильмов +- Получение человекочитаемых названий +- Поддержка различных форматов распространения +- Совместимость с Kinopoisk API + +## Значения перечисления + +### CINEMA + +```php +case CINEMA = 'CINEMA'; +``` + +**Описание:** Кинотеатры + +**Использование:** Традиционный прокат в кинотеатрах + +**Пример:** + +```php +$subType = DistributionSubType::CINEMA; +echo $subType->getDisplayName(); // "Кинотеатры" +``` + +### DVD + +```php +case DVD = 'DVD'; +``` + +**Описание:** DVD + +**Использование:** Распространение на DVD-носителях + +**Пример:** + +```php +$subType = DistributionSubType::DVD; +echo $subType->getDisplayName(); // "DVD" +``` + +### DIGITAL + +```php +case DIGITAL = 'DIGITAL'; +``` + +**Описание:** Цифровой релиз + +**Использование:** Цифровое распространение (стриминг, загрузка) + +**Пример:** + +```php +$subType = DistributionSubType::DIGITAL; +echo $subType->getDisplayName(); // "Цифровой релиз" +``` + +### BLURAY + +```php +case BLURAY = 'BLURAY'; +``` + +**Описание:** Blu-ray + +**Использование:** Распространение на Blu-ray-носителях + +**Пример:** + +```php +$subType = DistributionSubType::BLURAY; +echo $subType->getDisplayName(); // "Blu-ray" +``` + +## Методы + +### getDisplayName() + +```php +public function getDisplayName(): string +``` + +Возвращает человекочитаемое название подтипа проката. + +#### Возвращает + +- `string` - Человекочитаемое название подтипа + +#### Пример использования + +```php +use NotKinopoisk\Enums\DistributionSubType; + +$subType = DistributionSubType::CINEMA; +echo $subType->getDisplayName(); // "Кинотеатры" + +$subType = DistributionSubType::DIGITAL; +echo $subType->getDisplayName(); // "Цифровой релиз" +``` + +## Сравнение значений + +```php +use NotKinopoisk\Enums\DistributionSubType; + +// Проверка типа проката +$subType = DistributionSubType::CINEMA; + +if ($subType === DistributionSubType::CINEMA) { + echo "Это прокат в кинотеатрах"; +} + +// Использование в switch/match +$displayName = match ($subType) { + DistributionSubType::CINEMA => 'Кинотеатры', + DistributionSubType::DVD => 'DVD', + DistributionSubType::DIGITAL => 'Цифровой релиз', + DistributionSubType::BLURAY => 'Blu-ray', +}; +``` + +## Связь с DistributionType + +`DistributionSubType` является подтипом основного типа проката `DistributionType`. Подтипы уточняют конкретный способ распространения в рамках общего типа проката. + +### Пример использования вместе с DistributionType + +```php +use NotKinopoisk\Enums\DistributionType; +use NotKinopoisk\Enums\DistributionSubType; + +// Основной тип проката +$distributionType = DistributionType::WORLDWIDE; + +// Подтип проката +$subType = DistributionSubType::CINEMA; + +echo "Тип проката: {$distributionType->getDisplayName()}\n"; +echo "Подтип проката: {$subType->getDisplayName()}\n"; +// Выведет: +// Тип проката: Мировой прокат +// Подтип проката: Кинотеатры +``` + +## Использование в моделях + +Подтипы проката используются в моделях, связанных с дистрибуцией фильмов: + +```php +use NotKinopoisk\Enums\DistributionSubType; +use NotKinopoisk\Models\Distribution; + +// Создание объекта дистрибуции +$distribution = new Distribution( + type: DistributionType::WORLDWIDE, + subType: DistributionSubType::CINEMA, + date: '2024-01-15' +); + +// Получение информации о подтипе +echo "Способ проката: {$distribution->subType->getDisplayName()}\n"; +``` + +## Валидация значений + +```php +use NotKinopoisk\Enums\DistributionSubType; + +// Проверка существования значения +$value = 'CINEMA'; +if (DistributionSubType::tryFrom($value)) { + $subType = DistributionSubType::from($value); + echo "Подтип проката: {$subType->getDisplayName()}\n"; +} else { + echo "Неизвестный подтип проката: {$value}\n"; +} +``` + +## Получение всех значений + +```php +use NotKinopoisk\Enums\DistributionSubType; + +// Получение всех подтипов проката +$allSubTypes = DistributionSubType::cases(); + +foreach ($allSubTypes as $subType) { + echo "{$subType->value} => {$subType->getDisplayName()}\n"; +} + +// Выведет: +// CINEMA => Кинотеатры +// DVD => DVD +// DIGITAL => Цифровой релиз +// BLURAY => Blu-ray +``` + +## Обработка ошибок + +```php +use NotKinopoisk\Enums\DistributionSubType; + +try { + // Попытка создания из несуществующего значения + $subType = DistributionSubType::from('INVALID'); +} catch (\ValueError $e) { + echo "Ошибка: Неизвестный подтип проката\n"; +} + +// Безопасное получение значения +$subType = DistributionSubType::tryFrom('CINEMA'); +if ($subType) { + echo "Подтип найден: {$subType->getDisplayName()}\n"; +} else { + echo "Подтип не найден\n"; +} +``` + +## Пример полного использования + +```php +value}\n"; +echo "Название: {$subType->getDisplayName()}\n"; + +// Сравнение +if ($subType === DistributionSubType::DIGITAL) { + echo "Это цифровой релиз\n"; +} + +// Использование в массиве +$subTypes = [ + DistributionSubType::CINEMA, + DistributionSubType::DIGITAL, + DistributionSubType::BLURAY +]; + +echo "Доступные подтипы проката:\n"; +foreach ($subTypes as $type) { + echo "- {$type->getDisplayName()}\n"; +} + +// Проверка типа +$checkType = 'DVD'; +$foundType = DistributionSubType::tryFrom($checkType); + +if ($foundType) { + echo "Найден подтип: {$foundType->getDisplayName()}\n"; +} else { + echo "Подтип '{$checkType}' не найден\n"; +} +``` + +## Связанные классы + +- [`DistributionType`](./distribution-type.md) - Основные типы проката +- [`Distribution`](../models/distribution.md) - Модель дистрибуции фильма +- [`Film`](../models/film.md) - Модель фильма (может содержать информацию о прокате) + +## API Endpoints + +Подтипы проката используются в следующих API endpoints: + +- `/api/v1/films/{id}/distributions` - Информация о прокате фильма +- `/api/v1/films/{id}` - Детальная информация о фильме (включая прокат) diff --git a/docs/dev/notkinopoiskphp/enums/index.md b/docs/dev/notkinopoiskphp/enums/index.md index 14379ff..e585b8e 100644 --- a/docs/dev/notkinopoiskphp/enums/index.md +++ b/docs/dev/notkinopoiskphp/enums/index.md @@ -12,38 +12,39 @@ ### 🖼️ Медиа и контент -- [ImageType](image-type.md) - Типы изображений -- [VideoSite](video-site.md) - Сайты видео -- [ContentType](content-type.md) - Типы контента -- [CollectionType](collection-type.md) - Типы коллекций +- [ImageType](./image-type.md) - Типы изображений +- [VideoSite](./video-site.md) - Сайты видео +- [ContentType](./content-type.md) - Типы контента +- [CollectionType](./collection-type.md) - Типы коллекций ### 📝 Отзывы и факты -- [ReviewType](review-type.md) - Типы отзывов -- [ReviewOrder](review-order.md) - Порядок сортировки отзывов -- [FactType](fact-type.md) - Типы фактов +- [ReviewType](./review-type.md) - Типы отзывов +- [ReviewOrder](./review-order.md) - Порядок сортировки отзывов +- [FactType](./fact-type.md) - Типы фактов ### 👥 Персоны и профессии -- [ProfessionKey](profession-key.md) - Ключи профессий -- [Sex](sex.md) - Пол -- [RelationType](relation-type.md) - Типы связей +- [ProfessionKey](./profession-key.md) - Ключи профессий +- [Sex](./sex.md) - Пол +- [RelationType](./relation-type.md) - Типы связей ### 🎬 Фильмы и сериалы -- [FilmOrder](film-order.md) - Порядок сортировки фильмов -- [DistributionType](distribution-type.md) - Типы дистрибуции -- [DistributionSubType](distribution-sub-type.md) - Подтипы дистрибуции +- [FilmOrder](./film-order.md) - Порядок сортировки фильмов +- [DistributionType](./distribution-type.md) - Типы дистрибуции +- [DistributionSubType](./distribution-sub-type.md) - Подтипы дистрибуции +- [ProductionStatus](./production-status.md) - Статусы производства ### 📊 Статистика и награды -- [BoxOfficeType](box-office-type.md) - Типы кассовых сборов -- [Month](month.md) - Месяцы +- [BoxOfficeType](./box-office-type.md) - Типы кассовых сборов +- [Month](./month.md) - Месяцы ### 🔧 API и система -- [ApiVersion](api-version.md) - Версии API -- [AccountType](account-type.md) - Типы аккаунтов +- [ApiVersion](./api-version.md) - Версии API +- [AccountType](./account-type.md) - Типы аккаунтов ## 🔗 Связанные компоненты @@ -321,6 +322,3 @@ foreach ($reviewOrders as $order) { - [Исключения](../exceptions/index.md) - Обработка ошибок - [Интерфейсы](../interfaces/index.md) - Базовые интерфейсы ---- - -**📚 Навигация:** [Главная](../index.md) → Перечисления diff --git a/docs/dev/notkinopoiskphp/enums/production-status.md b/docs/dev/notkinopoiskphp/enums/production-status.md new file mode 100644 index 0000000..51cb98e --- /dev/null +++ b/docs/dev/notkinopoiskphp/enums/production-status.md @@ -0,0 +1,389 @@ +# ProductionStatus + +## Описание + +`ProductionStatus` - это перечисление статусов производства контента в Kinopoisk API. Определяет различные этапы производственного процесса фильмов и сериалов, от анонса до завершения. + +## Основные возможности + +- Определение статусов производства контента +- Проверка завершенности производства +- Проверка активной фазы производства +- Получение человекочитаемых названий статусов +- Совместимость с Kinopoisk API + +## Значения перечисления + +### FILMING + +```php +case FILMING = 'FILMING'; +``` + +**Описание:** Статус съемки фильма - активная фаза производства + +**Использование:** Фильм находится в процессе съемки + +**Пример:** + +```php +$status = ProductionStatus::FILMING; +echo $status->getDisplayName(); // "В производстве" +echo $status->inProduction(); // true +``` + +### PRE_PRODUCTION + +```php +case PRE_PRODUCTION = 'PRE_PRODUCTION'; +``` + +**Описание:** Статус пре-производства - подготовительная фаза + +**Использование:** Подготовка к съемкам (кастинг, сценарий, планирование) + +**Пример:** + +```php +$status = ProductionStatus::PRE_PRODUCTION; +echo $status->getDisplayName(); // "Пре-производство" +echo $status->inProduction(); // true +``` + +### POST_PRODUCTION + +```php +case POST_PRODUCTION = 'POST_PRODUCTION'; +``` + +**Описание:** Статус пост-производства - монтаж и обработка + +**Использование:** Монтаж, озвучивание, цветокоррекция + +**Пример:** + +```php +$status = ProductionStatus::POST_PRODUCTION; +echo $status->getDisplayName(); // "Пост-производство" +echo $status->inProduction(); // true +``` + +### COMPLETED + +```php +case COMPLETED = 'COMPLETED'; +``` + +**Описание:** Статус завершенного производства + +**Использование:** Фильм полностью готов к показу + +**Пример:** + +```php +$status = ProductionStatus::COMPLETED; +echo $status->getDisplayName(); // "Завершено" +echo $status->isFinished(); // true +echo $status->inProduction(); // false +``` + +### ANNOUNCED + +```php +case ANNOUNCED = 'ANNOUNCED'; +``` + +**Описание:** Статус объявленного проекта - только анонс + +**Использование:** Проект анонсирован, но производство еще не началось + +**Пример:** + +```php +$status = ProductionStatus::ANNOUNCED; +echo $status->getDisplayName(); // "Объявлено" +echo $status->isFinished(); // false +echo $status->inProduction(); // false +``` + +### UNKNOWN + +```php +case UNKNOWN = 'UNKNOWN'; +``` + +**Описание:** Неизвестный статус производства + +**Использование:** Статус производства не определен + +**Пример:** + +```php +$status = ProductionStatus::UNKNOWN; +echo $status->getDisplayName(); // "Неизвестно" +echo $status->isFinished(); // false +echo $status->inProduction(); // false +``` + +## Методы + +### isFinished() + +```php +public function isFinished(): bool +``` + +Проверяет, завершено ли производство контента. + +#### Возвращает + +- `bool` - true если производство завершено, false в противном случае + +#### Пример использования + +```php +use NotKinopoisk\Enums\ProductionStatus; + +$status = ProductionStatus::COMPLETED; + +if ($status->isFinished()) { + echo "Производство завершено, можно смотреть"; +} else { + echo "Производство еще не завершено"; +} +``` + +### inProduction() + +```php +public function inProduction(): bool +``` + +Проверяет, находится ли контент в активной фазе производства. + +#### Возвращает + +- `bool` - true если контент находится в производстве, false в противном случае + +#### Пример использования + +```php +use NotKinopoisk\Enums\ProductionStatus; + +$status = ProductionStatus::FILMING; + +if ($status->inProduction()) { + echo "Проект активно разрабатывается"; +} else { + echo "Проект либо завершен, либо только анонсирован"; +} +``` + +### getDisplayName() + +```php +public function getDisplayName(): string +``` + +Получает человекочитаемое название статуса производства. + +#### Возвращает + +- `string` - Название статуса производства на русском языке + +#### Пример использования + +```php +use NotKinopoisk\Enums\ProductionStatus; + +echo ProductionStatus::FILMING->getDisplayName(); // "В производстве" +echo ProductionStatus::COMPLETED->getDisplayName(); // "Завершено" +echo ProductionStatus::ANNOUNCED->getDisplayName(); // "Объявлено" +``` + +## Логика работы методов + +### isFinished() + +Метод `isFinished()` использует строковое сравнение значений enum для определения порядка: + +```php +// Значения в порядке возрастания +ANNOUNCED < UNKNOWN < PRE_PRODUCTION < FILMING < POST_PRODUCTION < COMPLETED + +// Метод возвращает true для COMPLETED и всех статусов "выше" +``` + +### inProduction() + +Метод `inProduction()` возвращает `true` только для активных стадий: + +- `PRE_PRODUCTION` - Пре-производство +- `FILMING` - Съемки +- `POST_PRODUCTION` - Пост-производство + +## Сравнение статусов + +```php +use NotKinopoisk\Enums\ProductionStatus; + +// Проверка конкретного статуса +$status = ProductionStatus::FILMING; + +if ($status === ProductionStatus::FILMING) { + echo "Фильм находится в съемках"; +} + +// Использование в switch/match +$message = match ($status) { + ProductionStatus::ANNOUNCED => 'Проект анонсирован', + ProductionStatus::PRE_PRODUCTION => 'Идет подготовка к съемкам', + ProductionStatus::FILMING => 'Идут съемки', + ProductionStatus::POST_PRODUCTION => 'Идет монтаж', + ProductionStatus::COMPLETED => 'Фильм готов', + ProductionStatus::UNKNOWN => 'Статус неизвестен', +}; +``` + +## Использование в моделях + +Статусы производства используются в моделях фильмов: + +```php +use NotKinopoisk\Enums\ProductionStatus; +use NotKinopoisk\Models\Film; + +// Создание фильма с определенным статусом +$film = new Film( + kinopoiskId: 123, + nameRu: 'Новый фильм', + productionStatus: ProductionStatus::FILMING +); + +// Проверка статуса +if ($film->productionStatus->inProduction()) { + echo "Фильм '{$film->nameRu}' находится в производстве"; +} + +if ($film->productionStatus->isFinished()) { + echo "Фильм '{$film->nameRu}' готов к показу"; +} +``` + +## Валидация значений + +```php +use NotKinopoisk\Enums\ProductionStatus; + +// Проверка существования значения +$value = 'FILMING'; +if (ProductionStatus::tryFrom($value)) { + $status = ProductionStatus::from($value); + echo "Статус производства: {$status->getDisplayName()}\n"; +} else { + echo "Неизвестный статус производства: {$value}\n"; +} +``` + +## Получение всех значений + +```php +use NotKinopoisk\Enums\ProductionStatus; + +// Получение всех статусов производства +$allStatuses = ProductionStatus::cases(); + +foreach ($allStatuses as $status) { + echo "{$status->value} => {$status->getDisplayName()}\n"; + echo " В производстве: " . ($status->inProduction() ? 'Да' : 'Нет') . "\n"; + echo " Завершено: " . ($status->isFinished() ? 'Да' : 'Нет') . "\n"; + echo "\n"; +} +``` + +## Обработка ошибок + +```php +use NotKinopoisk\Enums\ProductionStatus; + +try { + // Попытка создания из несуществующего значения + $status = ProductionStatus::from('INVALID'); +} catch (\ValueError $e) { + echo "Ошибка: Неизвестный статус производства\n"; +} + +// Безопасное получение значения +$status = ProductionStatus::tryFrom('FILMING'); +if ($status) { + echo "Статус найден: {$status->getDisplayName()}\n"; +} else { + echo "Статус не найден\n"; +} +``` + +## Пример полного использования + +```php +value}\n"; +echo "Название: {$status->getDisplayName()}\n"; +echo "В производстве: " . ($status->inProduction() ? 'Да' : 'Нет') . "\n"; +echo "Завершено: " . ($status->isFinished() ? 'Да' : 'Нет') . "\n"; + +// Сравнение +if ($status === ProductionStatus::FILMING) { + echo "Это статус съемок\n"; +} + +// Использование в массиве +$statuses = [ + ProductionStatus::ANNOUNCED, + ProductionStatus::FILMING, + ProductionStatus::COMPLETED +]; + +echo "Статусы производства:\n"; +foreach ($statuses as $s) { + echo "- {$s->getDisplayName()}\n"; +} + +// Фильтрация по статусам +$activeProjects = array_filter($statuses, fn($s) => $s->inProduction()); +$finishedProjects = array_filter($statuses, fn($s) => $s->isFinished()); + +echo "Активные проекты: " . count($activeProjects) . "\n"; +echo "Завершенные проекты: " . count($finishedProjects) . "\n"; + +// Проверка статуса +$checkStatus = 'COMPLETED'; +$foundStatus = ProductionStatus::tryFrom($checkStatus); + +if ($foundStatus) { + echo "Найден статус: {$foundStatus->getDisplayName()}\n"; +} else { + echo "Статус '{$checkStatus}' не найден\n"; +} +``` + +## Связанные классы + +- [`Film`](../models/film.md) - Модель фильма, использующая данное перечисление +- [`Distribution`](../models/distribution.md) - Модель дистрибуции (может зависеть от статуса производства) + +## API Endpoints + +Статусы производства используются в следующих API endpoints: + +- `/api/v1/films/{id}` - Детальная информация о фильме +- `/api/v1/films` - Список фильмов с фильтрацией по статусу +- `/api/v1/films/premieres` - Премьеры фильмов diff --git a/docs/dev/notkinopoiskphp/exceptions/api-exception.md b/docs/dev/notkinopoiskphp/exceptions/api-exception.md index 14fd374..a0fc3cd 100644 --- a/docs/dev/notkinopoiskphp/exceptions/api-exception.md +++ b/docs/dev/notkinopoiskphp/exceptions/api-exception.md @@ -397,7 +397,7 @@ foreach (array_slice($analysis['mostCommonErrors'], 0, 5, true) as $message => $ ## Связанные классы -- [`InvalidApiKeyException`](invalid-api-key-exception.md) - Исключение для неверного API ключа -- [`RateLimitException`](rate-limit-exception.md) - Исключение для превышения лимита запросов -- [`ResourceNotFoundException`](resource-not-found-exception.md) - Исключение для не найденных ресурсов -- [`KpValidationException`](kp-validation-exception.md) - Исключение для ошибок валидации +- [`InvalidApiKeyException`](./invalid-api-key-exception.md) - Исключение для неверного API ключа +- [`RateLimitException`](./rate-limit-exception.md) - Исключение для превышения лимита запросов +- [`ResourceNotFoundException`](./resource-not-found-exception.md) - Исключение для не найденных ресурсов +- [`KpValidationException`](./kp-validation-exception.md) - Исключение для ошибок валидации diff --git a/docs/dev/notkinopoiskphp/exceptions/index.md b/docs/dev/notkinopoiskphp/exceptions/index.md index 5a5a067..a054ec5 100644 --- a/docs/dev/notkinopoiskphp/exceptions/index.md +++ b/docs/dev/notkinopoiskphp/exceptions/index.md @@ -10,7 +10,7 @@ ## 📋 Список исключений -### ⚠️ [ApiException](api-exception.md) +### ⚠️ [ApiException](./api-exception.md) Базовое исключение для всех ошибок, связанных с Kinopoisk API. @@ -25,7 +25,7 @@ **Использование:** Обработка общих ошибок API -### 🔑 [InvalidApiKeyException](invalid-api-key-exception.md) +### 🔑 [InvalidApiKeyException](./invalid-api-key-exception.md) Исключение для неверного или отсутствующего API ключа. @@ -40,7 +40,7 @@ **Использование:** Обработка ошибок аутентификации -### ⏱️ [RateLimitException](rate-limit-exception.md) +### ⏱️ [RateLimitException](./rate-limit-exception.md) Исключение для превышения лимита запросов к API. @@ -55,7 +55,7 @@ **Использование:** Обработка превышения лимитов запросов -### 🔍 [ResourceNotFoundException](resource-not-found-exception.md) +### 🔍 [ResourceNotFoundException](./resource-not-found-exception.md) Исключение для ненайденных ресурсов в API. @@ -70,7 +70,7 @@ **Использование:** Обработка отсутствующих ресурсов -### ✅ [KpValidationException](kp-validation-exception.md) +### ✅ [KpValidationException](./kp-validation-exception.md) Исключение для ошибок валидации данных. @@ -416,7 +416,3 @@ public function getPrevious(): ?Throwable - [Ответы](../responses/index.md) - Могут выбрасывать исключения валидации - [Перечисления](../enums/index.md) - Используются в исключениях - [Интерфейсы](../interfaces/index.md) - Базовые интерфейсы - ---- - -**📚 Навигация:** [Главная](../index.md) → Исключения diff --git a/docs/dev/notkinopoiskphp/exceptions/invalid-api-key-exception.md b/docs/dev/notkinopoiskphp/exceptions/invalid-api-key-exception.md index 75b9c4d..d70217e 100644 --- a/docs/dev/notkinopoiskphp/exceptions/invalid-api-key-exception.md +++ b/docs/dev/notkinopoiskphp/exceptions/invalid-api-key-exception.md @@ -221,6 +221,6 @@ if (!$result['success']) { ## Связанные исключения -- [`ApiException`](api-exception.md) - Базовое исключение API -- [`RateLimitException`](rate-limit-exception.md) - Превышение лимитов запросов -- [`ResourceNotFoundException`](resource-not-found-exception.md) - Ресурс не найден +- [`ApiException`](./api-exception.md) - Базовое исключение API +- [`RateLimitException`](./rate-limit-exception.md) - Превышение лимитов запросов +- [`ResourceNotFoundException`](./resource-not-found-exception.md) - Ресурс не найден diff --git a/docs/dev/notkinopoiskphp/exceptions/kp-validation-exception.md b/docs/dev/notkinopoiskphp/exceptions/kp-validation-exception.md new file mode 100644 index 0000000..14e7a94 --- /dev/null +++ b/docs/dev/notkinopoiskphp/exceptions/kp-validation-exception.md @@ -0,0 +1,410 @@ +# KpValidationException + +## Описание + +`KpValidationException` - это исключение для ошибок валидации данных Kinopoisk API. Выбрасывается при обнаружении некорректных или недостающих данных в параметрах запросов к API или при валидации ответов от сервера. + +## Основные возможности + +- Обработка ошибок валидации входных данных +- Проверка корректности параметров запросов +- Валидация ответов от сервера +- Цепочка исключений с предыдущими ошибками +- Совместимость с Kinopoisk API + +## Наследование + +```php +\Exception +└── NotKinopoisk\Exception\KpValidationException +``` + +## Конструктор + +```php +public function __construct( + string $message = '', + int $code = 0, + ?\Throwable $previous = null +) +``` + +### Параметры + +- `$message` (string) - Сообщение об ошибке валидации +- `$code` (int) - Код ошибки (обычно 0 для ошибок валидации) +- `$previous` (\Throwable|null) - Предыдущее исключение в цепочке + +## Когда выбрасывается + +`KpValidationException` выбрасывается в следующих случаях: + +### Валидация параметров запросов + +```php +// Некорректный ID фильма +if ($filmId <= 0) { + throw new KpValidationException('ID фильма должен быть положительным числом'); +} + +// Неверный формат даты +if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) { + throw new KpValidationException('Неверный формат даты: ожидается YYYY-MM-DD'); +} + +// Пустое обязательное поле +if (empty($requiredField)) { + throw new KpValidationException('Обязательное поле не может быть пустым'); +} +``` + +### Валидация ответов API + +```php +// Отсутствует обязательное поле в ответе +if (!array_key_exists('items', $response)) { + throw new KpValidationException('Отсутствует обязательное поле "items" в данных API'); +} + +// Неверный тип данных +if (!is_array($response['items'])) { + throw new KpValidationException('Поле "items" должно быть массивом'); +} + +// Некорректная структура данных +if ($response['total'] < 0) { + throw new KpValidationException('Поле "total" должно быть неотрицательным целым числом'); +} +``` + +### Валидация классов и методов + +```php +// Класс не существует +if (!class_exists($className)) { + throw new KpValidationException("Указанный класс не существует: {$className}"); +} + +// Метод не найден +if (!method_exists($className, 'fromArray')) { + throw new KpValidationException("Класс {$className} не имеет статического метода fromArray"); +} + +// Метод не статический +$reflection = new \ReflectionMethod($className, 'fromArray'); +if (!$reflection->isStatic()) { + throw new KpValidationException("Метод fromArray в классе {$className} должен быть статическим"); +} +``` + +## Примеры использования + +### Базовое использование + +```php +use NotKinopoisk\Exception\KpValidationException; + +try { + // Некорректный ID фильма + $client->films->getById(-1); +} catch (KpValidationException $e) { + echo "Ошибка валидации: " . $e->getMessage(); + // Выведет: "Ошибка валидации: ID фильма должен быть положительным числом" +} +``` + +### Создание исключения + +```php +use NotKinopoisk\Exception\KpValidationException; + +// Простое исключение +throw new KpValidationException('Неверный формат даты: ожидается YYYY-MM-DD'); + +// Исключение с кодом ошибки +throw new KpValidationException('ID фильма не может быть отрицательным', 400); + +// Исключение с предыдущим исключением +try { + $data = json_decode($jsonString, true); + if (json_last_error() !== JSON_ERROR_NONE) { + throw new \JsonException('Ошибка парсинга JSON'); + } +} catch (\JsonException $e) { + throw new KpValidationException('Некорректный формат данных', 0, $e); +} +``` + +### Обработка в сервисах + +```php +use NotKinopoisk\Exception\KpValidationException; +use NotKinopoisk\Services\FilmService; + +class FilmService extends AbstractService +{ + public function getById(int $filmId): Film + { + // Валидация входных данных + if ($filmId <= 0) { + throw new KpValidationException('ID фильма должен быть положительным числом'); + } + + // Выполнение запроса + $response = $this->client->get("/api/v1/films/{$filmId}"); + + // Валидация ответа + if (!isset($response['kinopoiskId'])) { + throw new KpValidationException('Некорректная структура ответа API'); + } + + return Film::fromArray($response); + } +} +``` + +### Обработка в моделях + +```php +use NotKinopoisk\Exception\KpValidationException; +use NotKinopoisk\Models\Film; + +class Film +{ + public static function fromArray(array $data): self + { + // Валидация обязательных полей + if (!isset($data['kinopoiskId'])) { + throw new KpValidationException('Отсутствует обязательное поле "kinopoiskId"'); + } + + if (!is_int($data['kinopoiskId']) || $data['kinopoiskId'] <= 0) { + throw new KpValidationException('Поле "kinopoiskId" должно быть положительным целым числом'); + } + + // Создание объекта + return new self( + kinopoiskId: $data['kinopoiskId'], + nameRu: $data['nameRu'] ?? null, + nameEn: $data['nameEn'] ?? null + ); + } +} +``` + +### Обработка в ответах API + +```php +use NotKinopoisk\Exception\KpValidationException; +use NotKinopoisk\Responses\DefaultResponse; + +class DefaultResponse +{ + public static function fromArray(array $data, string $cls): self + { + // Валидация структуры данных + if (!array_key_exists('items', $data)) { + throw new KpValidationException('Отсутствует обязательное поле "items" в данных API'); + } + + if (!is_array($data['items'])) { + throw new KpValidationException('Поле "items" должно быть массивом'); + } + + // Валидация целевого класса + if (!class_exists($cls)) { + throw new KpValidationException("Указанный класс не существует: {$cls}"); + } + + if (!method_exists($cls, 'fromArray')) { + throw new KpValidationException("Класс {$cls} не имеет статического метода fromArray"); + } + + // Создание ответа + $items = array_map( + static fn (array $itemData): object => $cls::fromArray($itemData), + $data['items'] + ); + + return new self( + total: $data['total'] ?? count($items), + items: $items + ); + } +} +``` + +## Обработка исключений + +### Простая обработка + +```php +use NotKinopoisk\Exception\KpValidationException; + +try { + $film = $client->films->getById($filmId); +} catch (KpValidationException $e) { + echo "Ошибка валидации: " . $e->getMessage(); + echo "Код ошибки: " . $e->getCode(); +} +``` + +### Детальная обработка + +```php +use NotKinopoisk\Exception\KpValidationException; + +try { + $response = $client->films->search($query, $filters); +} catch (KpValidationException $e) { + echo "Ошибка валидации: " . $e->getMessage() . "\n"; + echo "Код ошибки: " . $e->getCode() . "\n"; + echo "Файл: " . $e->getFile() . "\n"; + echo "Строка: " . $e->getLine() . "\n"; + + // Проверка предыдущего исключения + if ($e->getPrevious()) { + echo "Предыдущее исключение: " . $e->getPrevious()->getMessage() . "\n"; + } +} +``` + +### Логирование ошибок + +```php +use NotKinopoisk\Exception\KpValidationException; + +try { + $result = $client->films->getById($filmId); +} catch (KpValidationException $e) { + // Логирование ошибки + error_log("Validation error: " . $e->getMessage()); + + // Возврат значения по умолчанию + return null; +} +``` + +### Перехват в middleware + +```php +use NotKinopoisk\Exception\KpValidationException; + +class ApiMiddleware +{ + public function handle($request, $next) + { + try { + return $next($request); + } catch (KpValidationException $e) { + // Возврат JSON ответа с ошибкой валидации + return response()->json([ + 'error' => 'validation_error', + 'message' => $e->getMessage(), + 'code' => $e->getCode() + ], 400); + } + } +} +``` + +## Связанные исключения + +`KpValidationException` является частью иерархии исключений Kinopoisk API: + +- `ApiException` - Базовое исключение API +- `InvalidApiKeyException` - Неверный API ключ +- `RateLimitException` - Превышение лимита запросов +- `ResourceNotFoundException` - Ресурс не найден +- `KpValidationException` - Ошибка валидации данных + +### Пример обработки разных типов исключений + +```php +use NotKinopoisk\Exception\ApiException; +use NotKinopoisk\Exception\InvalidApiKeyException; +use NotKinopoisk\Exception\RateLimitException; +use NotKinopoisk\Exception\ResourceNotFoundException; +use NotKinopoisk\Exception\KpValidationException; + +try { + $film = $client->films->getById($filmId); +} catch (KpValidationException $e) { + echo "Ошибка валидации: " . $e->getMessage(); +} catch (ResourceNotFoundException $e) { + echo "Фильм не найден: " . $e->getMessage(); +} catch (InvalidApiKeyException $e) { + echo "Неверный API ключ: " . $e->getMessage(); +} catch (RateLimitException $e) { + echo "Превышен лимит запросов: " . $e->getMessage(); +} catch (ApiException $e) { + echo "Общая ошибка API: " . $e->getMessage(); +} +``` + +## Лучшие практики + +### 1. Специфичные сообщения об ошибках + +```php +// Плохо +throw new KpValidationException('Ошибка'); + +// Хорошо +throw new KpValidationException('ID фильма должен быть положительным числом, получено: ' . $filmId); +``` + +### 2. Валидация на ранней стадии + +```php +public function search(string $query, array $filters = []): SearchResponse +{ + // Валидация входных данных в начале метода + if (empty(trim($query))) { + throw new KpValidationException('Поисковый запрос не может быть пустым'); + } + + if (strlen($query) < 2) { + throw new KpValidationException('Поисковый запрос должен содержать минимум 2 символа'); + } + + // Выполнение логики + return $this->performSearch($query, $filters); +} +``` + +### 3. Использование цепочки исключений + +```php +try { + $data = json_decode($response, true); + if (json_last_error() !== JSON_ERROR_NONE) { + throw new \JsonException('Ошибка парсинга JSON: ' . json_last_error_msg()); + } +} catch (\JsonException $e) { + throw new KpValidationException('Некорректный формат ответа API', 0, $e); +} +``` + +### 4. Проверка типов данных + +```php +public function validateFilmData(array $data): void +{ + if (!isset($data['kinopoiskId'])) { + throw new KpValidationException('Отсутствует обязательное поле "kinopoiskId"'); + } + + if (!is_int($data['kinopoiskId'])) { + throw new KpValidationException('Поле "kinopoiskId" должно быть целым числом'); + } + + if ($data['kinopoiskId'] <= 0) { + throw new KpValidationException('Поле "kinopoiskId" должно быть положительным числом'); + } +} +``` + +## Связанные классы + +- [`ApiException`](./api-exception.md) - Базовое исключение API diff --git a/docs/dev/notkinopoiskphp/exceptions/rate-limit-exception.md b/docs/dev/notkinopoiskphp/exceptions/rate-limit-exception.md index 08017bb..0c9ac48 100644 --- a/docs/dev/notkinopoiskphp/exceptions/rate-limit-exception.md +++ b/docs/dev/notkinopoiskphp/exceptions/rate-limit-exception.md @@ -318,6 +318,4 @@ try { ## Связанные исключения -- [`ApiException`](api-exception.md) - Базовое исключение API -- [`InvalidApiKeyException`](invalid-api-key-exception.md) - Неверный API ключ -- [`ResourceNotFoundException`](resource-not-found-exception.md) - Ресурс не найден +- [`ApiException`](./api-exception.md) - Базовое исключение API diff --git a/docs/dev/notkinopoiskphp/exceptions/resource-not-found-exception.md b/docs/dev/notkinopoiskphp/exceptions/resource-not-found-exception.md index e394ee1..16e7c3d 100644 --- a/docs/dev/notkinopoiskphp/exceptions/resource-not-found-exception.md +++ b/docs/dev/notkinopoiskphp/exceptions/resource-not-found-exception.md @@ -466,7 +466,4 @@ foreach (array_slice($analysis['mostCommonErrors'], 0, 5, true) as $message => $ ## Связанные классы -- [`ApiException`](api-exception.md) - Базовое исключение для всех ошибок API -- [`InvalidApiKeyException`](invalid-api-key-exception.md) - Исключение для неверного API ключа -- [`RateLimitException`](rate-limit-exception.md) - Исключение для превышения лимита запросов -- [`KpValidationException`](kp-validation-exception.md) - Исключение для ошибок валидации +- [`ApiException`](./api-exception.md) - Базовое исключение для всех ошибок API diff --git a/docs/dev/notkinopoiskphp/index.md b/docs/dev/notkinopoiskphp/index.md index 18af33a..b05aaf3 100644 --- a/docs/dev/notkinopoiskphp/index.md +++ b/docs/dev/notkinopoiskphp/index.md @@ -6,85 +6,85 @@ ### 🚀 Быстрый старт -- [Основной клиент](client.md) - Главный класс для работы с API -- [Примеры использования](../examples/) - Готовые примеры кода -- [Карта навигации](navigation-map.md) - Интерактивная карта всей документации +- [Основной клиент](./client.md) - Главный класс для работы с API +- [Примеры использования](./https://github.com/DevCraftClub/NotKinopoiskPHP/tree/main/examples) - Готовые примеры кода +- [Карта навигации](./navigation-map.md) - Интерактивная карта всей документации ### 📦 Основные компоненты #### 🔧 Сервисы -- [FilmService](services/film-service.md) - Работа с фильмами -- [PersonService](services/person-service.md) - Работа с персонами -- [MediaService](services/media-service.md) - Работа с медиа -- [UserService](services/user-service.md) - Работа с пользователями +- [FilmService](./services/film-service.md) - Работа с фильмами +- [PersonService](./services/person-service.md) - Работа с персонами +- [MediaService](./services/media-service.md) - Работа с медиа +- [UserService](./services/user-service.md) - Работа с пользователями #### 📊 Модели данных -- [Film](models/film.md) - Модель фильма -- [Person](models/person.md) - Модель персоны -- [Staff](models/staff.md) - Модель съемочной группы -- [Review](models/review.md) - Модель отзыва -- [Image](models/image.md) - Модель изображения -- [Video](models/video.md) - Модель видео -- [Fact](models/fact.md) - Модель факта -- [Award](models/award.md) - Модель награды -- [BoxOffice](models/box-office.md) - Модель кассовых сборов -- [Country](models/country.md) - Модель страны -- [Genre](models/genre.md) - Модель жанра -- [Episode](models/episode.md) - Модель эпизода -- [Season](models/season.md) - Модель сезона -- [ExternalSource](models/external-source.md) - Модель внешнего источника -- [Distribution](models/distribution.md) - Модель дистрибуции -- [FilmSearchResult](models/film-search-result.md) - Модель результата поиска -- [PersonSpouse](models/person-spouse.md) - Модель супруга -- [PersonFilm](models/person-film.md) - Модель фильма персоны -- [UserVote](models/user-vote.md) - Модель голоса пользователя -- [FilmCollection](models/film-collection.md) - Модель коллекции фильмов -- [Filters](models/filters.md) - Модель фильтров -- [RelatedFilm](models/related-film.md) - Модель связанного фильма -- [ApiKeyInfo](models/api-key-info.md) - Модель информации об API ключе -- [ApiKeyQouta](models/api-key-qouta.md) - Модель квоты API ключа -- [MediaPost](models/media-post.md) - Модель медиа поста +- [Film](./models/film.md) - Модель фильма +- [Person](./models/person.md) - Модель персоны +- [Staff](./models/staff.md) - Модель съемочной группы +- [Review](./models/review.md) - Модель отзыва +- [Image](./models/image.md) - Модель изображения +- [Video](./models/video.md) - Модель видео +- [Fact](./models/fact.md) - Модель факта +- [Award](./models/award.md) - Модель награды +- [BoxOffice](./models/box-office.md) - Модель кассовых сборов +- [Country](./models/country.md) - Модель страны +- [Genre](./models/genre.md) - Модель жанра +- [Episode](./models/episode.md) - Модель эпизода +- [Season](./models/season.md) - Модель сезона +- [ExternalSource](./models/external-source.md) - Модель внешнего источника +- [Distribution](./models/distribution.md) - Модель дистрибуции +- [FilmSearchResult](./models/film-search-result.md) - Модель результата поиска +- [PersonSpouse](./models/person-spouse.md) - Модель супруга +- [PersonFilm](./models/person-film.md) - Модель фильма персоны +- [UserVote](./models/user-vote.md) - Модель голоса пользователя +- [FilmCollection](./models/film-collection.md) - Модель коллекции фильмов +- [Filters](./models/filters.md) - Модель фильтров +- [RelatedFilm](./models/related-film.md) - Модель связанного фильма +- [ApiKeyInfo](./models/api-key-info.md) - Модель информации об API ключе +- [ApiKeyQouta](./models/api-key-qouta.md) - Модель квоты API ключа +- [MediaPost](./models/media-post.md) - Модель медиа поста #### 🔢 Перечисления (Enums) -- [ImageType](enums/image-type.md) - Типы изображений -- [ReviewOrder](enums/review-order.md) - Порядок сортировки отзывов -- [ReviewType](enums/review-type.md) - Типы отзывов -- [FactType](enums/fact-type.md) - Типы фактов -- [ProfessionKey](enums/profession-key.md) - Ключи профессий -- [VideoSite](enums/video-site.md) - Сайты видео -- [BoxOfficeType](enums/box-office-type.md) - Типы кассовых сборов -- [DistributionType](enums/distribution-type.md) - Типы дистрибуции -- [RelationType](enums/relation-type.md) - Типы связей -- [Sex](enums/sex.md) - Пол -- [ApiVersion](enums/api-version.md) - Версии API -- [Month](enums/month.md) - Месяцы -- [FilmOrder](enums/film-order.md) - Порядок сортировки фильмов -- [ContentType](enums/content-type.md) - Типы контента -- [CollectionType](enums/collection-type.md) - Типы коллекций -- [DistributionSubType](enums/distribution-sub-type.md) - Подтипы дистрибуции -- [AccountType](enums/account-type.md) - Типы аккаунтов +- [ImageType](./enums/image-type.md) - Типы изображений +- [ReviewOrder](./enums/review-order.md) - Порядок сортировки отзывов +- [ReviewType](./enums/review-type.md) - Типы отзывов +- [FactType](./enums/fact-type.md) - Типы фактов +- [ProfessionKey](./enums/profession-key.md) - Ключи профессий +- [VideoSite](./enums/video-site.md) - Сайты видео +- [BoxOfficeType](./enums/box-office-type.md) - Типы кассовых сборов +- [DistributionType](./enums/distribution-type.md) - Типы дистрибуции +- [RelationType](./enums/relation-type.md) - Типы связей +- [Sex](./enums/sex.md) - Пол +- [ApiVersion](./enums/api-version.md) - Версии API +- [Month](./enums/month.md) - Месяцы +- [FilmOrder](./enums/film-order.md) - Порядок сортировки фильмов +- [ContentType](./enums/content-type.md) - Типы контента +- [CollectionType](./enums/collection-type.md) - Типы коллекций +- [DistributionSubType](./enums/distribution-sub-type.md) - Подтипы дистрибуции +- [AccountType](./enums/account-type.md) - Типы аккаунтов #### 📤 Ответы API -- [DefaultResponse](responses/default-response.md) - Базовый ответ -- [PaginatedResponse](responses/paginated-response.md) - Пагинированный ответ -- [KeywordSearchResponse](responses/keyword-search-response.md) - Ответ поиска по ключевым словам +- [DefaultResponse](./responses/default-response.md) - Базовый ответ +- [PaginatedResponse](./responses/paginated-response.md) - Пагинированный ответ +- [KeywordSearchResponse](./responses/keyword-search-response.md) - Ответ поиска по ключевым словам #### ⚠️ Исключения -- [ApiException](exceptions/api-exception.md) - Базовое исключение API -- [InvalidApiKeyException](exceptions/invalid-api-key-exception.md) - Неверный API ключ -- [RateLimitException](exceptions/rate-limit-exception.md) - Превышение лимита запросов -- [ResourceNotFoundException](exceptions/resource-not-found-exception.md) - Ресурс не найден -- [KpValidationException](exceptions/kp-validation-exception.md) - Ошибка валидации +- [ApiException](./exceptions/api-exception.md) - Базовое исключение API +- [InvalidApiKeyException](./exceptions/invalid-api-key-exception.md) - Неверный API ключ +- [RateLimitException](./exceptions/rate-limit-exception.md) - Превышение лимита запросов +- [ResourceNotFoundException](./exceptions/resource-not-found-exception.md) - Ресурс не найден +- [KpValidationException](./exceptions/kp-validation-exception.md) - Ошибка валидации #### 🔗 Интерфейсы -- [ModelInterface](interfaces/model-interface.md) - Интерфейс модели -- [ResponseInterface](interfaces/response-interface.md) - Интерфейс ответа +- [ModelInterface](./interfaces/model-interface.md) - Интерфейс модели +- [ResponseInterface](./interfaces/response-interface.md) - Интерфейс ответа ## 🚀 Быстрый старт @@ -237,33 +237,15 @@ composer phpcs ## 📝 Примеры использования -Смотрите папку [`examples/`](../examples/) для готовых примеров: +Смотрите папку [`examples/`](https://github.com/DevCraftClub/NotKinopoiskPHP/tree/main/examples) для готовых примеров: -- [Базовое использование](../examples/basic_usage.php) -- [Примеры конфигурации](../examples/configuration_examples.php) -- [Работа с перечислениями](../examples/enums_usage.php) -- [Использование с .env](../examples/with_dotenv.php) +- [Базовое использование](https://github.com/DevCraftClub/NotKinopoiskPHP/blob/main/examples/basic_usage.php) +- [Примеры конфигурации](https://github.com/DevCraftClub/NotKinopoiskPHP/blob/main/examples/configuration_examples.php) +- [Работа с перечислениями](https://github.com/DevCraftClub/NotKinopoiskPHP/blob/main/examples/enums_usage.php) +- [Использование с .env](https://github.com/DevCraftClub/NotKinopoiskPHP/blob/main/examples/with_dotenv.php) ## 🔗 Полезные ссылки -- [GitHub репозиторий](https://github.com/your-username/NotKinopoiskPHP) -- [Kinopoisk Unofficial API](https://kinopoiskapiunofficial.tech/) +- [GitHub репозиторий](https://github.com/DevCraftClub/NotKinopoiskPHP/) +- [Kinopoisk Unofficial API](./https://kinopoiskapiunofficial.tech/) - [Composer](https://getcomposer.org/) -- [PHP](https://www.php.net/) - -## 📄 Лицензия - -Этот проект распространяется под лицензией MIT. См. файл [LICENSE](../LICENSE) для подробностей. - -## 🤝 Поддержка - -Если у вас есть вопросы или предложения: - -1. Создайте [Issue](https://github.com/your-username/NotKinopoiskPHP/issues) -2. Напишите на email: dev@devcraft.club -3. Обратитесь к [документации](https://kinopoiskapiunofficial.tech/) - ---- - -**Версия документации:** 1.0.0 -**Последнее обновление:** diff --git a/docs/dev/notkinopoiskphp/interfaces.md b/docs/dev/notkinopoiskphp/interfaces.md deleted file mode 100644 index 9b3c4b6..0000000 --- a/docs/dev/notkinopoiskphp/interfaces.md +++ /dev/null @@ -1,192 +0,0 @@ -# Интерфейсы - -Интерфейсы библиотеки NotKinopoisk PHP Wrapper. - -## ModelInterface - -Интерфейс для моделей с возможностью преобразования в/из массива. - -### Описание - -Определяет базовый контракт для всех моделей данных в системе. Обеспечивает единообразную работу с преобразованием объектов в массивы и создания объектов из массивов данных API. - -### Основные возможности - -- Унификация процесса создания моделей из данных API -- Стандартизация сериализации объектов в массивы -- Обеспечение совместимости с системами кэширования -- Упрощение работы с JSON API responses - -### Методы - -#### `fromArray(array $data): object` - -Создает экземпляр модели из массива данных API. - -**Параметры:** - -- `$data` (array) - Массив данных от API, содержащий все необходимые поля для модели - -**Возвращает:** - -- `object` - Новый экземпляр класса-реализации с данными из массива - -**Исключения:** - -- `InvalidArgumentException` - Если обязательные поля отсутствуют в массиве данных -- `InvalidArgumentException` - Если данные имеют неверный тип или формат - -**Пример:** - -```php -$apiData = [ - 'id' => 123, - 'name' => 'Название', - 'description' => 'Описание' -]; -$model = ConcreteModel::fromArray($apiData); -``` - -#### `toArray(): array` - -Преобразует объект модели в массив. - -**Возвращает:** - -- `array` - Ассоциативный массив со всеми данными объекта - -**Пример:** - -```php -$model = new ConcreteModel('value1', 'value2'); -$array = $model->toArray(); -// Результат: ['field1' => 'value1', 'field2' => 'value2'] -``` - -### Пример реализации - -```php -class ExampleModel implements ModelInterface { - public function __construct( - private string $field1, - private string $field2 - ) {} - - public static function fromArray(array $data): object { - return new static($data['field1'], $data['field2']); - } - - public function toArray(): array { - return [ - 'field1' => $this->field1, - 'field2' => $this->field2 - ]; - } -} - -// Использование -$model = ExampleModel::fromArray($apiData); -$serialized = $model->toArray(); -``` - -## ResponseInterface - -Интерфейс для объектов ответов Kinopoisk API. - -### Описание - -Определяет общий контракт для всех типов ответов API, включая методы для создания объектов из массивов данных и валидации целевых классов. Обеспечивает единообразие в обработке различных типов ответов. - -### Основные возможности - -- Создание экземпляров ответов из данных API -- Валидация классов для преобразования элементов -- Унификация работы с различными типами ответов - -### Методы - -#### `fromArray(array $data, string $cls): object` - -Создает экземпляр ответа из массива данных API. - -**Параметры:** - -- `$data` (array) - Массив данных от API, содержащий структуру ответа -- `$cls` (string) - Полное имя класса для преобразования элементов массива - -**Возвращает:** - -- `object` - Новый экземпляр класса-реализации с преобразованными данными - -**Исключения:** - -- `KpValidationException` - Если указанный класс не существует -- `KpValidationException` - Если класс не имеет метода fromArray -- `KpValidationException` - Если данные имеют неверный формат - -#### `checkClass(string $cls): void` - -Проверяет существование и совместимость класса. - -**Параметры:** - -- `$cls` (string) - Полное имя класса для проверки - -**Исключения:** - -- `KpValidationException` - Если указанный класс не существует -- `KpValidationException` - Если класс не имеет статического метода fromArray - -#### `toArray(): array` - -Преобразует объект в массив данных. - -**Возвращает:** - -- `array` - Массив данных, представляющий объект ответа - -**Пример:** - -```php -$response = SomeResponse::fromArray($apiData); -$array = $response->toArray(); - -// Использование для сериализации -$json = json_encode($array); - -// Использование для логирования -$logger->info('Ответ API', $array); -``` - -### Пример использования - -```php -// Создание ответа из данных API -$apiData = [ - 'total' => 100, - 'items' => [ - ['id' => 1, 'name' => 'Фильм 1'], - ['id' => 2, 'name' => 'Фильм 2'] - ] -]; - -$response = DefaultResponse::fromArray($apiData, Film::class); - -// Преобразование в массив -$array = $response->toArray(); -``` - -## Связанные классы - -- `\NotKinopoisk\Models\Film` - Реализация ModelInterface -- `\NotKinopoisk\Models\Person` - Реализация ModelInterface -- `\NotKinopoisk\Models\Review` - Реализация ModelInterface -- `\NotKinopoisk\Responses\DefaultResponse` - Реализация ResponseInterface -- `\NotKinopoisk\Responses\PaginatedResponse` - Реализация ResponseInterface -- `\NotKinopoisk\Responses\SimpleResponse` - Реализация ResponseInterface - -## Информация о пакете - -- **Пакет:** NotKinopoisk\Models, NotKinopoisk\Responses -- **Версия:** 1.0.0 -- **Автор:** Maxim Harder diff --git a/docs/dev/notkinopoiskphp/interfaces/index.md b/docs/dev/notkinopoiskphp/interfaces/index.md index 9339e11..a34ecb2 100644 --- a/docs/dev/notkinopoiskphp/interfaces/index.md +++ b/docs/dev/notkinopoiskphp/interfaces/index.md @@ -10,7 +10,7 @@ ## 📋 Список интерфейсов -### 📄 [ModelInterface](model-interface.md) +### 📄 [ModelInterface](./model-interface.md) Базовый интерфейс для всех моделей данных. @@ -28,7 +28,7 @@ **Реализуется:** Все модели данных -### 📄 [ResponseInterface](response-interface.md) +### 📄 [ResponseInterface](./response-interface.md) Базовый интерфейс для всех ответов API. diff --git a/docs/dev/notkinopoiskphp/interfaces/model-interface.md b/docs/dev/notkinopoiskphp/interfaces/model-interface.md new file mode 100644 index 0000000..ebd5575 --- /dev/null +++ b/docs/dev/notkinopoiskphp/interfaces/model-interface.md @@ -0,0 +1,97 @@ +# ModelInterface + +Интерфейс для моделей с возможностью преобразования в/из массива. + +## Описание + +Определяет базовый контракт для всех моделей данных в системе. Обеспечивает единообразную работу с преобразованием объектов в массивы и создания объектов из массивов данных API. + +## Основные возможности + +- Унификация процесса создания моделей из данных API +- Стандартизация сериализации объектов в массивы +- Обеспечение совместимости с системами кэширования +- Упрощение работы с JSON API responses + +## Методы + +### `fromArray(array $data): object` + +Создает экземпляр модели из массива данных API. + +**Параметры:** + +- `$data` (array) - Массив данных от API, содержащий все необходимые поля для модели + +**Возвращает:** + +- `object` - Новый экземпляр класса-реализации с данными из массива + +**Исключения:** + +- `InvalidArgumentException` - Если обязательные поля отсутствуют в массиве данных +- `InvalidArgumentException` - Если данные имеют неверный тип или формат + +**Пример:** + +```php +$apiData = [ + 'id' => 123, + 'name' => 'Название', + 'description' => 'Описание' +]; +$model = ConcreteModel::fromArray($apiData); +``` + +### `toArray(): array` + +Преобразует объект модели в массив. + +**Возвращает:** + +- `array` - Ассоциативный массив со всеми данными объекта + +**Пример:** + +```php +$model = new ConcreteModel('value1', 'value2'); +$array = $model->toArray(); +// Результат: ['field1' => 'value1', 'field2' => 'value2'] +``` + +## Пример реализации + +```php +class ExampleModel implements ModelInterface { + public function __construct( + private string $field1, + private string $field2 + ) {} + + public static function fromArray(array $data): object { + return new static($data['field1'], $data['field2']); + } + + public function toArray(): array { + return [ + 'field1' => $this->field1, + 'field2' => $this->field2 + ]; + } +} + +// Использование +$model = ExampleModel::fromArray($apiData); +$serialized = $model->toArray(); + +``` + +# Связанные файлы + +- [Film](../models/film.md) - Основная модель фильма +- [Person](../models/person.md) - Модель персоны +- [Staff](../models/staff.md) - Модель съемочной группы + +--- + +**📚 Навигация:** [Главная](../index.md) → [Интерфейсы](index.md) → ModelInterface diff --git a/docs/dev/notkinopoiskphp/interfaces/response-interface.md b/docs/dev/notkinopoiskphp/interfaces/response-interface.md new file mode 100644 index 0000000..42d9287 --- /dev/null +++ b/docs/dev/notkinopoiskphp/interfaces/response-interface.md @@ -0,0 +1,96 @@ +# ResponseInterface + +Интерфейс для объектов ответов Kinopoisk API. + +## Описание + +Определяет общий контракт для всех типов ответов API, включая методы для создания объектов из массивов данных и валидации целевых классов. Обеспечивает единообразие в обработке различных типов ответов. + +## Основные возможности + +- Создание экземпляров ответов из данных API +- Валидация классов для преобразования элементов +- Унификация работы с различными типами ответов + +## Методы + +### `fromArray(array $data, string $cls): object` + +Создает экземпляр ответа из массива данных API. + +**Параметры:** + +- `$data` (array) - Массив данных от API, содержащий структуру ответа +- `$cls` (string) - Полное имя класса для преобразования элементов массива + +**Возвращает:** + +- `object` - Новый экземпляр класса-реализации с преобразованными данными + +**Исключения:** + +- `KpValidationException` - Если указанный класс не существует +- `KpValidationException` - Если класс не имеет метода fromArray +- `KpValidationException` - Если данные имеют неверный формат + +### `checkClass(string $cls): void` + +Проверяет существование и совместимость класса. + +**Параметры:** + +- `$cls` (string) - Полное имя класса для проверки + +**Исключения:** + +- `KpValidationException` - Если указанный класс не существует +- `KpValidationException` - Если класс не имеет статического метода fromArray + +### `toArray(): array` + +Преобразует объект в массив данных. + +**Возвращает:** + +- `array` - Массив данных, представляющий объект ответа + +**Пример:** + +```php +$response = SomeResponse::fromArray($apiData); +$array = $response->toArray(); + +// Использование для сериализации +$json = json_encode($array); + +// Использование для логирования +$logger->info('Ответ API', $array); +``` + +## Пример использования + +```php +// Создание ответа из данных API +$apiData = [ + 'total' => 100, + 'items' => [ + ['id' => 1, 'name' => 'Фильм 1'], + ['id' => 2, 'name' => 'Фильм 2'] + ] +]; + +$response = DefaultResponse::fromArray($apiData, Film::class); + +// Преобразование в массив +$array = $response->toArray(); +``` + +# Связанные файлы + +- [DefaultResponse](../responses/default-response.md) - Базовый ответ +- [PaginatedResponse](../responses/paginated-response.md) - Пагинированный ответ +- [KeywordSearchResponse](../responses/keyword-search-response.md) - Ответ поиска + +--- + +**📚 Навигация:** [Главная](../index.md) → [Интерфейсы](index.md) → ResponseInterface diff --git a/docs/dev/notkinopoiskphp/models/api-key-info.md b/docs/dev/notkinopoiskphp/models/api-key-info.md new file mode 100644 index 0000000..82549e7 --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/api-key-info.md @@ -0,0 +1,416 @@ +# ApiKeyInfo + +## Описание + +`ApiKeyInfo` - это модель информации об API ключе из Kinopoisk API. Представляет информацию о текущем API ключе, включая лимиты запросов, тип аккаунта и доступные возможности. + +## Основные возможности + +- Хранение информации об API ключе в неизменяемом виде +- Создание объекта из массива данных API +- Проверка лимитов и типа аккаунта +- Получение информации о доступных запросах +- Совместимость с Kinopoisk API + +## Наследование + +```php +NotKinopoisk\Interfaces\ModelInterface +└── NotKinopoisk\Models\ApiKeyInfo +``` + +## Конструктор + +```php +public function __construct( + public readonly ApiKeyQouta $totalQuota, + public readonly ApiKeyQouta $dailyQuota, + public readonly AccountType $accountType, +) +``` + +### Параметры + +- `$totalQuota` (ApiKeyQouta) - Информация об общих лимитах запросов +- `$dailyQuota` (ApiKeyQouta) - Информация о дневных лимитах запросов +- `$accountType` (AccountType) - Тип аккаунта (FREE, PAID, UNLIMITED) + +## Свойства + +### totalQuota + +```php +public readonly ApiKeyQouta $totalQuota +``` + +**Описание:** Информация об общих лимитах запросов + +**Тип:** `ApiKeyQouta` + +**Доступ:** Только для чтения + +**Пример:** + +```php +$totalQuota = $apiKeyInfo->totalQuota; +echo "Общий лимит: {$totalQuota->value}\n"; +echo "Использовано: {$totalQuota->used}\n"; +``` + +### dailyQuota + +```php +public readonly ApiKeyQouta $dailyQuota +``` + +**Описание:** Информация о дневных лимитах запросов + +**Тип:** `ApiKeyQouta` + +**Доступ:** Только для чтения + +**Пример:** + +```php +$dailyQuota = $apiKeyInfo->dailyQuota; +echo "Дневной лимит: {$dailyQuota->value}\n"; +echo "Использовано сегодня: {$dailyQuota->used}\n"; +``` + +### accountType + +```php +public readonly AccountType $accountType +``` + +**Описание:** Тип аккаунта (FREE, PAID, UNLIMITED) + +**Тип:** `AccountType` + +**Доступ:** Только для чтения + +**Пример:** + +```php +$accountType = $apiKeyInfo->accountType; +echo "Тип аккаунта: {$accountType->getDisplayName()}\n"; +``` + +## Статические методы + +### fromArray() + +```php +public static function fromArray(array $data): self +``` + +Создает экземпляр информации об API ключе из массива данных API. + +#### Параметры + +- `$data` (array) - Массив данных информации об API ключе от API + +#### Возвращает + +- `self` - Новый экземпляр информации об API ключе + +#### Исключения + +- `\InvalidArgumentException` - Если данные имеют неверный формат + +#### Пример использования + +```php +use NotKinopoisk\Models\ApiKeyInfo; + +$apiData = [ + 'totalQuota' => ['total' => 1000, 'used' => 150], + 'dailyQuota' => ['total' => 100, 'used' => 25], + 'accountType' => 'FREE' +]; + +$apiKeyInfo = ApiKeyInfo::fromArray($apiData); +``` + +## Методы экземпляра + +### isUnlimited() + +```php +public function isUnlimited(): bool +``` + +Проверяет, является ли аккаунт безлимитным. + +#### Возвращает + +- `bool` - true если аккаунт безлимитный, false в противном случае + +#### Пример использования + +```php +use NotKinopoisk\Models\ApiKeyInfo; + +if ($apiKeyInfo->isUnlimited()) { + echo "Безлимитный аккаунт - можно делать неограниченное количество запросов"; +} +``` + +### getRemainingTotalQuota() + +```php +public function getRemainingTotalQuota(): int +``` + +Получает количество оставшихся общих запросов. + +#### Возвращает + +- `int` - Количество оставшихся запросов + +#### Пример использования + +```php +use NotKinopoisk\Models\ApiKeyInfo; + +$remaining = $apiKeyInfo->getRemainingTotalQuota(); +echo "Осталось запросов: {$remaining}"; +``` + +### getRemainingDailyQuota() + +```php +public function getRemainingDailyQuota(): int +``` + +Получает количество оставшихся дневных запросов. + +#### Возвращает + +- `int` - Количество оставшихся дневных запросов + +#### Пример использования + +```php +use NotKinopoisk\Models\ApiKeyInfo; + +$remaining = $apiKeyInfo->getRemainingDailyQuota(); +echo "Осталось дневных запросов: {$remaining}"; +``` + +### toArray() + +```php +public function toArray(): array +``` + +Преобразует объект информации об API ключе в массив. + +#### Возвращает + +- `array` - Массив данных информации об API ключе + +#### Пример использования + +```php +use NotKinopoisk\Models\ApiKeyInfo; + +$array = $apiKeyInfo->toArray(); +// [ +// 'totalQuota' => ['total' => 1000, 'used' => 150], +// 'dailyQuota' => ['total' => 100, 'used' => 25], +// 'accountType' => 'FREE' +// ] +``` + +## Примеры использования + +### Создание объекта + +```php +use NotKinopoisk\Models\ApiKeyInfo; +use NotKinopoisk\Models\ApiKeyQouta; +use NotKinopoisk\Enums\AccountType; + +// Создание квот +$totalQuota = new ApiKeyQouta(1000, 150); +$dailyQuota = new ApiKeyQouta(100, 25); + +// Создание информации об API ключе +$apiKeyInfo = new ApiKeyInfo( + totalQuota: $totalQuota, + dailyQuota: $dailyQuota, + accountType: AccountType::FREE +); +``` + +### Создание из данных API + +```php +use NotKinopoisk\Models\ApiKeyInfo; + +// Данные от API +$apiData = [ + 'totalQuota' => ['value' => 1000, 'used' => 150], + 'dailyQuota' => ['value' => 100, 'used' => 25], + 'accountType' => 'FREE' +]; + +$apiKeyInfo = ApiKeyInfo::fromArray($apiData); +``` + +### Работа с информацией + +```php +use NotKinopoisk\Models\ApiKeyInfo; + +// Получение информации +echo "Тип аккаунта: {$apiKeyInfo->accountType->getDisplayName()}\n"; +echo "Всего запросов: {$apiKeyInfo->totalQuota->value}\n"; +echo "Использовано: {$apiKeyInfo->totalQuota->used}\n"; +echo "Осталось: {$apiKeyInfo->getRemainingTotalQuota()}\n"; + +// Проверка типа аккаунта +if ($apiKeyInfo->accountType->isUnlimited()) { + echo "Безлимитный аккаунт!"; +} elseif ($apiKeyInfo->accountType->isFree()) { + echo "Бесплатный аккаунт"; +} else { + echo "Платный аккаунт"; +} +``` + +### Проверка лимитов + +```php +use NotKinopoisk\Models\ApiKeyInfo; + +// Проверка общих лимитов +$remainingTotal = $apiKeyInfo->getRemainingTotalQuota(); +if ($remainingTotal <= 0) { + echo "Общий лимит запросов исчерпан!"; +} else { + echo "Осталось общих запросов: {$remainingTotal}"; +} + +// Проверка дневных лимитов +$remainingDaily = $apiKeyInfo->getRemainingDailyQuota(); +if ($remainingDaily <= 0) { + echo "Дневной лимит запросов исчерпан!"; +} else { + echo "Осталось дневных запросов: {$remainingDaily}"; +} +``` + +### Использование в сервисах + +```php +use NotKinopoisk\Models\ApiKeyInfo; +use NotKinopoisk\Services\UserService; + +class UserService extends AbstractService +{ + public function getApiKeyInfo(): ApiKeyInfo + { + $response = $this->client->get('/api/v1/api_keys/info'); + return ApiKeyInfo::fromArray($response); + } + + public function checkQuota(): void + { + $apiKeyInfo = $this->getApiKeyInfo(); + + if ($apiKeyInfo->isUnlimited()) { + return; // Безлимитный аккаунт + } + + $remainingTotal = $apiKeyInfo->getRemainingTotalQuota(); + $remainingDaily = $apiKeyInfo->getRemainingDailyQuota(); + + if ($remainingTotal <= 0) { + throw new \Exception('Общий лимит запросов исчерпан'); + } + + if ($remainingDaily <= 0) { + throw new \Exception('Дневной лимит запросов исчерпан'); + } + } +} +``` + +### Мониторинг использования + +```php +use NotKinopoisk\Models\ApiKeyInfo; + +function monitorApiUsage(ApiKeyInfo $apiKeyInfo): void +{ + echo "=== Информация об API ключе ===\n"; + echo "Тип аккаунта: {$apiKeyInfo->accountType->getDisplayName()}\n"; + echo "\n"; + + echo "Общие лимиты:\n"; + echo " Всего: {$apiKeyInfo->totalQuota->value}\n"; + echo " Использовано: {$apiKeyInfo->totalQuota->used}\n"; + echo " Осталось: {$apiKeyInfo->getRemainingTotalQuota()}\n"; + echo "\n"; + + echo "Дневные лимиты:\n"; + echo " Всего: {$apiKeyInfo->dailyQuota->value}\n"; + echo " Использовано: {$apiKeyInfo->dailyQuota->used}\n"; + echo " Осталось: {$apiKeyInfo->getRemainingDailyQuota()}\n"; + echo "\n"; + + // Проверка статуса + if ($apiKeyInfo->isUnlimited()) { + echo "✅ Безлимитный аккаунт - ограничений нет\n"; + } else { + $totalRemaining = $apiKeyInfo->getRemainingTotalQuota(); + $dailyRemaining = $apiKeyInfo->getRemainingDailyQuota(); + + if ($totalRemaining <= 0) { + echo "❌ Общий лимит исчерпан!\n"; + } elseif ($dailyRemaining <= 0) { + echo "❌ Дневной лимит исчерпан!\n"; + } else { + echo "✅ Лимиты в порядке\n"; + } + } +} +``` + +### Сериализация и десериализация + +```php +use NotKinopoisk\Models\ApiKeyInfo; + +// Преобразование в массив +$array = $apiKeyInfo->toArray(); + +// Сохранение в JSON +$json = json_encode($array, JSON_PRETTY_PRINT); +file_put_contents('api_key_info.json', $json); + +// Загрузка из JSON +$loadedArray = json_decode(file_get_contents('api_key_info.json'), true); +$loadedApiKeyInfo = ApiKeyInfo::fromArray($loadedArray); +``` + +## Связанные файлы + +- [`UserService`](../services/user-service.md) - Сервис для работы с пользователями +- [`ApiKeyQouta`](./api-key-qouta.md) - Модель квоты API ключа +- [`AccountType`](../enums/account-type.md) - Перечисление типов аккаунтов +- [`UserService`](../services/user-service.md) - Сервис для работы с пользовательскими данными + +## API Endpoints + +Информация об API ключе используется в следующих API endpoints: + +- `/api/v1/api_keys/{apiKey}` - Информация об API ключе +- `/api/v1/user/info` - Информация о пользователе +- `/api/v1/user/quota` - Квота пользователя + +--- + +**📚 Навигация:** [Главная](../index.md) → [Модели](index.md) → ApiKeyInfo diff --git a/docs/dev/notkinopoiskphp/models/api-key-qouta.md b/docs/dev/notkinopoiskphp/models/api-key-qouta.md new file mode 100644 index 0000000..0cffd2e --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/api-key-qouta.md @@ -0,0 +1,463 @@ +# ApiKeyQouta + +## Описание + +`ApiKeyQouta` - это модель квоты API ключа из Kinopoisk API. Представляет информацию о лимитах использования API ключа, включая общий лимит запросов и количество уже использованных запросов. + +## Основные возможности + +- Хранение информации о квоте в неизменяемом виде +- Создание объекта из массива данных API +- Вычисление оставшихся лимитов запросов +- Проверка превышения квоты +- Поддержка безлимитных аккаунтов (значение -1) + +## Наследование + +```php +NotKinopoisk\Interfaces\ModelInterface +└── NotKinopoisk\Models\ApiKeyQouta +``` + +## Конструктор + +```php +public function __construct( + public readonly int $value, + public readonly int $used, +) +``` + +### Параметры + +- `$value` (int) - Общий лимит запросов (-1 для безлимитного аккаунта) +- `$used` (int) - Количество уже использованных запросов + +## Свойства + +### value + +```php +public readonly int $value +``` + +**Описание:** Общий лимит запросов (-1 для безлимитного аккаунта) + +**Тип:** `int` + +**Доступ:** Только для чтения + +**Особенности:** + +- `-1` означает безлимитный аккаунт +- Положительные значения указывают на конкретный лимит + +**Пример:** + +```php +$quota = $apiKeyQouta->value; +if ($quota === -1) { + echo "Безлимитный аккаунт"; +} else { + echo "Лимит запросов: {$quota}"; +} +``` + +### used + +```php +public readonly int $used +``` + +**Описание:** Количество уже использованных запросов + +**Тип:** `int` + +**Доступ:** Только для чтения + +**Пример:** + +```php +$used = $apiKeyQouta->used; +echo "Использовано запросов: {$used}"; +``` + +## Статические методы + +### fromArray() + +```php +public static function fromArray(array $data): self +``` + +Создает экземпляр квоты из массива данных API. + +#### Параметры + +- `$data` (array) - Массив данных квоты от API, должен содержать ключи 'value' и 'used' + +#### Возвращает + +- `self` - Новый экземпляр квоты API ключа + +#### Исключения + +- `\InvalidArgumentException` - Если данные имеют неверный формат или отсутствуют обязательные поля + +#### Пример использования + +```php +use NotKinopoisk\Models\ApiKeyQouta; + +$apiData = [ + 'value' => 1000, + 'used' => 250 +]; + +$quota = ApiKeyQouta::fromArray($apiData); + +// Для безлимитного аккаунта +$unlimitedData = [ + 'value' => -1, + 'used' => 1500 +]; + +$unlimitedQuota = ApiKeyQouta::fromArray($unlimitedData); +``` + +## Методы экземпляра + +### isQuotaExceeded() + +```php +public function isQuotaExceeded(): bool +``` + +Проверяет, превышена ли квота запросов. + +#### Возвращает + +- `bool` - true если квота превышена, false в противном случае + +#### Особенности + +- Безлимитные аккаунты (value = -1) никогда не превышают квоту +- Квота считается превышенной, если оставшееся количество запросов <= 0 + +#### Пример использования + +```php +use NotKinopoisk\Models\ApiKeyQouta; + +// Обычная квота в пределах лимита +$quota = new ApiKeyQouta(1000, 250); +echo $quota->isQuotaExceeded(); // false + +// Превышенная квота +$exceededQuota = new ApiKeyQouta(100, 150); +echo $exceededQuota->isQuotaExceeded(); // true + +// Исчерпанная квота +$emptyQuota = new ApiKeyQouta(100, 100); +echo $emptyQuota->isQuotaExceeded(); // true + +// Безлимитный аккаунт +$unlimitedQuota = new ApiKeyQouta(-1, 99999); +echo $unlimitedQuota->isQuotaExceeded(); // false +``` + +### getRemainingQuota() + +```php +public function getRemainingQuota(): int +``` + +Получает количество оставшихся запросов в квоте. + +#### Возвращает + +- `int` - Количество оставшихся запросов. Может быть отрицательным при превышении квоты + +#### Особенности + +- Для безлимитных аккаунтов (value = -1) возвращает разность между -1 и used +- Может возвращать отрицательные значения при превышении квоты + +#### Пример использования + +```php +use NotKinopoisk\Models\ApiKeyQouta; + +$quota = new ApiKeyQouta(1000, 250); +echo $quota->getRemainingQuota(); // 750 + +$exceededQuota = new ApiKeyQouta(100, 150); +echo $exceededQuota->getRemainingQuota(); // -50 + +$unlimitedQuota = new ApiKeyQouta(-1, 1500); +echo $unlimitedQuota->getRemainingQuota(); // -1501 (но квота не превышена) +``` + +### toArray() + +```php +public function toArray(): array +``` + +Преобразует объект квоты API ключа в массив. + +#### Возвращает + +- `array` - Массив данных квоты API ключа + +#### Пример использования + +```php +use NotKinopoisk\Models\ApiKeyQouta; + +$array = $quota->toArray(); +// [ +// 'value' => 1000, +// 'used' => 250 +// ] +``` + +## Примеры использования + +### Создание объекта + +```php +use NotKinopoisk\Models\ApiKeyQouta; + +// Обычная квота +$quota = new ApiKeyQouta(1000, 250); + +// Безлимитная квота +$unlimitedQuota = new ApiKeyQouta(-1, 1500); +``` + +### Создание из данных API + +```php +use NotKinopoisk\Models\ApiKeyQouta; + +// Данные от API +$apiData = [ + 'value' => 1000, + 'used' => 250 +]; + +$quota = ApiKeyQouta::fromArray($apiData); +``` + +### Работа с квотой + +```php +use NotKinopoisk\Models\ApiKeyQouta; + +// Получение информации +echo "Общий лимит: {$quota->value}\n"; +echo "Использовано: {$quota->used}\n"; +echo "Осталось: {$quota->getRemainingQuota()}\n"; + +// Проверка превышения квоты +if ($quota->isQuotaExceeded()) { + echo "Квота превышена!"; +} else { + echo "Квота в пределах лимита"; +} +``` + +### Обработка безлимитных аккаунтов + +```php +use NotKinopoisk\Models\ApiKeyQouta; + +function handleQuota(ApiKeyQouta $quota): void +{ + if ($quota->value === -1) { + echo "Безлимитный аккаунт - ограничений нет\n"; + echo "Использовано запросов: {$quota->used}\n"; + return; + } + + $remaining = $quota->getRemainingQuota(); + + if ($remaining <= 0) { + echo "Квота исчерпана! Использовано: {$quota->used}/{$quota->value}\n"; + } else { + echo "Осталось запросов: {$remaining}\n"; + echo "Использовано: {$quota->used}/{$quota->value}\n"; + } +} + +// Примеры +$regularQuota = new ApiKeyQouta(1000, 750); +handleQuota($regularQuota); // "Осталось запросов: 250" + +$unlimitedQuota = new ApiKeyQouta(-1, 5000); +handleQuota($unlimitedQuota); // "Безлимитный аккаунт - ограничений нет" +``` + +### Использование в сервисах + +```php +use NotKinopoisk\Models\ApiKeyQouta; +use NotKinopoisk\Services\UserService; + +class UserService extends AbstractService +{ + public function checkQuota(): bool + { + $response = $this->client->get('/api/v1/user/quota'); + $quota = ApiKeyQouta::fromArray($response); + + if ($quota->isQuotaExceeded()) { + throw new \Exception('Квота API запросов исчерпана'); + } + + return true; + } + + public function getQuotaInfo(): array + { + $response = $this->client->get('/api/v1/user/quota'); + $quota = ApiKeyQouta::fromArray($response); + + return [ + 'total' => $quota->value, + 'used' => $quota->used, + 'remaining' => $quota->getRemainingQuota(), + 'isUnlimited' => $quota->value === -1, + 'isExceeded' => $quota->isQuotaExceeded() + ]; + } +} +``` + +### Мониторинг использования + +```php +use NotKinopoisk\Models\ApiKeyQouta; + +function monitorQuotaUsage(ApiKeyQouta $quota): void +{ + echo "=== Информация о квоте API ===\n"; + + if ($quota->value === -1) { + echo "Тип: Безлимитный аккаунт\n"; + echo "Использовано запросов: {$quota->used}\n"; + echo "Статус: ✅ Без ограничений\n"; + } else { + echo "Тип: Ограниченный аккаунт\n"; + echo "Общий лимит: {$quota->value}\n"; + echo "Использовано: {$quota->used}\n"; + echo "Осталось: {$quota->getRemainingQuota()}\n"; + + $percentage = ($quota->used / $quota->value) * 100; + echo "Использовано: " . round($percentage, 1) . "%\n"; + + if ($quota->isQuotaExceeded()) { + echo "Статус: ❌ Квота превышена!\n"; + } elseif ($percentage >= 90) { + echo "Статус: ⚠️ Квота почти исчерпана\n"; + } else { + echo "Статус: ✅ Квота в порядке\n"; + } + } +} +``` + +### Сравнение квот + +```php +use NotKinopoisk\Models\ApiKeyQouta; + +function compareQuotas(ApiKeyQouta $quota1, ApiKeyQouta $quota2): void +{ + echo "Сравнение квот:\n"; + echo "Квота 1: {$quota1->used}/{$quota1->value} (осталось: {$quota1->getRemainingQuota()})\n"; + echo "Квота 2: {$quota2->used}/{$quota2->value} (осталось: {$quota2->getRemainingQuota()})\n"; + + $remaining1 = $quota1->getRemainingQuota(); + $remaining2 = $quota2->getRemainingQuota(); + + if ($remaining1 > $remaining2) { + echo "Квота 1 имеет больше оставшихся запросов\n"; + } elseif ($remaining2 > $remaining1) { + echo "Квота 2 имеет больше оставшихся запросов\n"; + } else { + echo "Квоты имеют одинаковое количество оставшихся запросов\n"; + } +} +``` + +### Сериализация и десериализация + +```php +use NotKinopoisk\Models\ApiKeyQouta; + +// Преобразование в массив +$array = $quota->toArray(); + +// Сохранение в JSON +$json = json_encode($array, JSON_PRETTY_PRINT); +file_put_contents('quota.json', $json); + +// Загрузка из JSON +$loadedArray = json_decode(file_get_contents('quota.json'), true); +$loadedQuota = ApiKeyQouta::fromArray($loadedArray); +``` + +### Валидация данных + +```php +use NotKinopoisk\Models\ApiKeyQouta; + +function validateQuotaData(array $data): bool +{ + if (!isset($data['value']) || !isset($data['used'])) { + throw new \InvalidArgumentException('Отсутствуют обязательные поля value и used'); + } + + if (!is_int($data['value']) || !is_int($data['used'])) { + throw new \InvalidArgumentException('Поля value и used должны быть целыми числами'); + } + + if ($data['value'] !== -1 && $data['value'] <= 0) { + throw new \InvalidArgumentException('Поле value должно быть -1 или положительным числом'); + } + + if ($data['used'] < 0) { + throw new \InvalidArgumentException('Поле used не может быть отрицательным'); + } + + return true; +} + +// Использование +try { + $data = ['value' => 1000, 'used' => 250]; + validateQuotaData($data); + $quota = ApiKeyQouta::fromArray($data); +} catch (\InvalidArgumentException $e) { + echo "Ошибка валидации: " . $e->getMessage(); +} +``` + +## Связанные классы + +- [`ApiKeyInfo`](./api-key-info.md) - Модель информации об API ключе +- [`UserService`](../services/user-service.md) - Сервис для работы с пользовательскими данными + +## API Endpoints + +Квота API ключа используется в следующих API endpoints: + +- `/api/v1/user/quota` - Квота пользователя +- `/api/v1/api_keys/{apiKey}` - Информация об API ключе +- `/api/v1/user/info` - Информация о пользователе + +--- + +**📚 Навигация:** [Главная](../index.md) → [Модели](index.md) → ApiKeyQouta diff --git a/docs/dev/notkinopoiskphp/models/award.md b/docs/dev/notkinopoiskphp/models/award.md index 2e38233..ea119d4 100644 --- a/docs/dev/notkinopoiskphp/models/award.md +++ b/docs/dev/notkinopoiskphp/models/award.md @@ -496,4 +496,4 @@ foreach (array_slice($analysis['persons'], 0, 5, true) as $person => $stats) { ## Связанные классы - [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами -- [`Film`](film.md) - Модель фильма +- [`Film`](./film.md) - Модель фильма diff --git a/docs/dev/notkinopoiskphp/models/box-office.md b/docs/dev/notkinopoiskphp/models/box-office.md index 688acda..6d3f2d6 100644 --- a/docs/dev/notkinopoiskphp/models/box-office.md +++ b/docs/dev/notkinopoiskphp/models/box-office.md @@ -551,4 +551,4 @@ foreach ($analysis['currencyBreakdown'] as $currency => $data) { - [`BoxOfficeType`](../enums/box-office-type.md) - Типы кассовых сборов - [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами -- [`Film`](film.md) - Модель фильма +- [`Film`](./film.md) - Модель фильма diff --git a/docs/dev/notkinopoiskphp/models/country.md b/docs/dev/notkinopoiskphp/models/country.md index 4bc9c61..8a53628 100644 --- a/docs/dev/notkinopoiskphp/models/country.md +++ b/docs/dev/notkinopoiskphp/models/country.md @@ -430,5 +430,5 @@ foreach (array_slice($analysis['mostDiverse'], 0, 5) as $item) { ## Связанные классы -- [`Film`](film.md) - Модель фильма +- [`Film`](./film.md) - Модель фильма - [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/models/distribution.md b/docs/dev/notkinopoiskphp/models/distribution.md index ff8c4b0..edf5c82 100644 --- a/docs/dev/notkinopoiskphp/models/distribution.md +++ b/docs/dev/notkinopoiskphp/models/distribution.md @@ -674,5 +674,5 @@ echo "\nПовторных прокатов: {$analysis['reReleaseAnalysis']['to ## Связанные классы - [`DistributionType`](../enums/distribution-type.md) - Типы проката -- [`Country`](country.md) - Модель страны +- [`Country`](./country.md) - Модель страны - [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/models/episode.md b/docs/dev/notkinopoiskphp/models/episode.md index b6e741e..f486a8b 100644 --- a/docs/dev/notkinopoiskphp/models/episode.md +++ b/docs/dev/notkinopoiskphp/models/episode.md @@ -536,5 +536,5 @@ foreach ($analysis['seasonDistribution'] as $season => $percentage) { ## Связанные классы -- [`Season`](season.md) - Модель сезона +- [`Season`](./season.md) - Модель сезона - [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/models/fact.md b/docs/dev/notkinopoiskphp/models/fact.md index a170814..421195d 100644 --- a/docs/dev/notkinopoiskphp/models/fact.md +++ b/docs/dev/notkinopoiskphp/models/fact.md @@ -381,4 +381,4 @@ if (!empty($gallery['interestingFacts'])) { - [`FactType`](../enums/fact-type.md) - Типы фактов - [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами -- [`Film`](film.md) - Модель фильма +- [`Film`](./film.md) - Модель фильма diff --git a/docs/dev/notkinopoiskphp/models/film-collection.md b/docs/dev/notkinopoiskphp/models/film-collection.md index 115fe79..bfd4b14 100644 --- a/docs/dev/notkinopoiskphp/models/film-collection.md +++ b/docs/dev/notkinopoiskphp/models/film-collection.md @@ -730,6 +730,6 @@ foreach ($topGenres as $genre => $count) { ## Связанные классы - [`ContentType`](../enums/content-type.md) - Типы контента -- [`Country`](country.md) - Модель страны -- [`Genre`](genre.md) - Модель жанра +- [`Country`](./country.md) - Модель страны +- [`Genre`](./genre.md) - Модель жанра - [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/models/film-search-result.md b/docs/dev/notkinopoiskphp/models/film-search-result.md index c21ff76..4b1f274 100644 --- a/docs/dev/notkinopoiskphp/models/film-search-result.md +++ b/docs/dev/notkinopoiskphp/models/film-search-result.md @@ -772,6 +772,6 @@ foreach ($topGenres as $genre => $count) { ## Связанные классы - [`ContentType`](../enums/content-type.md) - Типы контента -- [`Country`](country.md) - Модель страны -- [`Genre`](genre.md) - Модель жанра +- [`Country`](./country.md) - Модель страны +- [`Genre`](./genre.md) - Модель жанра - [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/models/film.md b/docs/dev/notkinopoiskphp/models/film.md index 3a31a20..34aa146 100644 --- a/docs/dev/notkinopoiskphp/models/film.md +++ b/docs/dev/notkinopoiskphp/models/film.md @@ -545,10 +545,3 @@ switch ($film->type) { - `\NotKinopoisk\Enums\ContentType` - Типы контента - `\NotKinopoisk\Enums\ProductionStatus` - Статусы производства -## Информация о пакете - -- **Пакет:** NotKinopoisk\Models -- **API:** /api/v2.2/films/{id} -- **Документация API:** https://kinopoiskapiunofficial.tech/documentation/api/#/films/get_api_v2_2_films__id_ -- **Автор:** Maxim Harder -- **Версия:** 1.0.0 diff --git a/docs/dev/notkinopoiskphp/models/filters.md b/docs/dev/notkinopoiskphp/models/filters.md index 7c0c22a..b705602 100644 --- a/docs/dev/notkinopoiskphp/models/filters.md +++ b/docs/dev/notkinopoiskphp/models/filters.md @@ -522,6 +522,6 @@ foreach ($analysis['popularCountries'] as $country) { ## Связанные классы -- [`Genre`](genre.md) - Модель жанра -- [`Country`](country.md) - Модель страны +- [`Genre`](./genre.md) - Модель жанра +- [`Country`](./country.md) - Модель страны - [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/models/genre.md b/docs/dev/notkinopoiskphp/models/genre.md index cee71c0..77734fb 100644 --- a/docs/dev/notkinopoiskphp/models/genre.md +++ b/docs/dev/notkinopoiskphp/models/genre.md @@ -496,5 +496,5 @@ foreach (array_slice($analysis['mostDiverse'], 0, 5) as $item) { ## Связанные классы -- [`Film`](film.md) - Модель фильма +- [`Film`](./film.md) - Модель фильма - [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/models/image.md b/docs/dev/notkinopoiskphp/models/image.md index ab23c26..c3e37c4 100644 --- a/docs/dev/notkinopoiskphp/models/image.md +++ b/docs/dev/notkinopoiskphp/models/image.md @@ -383,4 +383,4 @@ foreach ($images as $index => $image) { - [`ImageType`](../enums/image-type.md) - Типы изображений - [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами -- [`Film`](film.md) - Модель фильма +- [`Film`](./film.md) - Модель фильма diff --git a/docs/dev/notkinopoiskphp/models/index.md b/docs/dev/notkinopoiskphp/models/index.md index 5075789..3b76ac1 100644 --- a/docs/dev/notkinopoiskphp/models/index.md +++ b/docs/dev/notkinopoiskphp/models/index.md @@ -12,46 +12,47 @@ ### 🎬 Основные модели фильмов -- [Film](film.md) - Основная модель фильма/сериала -- [FilmSearchResult](film-search-result.md) - Результат поиска фильмов -- [FilmCollection](film-collection.md) - Коллекция фильмов -- [RelatedFilm](related-film.md) - Связанный фильм -- [Episode](episode.md) - Эпизод сериала -- [Season](season.md) - Сезон сериала +- [Film](./film.md) - Основная модель фильма/сериала +- [FilmSearchResult](./film-search-result.md) - Результат поиска фильмов +- [FilmCollection](./film-collection.md) - Коллекция фильмов +- [RelatedFilm](./related-film.md) - Связанный фильм +- [Episode](./episode.md) - Эпизод сериала +- [Season](./season.md) - Сезон сериала ### 👥 Модели персон -- [Person](person.md) - Основная модель персоны -- [Staff](staff.md) - Съемочная группа -- [PersonFilm](person-film.md) - Фильм персоны -- [PersonSpouse](person-spouse.md) - Супруг персоны +- [Person](./person.md) - Основная модель персоны +- [Staff](./staff.md) - Съемочная группа +- [PersonFilm](./person-film.md) - Фильм персоны +- [PersonSpouse](./person-spouse.md) - Супруг персоны +- [PersonByNameResult](./person-by-name-result.md) - Результат поиска персоны по имени ### 📊 Модели контента -- [Review](review.md) - Отзыв -- [Fact](fact.md) - Факт -- [Image](image.md) - Изображение -- [Video](video.md) - Видео -- [MediaPost](media-post.md) - Медиа пост +- [Review](./review.md) - Отзыв +- [Fact](./fact.md) - Факт +- [Image](./image.md) - Изображение +- [Video](./video.md) - Видео +- [MediaPost](./media-post.md) - Медиа пост ### 🏆 Модели наград и статистики -- [Award](award.md) - Награда -- [BoxOffice](box-office.md) - Кассовые сборы -- [UserVote](user-vote.md) - Голос пользователя -- [ExternalSource](external-source.md) - Внешний источник +- [Award](./award.md) - Награда +- [BoxOffice](./box-office.md) - Кассовые сборы +- [UserVote](./user-vote.md) - Голос пользователя +- [ExternalSource](./external-source.md) - Внешний источник ### 🌍 Справочные модели -- [Country](country.md) - Страна -- [Genre](genre.md) - Жанр -- [Distribution](distribution.md) - Дистрибуция +- [Country](./country.md) - Страна +- [Genre](./genre.md) - Жанр +- [Distribution](./distribution.md) - Дистрибуция ### 🔑 Модели API -- [ApiKeyInfo](api-key-info.md) - Информация об API ключе -- [ApiKeyQouta](api-key-qouta.md) - Квота API ключа -- [Filters](filters.md) - Фильтры +- [ApiKeyInfo](./api-key-info.md) - Информация об API ключе +- [ApiKeyQouta](./api-key-qouta.md) - Квота API ключа +- [Filters](./filters.md) - Фильтры ## 🔗 Связанные компоненты @@ -121,6 +122,28 @@ $staffData = [ ]; $staff = Staff::fromArray($staffData); + +// Создание модели информации об API ключе +$apiKeyData = [ + 'totalQuota' => ['value' => 1000, 'used' => 150], + 'dailyQuota' => ['value' => 100, 'used' => 25], + 'accountType' => 'FREE' +]; + +$apiKeyInfo = \NotKinopoisk\Models\ApiKeyInfo::fromArray($apiKeyData); + +// Создание модели результата поиска персоны +$personSearchData = [ + 'kinopoiskId' => 66539, + 'webUrl' => '10096', + 'nameRu' => 'Винс Гиллиган', + 'nameEn' => 'Vince Gilligan', + 'sex' => 'MALE', + 'posterUrl' => 'https://kinopoiskapiunofficial.tech/images/actor_posters/kp/10096.jpg' +]; + +$personResult = \NotKinopoisk\Models\PersonByNameResult::fromArray($personSearchData); + ``` ### Работа с моделями @@ -144,10 +167,28 @@ if ($staff->isDirector()) { // Преобразование в массив $filmArray = $film->toArray(); + +// Работа с информацией об API ключе +echo "Тип аккаунта: {$apiKeyInfo->accountType->getDisplayName()}\n"; +echo "Осталось запросов: {$apiKeyInfo->getRemainingTotalQuota()}\n"; + +if ($apiKeyInfo->isUnlimited()) { + echo "Безлимитный аккаунт!\n"; +} + +// Работа с результатом поиска персоны +echo "Персона: {$personResult->getDisplayName()}\n"; +echo "Полное имя: {$personResult->getFullName()}\n"; + +if ($personResult->isMale()) { + echo "Пол: Мужской\n"; +} ``` ## 📊 Статистика моделей +**Всего моделей: 28** + ### Основные модели фильмов (6) - **Film** - Самая сложная модель с 30+ свойствами @@ -163,6 +204,7 @@ $filmArray = $film->toArray(); - **Staff** - Съемочная группа - **PersonFilm** - Фильмография - **PersonSpouse** - Семейные связи +- **PersonByNameResult** - Результаты поиска персон ### Модели контента (5) @@ -295,6 +337,69 @@ if ($staff->isDirector()) { } ``` +### Работа с информацией об API ключе + +```php +$apiKeyInfo = ApiKeyInfo::fromArray($apiKeyData); + +echo "Тип аккаунта: {$apiKeyInfo->accountType->getDisplayName()}\n"; +echo "Общий лимит: {$apiKeyInfo->totalQuota->value}\n"; +echo "Использовано: {$apiKeyInfo->totalQuota->used}\n"; +echo "Осталось: {$apiKeyInfo->getRemainingTotalQuota()}\n"; + +// Проверка типа аккаунта +if ($apiKeyInfo->isUnlimited()) { + echo "Безлимитный аккаунт - ограничений нет\n"; +} else { + echo "Ограниченный аккаунт\n"; + + // Проверка лимитов + $remainingTotal = $apiKeyInfo->getRemainingTotalQuota(); + $remainingDaily = $apiKeyInfo->getRemainingDailyQuota(); + + if ($remainingTotal <= 0) { + echo "Общий лимит исчерпан!\n"; + } else { + echo "Осталось общих запросов: {$remainingTotal}\n"; + } + + if ($remainingDaily <= 0) { + echo "Дневной лимит исчерпан!\n"; + } else { + echo "Осталось дневных запросов: {$remainingDaily}\n"; + } +} +``` + +### Работа с результатом поиска персоны + +```php +$personResult = PersonByNameResult::fromArray($personSearchData); + +echo "ID: {$personResult->kinopoiskId}\n"; +echo "Имя: {$personResult->getDisplayName()}\n"; +echo "Полное имя: {$personResult->getFullName()}\n"; +echo "Постер: {$personResult->posterUrl}\n"; + +// Проверка пола +if ($personResult->isMale()) { + echo "Пол: Мужской\n"; +} elseif ($personResult->isFemale()) { + echo "Пол: Женский\n"; +} else { + echo "Пол: Неизвестен\n"; +} + +// Проверка наличия имен +if ($personResult->nameRu) { + echo "Имя на русском: {$personResult->nameRu}\n"; +} + +if ($personResult->nameEn) { + echo "Имя на английском: {$personResult->nameEn}\n"; +} +``` + ## 🔗 Связанные разделы - [Сервисы](../services/index.md) - Работа с API diff --git a/docs/dev/notkinopoiskphp/models/media-post.md b/docs/dev/notkinopoiskphp/models/media-post.md new file mode 100644 index 0000000..9c5b995 --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/media-post.md @@ -0,0 +1,452 @@ +# MediaPost + +## Описание + +`MediaPost` - это модель медиа-поста в Kinopoisk API. Представляет медиа-пост, содержащий информацию о изображении, заголовке, описании и ссылке. + +## Основные возможности + +- Хранение информации о медиа-посте в неизменяемом виде +- Создание объекта из массива данных API +- Преобразование в массив для сериализации +- Совместимость с Kinopoisk API + +## Наследование + +```php +NotKinopoisk\Interfaces\ModelInterface +└── NotKinopoisk\Models\MediaPost +``` + +## Конструктор + +```php +public function __construct( + public readonly int $kinopoiskId, + public readonly string $imageUrl, + public readonly string $title, + public readonly string $description, + public readonly string $url, + public readonly string $publishedAt, +) +``` + +### Параметры + +- `$kinopoiskId` (int) - Уникальный идентификатор в Кинопоиске +- `$imageUrl` (string) - URL изображения +- `$title` (string) - Заголовок поста +- `$description` (string) - Описание поста +- `$url` (string) - URL поста +- `$publishedAt` (string) - Дата публикации + +## Свойства + +### kinopoiskId + +```php +public readonly int $kinopoiskId +``` + +**Описание:** Уникальный идентификатор в Кинопоиске + +**Тип:** `int` + +**Доступ:** Только для чтения + +**Пример:** + +```php +$id = $mediaPost->kinopoiskId; +echo "ID поста: {$id}"; +``` + +### imageUrl + +```php +public readonly string $imageUrl +``` + +**Описание:** URL изображения + +**Тип:** `string` + +**Доступ:** Только для чтения + +**Пример:** + +```php +$imageUrl = $mediaPost->imageUrl; +echo "Изображение: {$imageUrl}"; +``` + +### title + +```php +public readonly string $title +``` + +**Описание:** Заголовок поста + +**Тип:** `string` + +**Доступ:** Только для чтения + +**Пример:** + +```php +$title = $mediaPost->title; +echo "Заголовок: {$title}"; +``` + +### description + +```php +public readonly string $description +``` + +**Описание:** Описание поста + +**Тип:** `string` + +**Доступ:** Только для чтения + +**Пример:** + +```php +$description = $mediaPost->description; +echo "Описание: {$description}"; +``` + +### url + +```php +public readonly string $url +``` + +**Описание:** URL поста + +**Тип:** `string` + +**Доступ:** Только для чтения + +**Пример:** + +```php +$url = $mediaPost->url; +echo "Ссылка на пост: {$url}"; +``` + +### publishedAt + +```php +public readonly string $publishedAt +``` + +**Описание:** Дата публикации + +**Тип:** `string` + +**Доступ:** Только для чтения + +**Пример:** + +```php +$publishedAt = $mediaPost->publishedAt; +echo "Дата публикации: {$publishedAt}"; +``` + +## Статические методы + +### fromArray() + +```php +public static function fromArray(array $data): self +``` + +Создает экземпляр модели из массива данных API. + +#### Параметры + +- `$data` (array) - Массив данных от API + +#### Возвращает + +- `self` - Экземпляр модели + +#### Исключения + +- `\InvalidArgumentException` - При некорректных данных + +#### Пример использования + +```php +use NotKinopoisk\Models\MediaPost; + +$postData = [ + 'kinopoiskId' => 301, + 'imageUrl' => 'https://example.com/image.jpg', + 'title' => 'Новости о фильме', + 'description' => 'Описание новости', + 'url' => 'https://example.com/post', + 'publishedAt' => '2024-01-01T12:00:00' +]; + +$post = MediaPost::fromArray($postData); +``` + +## Методы экземпляра + +### toArray() + +```php +public function toArray(): array +``` + +Преобразует объект медиа-поста в массив. + +#### Возвращает + +- `array` - Массив с данными медиа-поста + +#### Пример использования + +```php +use NotKinopoisk\Models\MediaPost; + +$postArray = $post->toArray(); +echo json_encode($postArray); // JSON представление медиа-поста +``` + +## Примеры использования + +### Создание объекта + +```php +use NotKinopoisk\Models\MediaPost; + +$mediaPost = new MediaPost( + kinopoiskId: 301, + imageUrl: 'https://example.com/image.jpg', + title: 'Новости о фильме', + description: 'Описание новости', + url: 'https://example.com/post', + publishedAt: '2024-01-01T12:00:00' +); +``` + +### Создание из данных API + +```php +use NotKinopoisk\Models\MediaPost; + +// Данные от API +$apiData = [ + 'kinopoiskId' => 301, + 'imageUrl' => 'https://example.com/image.jpg', + 'title' => 'Новости о фильме', + 'description' => 'Описание новости', + 'url' => 'https://example.com/post', + 'publishedAt' => '2024-01-01T12:00:00' +]; + +$mediaPost = MediaPost::fromArray($apiData); +``` + +### Работа с медиа-постом + +```php +use NotKinopoisk\Models\MediaPost; + +// Получение информации +echo "ID: {$mediaPost->kinopoiskId}\n"; +echo "Заголовок: {$mediaPost->title}\n"; +echo "Описание: {$mediaPost->description}\n"; +echo "Изображение: {$mediaPost->imageUrl}\n"; +echo "Ссылка: {$mediaPost->url}\n"; +echo "Дата публикации: {$mediaPost->publishedAt}\n"; +``` + +### Использование в сервисах + +```php +use NotKinopoisk\Models\MediaPost; +use NotKinopoisk\Services\MediaService; + +class MediaService extends AbstractService +{ + public function getMediaPosts(): array + { + $response = $this->client->get('/api/v1/media_posts'); + + return array_map( + fn(array $postData) => MediaPost::fromArray($postData), + $response['items'] ?? [] + ); + } + + public function getMediaPostById(int $id): MediaPost + { + $response = $this->client->get("/api/v1/media_posts/{$id}"); + return MediaPost::fromArray($response); + } +} +``` + +### Отображение медиа-постов + +```php +use NotKinopoisk\Models\MediaPost; + +function displayMediaPost(MediaPost $post): void +{ + echo "=== Медиа-пост ===\n"; + echo "ID: {$post->kinopoiskId}\n"; + echo "Заголовок: {$post->title}\n"; + echo "Описание: {$post->description}\n"; + echo "Изображение: {$post->imageUrl}\n"; + echo "Ссылка: {$post->url}\n"; + echo "Дата публикации: {$post->publishedAt}\n"; + echo "==================\n"; +} + +// Использование +$mediaPost = MediaPost::fromArray($apiData); +displayMediaPost($mediaPost); +``` + +### Фильтрация медиа-постов + +```php +use NotKinopoisk\Models\MediaPost; + +function filterMediaPosts(array $posts, string $keyword): array +{ + return array_filter($posts, function(MediaPost $post) use ($keyword) { + return stripos($post->title, $keyword) !== false || + stripos($post->description, $keyword) !== false; + }); +} + +// Использование +$allPosts = [/* массив MediaPost объектов */]; +$filteredPosts = filterMediaPosts($allPosts, 'фильм'); +``` + +### Сортировка медиа-постов + +```php +use NotKinopoisk\Models\MediaPost; + +function sortMediaPostsByDate(array $posts, bool $ascending = true): array +{ + usort($posts, function(MediaPost $a, MediaPost $b) use ($ascending) { + $comparison = strcmp($a->publishedAt, $b->publishedAt); + return $ascending ? $comparison : -$comparison; + }); + + return $posts; +} + +// Использование +$sortedPosts = sortMediaPostsByDate($allPosts, false); // По убыванию даты +``` + +### Сериализация и десериализация + +```php +use NotKinopoisk\Models\MediaPost; + +// Преобразование в массив +$array = $mediaPost->toArray(); + +// Сохранение в JSON +$json = json_encode($array, JSON_PRETTY_PRINT); +file_put_contents('media_post.json', $json); + +// Загрузка из JSON +$loadedArray = json_decode(file_get_contents('media_post.json'), true); +$loadedMediaPost = MediaPost::fromArray($loadedArray); +``` + +### Валидация данных + +```php +use NotKinopoisk\Models\MediaPost; + +function validateMediaPostData(array $data): bool +{ + $requiredFields = ['kinopoiskId', 'imageUrl', 'title', 'description', 'url', 'publishedAt']; + + foreach ($requiredFields as $field) { + if (!isset($data[$field])) { + throw new \InvalidArgumentException("Отсутствует обязательное поле: {$field}"); + } + } + + if (!is_int($data['kinopoiskId']) || $data['kinopoiskId'] <= 0) { + throw new \InvalidArgumentException('kinopoiskId должен быть положительным целым числом'); + } + + if (!filter_var($data['imageUrl'], FILTER_VALIDATE_URL)) { + throw new \InvalidArgumentException('imageUrl должен быть валидным URL'); + } + + if (!filter_var($data['url'], FILTER_VALIDATE_URL)) { + throw new \InvalidArgumentException('url должен быть валидным URL'); + } + + return true; +} + +// Использование +try { + validateMediaPostData($apiData); + $mediaPost = MediaPost::fromArray($apiData); +} catch (\InvalidArgumentException $e) { + echo "Ошибка валидации: " . $e->getMessage(); +} +``` + +### Работа с датами + +```php +use NotKinopoisk\Models\MediaPost; + +function formatPublishedDate(MediaPost $post): string +{ + $date = new DateTime($post->publishedAt); + return $date->format('d.m.Y H:i'); +} + +function isRecentPost(MediaPost $post, int $days = 7): bool +{ + $postDate = new DateTime($post->publishedAt); + $now = new DateTime(); + $diff = $now->diff($postDate); + + return $diff->days <= $days; +} + +// Использование +$formattedDate = formatPublishedDate($mediaPost); +echo "Дата публикации: {$formattedDate}"; + +if (isRecentPost($mediaPost)) { + echo "Это недавний пост"; +} +``` + +## Связанные классы + +- [MediaService](../services/media-service.md) - Сервис для работы с медиа + +## API Endpoints + +Медиа-посты используются в следующих API endpoints: + +- `/api/v1/media_posts` - Список медиа-постов + +--- + +**📚 Навигация:** [Главная](../index.md) → [Модели](index.md) → MediaPost diff --git a/docs/dev/notkinopoiskphp/models/person-by-name-result.md b/docs/dev/notkinopoiskphp/models/person-by-name-result.md new file mode 100644 index 0000000..bf2cbdf --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/person-by-name-result.md @@ -0,0 +1,627 @@ +# PersonByNameResult + +## Описание + +`PersonByNameResult` - это модель результата поиска персоны по имени из Kinopoisk API. Представляет краткую информацию о персоне в результатах поиска по имени, содержащую основные данные: идентификатор, имена, пол и постер. + +## Основные возможности + +- Хранение информации о персоне в неизменяемом виде +- Создание объекта из массива данных API +- Получение отображаемого имени персоны +- Доступ к метаданным поискового результата +- Совместимость с Kinopoisk API + +## Наследование + +```php +NotKinopoisk\Interfaces\ModelInterface +└── NotKinopoisk\Models\PersonByNameResult +``` + +## Конструктор + +```php +public function __construct( + public readonly int $kinopoiskId, + public readonly string $webUrl, + public readonly ?string $nameRu, + public readonly ?string $nameEn, + public readonly ?Sex $sex, + public readonly string $posterUrl, +) +``` + +### Параметры + +- `$kinopoiskId` (int) - Уникальный идентификатор персоны в Кинопоиске +- `$webUrl` (string) - URL веб-страницы персоны +- `$nameRu` (string|null) - Имя персоны на русском языке +- `$nameEn` (string|null) - Имя персоны на английском языке +- `$sex` (Sex|null) - Пол персоны (MALE, FEMALE, UNKNOWN) +- `$posterUrl` (string) - URL постера персоны + +## Свойства + +### kinopoiskId + +```php +public readonly int $kinopoiskId +``` + +**Описание:** Уникальный идентификатор персоны в Кинопоиске + +**Тип:** `int` + +**Доступ:** Только для чтения + +**Пример:** + +```php +$id = $personResult->kinopoiskId; +echo "ID персоны: {$id}"; +``` + +### webUrl + +```php +public readonly string $webUrl +``` + +**Описание:** URL веб-страницы персоны + +**Тип:** `string` + +**Доступ:** Только для чтения + +**Пример:** + +```php +$webUrl = $personResult->webUrl; +echo "Веб-страница: {$webUrl}"; +``` + +### nameRu + +```php +public readonly ?string $nameRu +``` + +**Описание:** Имя персоны на русском языке + +**Тип:** `string|null` + +**Доступ:** Только для чтения + +**Пример:** + +```php +$nameRu = $personResult->nameRu; +if ($nameRu) { + echo "Имя на русском: {$nameRu}"; +} +``` + +### nameEn + +```php +public readonly ?string $nameEn +``` + +**Описание:** Имя персоны на английском языке + +**Тип:** `string|null` + +**Доступ:** Только для чтения + +**Пример:** + +```php +$nameEn = $personResult->nameEn; +if ($nameEn) { + echo "Имя на английском: {$nameEn}"; +} +``` + +### sex + +```php +public readonly ?Sex $sex +``` + +**Описание:** Пол персоны (MALE, FEMALE, UNKNOWN) + +**Тип:** `Sex|null` + +**Доступ:** Только для чтения + +**Пример:** + +```php +$sex = $personResult->sex; +if ($sex) { + echo "Пол: {$sex->getDisplayName()}"; +} +``` + +### posterUrl + +```php +public readonly string $posterUrl +``` + +**Описание:** URL постера персоны + +**Тип:** `string` + +**Доступ:** Только для чтения + +**Пример:** + +```php +$posterUrl = $personResult->posterUrl; +echo "Постер: {$posterUrl}"; +``` + +## Статические методы + +### fromArray() + +```php +public static function fromArray(array $data): self +``` + +Создает экземпляр модели из массива данных API. + +#### Параметры + +- `$data` (array) - Массив данных из API ответа + +#### Возвращает + +- `static` - Новый экземпляр модели + +#### Исключения + +- `\ValueError` - Если неверное значение для enum Sex + +#### Пример использования + +```php +use NotKinopoisk\Models\PersonByNameResult; + +$apiData = [ + 'kinopoiskId' => 66539, + 'webUrl' => '10096', + 'nameRu' => 'Винс Гиллиган', + 'nameEn' => 'Vince Gilligan', + 'sex' => 'MALE', + 'posterUrl' => 'https://kinopoiskapiunofficial.tech/images/actor_posters/kp/10096.jpg' +]; + +$personResult = PersonByNameResult::fromArray($apiData); +``` + +## Методы экземпляра + +### getDisplayName() + +```php +public function getDisplayName(): string +``` + +Возвращает отображаемое имя персоны. + +#### Возвращает + +- `string` - Отображаемое имя персоны + +#### Особенности + +- Приоритет отдается русскому имени, затем английскому +- Если оба имени отсутствуют, возвращает строку "Неизвестно" + +#### Пример использования + +```php +use NotKinopoisk\Models\PersonByNameResult; + +echo $personResult->getDisplayName(); // "Винс Гиллиган" +``` + +### isMale() + +```php +public function isMale(): bool +``` + +Проверяет, является ли персона мужчиной. + +#### Возвращает + +- `bool` - true если персона мужского пола + +#### Пример использования + +```php +use NotKinopoisk\Models\PersonByNameResult; + +if ($personResult->isMale()) { + echo "Мужчина"; +} +``` + +### isFemale() + +```php +public function isFemale(): bool +``` + +Проверяет, является ли персона женщиной. + +#### Возвращает + +- `bool` - true если персона женского пола + +#### Пример использования + +```php +use NotKinopoisk\Models\PersonByNameResult; + +if ($personResult->isFemale()) { + echo "Женщина"; +} +``` + +### isSexUnknown() + +```php +public function isSexUnknown(): bool +``` + +Проверяет, известен ли пол персоны. + +#### Возвращает + +- `bool` - true если пол персоны неизвестен + +#### Пример использования + +```php +use NotKinopoisk\Models\PersonByNameResult; + +if ($personResult->isSexUnknown()) { + echo "Пол неизвестен"; +} +``` + +### getFullName() + +```php +public function getFullName(string $separator = ' / '): string +``` + +Возвращает полное имя персоны (русское + английское). + +#### Параметры + +- `$separator` (string) - Разделитель между именами (по умолчанию " / ") + +#### Возвращает + +- `string` - Полное имя персоны + +#### Особенности + +- Если есть оба имени, возвращает их через разделитель +- Если есть только одно имя, возвращает его + +#### Пример использования + +```php +use NotKinopoisk\Models\PersonByNameResult; + +echo $personResult->getFullName(); // "Винс Гиллиган / Vince Gilligan" +echo $personResult->getFullName(' | '); // "Винс Гиллиган | Vince Gilligan" +``` + +### toArray() + +```php +public function toArray(): array +``` + +Преобразует модель в массив. + +#### Возвращает + +- `array` - Массив данных модели + +#### Особенности + +- Возвращает массив с данными модели в том же формате, что и API +- Enum значения преобразуются в строки + +#### Пример использования + +```php +use NotKinopoisk\Models\PersonByNameResult; + +$array = $personResult->toArray(); +// [ +// 'kinopoiskId' => 66539, +// 'webUrl' => '10096', +// 'nameRu' => 'Винс Гиллиган', +// 'nameEn' => 'Vince Gilligan', +// 'sex' => 'MALE', +// 'posterUrl' => 'https://kinopoiskapiunofficial.tech/images/actor_posters/kp/10096.jpg' +// ] +``` + +## Примеры использования + +### Создание объекта + +```php +use NotKinopoisk\Models\PersonByNameResult; +use NotKinopoisk\Enums\Sex; + +$personResult = new PersonByNameResult( + kinopoiskId: 66539, + webUrl: '10096', + nameRu: 'Винс Гиллиган', + nameEn: 'Vince Gilligan', + sex: Sex::MALE, + posterUrl: 'https://kinopoiskapiunofficial.tech/images/actor_posters/kp/10096.jpg' +); +``` + +### Создание из данных API + +```php +use NotKinopoisk\Models\PersonByNameResult; + +// Данные от API +$apiData = [ + 'kinopoiskId' => 66539, + 'webUrl' => '10096', + 'nameRu' => 'Винс Гиллиган', + 'nameEn' => 'Vince Gilligan', + 'sex' => 'MALE', + 'posterUrl' => 'https://kinopoiskapiunofficial.tech/images/actor_posters/kp/10096.jpg' +]; + +$personResult = PersonByNameResult::fromArray($apiData); +``` + +### Работа с результатом поиска + +```php +use NotKinopoisk\Models\PersonByNameResult; + +// Получение информации +echo "ID: {$personResult->kinopoiskId}\n"; +echo "Имя: {$personResult->getDisplayName()}\n"; +echo "Полное имя: {$personResult->getFullName()}\n"; +echo "Пол: {$personResult->sex?->getDisplayName()}\n"; +echo "Постер: {$personResult->posterUrl}\n"; +``` + +### Использование в сервисах + +```php +use NotKinopoisk\Models\PersonByNameResult; +use NotKinopoisk\Services\PersonService; + +class PersonService extends AbstractService +{ + public function searchByName(string $name): array + { + $response = $this->client->get('/api/v1/persons', [ + 'name' => $name + ]); + + return array_map( + fn(array $personData) => PersonByNameResult::fromArray($personData), + $response['items'] ?? [] + ); + } +} +``` + +### Отображение результатов поиска + +```php +use NotKinopoisk\Models\PersonByNameResult; + +function displayPersonResult(PersonByNameResult $person): void +{ + echo "=== Результат поиска ===\n"; + echo "ID: {$person->kinopoiskId}\n"; + echo "Имя: {$person->getDisplayName()}\n"; + echo "Полное имя: {$person->getFullName()}\n"; + + if ($person->sex) { + echo "Пол: {$person->sex->getDisplayName()}\n"; + } else { + echo "Пол: Неизвестен\n"; + } + + echo "Постер: {$person->posterUrl}\n"; + echo "Веб-страница: {$person->webUrl}\n"; + echo "=======================\n"; +} + +// Использование +$personResult = PersonByNameResult::fromArray($apiData); +displayPersonResult($personResult); +``` + +### Фильтрация по полу + +```php +use NotKinopoisk\Models\PersonByNameResult; + +function filterBySex(array $persons, string $sex): array +{ + return array_filter($persons, function(PersonByNameResult $person) use ($sex) { + if (!$person->sex) return false; + + return match($sex) { + 'male' => $person->isMale(), + 'female' => $person->isFemale(), + 'unknown' => $person->isSexUnknown(), + default => false + }; + }); +} + +// Использование +$allPersons = [/* массив PersonByNameResult объектов */]; +$malePersons = filterBySex($allPersons, 'male'); +$femalePersons = filterBySex($allPersons, 'female'); +``` + +### Поиск по имени + +```php +use NotKinopoisk\Models\PersonByNameResult; + +function searchInResults(array $persons, string $query): array +{ + $query = strtolower($query); + + return array_filter($persons, function(PersonByNameResult $person) use ($query) { + $nameRu = strtolower($person->nameRu ?? ''); + $nameEn = strtolower($person->nameEn ?? ''); + + return str_contains($nameRu, $query) || str_contains($nameEn, $query); + }); +} + +// Использование +$searchResults = searchInResults($allPersons, 'винс'); +``` + +### Сортировка результатов + +```php +use NotKinopoisk\Models\PersonByNameResult; + +function sortByName(array $persons, bool $ascending = true): array +{ + usort($persons, function(PersonByNameResult $a, PersonByNameResult $b) use ($ascending) { + $nameA = $a->getDisplayName(); + $nameB = $b->getDisplayName(); + + $comparison = strcmp($nameA, $nameB); + return $ascending ? $comparison : -$comparison; + }); + + return $persons; +} + +// Использование +$sortedPersons = sortByName($allPersons, true); // По возрастанию +``` + +### Статистика результатов + +```php +use NotKinopoisk\Models\PersonByNameResult; + +function getSearchStatistics(array $persons): array +{ + $total = count($persons); + $male = count(array_filter($persons, fn($p) => $p->isMale())); + $female = count(array_filter($persons, fn($p) => $p->isFemale())); + $unknown = count(array_filter($persons, fn($p) => $p->isSexUnknown())); + + return [ + 'total' => $total, + 'male' => $male, + 'female' => $female, + 'unknown' => $unknown, + 'male_percent' => $total > 0 ? round(($male / $total) * 100, 1) : 0, + 'female_percent' => $total > 0 ? round(($female / $total) * 100, 1) : 0, + 'unknown_percent' => $total > 0 ? round(($unknown / $total) * 100, 1) : 0 + ]; +} + +// Использование +$stats = getSearchStatistics($allPersons); +echo "Всего найдено: {$stats['total']}\n"; +echo "Мужчин: {$stats['male']} ({$stats['male_percent']}%)\n"; +echo "Женщин: {$stats['female']} ({$stats['female_percent']}%)\n"; +echo "Неизвестно: {$stats['unknown']} ({$stats['unknown_percent']}%)\n"; +``` + +### Сериализация и десериализация + +```php +use NotKinopoisk\Models\PersonByNameResult; + +// Преобразование в массив +$array = $personResult->toArray(); + +// Сохранение в JSON +$json = json_encode($array, JSON_PRETTY_PRINT); +file_put_contents('person_result.json', $json); + +// Загрузка из JSON +$loadedArray = json_decode(file_get_contents('person_result.json'), true); +$loadedPersonResult = PersonByNameResult::fromArray($loadedArray); +``` + +### Валидация данных + +```php +use NotKinopoisk\Models\PersonByNameResult; + +function validatePersonData(array $data): bool +{ + if (!isset($data['kinopoiskId']) || !is_int($data['kinopoiskId'])) { + throw new \InvalidArgumentException('kinopoiskId должен быть целым числом'); + } + + if (!isset($data['webUrl']) || !is_string($data['webUrl'])) { + throw new \InvalidArgumentException('webUrl должен быть строкой'); + } + + if (!isset($data['posterUrl']) || !is_string($data['posterUrl'])) { + throw new \InvalidArgumentException('posterUrl должен быть строкой'); + } + + if (isset($data['sex']) && !in_array($data['sex'], ['MALE', 'FEMALE', 'UNKNOWN'])) { + throw new \InvalidArgumentException('sex должен быть одним из: MALE, FEMALE, UNKNOWN'); + } + + return true; +} + +// Использование +try { + validatePersonData($apiData); + $personResult = PersonByNameResult::fromArray($apiData); +} catch (\InvalidArgumentException $e) { + echo "Ошибка валидации: " . $e->getMessage(); +} +``` + +## Связанные классы + +- [PersonService](../services/person-service.md) - Сервис для работы с персонами +- [Person](./person.md) - Модель персоны +- [Staff](./staff.md) - Модель съемочной группы + +## API Endpoints + +Результаты поиска персон используются в следующих API endpoints: + +- `/api/v1/persons` - Поиск персон по имени + +## Связанные файлы + +--- + +**📚 Навигация:** [Главная](../index.md) → [Модели](index.md) → PersonByNameResult diff --git a/docs/dev/notkinopoiskphp/models/person-film.md b/docs/dev/notkinopoiskphp/models/person-film.md index 911145b..a32ba5b 100644 --- a/docs/dev/notkinopoiskphp/models/person-film.md +++ b/docs/dev/notkinopoiskphp/models/person-film.md @@ -1039,6 +1039,6 @@ echo "- Специфичных фильмов: {$analysis['filmTypes']['specific ## Связанные классы -- [`Person`](person.md) - Модель персоны +- [`Person`](./person.md) - Модель персоны - [`ProfessionKey`](../enums/profession-key.md) - Enum профессий - [`PersonService`](../services/person-service.md) - Сервис для работы с персонами diff --git a/docs/dev/notkinopoiskphp/models/person-spouse.md b/docs/dev/notkinopoiskphp/models/person-spouse.md index 81c03f3..6cac1e3 100644 --- a/docs/dev/notkinopoiskphp/models/person-spouse.md +++ b/docs/dev/notkinopoiskphp/models/person-spouse.md @@ -718,5 +718,5 @@ if (!empty($analysis['divorceReasons'])) { ## Связанные классы -- [`Person`](person.md) - Модель персоны +- [`Person`](./person.md) - Модель персоны - [`PersonService`](../services/person-service.md) - Сервис для работы с персонами diff --git a/docs/dev/notkinopoiskphp/models/premiere.md b/docs/dev/notkinopoiskphp/models/premiere.md index 0ea0880..dd03e29 100644 --- a/docs/dev/notkinopoiskphp/models/premiere.md +++ b/docs/dev/notkinopoiskphp/models/premiere.md @@ -650,6 +650,6 @@ foreach ($analysis['monthlyDistribution'] as $month => $count) { ## Связанные классы -- [`Country`](country.md) - Модель страны -- [`Genre`](genre.md) - Модель жанра +- [`Country`](./country.md) - Модель страны +- [`Genre`](./genre.md) - Модель жанра - [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/models/review.md b/docs/dev/notkinopoiskphp/models/review.md index d162271..a9d8a9b 100644 --- a/docs/dev/notkinopoiskphp/models/review.md +++ b/docs/dev/notkinopoiskphp/models/review.md @@ -320,4 +320,4 @@ echo "Рецензии со словом 'матрица': " . count($matrixRevi - [`ReviewType`](../enums/review-type.md) - Типы рецензий - [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами -- [`Film`](film.md) - Модель фильма +- [`Film`](./film.md) - Модель фильма diff --git a/docs/dev/notkinopoiskphp/models/season.md b/docs/dev/notkinopoiskphp/models/season.md index 55aa6a8..b278060 100644 --- a/docs/dev/notkinopoiskphp/models/season.md +++ b/docs/dev/notkinopoiskphp/models/season.md @@ -504,5 +504,5 @@ foreach ($analysis['seasonDistribution'] as $season => $percentage) { ## Связанные классы -- [`Episode`](episode.md) - Модель эпизода +- [`Episode`](./episode.md) - Модель эпизода - [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами diff --git a/docs/dev/notkinopoiskphp/models/staff.md b/docs/dev/notkinopoiskphp/models/staff.md index 959eea6..c288290 100644 --- a/docs/dev/notkinopoiskphp/models/staff.md +++ b/docs/dev/notkinopoiskphp/models/staff.md @@ -334,7 +334,7 @@ foreach ($stats as $profession => $count) { ## Связанные классы -- [`Person`](person.md) - Модель персоны +- [`Person`](./person.md) - Модель персоны - [`ProfessionKey`](../enums/profession-key.md) - Ключи профессий - [`PersonService`](../services/person-service.md) - Сервис для работы с персонами - [`MovieStaffResponse`](../responses/movie-staff-response.md) - Ответ с персоналом фильма diff --git a/docs/dev/notkinopoiskphp/models/user-vote.md b/docs/dev/notkinopoiskphp/models/user-vote.md index 1647f84..59f9fe4 100644 --- a/docs/dev/notkinopoiskphp/models/user-vote.md +++ b/docs/dev/notkinopoiskphp/models/user-vote.md @@ -813,7 +813,7 @@ foreach (array_slice($genreAverages, 0, 10, true) as $genre => $average) { ## Связанные классы -- [`Country`](country.md) - Модель страны -- [`Genre`](genre.md) - Модель жанра +- [`Country`](./country.md) - Модель страны +- [`Genre`](./genre.md) - Модель жанра - [`ContentType`](../enums/content-type.md) - Enum типов контента - [`UserService`](../services/user-service.md) - Сервис для работы с пользователями diff --git a/docs/dev/notkinopoiskphp/models/video.md b/docs/dev/notkinopoiskphp/models/video.md index 360f362..235fdbd 100644 --- a/docs/dev/notkinopoiskphp/models/video.md +++ b/docs/dev/notkinopoiskphp/models/video.md @@ -457,4 +457,4 @@ foreach ($videos as $index => $video) { - [`VideoSite`](../enums/video-site.md) - Платформы для видео - [`FilmService`](../services/film-service.md) - Сервис для работы с фильмами -- [`Film`](film.md) - Модель фильма +- [`Film`](./film.md) - Модель фильма diff --git a/docs/dev/notkinopoiskphp/navigation-map.md b/docs/dev/notkinopoiskphp/navigation-map.md index ca1c0dc..ecbcdbd 100644 --- a/docs/dev/notkinopoiskphp/navigation-map.md +++ b/docs/dev/notkinopoiskphp/navigation-map.md @@ -4,7 +4,7 @@ --- -**📚 Навигация:** [Главная](index.md) → Карта навигации +**📚 Навигация:** [Главная](./index.md) → Карта навигации --- @@ -12,206 +12,212 @@ ``` 📁 docs/ -├── 📄 index.md # Главная страница -├── 📄 navigation-map.md # Карта навигации (этот файл) -├── 📄 client.md # Основной клиент +├── 📄 index.md # Главная страница +├── 📄 navigation-map.md # Карта навигации (этот файл) +├── 📄 client.md # Основной клиент │ -├── 📁 services/ # Сервисы API -│ ├── 📄 index.md # Обзор сервисов -│ ├── 📄 film-service.md # Сервис фильмов -│ ├── 📄 person-service.md # Сервис персон -│ ├── 📄 media-service.md # Сервис медиа -│ └── 📄 user-service.md # Сервис пользователей +├── 📁 services/ # Сервисы API +│ ├── 📄 index.md # Обзор сервисов +│ ├── 📄 film-service.md # Сервис фильмов +│ ├── 📄 person-service.md # Сервис персон +│ ├── 📄 media-service.md # Сервис медиа +│ └── 📄 user-service.md # Сервис пользователей │ -├── 📁 models/ # Модели данных -│ ├── 📄 index.md # Обзор моделей -│ ├── 📄 film.md # Модель фильма -│ ├── 📄 person.md # Модель персоны -│ ├── 📄 staff.md # Модель съемочной группы -│ ├── 📄 review.md # Модель отзыва -│ ├── 📄 fact.md # Модель факта -│ ├── 📄 image.md # Модель изображения -│ ├── 📄 video.md # Модель видео -│ ├── 📄 award.md # Модель награды -│ ├── 📄 box-office.md # Модель кассовых сборов -│ ├── 📄 country.md # Модель страны -│ ├── 📄 genre.md # Модель жанра -│ ├── 📄 episode.md # Модель эпизода -│ ├── 📄 season.md # Модель сезона -│ ├── 📄 external-source.md # Модель внешнего источника -│ ├── 📄 distribution.md # Модель дистрибуции -│ ├── 📄 film-search-result.md # Модель результата поиска -│ ├── 📄 person-spouse.md # Модель супруга -│ ├── 📄 person-film.md # Модель фильма персоны -│ ├── 📄 user-vote.md # Модель голоса пользователя -│ ├── 📄 film-collection.md # Модель коллекции фильмов -│ ├── 📄 filters.md # Модель фильтров -│ ├── 📄 related-film.md # Модель связанного фильма -│ ├── 📄 api-key-info.md # Модель информации об API ключе -│ ├── 📄 api-key-qouta.md # Модель квоты API ключа -│ └── 📄 media-post.md # Модель медиа поста +├── 📁 models/ # Модели данных +│ ├── 📄 index.md # Обзор моделей +│ ├── 📄 film.md # Модель фильма +│ ├── 📄 person.md # Модель персоны +│ ├── 📄 staff.md # Модель съемочной группы +│ ├── 📄 review.md # Модель отзыва +│ ├── 📄 fact.md # Модель факта +│ ├── 📄 image.md # Модель изображения +│ ├── 📄 video.md # Модель видео +│ ├── 📄 award.md # Модель награды +│ ├── 📄 box-office.md # Модель кассовых сборов +│ ├── 📄 country.md # Модель страны +│ ├── 📄 genre.md # Модель жанра +│ ├── 📄 episode.md # Модель эпизода +│ ├── 📄 season.md # Модель сезона +│ ├── 📄 external-source.md # Модель внешнего источника +│ ├── 📄 distribution.md # Модель дистрибуции +│ ├── 📄 film-search-result.md # Модель результата поиска +│ ├── 📄 person-spouse.md # Модель супруга +│ ├── 📄 person-film.md # Модель фильма персоны +│ ├── 📄 user-vote.md # Модель голоса пользователя +│ ├── 📄 film-collection.md # Модель коллекции фильмов +│ ├── 📄 filters.md # Модель фильтров +│ ├── 📄 related-film.md # Модель связанного фильма +│ ├── 📄 api-key-info.md # Модель информации об API ключе +│ ├── 📄 api-key-qouta.md # Модель квоты API ключа +│ └── 📄 media-post.md # Модель медиа поста │ -├── 📁 enums/ # Перечисления -│ ├── 📄 index.md # Обзор перечислений -│ ├── 📄 image-type.md # Типы изображений -│ ├── 📄 review-order.md # Порядок сортировки отзывов -│ ├── 📄 review-type.md # Типы отзывов -│ ├── 📄 fact-type.md # Типы фактов -│ ├── 📄 profession-key.md # Ключи профессий -│ ├── 📄 video-site.md # Сайты видео -│ ├── 📄 box-office-type.md # Типы кассовых сборов -│ ├── 📄 distribution-type.md # Типы дистрибуции -│ ├── 📄 relation-type.md # Типы связей -│ ├── 📄 sex.md # Пол -│ ├── 📄 api-version.md # Версии API -│ ├── 📄 month.md # Месяцы -│ ├── 📄 film-order.md # Порядок сортировки фильмов -│ ├── 📄 content-type.md # Типы контента -│ ├── 📄 collection-type.md # Типы коллекций -│ ├── 📄 distribution-sub-type.md # Подтипы дистрибуции -│ └── 📄 account-type.md # Типы аккаунтов +├── 📁 enums/ # Перечисления +│ ├── 📄 index.md # Обзор перечислений +│ ├── 📄 image-type.md # Типы изображений +│ ├── 📄 review-order.md # Порядок сортировки отзывов +│ ├── 📄 review-type.md # Типы отзывов +│ ├── 📄 fact-type.md # Типы фактов +│ ├── 📄 profession-key.md # Ключи профессий +│ ├── 📄 video-site.md # Сайты видео +│ ├── 📄 box-office-type.md # Типы кассовых сборов +│ ├── 📄 distribution-type.md # Типы дистрибуции +│ ├── 📄 relation-type.md # Типы связей +│ ├── 📄 sex.md # Пол +│ ├── 📄 api-version.md # Версии API +│ ├── 📄 month.md # Месяцы +│ ├── 📄 film-order.md # Порядок сортировки фильмов +│ ├── 📄 content-type.md # Типы контента +│ ├── 📄 collection-type.md # Типы коллекций +│ ├── 📄 distribution-sub-type.md # Подтипы дистрибуции +│ ├── 📄 production-status.md # Статусы производства +│ └── 📄 account-type.md # Типы аккаунтов │ -├── 📁 responses/ # Ответы API -│ ├── 📄 index.md # Обзор ответов -│ ├── 📄 default-response.md # Базовый ответ -│ ├── 📄 paginated-response.md # Пагинированный ответ -│ └── 📄 keyword-search-response.md # Ответ поиска +├── 📁 responses/ # Ответы API +│ ├── 📄 index.md # Обзор ответов +│ ├── 📄 default-response.md # Базовый ответ +│ ├── 📄 paginated-response.md # Пагинированный ответ +│ ├── 📄 keyword-search-response.md # Ответ поиска +│ ├── 📄 budget-response.md # Ответ с бюджетом +│ ├── 📄 sequel-prequel-response.md # Ответ с сиквелами/приквелами +│ ├── 📄 movie-staff-response.md # Ответ со съемочной командой +│ ├── 📄 review-response.md # Ответ с отзывами +│ └── 📄 simple-response.md # Простой ответ │ -├── 📁 exceptions/ # Исключения -│ ├── 📄 index.md # Обзор исключений -│ ├── 📄 api-exception.md # Базовое исключение API -│ ├── 📄 invalid-api-key-exception.md # Неверный API ключ -│ ├── 📄 rate-limit-exception.md # Превышение лимита -│ ├── 📄 resource-not-found-exception.md # Ресурс не найден -│ └── 📄 kp-validation-exception.md # Ошибка валидации +├── 📁 exceptions/ # Исключения +│ ├── 📄 index.md # Обзор исключений +│ ├── 📄 api-exception.md # Базовое исключение API +│ ├── 📄 invalid-api-key-exception.md # Неверный API ключ +│ ├── 📄 rate-limit-exception.md # Превышение лимита +│ ├── 📄 resource-not-found-exception.md # Ресурс не найден +│ └── 📄 kp-validation-exception.md # Ошибка валидации │ -└── 📁 interfaces/ # Интерфейсы - ├── 📄 index.md # Обзор интерфейсов - ├── 📄 model-interface.md # Интерфейс модели - └── 📄 response-interface.md # Интерфейс ответа +└── 📁 interfaces/ # Интерфейсы + ├── 📄 index.md # Обзор интерфейсов + ├── 📄 model-interface.md # Интерфейс модели + └── 📄 response-interface.md # Интерфейс ответа ``` ## 🔗 Быстрые ссылки ### 🚀 Начало работы -- **[Главная страница](index.md)** - Обзор библиотеки и быстрый старт -- **[Основной клиент](client.md)** - Главный класс для работы с API -- **[Примеры использования](../examples/)** - Готовые примеры кода +- **[Главная страница](./index.md)** - Обзор библиотеки и быстрый старт +- **[Основной клиент](./client.md)** - Главный класс для работы с API +- **[Примеры использования](./https://github.com/DevCraftClub/NotKinopoiskPHP/tree/main/examples)** - Готовые примеры кода ### 📦 Основные компоненты -- **[Сервисы](services/index.md)** - Работа с различными типами данных -- **[Модели](models/index.md)** - Структуры данных API -- **[Перечисления](enums/index.md)** - Константы и типы -- **[Ответы](responses/index.md)** - Классы ответов API -- **[Исключения](exceptions/index.md)** - Обработка ошибок -- **[Интерфейсы](interfaces/index.md)** - Базовые контракты +- **[Сервисы](./services/index.md)** - Работа с различными типами данных +- **[Модели](./models/index.md)** - Структуры данных API +- **[Перечисления](./enums/index.md)** - Константы и типы +- **[Ответы](./responses/index.md)** - Классы ответов API +- **[Исключения](./exceptions/index.md)** - Обработка ошибок +- **[Интерфейсы](./interfaces/index.md)** - Базовые контракты ## 🎯 Популярные разделы ### 🎬 Работа с фильмами -1. **[FilmService](services/film-service.md)** - Основной сервис для работы с фильмами -2. **[Film](models/film.md)** - Модель фильма с полной информацией -3. **[FilmSearchResult](models/film-search-result.md)** - Результаты поиска фильмов -4. **[ContentType](enums/content-type.md)** - Типы контента (фильм, сериал, etc.) -5. **[FilmOrder](enums/film-order.md)** - Сортировка фильмов +1. **[FilmService](./services/film-service.md)** - Основной сервис для работы с фильмами +2. **[Film](./models/film.md)** - Модель фильма с полной информацией +3. **[FilmSearchResult](./models/film-search-result.md)** - Результаты поиска фильмов +4. **[ContentType](./enums/content-type.md)** - Типы контента (фильм, сериал, etc.) +5. **[FilmOrder](./enums/film-order.md)** - Сортировка фильмов ### 👥 Работа с персонами -1. **[PersonService](services/person-service.md)** - Сервис для работы с персонами -2. **[Person](models/person.md)** - Модель персоны -3. **[Staff](models/staff.md)** - Съемочная группа -4. **[ProfessionKey](enums/profession-key.md)** - Профессии в кино -5. **[PersonFilm](models/person-film.md)** - Фильмография персоны +1. **[PersonService](./services/person-service.md)** - Сервис для работы с персонами +2. **[Person](./models/person.md)** - Модель персоны +3. **[Staff](./models/staff.md)** - Съемочная группа +4. **[ProfessionKey](./enums/profession-key.md)** - Профессии в кино +5. **[PersonFilm](./models/person-film.md)** - Фильмография персоны ### 🎥 Работа с медиа -1. **[MediaService](services/media-service.md)** - Сервис для работы с медиа -2. **[Image](models/image.md)** - Изображения фильмов -3. **[Video](models/video.md)** - Видео контент -4. **[ImageType](enums/image-type.md)** - Типы изображений -5. **[VideoSite](enums/video-site.md)** - Сайты видео +1. **[MediaService](./services/media-service.md)** - Сервис для работы с медиа +2. **[Image](./models/image.md)** - Изображения фильмов +3. **[Video](./models/video.md)** - Видео контент +4. **[ImageType](./enums/image-type.md)** - Типы изображений +5. **[VideoSite](./enums/video-site.md)** - Сайты видео ### 📝 Работа с отзывами и фактами -1. **[Review](models/review.md)** - Отзывы пользователей -2. **[Fact](models/fact.md)** - Интересные факты -3. **[ReviewType](enums/review-type.md)** - Типы отзывов -4. **[ReviewOrder](enums/review-order.md)** - Сортировка отзывов -5. **[FactType](enums/fact-type.md)** - Типы фактов +1. **[Review](./models/review.md)** - Отзывы пользователей +2. **[Fact](./models/fact.md)** - Интересные факты +3. **[ReviewType](./enums/review-type.md)** - Типы отзывов +4. **[ReviewOrder](./enums/review-order.md)** - Сортировка отзывов +5. **[FactType](./enums/fact-type.md)** - Типы фактов ## 🔍 Поиск по функциональности ### 🔍 Поиск и фильтрация -- **[FilmService::searchByKeyword()](services/film-service.md#searchbykeyword)** - Поиск фильмов -- **[PersonService::searchByName()](services/person-service.md#searchbyname)** - Поиск персон -- **[FilmService::getTop()](services/film-service.md#gettop)** - Топ фильмов -- **[Filters](models/filters.md)** - Фильтры для поиска +- **[FilmService::searchByKeyword()](./services/film-service.md#searchbykeyword)** - Поиск фильмов +- **[PersonService::searchByName()](./services/person-service.md#searchbyname)** - Поиск персон +- **[FilmService::getTop()](./services/film-service.md#gettop)** - Топ фильмов +- **[Filters](./models/filters.md)** - Фильтры для поиска ### 📊 Статистика и рейтинги -- **[BoxOffice](models/box-office.md)** - Кассовые сборы -- **[Award](models/award.md)** - Награды и номинации -- **[UserVote](models/user-vote.md)** - Пользовательские голоса -- **[ExternalSource](models/external-source.md)** - Внешние источники рейтингов +- **[BoxOffice](./models/box-office.md)** - Кассовые сборы +- **[Award](./models/award.md)** - Награды и номинации +- **[UserVote](./models/user-vote.md)** - Пользовательские голоса +- **[ExternalSource](./models/external-source.md)** - Внешние источники рейтингов ### 🎬 Сериалы и эпизоды -- **[Episode](models/episode.md)** - Эпизоды сериалов -- **[Season](models/season.md)** - Сезоны сериалов -- **[FilmService::getSeasons()](services/film-service.md#getseasons)** - Получение сезонов +- **[Episode](./models/episode.md)** - Эпизоды сериалов +- **[Season](./models/season.md)** - Сезоны сериалов +- **[FilmService::getSeasons()](./services/film-service.md#getseasons)** - Получение сезонов ### 🔗 Связанный контент -- **[RelatedFilm](models/related-film.md)** - Связанные фильмы -- **[FilmService::getSequelsAndPrequels()](services/film-service.md#getsequelsandprequels)** - Сиквелы и приквелы -- **[FilmCollection](models/film-collection.md)** - Коллекции фильмов +- **[RelatedFilm](./models/related-film.md)** - Связанные фильмы +- **[FilmService::getSequelsAndPrequels()](./services/film-service.md#getsequelsandprequels)** - Сиквелы и приквелы +- **[FilmCollection](./models/film-collection.md)** - Коллекции фильмов ## ⚠️ Обработка ошибок ### 🔑 Аутентификация -- **[InvalidApiKeyException](exceptions/invalid-api-key-exception.md)** - Неверный API ключ -- **[ApiKeyInfo](models/api-key-info.md)** - Информация об API ключе -- **[ApiKeyQouta](models/api-key-qouta.md)** - Квоты запросов +- **[InvalidApiKeyException](./exceptions/invalid-api-key-exception.md)** - Неверный API ключ +- **[ApiKeyInfo](./models/api-key-info.md)** - Информация об API ключе +- **[ApiKeyQouta](./models/api-key-qouta.md)** - Квоты запросов ### ⏱️ Лимиты и ограничения -- **[RateLimitException](exceptions/rate-limit-exception.md)** - Превышение лимита запросов -- **[UserService](services/user-service.md)** - Информация о лимитах +- **[RateLimitException](./exceptions/rate-limit-exception.md)** - Превышение лимита запросов +- **[UserService](./services/user-service.md)** - Информация о лимитах ### 🔍 Ошибки ресурсов -- **[ResourceNotFoundException](exceptions/resource-not-found-exception.md)** - Ресурс не найден -- **[ApiException](exceptions/api-exception.md)** - Общие ошибки API +- **[ResourceNotFoundException](./exceptions/resource-not-found-exception.md)** - Ресурс не найден +- **[ApiException](./exceptions/api-exception.md)** - Общие ошибки API ### ✅ Валидация данных -- **[KpValidationException](exceptions/kp-validation-exception.md)** - Ошибки валидации -- **[ModelInterface](interfaces/model-interface.md)** - Базовый интерфейс моделей +- **[KpValidationException](./exceptions/kp-validation-exception.md)** - Ошибки валидации +- **[ModelInterface](./interfaces/model-interface.md)** - Базовый интерфейс моделей ## 🛠️ Утилиты и помощники ### 📄 Ответы API -- **[DefaultResponse](responses/default-response.md)** - Базовый ответ -- **[PaginatedResponse](responses/paginated-response.md)** - Пагинация -- **[KeywordSearchResponse](responses/keyword-search-response.md)** - Ответ поиска +- **[DefaultResponse](./responses/default-response.md)** - Базовый ответ +- **[PaginatedResponse](./responses/paginated-response.md)** - Пагинация +- **[KeywordSearchResponse](./responses/keyword-search-response.md)** - Ответ поиска ### 🔧 Интерфейсы -- **[ModelInterface](interfaces/model-interface.md)** - Интерфейс моделей -- **[ResponseInterface](interfaces/response-interface.md)** - Интерфейс ответов +- **[ModelInterface](./interfaces/model-interface.md)** - Интерфейс моделей +- **[ResponseInterface](./interfaces/response-interface.md)** - Интерфейс ответов ### 🌍 Справочные данные -- **[Country](models/country.md)** - Страны -- **[Genre](models/genre.md)** - Жанры -- **[Distribution](models/distribution.md)** - Дистрибуция -- **[Month](enums/month.md)** - Месяцы +- **[Country](./models/country.md)** - Страны +- **[Genre](./models/genre.md)** - Жанры +- **[Distribution](./models/distribution.md)** - Дистрибуция +- **[Month](./enums/month.md)** - Месяцы ## 📚 Примеры использования @@ -260,35 +266,35 @@ try { ### 🆕 Для новичков -1. **[Главная страница](index.md)** - Обзор библиотеки -2. **[Основной клиент](client.md)** - Базовые концепции -3. **[FilmService](services/film-service.md)** - Работа с фильмами -4. **[Film](models/film.md)** - Структура данных фильма -5. **[Примеры](../examples/)** - Практические примеры +1. **[Главная страница](./index.md)** - Обзор библиотеки +2. **[Основной клиент](./client.md)** - Базовые концепции +3. **[FilmService](./services/film-service.md)** - Работа с фильмами +4. **[Film](./models/film.md)** - Структура данных фильма +5. **[Примеры](https://github.com/DevCraftClub/NotKinopoiskPHP/tree/main/examples)** - Практические примеры ### 🔧 Для разработчиков -1. **[Интерфейсы](interfaces/index.md)** - Базовые контракты -2. **[Исключения](exceptions/index.md)** - Обработка ошибок -3. **[Ответы](responses/index.md)** - Структура ответов API -4. **[Перечисления](enums/index.md)** - Константы и типы -5. **[Модели](models/index.md)** - Все модели данных +1. **[Интерфейсы](./interfaces/index.md)** - Базовые контракты +2. **[Исключения](./exceptions/index.md)** - Обработка ошибок +3. **[Ответы](./responses/index.md)** - Структура ответов API +4. **[Перечисления](./enums/index.md)** - Константы и типы +5. **[Модели](./models/index.md)** - Все модели данных ### 🎬 Для работы с контентом -1. **[FilmService](services/film-service.md)** - Основные операции с фильмами -2. **[PersonService](services/person-service.md)** - Работа с персонами -3. **[MediaService](services/media-service.md)** - Медиа контент -4. **[UserService](services/user-service.md)** - Пользовательские данные -5. **[Связанные модели](models/index.md)** - Дополнительные данные +1. **[FilmService](./services/film-service.md)** - Основные операции с фильмами +2. **[PersonService](./services/person-service.md)** - Работа с персонами +3. **[MediaService](./services/media-service.md)** - Медиа контент +4. **[UserService](./services/user-service.md)** - Пользовательские данные +5. **[Связанные модели](./models/index.md)** - Дополнительные данные ### 🔍 Для поиска и анализа -1. **[FilmService::searchByKeyword()](services/film-service.md#searchbykeyword)** - Поиск фильмов -2. **[PersonService::searchByName()](services/person-service.md#searchbyname)** - Поиск персон -3. **[FilmService::getTop()](services/film-service.md#gettop)** - Топ фильмов -4. **[Статистические модели](models/index.md)** - Анализ данных -5. **[Перечисления для сортировки](enums/index.md)** - Настройка поиска +1. **[FilmService::searchByKeyword()](./services/film-service.md#searchbykeyword)** - Поиск фильмов +2. **[PersonService::searchByName()](./services/person-service.md#searchbyname)** - Поиск персон +3. **[FilmService::getTop()](./services/film-service.md#gettop)** - Топ фильмов +4. **[Статистические модели](./models/index.md)** - Анализ данных +5. **[Перечисления для сортировки](./enums/index.md)** - Настройка поиска ## 📊 Статистика документации @@ -320,6 +326,5 @@ try { --- -**📚 Навигация:** [Главная](index.md) → Карта навигации +**📚 Навигация:** [Главная](./index.md) → Карта навигации -**🔄 Последнее обновление:** diff --git a/docs/dev/notkinopoiskphp/responses/budget-response.md b/docs/dev/notkinopoiskphp/responses/budget-response.md new file mode 100644 index 0000000..0a5d96a --- /dev/null +++ b/docs/dev/notkinopoiskphp/responses/budget-response.md @@ -0,0 +1,179 @@ +# BudgetResponse + +## Описание + +`BudgetResponse` - это специализированный класс ответа для работы с данными о бюджете фильма от Kinopoisk API. Наследует функциональность `DefaultResponse` и добавляет специфичные методы для анализа финансовых показателей. + +## Основные возможности + +- Создание объекта из данных API с валидацией +- Вычисление общего дохода от всех источников +- Детализированная разбивка доходов по типам +- Подсчет количества доходных статей +- Безопасная обработка ошибок типизации + +## Наследование + +```php +NotKinopoisk\Responses\DefaultResponse +└── NotKinopoisk\Responses\BudgetResponse +``` + +## Конструктор + +```php +public function __construct( + public int $total, + public array $items +) +``` + +### Параметры + +- `$total` (int) - Общее количество элементов в коллекции +- `$items` (array) - Массив элементов данных о бюджете + +## Статические методы + +### fromArray() + +```php +public static function fromArray(array $data, string $cls): self +``` + +Создает экземпляр `BudgetResponse` из массива данных API. + +#### Параметры + +- `$data` (array) - Массив данных от API, содержащий информацию о бюджете +- `$cls` (string) - Имя класса для элементов коллекции (обычно `BoxOffice::class`) + +#### Возвращает + +- `BudgetResponse` - Новый экземпляр с данными о бюджете + +#### Исключения + +- `KpValidationException` - Если данные имеют некорректную структуру + +#### Пример использования + +```php +$apiData = ['total' => 5, 'items' => [...]]; +$budgetResponse = BudgetResponse::fromArray($apiData, BoxOffice::class); +``` + +## Методы экземпляра + +### getTotalRevenue() + +```php +public function getTotalRevenue(): int +``` + +Вычисляет общий доход от всех источников поступлений. + +#### Возвращает + +- `int` - Общая сумма дохода в указанной валюте + +#### Исключения + +- `KpValidationException` - Если элементы не содержат корректных данных + +#### Пример использования + +```php +$budgetResponse = BudgetResponse::fromArray($apiData, BoxOffice::class); +$totalRevenue = $budgetResponse->getTotalRevenue(); +echo "Общий доход: {$totalRevenue}"; +``` + +### getRevenueBreakdown() + +```php +public function getRevenueBreakdown(): array +``` + +Получает детализированную информацию о доходах по типам. + +#### Возвращает + +- `array` - Ассоциативный массив типов доходов и их сумм + +#### Пример использования + +```php +$revenueBreakdown = $budgetResponse->getRevenueBreakdown(); +foreach ($revenueBreakdown as $type => $amount) { + echo "Доход от {$type}: {$amount}"; +} +``` + +### getRevenueItemsCount() + +```php +public function getRevenueItemsCount(): int +``` + +Получает количество доходных статей. + +#### Возвращает + +- `int` - Количество элементов с доходными статьями + +## Типы доходов + +Класс поддерживает следующие типы доходов: + +- **Россия** (`BoxOfficeType::RUS`) - Доходы от российского проката +- **США** (`BoxOfficeType::USA`) - Доходы от американского проката +- **Мировые сборы** (`BoxOfficeType::WORLD`) - Общемировые доходы + +## Обработка ошибок + +Класс включает комплексную обработку ошибок: + +- Валидация структуры данных API +- Проверка типизации элементов +- Обработка некорректных финансовых данных +- Безопасное вычисление сумм + +## Пример полного использования + +```php +use NotKinopoisk\Responses\BudgetResponse; +use NotKinopoisk\Models\BoxOffice; + +// Получение данных от API +$apiData = [ + 'total' => 3, + 'items' => [ + ['type' => 'RUS', 'amount' => 1000000], + ['type' => 'USA', 'amount' => 5000000], + ['type' => 'WORLD', 'amount' => 15000000] + ] +]; + +// Создание объекта ответа +$budgetResponse = BudgetResponse::fromArray($apiData, BoxOffice::class); + +// Анализ финансовых показателей +$totalRevenue = $budgetResponse->getTotalRevenue(); +$breakdown = $budgetResponse->getRevenueBreakdown(); +$revenueCount = $budgetResponse->getRevenueItemsCount(); + +echo "Общий доход: {$totalRevenue}\n"; +echo "Количество источников дохода: {$revenueCount}\n"; + +foreach ($breakdown as $source => $amount) { + echo "Доход от {$source}: {$amount}\n"; +} +``` + +## Связанные классы + +- `DefaultResponse` - Базовый класс для всех ответов API +- `BoxOffice` - Модель данных о кассовых сборах +- `BoxOfficeType` - Перечисление типов кассовых сборов +- `KpValidationException` - Исключение для ошибок валидации diff --git a/docs/dev/notkinopoiskphp/responses/index.md b/docs/dev/notkinopoiskphp/responses/index.md index ed0f38a..16887d2 100644 --- a/docs/dev/notkinopoiskphp/responses/index.md +++ b/docs/dev/notkinopoiskphp/responses/index.md @@ -76,6 +76,97 @@ - `$searchFilmsCountResult` (int) - Общее количество найденных фильмов - `$films` (array) - Массив найденных фильмов +### 💰 [BudgetResponse](budget-response.md) + +Ответ с данными о бюджете фильма. + +**Основные возможности:** + +- Наследует функциональность DefaultResponse +- Вычисление общего дохода от всех источников +- Детализированная разбивка доходов по типам +- Подсчет количества доходных статей + +**Специфичные методы:** + +- `getTotalRevenue()` - Вычисление общего дохода +- `getRevenueBreakdown()` - Разбивка доходов по типам +- `getRevenueItemsCount()` - Количество доходных статей + +### 🎬 [SequelPrequelResponse](sequel-prequel-response.md) + +Ответ с сиквелами, приквелами и связанными фильмами. + +**Основные возможности:** + +- Наследует функциональность SimpleResponse +- Фильтрация фильмов по типу отношения +- Статистика по типам отношений +- Группировка фильмов по типам связей + +**Специфичные методы:** + +- `getSequels()` - Получение сиквелов +- `getPrequels()` - Получение приквелов +- `getRemakes()` - Получение римейков +- `getSimilar()` - Получение похожих фильмов +- `getStatistics()` - Статистика по типам отношений + +### 👥 [MovieStaffResponse](movie-staff-response.md) + +Ответ со съемочной командой фильма. + +**Основные возможности:** + +- Наследует функциональность SimpleResponse +- Фильтрация персонала по профессиональным ролям +- Получение различных групп персонала + +**Специфичные методы:** + +- `getActors()` - Получение актеров +- `getDirectors()` - Получение режиссеров +- `getWriters()` - Получение сценаристов +- `getProducers()` - Получение продюсеров +- `getCompositors()` - Получение композиторов +- `getEditors()` - Получение монтажеров +- `getDesigners()` - Получение художников + +### 📝 [ReviewResponse](review-response.md) + +Ответ с отзывами на фильм. + +**Основные возможности:** + +- Наследует функциональность PaginatedResponse +- Статистика отзывов по типам +- Поддержка всех методов пагинации + +**Специфичные свойства:** + +- `$totalPositiveReviews` (int) - Количество положительных отзывов +- `$totalNegativeReviews` (int) - Количество отрицательных отзывов +- `$totalNeutralReviews` (int) - Количество нейтральных отзывов + +### 📦 [SimpleResponse](simple-response.md) + +Базовый класс для простых ответов API. + +**Основные возможности:** + +- Хранение массива элементов без метаинформации +- Создание из данных API с валидацией класса +- Преобразование в массив для сериализации + +**Свойства:** + +- `$items` (array) - Массив элементов данных + +**Методы:** + +- `fromArray()` - Создание из массива данных +- `toArray()` - Преобразование в массив + ## 🔗 Связанные компоненты ### Модели @@ -88,6 +179,8 @@ - [Image](../models/image.md) - Изображения - [Video](../models/video.md) - Видео - [Person](../models/person.md) - Персоны +- [BoxOffice](../models/box-office.md) - Данные о кассовых сборах +- [RelatedFilm](../models/related-film.md) - Связанные фильмы ### Сервисы @@ -258,6 +351,159 @@ $response = KeywordSearchResponse::fromArray($apiData, Film::class); echo "По запросу '{$response->keyword}' найдено {$response->searchFilmsCountResult} фильмов\n"; ``` +### Работа с бюджетом фильма + +```php +use NotKinopoisk\Responses\BudgetResponse; +use NotKinopoisk\Models\BoxOffice; + +// Данные о бюджете от API +$budgetData = [ + 'total' => 3, + 'items' => [ + ['type' => 'RUS', 'amount' => 1000000], + ['type' => 'USA', 'amount' => 5000000], + ['type' => 'WORLD', 'amount' => 15000000] + ] +]; + +$budgetResponse = BudgetResponse::fromArray($budgetData, BoxOffice::class); + +// Анализ финансовых показателей +$totalRevenue = $budgetResponse->getTotalRevenue(); +$breakdown = $budgetResponse->getRevenueBreakdown(); +$revenueCount = $budgetResponse->getRevenueItemsCount(); + +echo "Общий доход: {$totalRevenue}\n"; +echo "Количество источников дохода: {$revenueCount}\n"; + +foreach ($breakdown as $source => $amount) { + echo "Доход от {$source}: {$amount}\n"; +} +``` + +### Работа с сиквелами и приквелами + +```php +use NotKinopoisk\Responses\SequelPrequelResponse; +use NotKinopoisk\Models\RelatedFilm; + +// Данные о связанных фильмах +$relatedData = [ + ['kinopoiskId' => 1, 'relationType' => 'SEQUEL'], + ['kinopoiskId' => 2, 'relationType' => 'PREQUEL'], + ['kinopoiskId' => 3, 'relationType' => 'SIMILAR'] +]; + +$response = SequelPrequelResponse::fromArray($relatedData, RelatedFilm::class); + +// Получение различных типов связанных фильмов +$sequels = $response->getSequels(); +$prequels = $response->getPrequels(); +$similar = $response->getSimilar(); + +$stats = $response->getStatistics(); + +echo "Статистика связанных фильмов:\n"; +foreach ($stats as $type => $count) { + echo "- {$type}: {$count}\n"; +} +``` + +### Работа со съемочной командой + +```php +use NotKinopoisk\Responses\MovieStaffResponse; +use NotKinopoisk\Models\Staff; + +// Данные о съемочной команде +$staffData = [ + ['staffId' => 1, 'nameRu' => 'Иван Иванов', 'professionKey' => 'ACTOR'], + ['staffId' => 2, 'nameRu' => 'Петр Петров', 'professionKey' => 'DIRECTOR'], + ['staffId' => 3, 'nameRu' => 'Сидор Сидоров', 'professionKey' => 'WRITER'] +]; + +$staffResponse = MovieStaffResponse::fromArray($staffData, Staff::class); + +// Получение различных групп персонала +$actors = $staffResponse->getActors(); +$directors = $staffResponse->getDirectors(); +$writers = $staffResponse->getWriters(); + +echo "Актеры (" . count($actors) . "):\n"; +foreach ($actors as $actor) { + echo "- {$actor->nameRu}\n"; +} + +echo "Режиссеры (" . count($directors) . "):\n"; +foreach ($directors as $director) { + echo "- {$director->nameRu}\n"; +} +``` + +### Работа с отзывами + +```php +use NotKinopoisk\Responses\ReviewResponse; +use NotKinopoisk\Models\Review; + +// Данные об отзывах +$reviewData = [ + 'total' => 150, + 'items' => [ + ['reviewId' => 1, 'reviewType' => 'POSITIVE', 'reviewText' => 'Отличный фильм!'], + ['reviewId' => 2, 'reviewType' => 'NEGATIVE', 'reviewText' => 'Не понравилось'] + ], + 'current_page' => 1, + 'total_pages' => 5, + 'totalPositiveReviews' => 100, + 'totalNegativeReviews' => 30, + 'totalNeutralReviews' => 20 +]; + +$reviewResponse = ReviewResponse::fromArray($reviewData, Review::class); + +// Анализ статистики отзывов +$positiveCount = $reviewResponse->totalPositiveReviews; +$negativeCount = $reviewResponse->totalNegativeReviews; +$neutralCount = $reviewResponse->totalNeutralReviews; + +$totalReviews = $positiveCount + $negativeCount + $neutralCount; + +echo "Статистика отзывов:\n"; +echo "- Положительных: {$positiveCount} (" . round(($positiveCount / $totalReviews) * 100, 1) . "%)\n"; +echo "- Отрицательных: {$negativeCount} (" . round(($negativeCount / $totalReviews) * 100, 1) . "%)\n"; +echo "- Нейтральных: {$neutralCount} (" . round(($neutralCount / $totalReviews) * 100, 1) . "%)\n"; +``` + +### Работа с простыми ответами + +```php +use NotKinopoisk\Responses\SimpleResponse; +use NotKinopoisk\Models\MyModel; + +// Простые данные от API +$simpleData = [ + ['id' => 1, 'name' => 'Первый элемент'], + ['id' => 2, 'name' => 'Второй элемент'], + ['id' => 3, 'name' => 'Третий элемент'] +]; + +$response = SimpleResponse::fromArray($simpleData, MyModel::class); + +// Доступ к элементам +$items = $response->items; +echo "Количество элементов: " . count($items) . "\n"; + +// Обработка элементов +foreach ($items as $item) { + echo "ID: {$item->id}, Имя: {$item->name}\n"; +} + +// Преобразование в массив +$array = $response->toArray(); +``` + ### Обработка пустых ответов ```php diff --git a/docs/dev/notkinopoiskphp/responses/movie-staff-response.md b/docs/dev/notkinopoiskphp/responses/movie-staff-response.md new file mode 100644 index 0000000..c5f66c6 --- /dev/null +++ b/docs/dev/notkinopoiskphp/responses/movie-staff-response.md @@ -0,0 +1,292 @@ +# MovieStaffResponse + +## Описание + +`MovieStaffResponse` - это специализированный класс ответа для работы со съемочной командой фильма от Kinopoisk API. Наследует функциональность `SimpleResponse` и предоставляет методы для фильтрации участников съемочного процесса по их профессиональным ролям. + +## Основные возможности + +- Хранение списка сотрудников в неизменяемом виде +- Создание объекта из массива данных API с валидацией класса +- Фильтрация персонала по профессиональным ролям +- Преобразование в массив для сериализации +- Получение различных групп персонала (актеры, режиссеры, сценаристы и др.) + +## Наследование + +```php +NotKinopoisk\Responses\SimpleResponse +└── NotKinopoisk\Responses\MovieStaffResponse +``` + +## Конструктор + +```php +public function __construct(public array $items) +``` + +### Параметры + +- `$items` (array) - Массив сотрудников съемочной команды + +## Статические методы + +### fromArray() + +```php +public static function fromArray(array $data, string $cls): self +``` + +Создает экземпляр `MovieStaffResponse` из массива данных API. + +#### Параметры + +- `$data` (array) - Массив данных от API +- `$cls` (string) - Имя класса для элементов коллекции + +#### Возвращает + +- `MovieStaffResponse` - Новый экземпляр с данными о съемочной команде + +#### Исключения + +- `KpValidationException` - Если данные имеют некорректную структуру + +## Методы экземпляра + +### getActors() + +```php +public function getActors(): array +``` + +Получает список актеров из съемочной команды. + +#### Возвращает + +- `Staff[]` - Массив объектов актеров + +#### Пример использования + +```php +$actors = $staffResponse->getActors(); +foreach ($actors as $actor) { + echo "Актер: " . $actor->nameRu . "\n"; +} +``` + +### getWriters() + +```php +public function getWriters(): array +``` + +Получает список сценаристов из съемочной команды. + +#### Возвращает + +- `Staff[]` - Массив объектов сценаристов + +#### Пример использования + +```php +$writers = $staffResponse->getWriters(); +foreach ($writers as $writer) { + echo "Сценарист: " . $writer->nameRu . "\n"; +} +``` + +### getDirectors() + +```php +public function getDirectors(): array +``` + +Получает список режиссеров из съемочной команды. + +#### Возвращает + +- `Staff[]` - Массив объектов режиссеров + +#### Пример использования + +```php +$directors = $staffResponse->getDirectors(); +foreach ($directors as $director) { + echo "Режиссер: " . $director->nameRu . "\n"; +} +``` + +### getProducers() + +```php +public function getProducers(): array +``` + +Получает список продюсеров из съемочной команды. + +#### Возвращает + +- `Staff[]` - Массив объектов продюсеров + +#### Пример использования + +```php +$producers = $staffResponse->getProducers(); +foreach ($producers as $producer) { + echo "Продюсер: " . $producer->nameRu . "\n"; +} +``` + +### getCompositors() + +```php +public function getCompositors(): array +``` + +Получает список композиторов из съемочной команды. + +#### Возвращает + +- `Staff[]` - Массив объектов композиторов + +#### Пример использования + +```php +$composers = $staffResponse->getCompositors(); +foreach ($composers as $composer) { + echo "Композитор: " . $composer->nameRu . "\n"; +} +``` + +### getEditors() + +```php +public function getEditors(): array +``` + +Получает список монтажеров из съемочной команды. + +#### Возвращает + +- `Staff[]` - Массив объектов монтажеров + +#### Пример использования + +```php +$editors = $staffResponse->getEditors(); +foreach ($editors as $editor) { + echo "Монтажер: " . $editor->nameRu . "\n"; +} +``` + +### getDesigners() + +```php +public function getDesigners(): array +``` + +Получает список художников из съемочной команды. + +#### Возвращает + +- `Staff[]` - Массив объектов художников + +#### Пример использования + +```php +$designers = $staffResponse->getDesigners(); +foreach ($designers as $designer) { + echo "Художник: " . $designer->nameRu . "\n"; +} +``` + +## Профессиональные роли + +Класс поддерживает фильтрацию по следующим профессиональным ролям: + +- **Актеры** - Участники актерского состава +- **Режиссеры** - Режиссеры фильма +- **Сценаристы** - Авторы сценария +- **Продюсеры** - Продюсеры проекта +- **Композиторы** - Авторы музыки +- **Монтажеры** - Редакторы фильма +- **Художники** - Художники-постановщики + +## API Endpoint + +Класс используется для работы с API endpoint: + +- `/api/v1/staff` - Получение информации о съемочной команде + +## Обработка ошибок + +Класс включает обработку ошибок: + +- Валидация структуры данных API +- Проверка существования и совместимости целевого класса +- Обработка некорректных данных персонала + +## Пример полного использования + +```php +use NotKinopoisk\Responses\MovieStaffResponse; +use NotKinopoisk\Models\Staff; + +// Получение данных от API +$apiData = [ + [ + 'staffId' => 1, + 'nameRu' => 'Иван Иванов', + 'professionKey' => 'ACTOR' + ], + [ + 'staffId' => 2, + 'nameRu' => 'Петр Петров', + 'professionKey' => 'DIRECTOR' + ], + [ + 'staffId' => 3, + 'nameRu' => 'Сидор Сидоров', + 'professionKey' => 'WRITER' + ] +]; + +// Создание объекта ответа +$staffResponse = MovieStaffResponse::fromArray($apiData, Staff::class); + +// Получение различных групп персонала +$actors = $staffResponse->getActors(); +$directors = $staffResponse->getDirectors(); +$writers = $staffResponse->getWriters(); +$producers = $staffResponse->getProducers(); +$composers = $staffResponse->getCompositors(); +$editors = $staffResponse->getEditors(); +$designers = $staffResponse->getDesigners(); + +// Вывод информации о персонале +echo "Актеры (" . count($actors) . "):\n"; +foreach ($actors as $actor) { + echo "- {$actor->nameRu}\n"; +} + +echo "\nРежиссеры (" . count($directors) . "):\n"; +foreach ($directors as $director) { + echo "- {$director->nameRu}\n"; +} + +echo "\nСценаристы (" . count($writers) . "):\n"; +foreach ($writers as $writer) { + echo "- {$writer->nameRu}\n"; +} + +// Преобразование в массив +$staffArray = $staffResponse->toArray(); +``` + +## Связанные классы + +- `SimpleResponse` - Базовый класс для простых ответов +- `Staff` - Модель сотрудника съемочной команды +- `ProfessionKey` - Перечисление профессиональных ролей +- `KpValidationException` - Исключение для ошибок валидации diff --git a/docs/dev/notkinopoiskphp/responses/review-response.md b/docs/dev/notkinopoiskphp/responses/review-response.md new file mode 100644 index 0000000..0c6ac1f --- /dev/null +++ b/docs/dev/notkinopoiskphp/responses/review-response.md @@ -0,0 +1,255 @@ +# ReviewResponse + +## Описание + +`ReviewResponse` - это специализированный класс ответа для работы с отзывами на фильмы от Kinopoisk API. Наследует функциональность `PaginatedResponse` и добавляет специфичные поля для статистики отзывов по типам (положительные, отрицательные, нейтральные). + +## Основные возможности + +- Наследование функциональности пагинации от `PaginatedResponse` +- Хранение статистики отзывов по типам +- Создание объекта из данных API с валидацией +- Преобразование в массив для сериализации +- Поддержка всех методов пагинации + +## Наследование + +```php +NotKinopoisk\Responses\PaginatedResponse +└── NotKinopoisk\Responses\ReviewResponse +``` + +## Конструктор + +```php +public function __construct( + int $total, + array $items, + int $currentPage, + int $totalPages, + public int $totalPositiveReviews, + public int $totalNegativeReviews, + public int $totalNeutralReviews +) +``` + +### Параметры + +- `$total` (int) - Общее количество отзывов +- `$items` (array) - Массив отзывов текущей страницы +- `$currentPage` (int) - Номер текущей страницы +- `$totalPages` (int) - Общее количество страниц +- `$totalPositiveReviews` (int) - Общее количество положительных отзывов +- `$totalNegativeReviews` (int) - Общее количество отрицательных отзывов +- `$totalNeutralReviews` (int) - Общее количество нейтральных отзывов + +## Свойства + +### totalPositiveReviews + +```php +public int $totalPositiveReviews +``` + +Общее количество положительных отзывов на фильм. + +### totalNegativeReviews + +```php +public int $totalNegativeReviews +``` + +Общее количество отрицательных отзывов на фильм. + +### totalNeutralReviews + +```php +public int $totalNeutralReviews +``` + +Общее количество нейтральных отзывов на фильм. + +## Статические методы + +### fromArray() + +```php +public static function fromArray(array $data, string $cls): self +``` + +Создает экземпляр `ReviewResponse` из массива данных API. + +#### Параметры + +- `$data` (array) - Массив данных от API, содержащий информацию об отзывах +- `$cls` (string) - Имя класса для элементов коллекции (обычно `Review::class`) + +#### Возвращает + +- `ReviewResponse` - Новый экземпляр с данными об отзывах + +#### Исключения + +- `KpValidationException` - Если данные имеют некорректную структуру + +#### Пример использования + +```php +$apiData = [ + 'total' => 150, + 'items' => [...], + 'current_page' => 1, + 'total_pages' => 5, + 'totalPositiveReviews' => 100, + 'totalNegativeReviews' => 30, + 'totalNeutralReviews' => 20 +]; + +$reviewResponse = ReviewResponse::fromArray($apiData, Review::class); +``` + +## Методы экземпляра + +### toArray() + +```php +public function toArray(): array +``` + +Преобразует объект в массив. + +#### Возвращает + +- `array` - Массив с данными об отзывах и статистикой + +#### Пример использования + +```php +$response = ReviewResponse::fromArray($apiData, Review::class); +$array = $response->toArray(); +``` + +## Наследованные методы + +`ReviewResponse` наследует все методы от `PaginatedResponse`: + +- `getNextPage()` - Получение номера следующей страницы +- `hasNextPage()` - Проверка наличия следующей страницы +- `getPreviousPage()` - Получение номера предыдущей страницы +- `hasPreviousPage()` - Проверка наличия предыдущей страницы +- `getFirstPage()` - Получение номера первой страницы +- `getLastPage()` - Получение номера последней страницы +- `isFirstPage()` - Проверка, является ли текущая страница первой +- `isLastPage()` - Проверка, является ли текущая страница последней +- `getPaginationInfo()` - Получение информации о пагинации + +## Типы отзывов + +Класс поддерживает следующие типы отзывов: + +- **Положительные** - Отзывы с положительной оценкой +- **Отрицательные** - Отзывы с отрицательной оценкой +- **Нейтральные** - Отзывы с нейтральной оценкой + +## Статистика отзывов + +Класс предоставляет доступ к статистике отзывов: + +```php +// Получение статистики +$positiveCount = $reviewResponse->totalPositiveReviews; +$negativeCount = $reviewResponse->totalNegativeReviews; +$neutralCount = $reviewResponse->totalNeutralReviews; + +// Вычисление общего количества +$totalReviews = $positiveCount + $negativeCount + $neutralCount; + +// Вычисление процентов +$positivePercent = ($positiveCount / $totalReviews) * 100; +$negativePercent = ($negativeCount / $totalReviews) * 100; +$neutralPercent = ($neutralCount / $totalReviews) * 100; +``` + +## Обработка ошибок + +Класс включает обработку ошибок: + +- Валидация структуры данных API +- Проверка типизации элементов +- Обработка некорректных данных отзывов +- Валидация параметров пагинации + +## Пример полного использования + +```php +use NotKinopoisk\Responses\ReviewResponse; +use NotKinopoisk\Models\Review; + +// Получение данных от API +$apiData = [ + 'total' => 150, + 'items' => [ + [ + 'reviewId' => 1, + 'reviewType' => 'POSITIVE', + 'reviewText' => 'Отличный фильм!' + ], + [ + 'reviewId' => 2, + 'reviewType' => 'NEGATIVE', + 'reviewText' => 'Не понравилось' + ] + ], + 'current_page' => 1, + 'total_pages' => 5, + 'totalPositiveReviews' => 100, + 'totalNegativeReviews' => 30, + 'totalNeutralReviews' => 20 +]; + +// Создание объекта ответа +$reviewResponse = ReviewResponse::fromArray($apiData, Review::class); + +// Анализ статистики отзывов +$positiveCount = $reviewResponse->totalPositiveReviews; +$negativeCount = $reviewResponse->totalNegativeReviews; +$neutralCount = $reviewResponse->totalNeutralReviews; + +$totalReviews = $positiveCount + $negativeCount + $neutralCount; + +echo "Статистика отзывов:\n"; +echo "- Положительных: {$positiveCount} (" . round(($positiveCount / $totalReviews) * 100, 1) . "%)\n"; +echo "- Отрицательных: {$negativeCount} (" . round(($negativeCount / $totalReviews) * 100, 1) . "%)\n"; +echo "- Нейтральных: {$neutralCount} (" . round(($neutralCount / $totalReviews) * 100, 1) . "%)\n"; +echo "- Всего: {$totalReviews}\n"; + +// Работа с пагинацией +echo "\nПагинация:\n"; +echo "- Текущая страница: {$reviewResponse->currentPage}\n"; +echo "- Всего страниц: {$reviewResponse->totalPages}\n"; +echo "- Отзывов на странице: " . count($reviewResponse->items) . "\n"; + +if ($reviewResponse->hasNextPage()) { + echo "- Следующая страница: {$reviewResponse->getNextPage()}\n"; +} + +if ($reviewResponse->hasPreviousPage()) { + echo "- Предыдущая страница: {$reviewResponse->getPreviousPage()}\n"; +} + +// Обработка отзывов +echo "\nОтзывы на текущей странице:\n"; +foreach ($reviewResponse->items as $review) { + echo "- [{$review->reviewType}] {$review->reviewText}\n"; +} + +// Преобразование в массив +$array = $reviewResponse->toArray(); +``` + +## Связанные классы + +- `PaginatedResponse` - Базовый класс для пагинированных ответов +- `Review` - Модель отзыва на фильм +- `KpValidationException` - Исключение для ошибок валидации +- `DefaultResponse` - Базовый класс для всех ответов API diff --git a/docs/dev/notkinopoiskphp/responses/sequel-prequel-response.md b/docs/dev/notkinopoiskphp/responses/sequel-prequel-response.md new file mode 100644 index 0000000..c5e86e5 --- /dev/null +++ b/docs/dev/notkinopoiskphp/responses/sequel-prequel-response.md @@ -0,0 +1,442 @@ +# SequelPrequelResponse + +## Описание + +`SequelPrequelResponse` - это специализированный класс ответа для работы с сиквелами, приквелами и связанными фильмами от Kinopoisk API. Наследует функциональность `SimpleResponse` и предоставляет расширенные методы для фильтрации и анализа связанных фильмов по типам отношений. + +## Основные возможности + +- Фильтрация фильмов по типу отношения (сиквелы, приквелы, римейки, похожие) +- Объединение и сортировка связанных фильмов +- Статистика по типам отношений +- Группировка фильмов по типам связей +- Проверка наличия фильмов определенного типа +- Получение уникальных типов отношений + +## Наследование + +```php +NotKinopoisk\Responses\SimpleResponse +└── NotKinopoisk\Responses\SequelPrequelResponse +``` + +## Конструктор + +```php +public function __construct(public array $items) +``` + +### Параметры + +- `$items` (array) - Массив связанных фильмов + +## Статические методы + +### fromArray() + +```php +public static function fromArray(array $data, string $cls): self +``` + +Создает экземпляр `SequelPrequelResponse` из массива данных API. + +#### Параметры + +- `$data` (array) - Массив данных от API +- `$cls` (string) - Имя класса для элементов коллекции + +#### Возвращает + +- `SequelPrequelResponse` - Новый экземпляр с данными о связанных фильмах + +#### Исключения + +- `KpValidationException` - Если данные имеют некорректную структуру + +## Методы экземпляра + +### getPrequelsAndSequels() + +```php +public function getPrequelsAndSequels(): array +``` + +Получает объединённый и отсортированный список приквелов и сиквелов. + +#### Возвращает + +- `array` - Отсортированный массив приквелов и сиквелов + +#### Исключения + +- `KpValidationException` - При ошибках во время обработки данных + +#### Пример использования + +```php +$combined = $response->getPrequelsAndSequels(); +foreach ($combined as $film) { + echo "{$film->getDisplayName()} - {$film->relationType->getDescription()}\n"; +} +``` + +### getSequels() + +```php +public function getSequels(): array +``` + +Получает все фильмы-сиквелы. + +#### Возвращает + +- `array` - Массив фильмов-сиквелов + +#### Исключения + +- `KpValidationException` - При некорректной структуре данных + +#### Пример использования + +```php +$sequels = $response->getSequels(); +echo "Найдено сиквелов: " . count($sequels) . "\n"; +foreach ($sequels as $sequel) { + echo "- {$sequel->getDisplayName()}\n"; +} +``` + +### getPrequels() + +```php +public function getPrequels(): array +``` + +Получает все фильмы-приквелы. + +#### Возвращает + +- `array` - Массив фильмов-приквелов + +#### Исключения + +- `KpValidationException` - При некорректной структуре данных + +#### Пример использования + +```php +$prequels = $response->getPrequels(); +echo "Найдено приквелов: " . count($prequels) . "\n"; +foreach ($prequels as $prequel) { + echo "- {$prequel->getDisplayName()}\n"; +} +``` + +### hasFilmsOfType() + +```php +public function hasFilmsOfType(RelationType $type): bool +``` + +Проверяет наличие фильмов указанного типа. + +#### Параметры + +- `$type` (RelationType) - Тип отношения для проверки + +#### Возвращает + +- `bool` - True если фильмы присутствуют, false в противном случае + +#### Пример использования + +```php +if ($response->hasFilmsOfType(RelationType::SEQUEL)) { + echo "У фильма есть сиквелы\n"; +} + +if ($response->hasFilmsOfType(RelationType::PREQUEL)) { + echo "У фильма есть приквелы\n"; +} +``` + +### getByRelationType() + +```php +public function getByRelationType(RelationType $type): array +``` + +Получает все фильмы указанного типа отношения. + +#### Параметры + +- `$type` (RelationType) - Тип отношения между фильмами + +#### Возвращает + +- `array` - Массив фильмов указанного типа + +#### Исключения + +- `KpValidationException` - При некорректных параметрах или данных + +#### Пример использования + +```php +// Получение фильмов по типу +$sequels = $response->getByRelationType(RelationType::SEQUEL); +$prequels = $response->getByRelationType(RelationType::PREQUEL); +$remakes = $response->getByRelationType(RelationType::REMAKE); +$similar = $response->getByRelationType(RelationType::SIMILAR); +``` + +### getRemakes() + +```php +public function getRemakes(): array +``` + +Получает все римейки. + +#### Возвращает + +- `array` - Массив римейков + +#### Исключения + +- `KpValidationException` - При некорректной структуре данных + +#### Пример использования + +```php +$remakes = $response->getRemakes(); +echo "Найдено римейков: " . count($remakes) . "\n"; +foreach ($remakes as $remake) { + echo "- {$remake->getDisplayName()}\n"; +} +``` + +### getSimilar() + +```php +public function getSimilar(): array +``` + +Получает все похожие фильмы. + +#### Возвращает + +- `array` - Массив похожих фильмов + +#### Исключения + +- `KpValidationException` - При некорректной структуре данных + +#### Пример использования + +```php +$similar = $response->getSimilar(); +echo "Найдено похожих фильмов: " . count($similar) . "\n"; +foreach ($similar as $film) { + echo "- {$film->getDisplayName()}\n"; +} +``` + +### getStatistics() + +```php +public function getStatistics(): array +``` + +Получает статистику по всем типам отношений. + +#### Возвращает + +- `array` - Статистика по типам отношений + +#### Пример использования + +```php +$stats = $response->getStatistics(); +echo "Общая статистика:\n"; +echo "- Сиквелов: {$stats['sequels']}\n"; +echo "- Приквелов: {$stats['prequels']}\n"; +echo "- Римейков: {$stats['remakes']}\n"; +echo "- Похожих: {$stats['similar']}\n"; +echo "- Всего: {$stats['total']}\n"; +``` + +### countByType() + +```php +public function countByType(RelationType $type): int +``` + +Возвращает количество фильмов указанного типа. + +#### Параметры + +- `$type` (RelationType) - Тип отношения для подсчёта + +#### Возвращает + +- `int` - Количество фильмов указанного типа + +#### Пример использования + +```php +$sequelCount = $response->countByType(RelationType::SEQUEL); +echo "Количество сиквелов: $sequelCount\n"; + +$prequelCount = $response->countByType(RelationType::PREQUEL); +echo "Количество приквелов: $prequelCount\n"; +``` + +### hasRelatedFilms() + +```php +public function hasRelatedFilms(): bool +``` + +Проверяет, есть ли связанные фильмы. + +#### Возвращает + +- `bool` - True если есть хотя бы один связанный фильм + +#### Пример использования + +```php +if ($response->hasRelatedFilms()) { + echo "У фильма есть связанные фильмы\n"; +} else { + echo "У фильма нет связанных фильмов\n"; +} +``` + +### getAvailableRelationTypes() + +```php +public function getAvailableRelationTypes(): array +``` + +Получает все уникальные типы отношений в текущем наборе данных. + +#### Возвращает + +- `array` - Массив уникальных типов отношений + +#### Пример использования + +```php +$types = $response->getAvailableRelationTypes(); +echo "Доступные типы отношений:\n"; +foreach ($types as $type) { + echo "- {$type->getDescription()}\n"; +} +``` + +### groupByRelationType() + +```php +public function groupByRelationType(): array +``` + +Группирует связанные фильмы по типу отношения. + +#### Возвращает + +- `array>` - Ассоциативный массив, где ключи - строковые значения типов связей, а значения - массивы объектов RelatedFilm + +#### Пример использования + +```php +// Группировка связанных фильмов +$groups = $response->groupByRelationType(); + +// Обработка сгруппированных данных +foreach ($groups as $relationType => $films) { + echo "Тип связи: {$relationType}\n"; + echo "Количество фильмов: " . count($films) . "\n"; + + foreach ($films as $film) { + echo " - {$film->getDisplayName()}\n"; + } + echo "\n"; +} + +// Получение конкретного типа связи +$sequels = $groups['SEQUEL'] ?? []; +$similars = $groups['SIMILAR'] ?? []; + +// Проверка наличия определённого типа +if (isset($groups['PREQUEL'])) { + echo "Найдены приквелы: " . count($groups['PREQUEL']) . " шт.\n"; +} +``` + +## Типы отношений + +Класс поддерживает следующие типы отношений: + +- **SEQUEL** - Сиквелы (продолжения) +- **PREQUEL** - Приквелы (предыстории) +- **REMAKE** - Римейки (новые версии) +- **SIMILAR** - Похожие фильмы +- **UNKNOWN** - Неизвестный тип связи + +## Обработка ошибок + +Класс включает комплексную обработку ошибок: + +- Валидация структуры данных API +- Проверка типизации элементов +- Обработка некорректных типов отношений +- Безопасная фильтрация и группировка + +## Пример полного использования + +```php +use NotKinopoisk\Responses\SequelPrequelResponse; +use NotKinopoisk\Models\RelatedFilm; +use NotKinopoisk\Enums\RelationType; + +// Получение данных от API +$apiData = [ + ['kinopoiskId' => 1, 'relationType' => 'SEQUEL'], + ['kinopoiskId' => 2, 'relationType' => 'PREQUEL'], + ['kinopoiskId' => 3, 'relationType' => 'SIMILAR'] +]; + +// Создание объекта ответа +$response = SequelPrequelResponse::fromArray($apiData, RelatedFilm::class); + +// Анализ связанных фильмов +$sequels = $response->getSequels(); +$prequels = $response->getPrequels(); +$similar = $response->getSimilar(); + +$stats = $response->getStatistics(); +$groups = $response->groupByRelationType(); + +echo "Статистика связанных фильмов:\n"; +foreach ($stats as $type => $count) { + echo "- {$type}: {$count}\n"; +} + +// Проверка наличия определенных типов +if ($response->hasFilmsOfType(RelationType::SEQUEL)) { + echo "Найдены сиквелы: " . count($sequels) . "\n"; +} + +if ($response->hasRelatedFilms()) { + echo "Всего связанных фильмов: " . count($response->items) . "\n"; +} +``` + +## Связанные классы + +- `SimpleResponse` - Базовый класс для простых ответов +- `RelatedFilm` - Модель связанного фильма +- `RelationType` - Перечисление типов отношений +- `KpValidationException` - Исключение для ошибок валидации diff --git a/docs/dev/notkinopoiskphp/responses/simple-response.md b/docs/dev/notkinopoiskphp/responses/simple-response.md new file mode 100644 index 0000000..40993fa --- /dev/null +++ b/docs/dev/notkinopoiskphp/responses/simple-response.md @@ -0,0 +1,205 @@ +# SimpleResponse + +## Описание + +`SimpleResponse` - это базовый класс для простых ответов API, содержащих только массив элементов без дополнительной метаинформации. Реализует интерфейс `ResponseInterface` и предоставляет базовую функциональность для работы с коллекциями объектов. + +## Основные возможности + +- Хранение массива элементов в неизменяемом виде +- Создание объекта из массива данных API с валидацией класса +- Преобразование элементов в указанный тип через статический метод `fromArray` +- Преобразование в массив для сериализации +- Валидация целевого класса для преобразования элементов + +## Наследование + +```php +NotKinopoisk\Interfaces\ResponseInterface +└── NotKinopoisk\Responses\SimpleResponse +``` + +## Конструктор + +```php +public function __construct(public array $items) +``` + +### Параметры + +- `$items` (array) - Массив элементов данных + +## Свойства + +### items + +```php +public array $items +``` + +Массив элементов ответа. Доступен только для чтения. + +## Статические методы + +### fromArray() + +```php +public static function fromArray(array $data, string $cls): object +``` + +Создает экземпляр `SimpleResponse` из массива данных API. + +#### Параметры + +- `$data` (array) - Массив данных от API +- `$cls` (string) - Имя класса для преобразования элементов + +#### Возвращает + +- `object` - Новый экземпляр с преобразованными данными + +#### Исключения + +- `KpValidationException` - Если указанный класс не существует или не имеет метода `fromArray` + +#### Пример использования + +```php +$apiData = [ + ['id' => 1, 'name' => 'Item 1'], + ['id' => 2, 'name' => 'Item 2'] +]; + +$response = SimpleResponse::fromArray($apiData, MyModel::class); +``` + +### checkClass() + +```php +public static function checkClass(string $cls): void +``` + +Валидирует целевой класс для преобразования элементов. + +#### Параметры + +- `$cls` (string) - Полное имя класса + +#### Исключения + +- `KpValidationException` - Если класс не существует или не имеет статического метода `fromArray` + +#### Пример использования + +```php +SimpleResponse::checkClass(MyModel::class); +``` + +## Методы экземпляра + +### toArray() + +```php +public function toArray(): array +``` + +Преобразует объект в массив. + +#### Возвращает + +- `array` - Массив с преобразованными элементами + +#### Пример использования + +```php +$response = SimpleResponse::fromArray($apiData, MyModel::class); +$array = $response->toArray(); +``` + +## Требования к целевым классам + +Для корректной работы с `SimpleResponse` целевые классы должны: + +1. **Существовать** - Класс должен быть доступен в системе +2. **Иметь статический метод `fromArray`** - Метод должен принимать массив и возвращать объект +3. **Иметь метод `toArray`** - Для корректного преобразования в массив + +### Пример корректного класса + +```php +class MyModel +{ + public function __construct( + public int $id, + public string $name + ) {} + + public static function fromArray(array $data): self + { + return new self( + id: $data['id'], + name: $data['name'] + ); + } + + public function toArray(): array + { + return [ + 'id' => $this->id, + 'name' => $this->name + ]; + } +} +``` + +## Обработка ошибок + +Класс включает комплексную обработку ошибок: + +- **Проверка существования класса** - Убеждается, что указанный класс существует +- **Валидация метода `fromArray`** - Проверяет наличие и статичность метода +- **Обработка ошибок типизации** - Безопасное преобразование данных + +## Пример полного использования + +```php +use NotKinopoisk\Responses\SimpleResponse; +use NotKinopoisk\Models\MyModel; + +// Получение данных от API +$apiData = [ + ['id' => 1, 'name' => 'Первый элемент'], + ['id' => 2, 'name' => 'Второй элемент'], + ['id' => 3, 'name' => 'Третий элемент'] +]; + +// Создание объекта ответа +$response = SimpleResponse::fromArray($apiData, MyModel::class); + +// Доступ к элементам +$items = $response->items; +echo "Количество элементов: " . count($items) . "\n"; + +// Обработка элементов +foreach ($items as $item) { + echo "ID: {$item->id}, Имя: {$item->name}\n"; +} + +// Преобразование в массив +$array = $response->toArray(); +echo "Преобразовано в массив: " . json_encode($array, JSON_UNESCAPED_UNICODE) . "\n"; +``` + +## Наследование + +`SimpleResponse` является базовым классом для более специализированных ответов: + +- `MovieStaffResponse` - Для работы со съемочной командой +- `SequelPrequelResponse` - Для работы с сиквелами и приквелами + +## Связанные классы + +- `ResponseInterface` - Интерфейс для всех ответов API +- `KpValidationException` - Исключение для ошибок валидации +- `DefaultResponse` - Расширенный базовый класс с метаинформацией +- `PaginatedResponse` - Класс для пагинированных ответов diff --git a/docs/dev/notkinopoiskphp/services/abstract-service.md b/docs/dev/notkinopoiskphp/services/abstract-service.md new file mode 100644 index 0000000..092837d --- /dev/null +++ b/docs/dev/notkinopoiskphp/services/abstract-service.md @@ -0,0 +1,482 @@ +# AbstractService + +## Описание + +`AbstractService` - это абстрактный базовый класс для сервисов Kinopoisk API. Предоставляет общую функциональность для всех сервисов, работающих с Kinopoisk API, содержащий базовые методы для выполнения запросов и обработки ответов. + +## Основные возможности + +- Общие методы для работы с API +- Обработка ошибок и исключений +- Логирование запросов и ответов +- Базовая валидация данных +- Управление версиями API + +## Наследование + +```php +NotKinopoisk\Services\AbstractService +├── NotKinopoisk\Services\FilmService +├── NotKinopoisk\Services\PersonService +├── NotKinopoisk\Services\MediaService +└── NotKinopoisk\Services\UserService +``` + +## Конструктор + +```php +public function __construct(Client $client, ApiVersion $apiVersion = ApiVersion::V1) +``` + +### Параметры + +- `$client` (Client) - Основной клиент для работы с API +- `$apiVersion` (ApiVersion) - Версия API для использования (по умолчанию 'v1') + +## Свойства + +### client + +```php +protected Client $client +``` + +**Описание:** Основной клиент для работы с API + +**Тип:** `Client` + +**Доступ:** Защищенный + +**Пример:** + +```php +// В наследующем классе +$response = $this->client->get('/api/v1/endpoint'); +``` + +### apiVersion + +```php +protected ApiVersion $apiVersion +``` + +**Описание:** Версия API для использования + +**Тип:** `ApiVersion` + +**Доступ:** Защищенный + +**Пример:** + +```php +// В наследующем классе +$version = $this->apiVersion->value; // 'v1', 'v2.1', 'v2.2' +``` + +## Защищенные методы + +### setApiVersion() + +```php +protected function setApiVersion(ApiVersion $apiVersion): void +``` + +Устанавливает версию API для работы сервиса. + +#### Параметры + +- `$apiVersion` (ApiVersion) - Версия API из перечисления доступных версий + +#### Особенности + +- Защищенный метод для внутреннего использования в классах-наследниках +- Версия определяет структуру запросов и доступные эндпоинты + +#### Пример использования + +```php +use NotKinopoisk\Services\AbstractService; +use NotKinopoisk\Enums\ApiVersion; + +class MyService extends AbstractService +{ + public function useNewApiVersion(): void + { + // Установка версии API v2.2 + $this->setApiVersion(ApiVersion::V22); + + // Установка версии API v2.1 + $this->setApiVersion(ApiVersion::V21); + } +} +``` + +### get() + +```php +protected function get(string $uri, array $query = []): array +``` + +Выполняет GET запрос к API. + +#### Параметры + +- `$uri` (string) - URI запроса относительно базового URL API +- `$query` (array) - Параметры запроса, которые будут добавлены к URI как query string + +#### Возвращает + +- `array` - Декодированный JSON ответ от API + +#### Исключения + +- `ApiException` - При ошибках API или сети +- `InvalidApiKeyException` - При неверном API ключе +- `RateLimitException` - При превышении лимитов запросов +- `ResourceNotFoundException` - При отсутствии ресурса + +#### Пример использования + +```php +use NotKinopoisk\Services\AbstractService; + +class FilmService extends AbstractService +{ + public function getFilm(int $id): array + { + return $this->get("/films/{$id}"); + } + + public function searchFilms(string $keyword, int $page = 1): array + { + return $this->get('/films', [ + 'keyword' => $keyword, + 'page' => $page + ]); + } +} +``` + +### buildUri() + +```php +protected function buildUri(string $endpoint, ?ApiVersion $api_version = null): string +``` + +Строит URI для API запроса указанной версии. + +#### Параметры + +- `$endpoint` (string) - Путь к ресурсу API (без префикса /api/vX.X/) +- `$api_version` (ApiVersion|null) - Версия API для использования; если null, используется $this->apiVersion + +#### Возвращает + +- `string` - Полный URI для запроса к указанной версии API + +#### Особенности + +- Автоматически использует версию API по умолчанию, если версия не указана явно +- Автоматически удаляет ведущий слеш из endpoint + +#### Пример использования + +```php +use NotKinopoisk\Services\AbstractService; +use NotKinopoisk\Enums\ApiVersion; + +class MyService extends AbstractService +{ + public function buildEndpoints(): void + { + // Использование версии по умолчанию + $uri = $this->buildUri('films/301'); + // Результат: '/api/v2.2/films/301' (если $this->apiVersion = ApiVersion::V22) + + // Явное указание версии API + $uri = $this->buildUri('films/301', ApiVersion::V21); + // Результат: '/api/v2.1/films/301' + + // Автоматическое удаление ведущего слеша + $uri = $this->buildUri('/films/301'); + // Результат: '/api/v2.2/films/301' + + // Работа с вложенными ресурсами + $uri = $this->buildUri('films/301/similars'); + // Результат: '/api/v2.2/films/301/similars' + } +} +``` + +## Примеры использования + +### Создание базового сервиса + +```php +use NotKinopoisk\Client; +use NotKinopoisk\Services\AbstractService; +use NotKinopoisk\Enums\ApiVersion; + +// Создание сервиса с версией API по умолчанию +$client = new Client('your-api-key'); +$service = new FilmService($client); + +// Создание сервиса с указанной версией API +$service = new FilmService($client, ApiVersion::V22); +``` + +### Создание собственного сервиса + +```php +use NotKinopoisk\Client; +use NotKinopoisk\Services\AbstractService; +use NotKinopoisk\Enums\ApiVersion; + +class CustomService extends AbstractService +{ + public function getData(): array + { + $response = $this->client->get('/api/v1/endpoint'); + return $response->getData(); + } + + public function getCustomEndpoint(): array + { + $uri = $this->buildUri('custom/endpoint'); + return $this->get($uri); + } + + public function switchToNewApiVersion(): void + { + $this->setApiVersion(ApiVersion::V22); + } +} +``` + +### Работа с разными версиями API + +```php +use NotKinopoisk\Services\AbstractService; +use NotKinopoisk\Enums\ApiVersion; + +class VersionedService extends AbstractService +{ + public function getDataWithVersion(ApiVersion $version): array + { + $uri = $this->buildUri('data', $version); + return $this->get($uri); + } + + public function compareApiVersions(): void + { + // Получение данных с версии v2.1 + $dataV21 = $this->getDataWithVersion(ApiVersion::V21); + + // Получение данных с версии v2.2 + $dataV22 = $this->getDataWithVersion(ApiVersion::V22); + + // Сравнение результатов + $this->compareResults($dataV21, $dataV22); + } +} +``` + +### Обработка ошибок + +```php +use NotKinopoisk\Services\AbstractService; +use NotKinopoisk\Exception\ApiException; +use NotKinopoisk\Exception\RateLimitException; + +class ErrorHandlingService extends AbstractService +{ + public function getDataWithErrorHandling(): array + { + try { + return $this->get('/api/v1/data'); + } catch (RateLimitException $e) { + // Обработка превышения лимитов + $this->handleRateLimit($e); + throw $e; + } catch (ApiException $e) { + // Обработка общих ошибок API + $this->handleApiError($e); + throw $e; + } + } + + private function handleRateLimit(RateLimitException $e): void + { + // Логирование и ожидание + error_log("Rate limit exceeded: " . $e->getMessage()); + sleep(60); // Ждем минуту + } + + private function handleApiError(ApiException $e): void + { + // Логирование ошибки + error_log("API Error: " . $e->getMessage()); + } +} +``` + +### Логирование запросов + +```php +use NotKinopoisk\Services\AbstractService; + +class LoggingService extends AbstractService +{ + public function getWithLogging(string $uri, array $query = []): array + { + $startTime = microtime(true); + + try { + $response = $this->get($uri, $query); + + $duration = microtime(true) - $startTime; + $this->logSuccess($uri, $query, $duration); + + return $response; + } catch (\Exception $e) { + $duration = microtime(true) - $startTime; + $this->logError($uri, $query, $duration, $e); + throw $e; + } + } + + private function logSuccess(string $uri, array $query, float $duration): void + { + error_log(sprintf( + "API Success: %s (%.3fs) - Query: %s", + $uri, + $duration, + json_encode($query) + )); + } + + private function logError(string $uri, array $query, float $duration, \Exception $e): void + { + error_log(sprintf( + "API Error: %s (%.3fs) - Query: %s - Error: %s", + $uri, + $duration, + json_encode($query), + $e->getMessage() + )); + } +} +``` + +### Кэширование запросов + +```php +use NotKinopoisk\Services\AbstractService; + +class CachingService extends AbstractService +{ + private array $cache = []; + private int $cacheTtl = 300; // 5 минут + + public function getWithCache(string $uri, array $query = []): array + { + $cacheKey = $this->buildCacheKey($uri, $query); + + // Проверяем кэш + if (isset($this->cache[$cacheKey]) && $this->isCacheValid($cacheKey)) { + return $this->cache[$cacheKey]['data']; + } + + // Выполняем запрос + $data = $this->get($uri, $query); + + // Сохраняем в кэш + $this->cache[$cacheKey] = [ + 'data' => $data, + 'timestamp' => time() + ]; + + return $data; + } + + private function buildCacheKey(string $uri, array $query): string + { + return md5($uri . json_encode($query)); + } + + private function isCacheValid(string $cacheKey): bool + { + $cached = $this->cache[$cacheKey]; + return (time() - $cached['timestamp']) < $this->cacheTtl; + } +} +``` + +### Валидация параметров + +```php +use NotKinopoisk\Services\AbstractService; + +class ValidatingService extends AbstractService +{ + public function getWithValidation(string $uri, array $query = []): array + { + $this->validateUri($uri); + $this->validateQuery($query); + + return $this->get($uri, $query); + } + + private function validateUri(string $uri): void + { + if (empty($uri)) { + throw new \InvalidArgumentException('URI cannot be empty'); + } + + if (!preg_match('/^[a-zA-Z0-9\/\-_]+$/', $uri)) { + throw new \InvalidArgumentException('Invalid URI format'); + } + } + + private function validateQuery(array $query): void + { + foreach ($query as $key => $value) { + if (empty($key)) { + throw new \InvalidArgumentException('Query key cannot be empty'); + } + + if (!is_string($key)) { + throw new \InvalidArgumentException('Query key must be string'); + } + } + } +} +``` + +## Связанные классы + +- `Client` - Основной клиент для работы с API +- `ApiVersion` - Перечисление версий API +- `FilmService` - Сервис для работы с фильмами +- `PersonService` - Сервис для работы с персонами +- `MediaService` - Сервис для работы с медиа +- `UserService` - Сервис для работы с пользователями + +## Исключения + +- `ApiException` - Общие ошибки API +- `InvalidApiKeyException` - Неверный API ключ +- `RateLimitException` - Превышение лимитов запросов +- `ResourceNotFoundException` - Ресурс не найден + +## API Endpoints + +AbstractService используется как базовый класс для всех сервисов, работающих с Kinopoisk API: + +- `/api/v1/*` - API версии 1.0 +- `/api/v2.1/*` - API версии 2.1 +- `/api/v2.2/*` - API версии 2.2 + +--- + +**📚 Навигация:** [Главная](../index.md) → [Сервисы](index.md) → AbstractService diff --git a/docs/dev/notkinopoiskphp/services/index.md b/docs/dev/notkinopoiskphp/services/index.md index e1969b7..315eea2 100644 --- a/docs/dev/notkinopoiskphp/services/index.md +++ b/docs/dev/notkinopoiskphp/services/index.md @@ -4,13 +4,13 @@ --- -**📚 Навигация:** [Главная](../index.md) → Сервисы +**📚 Навигация:** [Главная](./index.md) → Сервисы --- ## 📋 Список сервисов -### 🎬 [FilmService](film-service.md) +### 🎬 [FilmService](./film-service.md) Сервис для работы с фильмами, сериалами и связанным контентом. @@ -32,7 +32,7 @@ - [Image](../models/image.md) - [Video](../models/video.md) -### 👥 [PersonService](person-service.md) +### 👥 [PersonService](./person-service.md) Сервис для работы с персонами (актеры, режиссеры, сценаристы). @@ -49,7 +49,7 @@ - [PersonFilm](../models/person-film.md) - [PersonSpouse](../models/person-spouse.md) -### 🎥 [MediaService](media-service.md) +### 🎥 [MediaService](./media-service.md) Сервис для работы с медиа контентом (изображения, видео). @@ -66,7 +66,7 @@ - [ImageType](../enums/image-type.md) - [VideoSite](../enums/video-site.md) -### 👤 [UserService](user-service.md) +### 👤 [UserService](./user-service.md) Сервис для работы с пользовательскими данными. @@ -217,4 +217,4 @@ try { --- -**📚 Навигация:** [Главная](../index.md) → Сервисы +**📚 Навигация:** [Главная](./index.md) → Сервисы diff --git a/docs/extras/css/search-enhancement.css b/docs/extras/css/search-enhancement.css new file mode 100644 index 0000000..eb70428 --- /dev/null +++ b/docs/extras/css/search-enhancement.css @@ -0,0 +1,379 @@ +/* Enhanced Search Styles */ + +/* Search suggestions container */ +.search-suggestions { + position: absolute; + top: 100%; + left: 0; + right: 0; + background: var(--md-default-bg-color); + border: 1px solid var(--md-default-fg-color--lightest); + border-top: none; + border-radius: 0 0 4px 4px; + max-height: 300px; + overflow-y: auto; + z-index: 1000; + display: none; + box-shadow: 0 4px 8px rgba(0,0,0,0.1); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); +} + +/* Individual suggestion items */ +.search-suggestion { + padding: 8px 12px; + cursor: pointer; + border-bottom: 1px solid var(--md-default-fg-color--lightest); + transition: all 0.2s ease; + position: relative; + overflow: hidden; +} + +.search-suggestion:last-child { + border-bottom: none; +} + +.search-suggestion:hover, +.search-suggestion-active { + background: var(--md-primary-fg-color--light); + color: var(--md-primary-bg-color); + transform: translateX(4px); +} + +.search-suggestion:hover::before, +.search-suggestion-active::before { + content: ''; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 3px; + background: var(--md-primary-fg-color); +} + +/* Search input enhancements */ +.md-search__input { + transition: all 0.3s ease; +} + +.md-search__input:focus { + box-shadow: 0 0 0 2px var(--md-primary-fg-color--light); + border-color: var(--md-primary-fg-color); +} + +/* Search results enhancements */ +.md-search-result__item { + transition: all 0.2s ease; +} + +.md-search-result__item:hover { + background: var(--md-primary-fg-color--light); + transform: translateX(4px); +} + +/* Search highlighting */ +.search-highlight { + background: var(--md-primary-fg-color--light); + color: var(--md-primary-bg-color); + padding: 2px 4px; + border-radius: 3px; + font-weight: bold; + text-shadow: none; +} + +/* Search result title highlighting */ +.md-search-result__title .search-highlight { + background: var(--md-accent-fg-color); + color: var(--md-accent-bg-color); +} + +/* Search result text highlighting */ +.md-search-result__teaser .search-highlight { + background: var(--md-primary-fg-color--light); + color: var(--md-primary-bg-color); + opacity: 0.8; +} + +/* Search loading indicator */ +.search-loading { + display: inline-block; + width: 16px; + height: 16px; + border: 2px solid var(--md-default-fg-color--lightest); + border-radius: 50%; + border-top-color: var(--md-primary-fg-color); + animation: spin 1s ease-in-out infinite; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +/* Search suggestions scrollbar */ +.search-suggestions::-webkit-scrollbar { + width: 6px; +} + +.search-suggestions::-webkit-scrollbar-track { + background: var(--md-default-fg-color--lightest); + border-radius: 3px; +} + +.search-suggestions::-webkit-scrollbar-thumb { + background: var(--md-default-fg-color--light); + border-radius: 3px; +} + +.search-suggestions::-webkit-scrollbar-thumb:hover { + background: var(--md-default-fg-color); +} + +/* Search result categories */ +.search-category { + font-size: 0.8em; + color: var(--md-default-fg-color--light); + text-transform: uppercase; + letter-spacing: 0.5px; + margin-bottom: 4px; +} + +.search-category::before { + content: '📁 '; + margin-right: 4px; +} + +/* Search result relevance indicator */ +.search-relevance { + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; + margin-right: 8px; +} + +.search-relevance.high { + background: #4caf50; +} + +.search-relevance.medium { + background: #ff9800; +} + +.search-relevance.low { + background: #f44336; +} + +/* Search filters */ +.search-filters { + display: flex; + gap: 8px; + margin-bottom: 12px; + flex-wrap: wrap; +} + +.search-filter { + padding: 4px 8px; + border: 1px solid var(--md-default-fg-color--lightest); + border-radius: 12px; + font-size: 0.8em; + cursor: pointer; + transition: all 0.2s ease; + background: var(--md-default-bg-color); + color: var(--md-default-fg-color); +} + +.search-filter:hover, +.search-filter.active { + background: var(--md-primary-fg-color); + color: var(--md-primary-bg-color); + border-color: var(--md-primary-fg-color); +} + +/* Search result metadata */ +.search-meta { + font-size: 0.8em; + color: var(--md-default-fg-color--light); + margin-top: 4px; +} + +.search-meta span { + margin-right: 12px; +} + +.search-meta .search-date::before { + content: '📅 '; +} + +.search-meta .search-category::before { + content: '📁 '; +} + +.search-meta .search-author::before { + content: '👤 '; +} + +/* Search suggestions with icons */ +.search-suggestion[data-type="api"]::before { + content: '🔌 '; + margin-right: 8px; +} + +.search-suggestion[data-type="install"]::before { + content: '⚙️ '; + margin-right: 8px; +} + +.search-suggestion[data-type="config"]::before { + content: '🔧 '; + margin-right: 8px; +} + +.search-suggestion[data-type="error"]::before { + content: '⚠️ '; + margin-right: 8px; +} + +.search-suggestion[data-type="test"]::before { + content: '🧪 '; + margin-right: 8px; +} + +.search-suggestion[data-type="deploy"]::before { + content: '🚀 '; + margin-right: 8px; +} + +.search-suggestion[data-type="security"]::before { + content: '🔒 '; + margin-right: 8px; +} + +.search-suggestion[data-type="performance"]::before { + content: '⚡ '; + margin-right: 8px; +} + +.search-suggestion[data-type="database"]::before { + content: '🗄️ '; + margin-right: 8px; +} + +.search-suggestion[data-type="documentation"]::before { + content: '📚 '; + margin-right: 8px; +} + +.search-suggestion[data-type="tutorial"]::before { + content: '📖 '; + margin-right: 8px; +} + +.search-suggestion[data-type="faq"]::before { + content: '❓ '; + margin-right: 8px; +} + +.search-suggestion[data-type="changelog"]::before { + content: '📋 '; + margin-right: 8px; +} + +/* Search suggestions animation */ +.search-suggestions { + animation: slideDown 0.3s ease-out; +} + +@keyframes slideDown { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Search input with suggestions active */ +.md-search__input:focus + .search-suggestions { + display: block; +} + +/* Responsive design for search */ +@media (max-width: 768px) { + .search-suggestions { + position: fixed; + top: 60px; + left: 0; + right: 0; + max-height: 50vh; + border-radius: 0; + z-index: 1001; + } + + .search-suggestion { + padding: 12px 16px; + font-size: 16px; /* Prevent zoom on iOS */ + } + + .search-filters { + justify-content: center; + } +} + +/* Dark mode enhancements */ +[data-md-color-scheme="slate"] .search-suggestions { + background: var(--md-default-bg-color); + border-color: var(--md-default-fg-color--lightest); + box-shadow: 0 4px 8px rgba(0,0,0,0.3); +} + +[data-md-color-scheme="slate"] .search-suggestion:hover, +[data-md-color-scheme="slate"] .search-suggestion-active { + background: var(--md-primary-fg-color--light); + color: var(--md-primary-bg-color); +} + +/* Search accessibility improvements */ +.search-suggestion:focus { + outline: 2px solid var(--md-primary-fg-color); + outline-offset: -2px; +} + +.search-suggestion[aria-selected="true"] { + background: var(--md-primary-fg-color--light); + color: var(--md-primary-bg-color); +} + +/* Search result count */ +.search-result-count { + font-size: 0.8em; + color: var(--md-default-fg-color--light); + margin-top: 8px; + text-align: center; + padding: 8px; + border-top: 1px solid var(--md-default-fg-color--lightest); +} + +/* Search suggestions empty state */ +.search-suggestions:empty::after { + content: 'Начните вводить текст для поиска...'; + display: block; + padding: 16px; + text-align: center; + color: var(--md-default-fg-color--light); + font-style: italic; +} + +/* Search suggestions loading state */ +.search-suggestions.loading::after { + content: ''; + display: block; + width: 20px; + height: 20px; + border: 2px solid var(--md-default-fg-color--lightest); + border-radius: 50%; + border-top-color: var(--md-primary-fg-color); + animation: spin 1s ease-in-out infinite; + margin: 16px auto; +} \ No newline at end of file diff --git a/docs/extras/js/search-enhancement.js b/docs/extras/js/search-enhancement.js new file mode 100644 index 0000000..b8a74c4 --- /dev/null +++ b/docs/extras/js/search-enhancement.js @@ -0,0 +1,450 @@ +// Enhanced Search with Autocomplete +(function() { + 'use strict'; + + // Search enhancement configuration + const SEARCH_CONFIG = { + minLength: 2, + maxSuggestions: 10, + debounceDelay: 300, + highlightClass: 'search-highlight', + suggestionClass: 'search-suggestion', + activeClass: 'search-suggestion-active' + }; + + // Динамические синонимы (загружаются из метаданных) + let SEARCH_SYNONYMS = { + 'api': ['api', 'interface', 'endpoint', 'rest', 'интерфейс', 'апи', 'эндпоинт', 'сервис'], + 'install': ['install', 'setup', 'installation', 'configure', 'установка', 'установить', 'настройка', 'развертывание'], + 'config': ['config', 'configuration', 'settings', 'options', 'настройки', 'конфигурация', 'параметры'], + 'error': ['error', 'exception', 'bug', 'issue', 'problem', 'ошибка', 'исключение', 'баг', 'проблема'], + 'test': ['test', 'testing', 'unit', 'integration', 'тестирование', 'тест', 'проверка'], + 'deploy': ['deploy', 'deployment', 'publish', 'release', 'развертывание', 'публикация', 'релиз'], + 'security': ['security', 'secure', 'vulnerability', 'safety', 'безопасность', 'защита', 'уязвимость'], + 'performance': ['performance', 'speed', 'optimization', 'fast', 'производительность', 'скорость', 'оптимизация'], + 'database': ['database', 'db', 'sql', 'mysql', 'postgresql', 'база данных', 'бд', 'хранилище'], + 'authentication': ['auth', 'authentication', 'login', 'user', 'аутентификация', 'вход', 'логин', 'пользователь'], + 'authorization': ['authorization', 'permissions', 'access', 'roles', 'права', 'доступ', 'роли'], + 'logging': ['log', 'logging', 'debug', 'trace', 'логирование', 'лог', 'отладка'], + 'monitoring': ['monitor', 'monitoring', 'metrics', 'analytics', 'мониторинг', 'метрики', 'аналитика'], + 'backup': ['backup', 'restore', 'recovery', 'snapshot', 'резервная копия', 'бэкап', 'восстановление'], + 'migration': ['migration', 'upgrade', 'update', 'version', 'миграция', 'обновление', 'версия'], + 'documentation': ['docs', 'documentation', 'guide', 'manual', 'документация', 'руководство', 'справочник'], + 'tutorial': ['tutorial', 'guide', 'example', 'howto', 'туториал', 'руководство', 'пример'], + 'faq': ['faq', 'question', 'answer', 'help', 'вопросы', 'ответы', 'помощь'], + 'changelog': ['changelog', 'changes', 'updates', 'version', 'изменения', 'обновления', 'история'], + 'license': ['license', 'licensing', 'legal', 'terms', 'лицензия', 'лицензирование', 'правовые'], + // Дополнительные русские термины + 'php': ['php', 'пхп', 'пхп'], + 'mysql': ['mysql', 'мускул', 'база данных'], + 'composer': ['composer', 'композер', 'менеджер пакетов'], + 'git': ['git', 'гит', 'система контроля версий'], + 'docker': ['docker', 'докер', 'контейнеризация'], + 'nginx': ['nginx', 'энжинкс', 'веб сервер'], + 'apache': ['apache', 'апач', 'веб сервер'], + 'linux': ['linux', 'линукс', 'операционная система'], + 'windows': ['windows', 'виндовс', 'операционная система'], + 'javascript': ['javascript', 'джаваскрипт', 'js'], + 'python': ['python', 'питон', 'язык программирования'], + 'nodejs': ['nodejs', 'ноде', 'node.js'], + 'react': ['react', 'реакт', 'фреймворк'], + 'vue': ['vue', 'vue.js', 'фреймворк'], + 'angular': ['angular', 'ангуляр', 'фреймворк'], + 'laravel': ['laravel', 'лаravel', 'фреймворк'], + 'symfony': ['symfony', 'симфони', 'фреймворк'], + 'wordpress': ['wordpress', 'вордпресс', 'cms'], + 'drupal': ['drupal', 'друпал', 'cms'], + 'joomla': ['joomla', 'джумла', 'cms'] + }; + + // Динамические популярные запросы (загружаются из метаданных) + let POPULAR_SEARCHES = [ + 'installation guide', + 'api documentation', + 'configuration', + 'troubleshooting', + 'examples', + 'changelog', + 'faq', + 'security', + 'performance', + 'deployment', + // Русские популярные запросы + 'руководство по установке', + 'документация api', + 'настройка', + 'решение проблем', + 'примеры', + 'история изменений', + 'частые вопросы', + 'безопасность', + 'производительность', + 'развертывание', + 'установка php', + 'настройка mysql', + 'composer install', + 'git команды', + 'docker контейнер', + 'nginx конфигурация', + 'apache настройка', + 'linux команды', + 'windows установка', + 'javascript примеры', + 'python скрипт', + 'nodejs сервер', + 'react компонент', + 'vue приложение', + 'angular проект', + 'laravel миграция', + 'symfony бандл', + 'wordpress плагин', + 'drupal модуль', + 'joomla расширение' + ]; + + // Initialize enhanced search + function initEnhancedSearch() { + const searchInput = document.querySelector('input[type="search"]'); + if (!searchInput) return; + + // Create suggestion container + const suggestionContainer = document.createElement('div'); + suggestionContainer.className = 'search-suggestions'; + suggestionContainer.style.cssText = ` + position: absolute; + top: 100%; + left: 0; + right: 0; + background: var(--md-default-bg-color); + border: 1px solid var(--md-default-fg-color--lightest); + border-top: none; + border-radius: 0 0 4px 4px; + max-height: 300px; + overflow-y: auto; + z-index: 1000; + display: none; + box-shadow: 0 4px 8px rgba(0,0,0,0.1); + `; + + // Insert suggestion container + const searchContainer = searchInput.closest('.md-search'); + if (searchContainer) { + searchContainer.style.position = 'relative'; + searchContainer.appendChild(suggestionContainer); + } + + // Add event listeners + let debounceTimer; + searchInput.addEventListener('input', function(e) { + clearTimeout(debounceTimer); + debounceTimer = setTimeout(() => { + handleSearchInput(e.target.value); + }, SEARCH_CONFIG.debounceDelay); + }); + + searchInput.addEventListener('keydown', function(e) { + handleSearchKeydown(e); + }); + + searchInput.addEventListener('focus', function() { + if (this.value.length >= SEARCH_CONFIG.minLength) { + showSuggestions(this.value); + } + }); + + // Hide suggestions when clicking outside + document.addEventListener('click', function(e) { + if (!searchContainer.contains(e.target)) { + hideSuggestions(); + } + }); + } + + // Handle search input + function handleSearchInput(query) { + if (query.length < SEARCH_CONFIG.minLength) { + hideSuggestions(); + return; + } + + showSuggestions(query); + } + + // Show search suggestions + function showSuggestions(query) { + const suggestionContainer = document.querySelector('.search-suggestions'); + if (!suggestionContainer) return; + + const suggestions = generateSuggestions(query); + + if (suggestions.length === 0) { + hideSuggestions(); + return; + } + + suggestionContainer.innerHTML = ''; + + suggestions.forEach((suggestion, index) => { + const suggestionElement = createSuggestionElement(suggestion, index === 0); + suggestionContainer.appendChild(suggestionElement); + }); + + suggestionContainer.style.display = 'block'; + } + + // Hide search suggestions + function hideSuggestions() { + const suggestionContainer = document.querySelector('.search-suggestions'); + if (suggestionContainer) { + suggestionContainer.style.display = 'none'; + } + } + + // Generate search suggestions + function generateSuggestions(query) { + const suggestions = []; + const queryLower = query.toLowerCase(); + + // Add exact matches first + const exactMatches = POPULAR_SEARCHES.filter(term => + term.toLowerCase().includes(queryLower) + ); + suggestions.push(...exactMatches); + + // Add synonym matches + Object.entries(SEARCH_SYNONYMS).forEach(([key, synonyms]) => { + if (key.includes(queryLower) || synonyms.some(syn => syn.includes(queryLower))) { + suggestions.push(key); + } + }); + + // Add navigation-based suggestions + const navSuggestions = generateNavigationSuggestions(queryLower); + suggestions.push(...navSuggestions); + + // Remove duplicates and limit results + return [...new Set(suggestions)].slice(0, SEARCH_CONFIG.maxSuggestions); + } + + // Generate suggestions based on navigation structure + function generateNavigationSuggestions(query) { + const suggestions = []; + + // Get all navigation links + const navLinks = document.querySelectorAll('.md-nav__link, .md-header__title'); + + navLinks.forEach(link => { + const text = link.textContent.toLowerCase(); + if (text.includes(query)) { + suggestions.push(link.textContent.trim()); + } + }); + + return suggestions; + } + + // Create suggestion element + function createSuggestionElement(suggestion, isActive = false) { + const element = document.createElement('div'); + element.className = `search-suggestion ${isActive ? SEARCH_CONFIG.activeClass : ''}`; + element.textContent = suggestion; + element.style.cssText = ` + padding: 8px 12px; + cursor: pointer; + border-bottom: 1px solid var(--md-default-fg-color--lightest); + transition: background-color 0.2s; + `; + + element.addEventListener('mouseenter', function() { + document.querySelectorAll('.search-suggestion').forEach(el => + el.classList.remove(SEARCH_CONFIG.activeClass) + ); + this.classList.add(SEARCH_CONFIG.activeClass); + }); + + element.addEventListener('click', function() { + const searchInput = document.querySelector('input[type="search"]'); + if (searchInput) { + searchInput.value = suggestion; + searchInput.dispatchEvent(new Event('input', { bubbles: true })); + hideSuggestions(); + } + }); + + return element; + } + + // Handle keyboard navigation + function handleSearchKeydown(e) { + const suggestions = document.querySelectorAll('.search-suggestion'); + const activeSuggestion = document.querySelector(`.${SEARCH_CONFIG.activeClass}`); + + if (suggestions.length === 0) return; + + switch (e.key) { + case 'ArrowDown': + e.preventDefault(); + navigateSuggestions(suggestions, activeSuggestion, 1); + break; + case 'ArrowUp': + e.preventDefault(); + navigateSuggestions(suggestions, activeSuggestion, -1); + break; + case 'Enter': + if (activeSuggestion) { + e.preventDefault(); + activeSuggestion.click(); + } + break; + case 'Escape': + hideSuggestions(); + break; + } + } + + // Navigate through suggestions + function navigateSuggestions(suggestions, activeSuggestion, direction) { + const currentIndex = activeSuggestion ? + Array.from(suggestions).indexOf(activeSuggestion) : -1; + + let newIndex = currentIndex + direction; + + if (newIndex < 0) { + newIndex = suggestions.length - 1; + } else if (newIndex >= suggestions.length) { + newIndex = 0; + } + + suggestions.forEach(el => el.classList.remove(SEARCH_CONFIG.activeClass)); + suggestions[newIndex].classList.add(SEARCH_CONFIG.activeClass); + } + + // Enhanced search highlighting + function enhanceSearchHighlighting() { + const searchInput = document.querySelector('input[type="search"]'); + if (!searchInput) return; + + // Override default search behavior + const originalSearch = window.md_search; + if (originalSearch) { + window.md_search = function(query) { + const results = originalSearch(query); + + // Add custom highlighting + if (results && results.length > 0) { + results.forEach(result => { + if (result.text) { + result.text = highlightSearchTerms(result.text, query); + } + }); + } + + return results; + }; + } + } + + // Highlight search terms in text + function highlightSearchTerms(text, query) { + if (!query || query.length < 2) return text; + + const terms = query.split(/\s+/); + let highlightedText = text; + + terms.forEach(term => { + const regex = new RegExp(`(${term})`, 'gi'); + highlightedText = highlightedText.replace(regex, + `$1` + ); + }); + + return highlightedText; + } + + // Add search analytics + function addSearchAnalytics() { + const searchInput = document.querySelector('input[type="search"]'); + if (!searchInput) return; + + searchInput.addEventListener('input', function(e) { + if (e.target.value.length >= 3) { + // Track search queries (anonymized) + if (typeof gtag !== 'undefined') { + gtag('event', 'search', { + 'search_term': e.target.value + }); + } + } + }); + } + + // Функция для загрузки метаданных и обновления синонимов + function loadSearchMetadata() { + if (window.searchMetadataLoader) { + window.searchMetadataLoader.load().then(() => { + // Обновляем синонимы из метаданных + if (window.searchMetadataLoader.metadata && window.searchMetadataLoader.metadata.search_synonyms) { + SEARCH_SYNONYMS = { ...SEARCH_SYNONYMS, ...window.searchMetadataLoader.metadata.search_synonyms }; + console.log('Search synonyms updated from metadata'); + } + + // Обновляем популярные запросы из метаданных + if (window.searchMetadataLoader.metadata && window.searchMetadataLoader.metadata.popular_searches) { + POPULAR_SEARCHES = [...POPULAR_SEARCHES, ...window.searchMetadataLoader.metadata.popular_searches]; + console.log('Popular searches updated from metadata'); + } + + // Инициализируем улучшенный поиск после загрузки метаданных + initEnhancedSearch(); + enhanceSearchHighlighting(); + addSearchAnalytics(); + }).catch(error => { + console.warn('Failed to load search metadata, using defaults:', error); + initEnhancedSearch(); + enhanceSearchHighlighting(); + addSearchAnalytics(); + }); + } else { + // Если загрузчик метаданных недоступен, используем стандартную инициализацию + initEnhancedSearch(); + enhanceSearchHighlighting(); + addSearchAnalytics(); + } + } + + // Initialize when DOM is ready + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', function() { + loadSearchMetadata(); + }); + } else { + loadSearchMetadata(); + } + + // Слушаем событие загрузки метаданных + document.addEventListener('searchMetadataLoaded', function(event) { + console.log('Search metadata loaded, updating search configuration'); + loadSearchMetadata(); + }); + + // Re-initialize after navigation (for SPA behavior) + document.addEventListener('DOMContentLoaded', function() { + const observer = new MutationObserver(function(mutations) { + mutations.forEach(function(mutation) { + if (mutation.type === 'childList') { + const searchInput = document.querySelector('input[type="search"]'); + if (searchInput && !searchInput.hasAttribute('data-enhanced')) { + searchInput.setAttribute('data-enhanced', 'true'); + initEnhancedSearch(); + } + } + }); + }); + + observer.observe(document.body, { + childList: true, + subtree: true + }); + }); + +})(); \ No newline at end of file diff --git a/docs/extras/js/search-metadata-loader.js b/docs/extras/js/search-metadata-loader.js new file mode 100644 index 0000000..d90a6cc --- /dev/null +++ b/docs/extras/js/search-metadata-loader.js @@ -0,0 +1,316 @@ +// Search Metadata Loader +// Загружает метаданные поиска из встроенных данных + +(function() { + 'use strict'; + + // Встроенные метаданные поиска (из search-metadata.yml) + const EMBEDDED_SEARCH_METADATA = { + search_synonyms: { + api: ['interface', 'endpoint', 'rest', 'webhook', 'service', 'интерфейс', 'апи', 'эндпоинт', 'сервис', 'вебхук'], + install: ['setup', 'installation', 'configure', 'deployment', 'setup guide', 'установка', 'установить', 'настройка', 'развертывание', 'инсталляция', 'конфигурация'], + config: ['configuration', 'settings', 'options', 'parameters', 'preferences', 'настройки', 'конфигурация', 'параметры', 'опции', 'предпочтения'], + error: ['exception', 'bug', 'issue', 'problem', 'troubleshooting', 'debug', 'ошибка', 'исключение', 'баг', 'проблема', 'отладка', 'исправление', 'решение проблем'], + test: ['testing', 'unit test', 'integration test', 'automated test', 'test suite', 'тестирование', 'тест', 'проверка', 'автоматический тест', 'модульный тест', 'интеграционный тест'], + deploy: ['deployment', 'publish', 'release', 'production', 'staging', 'развертывание', 'публикация', 'релиз', 'продакшн', 'продакшен', 'стейджинг'], + security: ['secure', 'vulnerability', 'safety', 'authentication', 'authorization', 'безопасность', 'защита', 'уязвимость', 'аутентификация', 'авторизация', 'вход', 'логин'], + performance: ['speed', 'optimization', 'fast', 'efficient', 'caching', 'производительность', 'скорость', 'оптимизация', 'быстрый'], + php: ['пхп', 'пхп-скрипт', 'пхп-код', 'пхп-файл', 'пхп-приложение'], + mysql: ['мускул', 'мускул-база', 'мускул-сервер', 'мускул-данные'], + composer: ['композер', 'композер-пакет', 'композер-зависимости', 'композер-установка'], + docker: ['докер', 'докер-контейнер', 'докер-образ', 'докер-файл'], + nginx: ['энжинкс', 'энжинкс-сервер', 'энжинкс-конфигурация', 'энжинкс-настройка'], + git: ['гит', 'гит-репозиторий', 'гит-коммит', 'гит-ветка', 'гит-пулл'], + database: ['база данных', 'бд', 'датабаза', 'база', 'данные'], + server: ['сервер', 'сервер-настройка', 'сервер-конфигурация', 'хостинг'], + cache: ['кэш', 'кэширование', 'кэш-файлы', 'кэш-память'], + log: ['лог', 'логи', 'журнал', 'журнал-событий', 'логирование'], + backup: ['бэкап', 'резервная копия', 'бэкап-файлы', 'восстановление'], + ssl: ['эс-эс-эл', 'сертификат', 'безопасное соединение', 'https'], + cron: ['крон', 'крон-задача', 'планировщик', 'автоматизация'], + api: ['апи', 'интерфейс', 'эндпоинт', 'сервис', 'вебхук'] + }, + popular_searches: [ + 'руководство по установке', + 'документация api', + 'установка php', + 'docker контейнер', + 'настройка базы данных', + 'конфигурация сервера', + 'обработка ошибок', + 'тестирование кода', + 'развертывание приложения', + 'безопасность приложения', + 'оптимизация производительности', + 'работа с базой данных', + 'настройка nginx', + 'git workflow', + 'composer зависимости' + ], + search_categories: { + 'Начало работы': ['install', 'setup', 'configuration', 'установка', 'настройка', 'конфигурация'], + 'API Справочник': ['api', 'endpoint', 'service', 'апи', 'эндпоинт', 'сервис'], + 'Разработка': ['development', 'coding', 'programming', 'разработка', 'программирование', 'код'], + 'Развертывание': ['deploy', 'production', 'staging', 'развертывание', 'продакшн', 'стейджинг'], + 'Безопасность': ['security', 'authentication', 'authorization', 'безопасность', 'аутентификация', 'авторизация'], + 'Производительность': ['performance', 'optimization', 'caching', 'производительность', 'оптимизация', 'кэширование'], + 'Отладка': ['debug', 'error', 'troubleshooting', 'отладка', 'ошибка', 'решение проблем'] + }, + search_weights: { + title: 10, + headings: 5, + content: 1, + tags: 3, + categories: 2 + }, + search_filters: [], + search_suggestions: { + min_length: 2, + max_suggestions: 10, + debounce_delay: 300 + }, + search_results: { + max_results: 20, + highlight: "auto" + } + }; + + // Конфигурация загрузки метаданных + const SEARCH_METADATA_CONFIG = { + cacheKey: 'search_metadata_cache', + cacheExpiry: 24 * 60 * 60 * 1000, // 24 часа + fallbackData: { + search_synonyms: {}, + popular_searches: [], + search_categories: {}, + search_weights: {}, + search_filters: [], + search_suggestions: {}, + search_results: {} + } + }; + + // Класс для загрузки и управления метаданными поиска + class SearchMetadataLoader { + constructor() { + this.metadata = null; + this.isLoaded = false; + this.loadPromise = null; + } + + // Загрузка метаданных + async load() { + if (this.loadPromise) { + return this.loadPromise; + } + + this.loadPromise = this._loadMetadata(); + return this.loadPromise; + } + + // Основная логика загрузки + async _loadMetadata() { + try { + // Сначала проверяем кэш + const cached = this._getFromCache(); + if (cached) { + this.metadata = cached; + this.isLoaded = true; + console.log('Search metadata loaded from cache'); + return this.metadata; + } + + // Используем встроенные данные + this.metadata = EMBEDDED_SEARCH_METADATA; + + // Сохраняем в кэш + this._saveToCache(this.metadata); + + this.isLoaded = true; + console.log('Search metadata loaded successfully from embedded data'); + return this.metadata; + + } catch (error) { + console.warn('Failed to load search metadata, using fallback:', error); + this.metadata = SEARCH_METADATA_CONFIG.fallbackData; + this.isLoaded = true; + return this.metadata; + } + } + + // Caching + _getFromCache() { + try { + const cached = localStorage.getItem(SEARCH_METADATA_CONFIG.cacheKey); + if (!cached) return null; + + const data = JSON.parse(cached); + const now = Date.now(); + + if (data.timestamp && (now - data.timestamp) < SEARCH_METADATA_CONFIG.cacheExpiry) { + return data.metadata; + } + + // Cache expired, remove + localStorage.removeItem(SEARCH_METADATA_CONFIG.cacheKey); + return null; + } catch (error) { + console.warn('Error reading cache:', error); + return null; + } + } + + _saveToCache(metadata) { + try { + const data = { + metadata: metadata, + timestamp: Date.now() + }; + localStorage.setItem(SEARCH_METADATA_CONFIG.cacheKey, JSON.stringify(data)); + } catch (error) { + console.warn('Error saving to cache:', error); + } + } + + // Get synonyms + getSynonyms(term) { + if (!this.isLoaded || !this.metadata || !this.metadata.search_synonyms) { + return []; + } + + const synonyms = this.metadata.search_synonyms[term]; + return synonyms ? synonyms : []; + } + + // Get popular queries + getPopularSearches() { + if (!this.isLoaded || !this.metadata || !this.metadata.popular_searches) { + return []; + } + + return this.metadata.popular_searches; + } + + // Get categories + getCategories() { + if (!this.isLoaded || !this.metadata || !this.metadata.search_categories) { + return {}; + } + + return this.metadata.search_categories; + } + + // Get weights + getWeights() { + if (!this.isLoaded || !this.metadata || !this.metadata.search_weights) { + return { + title: 10, + headings: 5, + content: 1, + tags: 3, + categories: 2 + }; + } + + return this.metadata.search_weights; + } + + // Get filters + getFilters() { + if (!this.isLoaded || !this.metadata || !this.metadata.search_filters) { + return []; + } + + return this.metadata.search_filters; + } + + // Get suggestions settings + getSuggestionsConfig() { + if (!this.isLoaded || !this.metadata || !this.metadata.search_suggestions) { + return { + min_length: 2, + max_suggestions: 10, + debounce_delay: 300 + }; + } + + return this.metadata.search_suggestions; + } + + // Get results settings + getResultsConfig() { + if (!this.isLoaded || !this.metadata || !this.metadata.search_results) { + return { + max_results: 20, + highlight: "auto" + }; + } + + return this.metadata.search_results; + } + + // Find synonyms for a term + findSynonyms(term) { + if (!this.isLoaded || !this.metadata || !this.metadata.search_synonyms) { + return []; + } + + const allSynonyms = []; + + // Look for exact match + if (this.metadata.search_synonyms[term]) { + allSynonyms.push(...this.metadata.search_synonyms[term]); + } + + // Look for partial matches + for (const [key, synonyms] of Object.entries(this.metadata.search_synonyms)) { + if (key.includes(term) || term.includes(key)) { + allSynonyms.push(...synonyms); + } + } + + // Remove duplicates + return [...new Set(allSynonyms)]; + } + + // Get all synonyms + getAllSynonyms() { + if (!this.isLoaded || !this.metadata || !this.metadata.search_synonyms) { + return {}; + } + + return this.metadata.search_synonyms; + } + + // Refresh metadata + async refresh() { + this.isLoaded = false; + this.loadPromise = null; + localStorage.removeItem(SEARCH_METADATA_CONFIG.cacheKey); + return await this.load(); + } + } + + // Create global instance + window.SearchMetadataLoader = SearchMetadataLoader; + window.searchMetadataLoader = new SearchMetadataLoader(); + + // Automatic loading on page load + document.addEventListener('DOMContentLoaded', function() { + window.searchMetadataLoader.load().then(() => { + console.log('Search metadata ready'); + + // Notify other scripts about metadata readiness + const event = new CustomEvent('searchMetadataLoaded', { + detail: { metadata: window.searchMetadataLoader.metadata } + }); + document.dispatchEvent(event); + }); + }); + + // Export for use in other modules + if (typeof module !== 'undefined' && module.exports) { + module.exports = SearchMetadataLoader; + } + +})(); \ No newline at end of file diff --git a/docs/extras/search-metadata.yml b/docs/extras/search-metadata.yml new file mode 100644 index 0000000..e9ab896 --- /dev/null +++ b/docs/extras/search-metadata.yml @@ -0,0 +1,623 @@ +# Search Metadata for Enhanced Search +# This file contains metadata to improve search functionality + +# Popular search terms and their synonyms +search_synonyms: + api: + - interface + - endpoint + - rest + - webhook + - service + - интерфейс + - апи + - эндпоинт + - сервис + - вебхук + install: + - setup + - installation + - configure + - deployment + - setup guide + - установка + - установить + - настройка + - развертывание + - инсталляция + - конфигурация + config: + - configuration + - settings + - options + - parameters + - preferences + - настройки + - конфигурация + - параметры + - опции + - предпочтения + error: + - exception + - bug + - issue + - problem + - troubleshooting + - debug + - ошибка + - исключение + - баг + - проблема + - отладка + - исправление + - решение проблем + test: + - testing + - unit test + - integration test + - automated test + - test suite + - тестирование + - тест + - проверка + - автоматический тест + - модульный тест + - интеграционный тест + deploy: + - deployment + - publish + - release + - production + - staging + - развертывание + - публикация + - релиз + - продакшн + - продакшен + - стейджинг + security: + - secure + - vulnerability + - safety + - authentication + - authorization + - безопасность + - защита + - уязвимость + - аутентификация + - авторизация + - вход + - логин + performance: + - speed + - optimization + - fast + - efficient + - caching + - производительность + - скорость + - оптимизация + - быстрый + - эффективность + - кэширование + - кеширование + database: + - db + - sql + - mysql + - postgresql + - mongodb + - storage + - база данных + - бд + - хранилище + - датабаза + authentication: + - auth + - login + - user + - password + - oauth + - аутентификация + - авторизация + - вход + - логин + - пользователь + - пароль + authorization: + - permissions + - access + - roles + - rights + - privileges + - права + - доступ + - роли + - привилегии + - разрешения + logging: + - log + - debug + - trace + - monitor + - audit + - логирование + - лог + - отладка + - трассировка + - мониторинг + - аудит + monitoring: + - monitor + - metrics + - analytics + - dashboard + - alerting + - мониторинг + - метрики + - аналитика + - дашборд + - уведомления + - алерты + backup: + - restore + - recovery + - snapshot + - archive + - data protection + - резервная копия + - бэкап + - восстановление + - снимок + - архив + - защита данных + migration: + - upgrade + - update + - version + - migration guide + - changelog + - миграция + - обновление + - версия + - апгрейд + - руководство по миграции + - история изменений + documentation: + - docs + - guide + - manual + - reference + - help + - документация + - документы + - руководство + - справочник + - помощь + - мануал + tutorial: + - guide + - example + - howto + - walkthrough + - step by step + - туториал + - руководство + - пример + - пошаговая инструкция + - как сделать + - инструкция + faq: + - question + - answer + - help + - support + - common issues + - вопросы + - ответы + - помощь + - поддержка + - частые вопросы + - часто задаваемые вопросы + changelog: + - changes + - updates + - version history + - release notes + - what's new + - изменения + - обновления + - история версий + - заметки о релизе + - что нового + - чейнджлог + license: + - licensing + - legal + - terms + - copyright + - mit license + - лицензия + - лицензирование + - правовые + - условия + - авторские права + # Дополнительные русские термины + php: + - пхп + - пхп + - php + mysql: + - мускул + - mysql + - база данных + composer: + - композер + - composer + - менеджер пакетов + git: + - гит + - git + - система контроля версий + docker: + - докер + - docker + - контейнеризация + nginx: + - энжинкс + - nginx + - веб сервер + apache: + - апач + - apache + - веб сервер + linux: + - линукс + - linux + - операционная система + windows: + - виндовс + - windows + - операционная система + macos: + - макос + - macos + - операционная система + javascript: + - джаваскрипт + - javascript + - js + python: + - питон + - python + - язык программирования + nodejs: + - ноде + - nodejs + - node.js + react: + - реакт + - react + - фреймворк + vue: + - vue + - vue.js + - фреймворк + angular: + - ангуляр + - angular + - фреймворк + laravel: + - лаravel + - laravel + - фреймворк + symfony: + - симфони + - symfony + - фреймворк + wordpress: + - вордпресс + - wordpress + - cms + drupal: + - друпал + - drupal + - cms + joomla: + - джумла + - joomla + - cms + +# Popular search queries +popular_searches: + - installation guide + - api documentation + - configuration + - troubleshooting + - examples + - changelog + - faq + - security + - performance + - deployment + - database setup + - authentication + - error handling + - testing + - monitoring + - backup + - migration + - documentation + - tutorial + - license + - руководство по установке + - документация api + - настройка + - решение проблем + - примеры + - история изменений + - частые вопросы + - безопасность + - производительность + - развертывание + - настройка базы данных + - аутентификация + - обработка ошибок + - тестирование + - мониторинг + - резервное копирование + - миграция + - документация + - туториал + - лицензия + # Дополнительные русские популярные запросы + - установка php + - настройка mysql + - composer install + - git команды + - docker контейнер + - nginx конфигурация + - apache настройка + - linux команды + - windows установка + - javascript примеры + - python скрипт + - nodejs сервер + - react компонент + - vue приложение + - angular проект + - laravel миграция + - symfony бандл + - wordpress плагин + - drupal модуль + - joomla расширение + +# Search categories +search_categories: + getting_started: + - install + - setup + - configuration + - quick start + - first steps + - установка + - настройка + - конфигурация + - быстрый старт + - первые шаги + api_reference: + - api + - endpoint + - method + - parameter + - response + - апи + - эндпоинт + - метод + - параметр + - ответ + development: + - development + - coding + - programming + - sdk + - library + - разработка + - программирование + - код + - библиотека + - пакет разработчика + deployment: + - deploy + - production + - hosting + - server + - cloud + - развертывание + - продакшн + - хостинг + - сервер + - облако + troubleshooting: + - error + - problem + - issue + - debug + - fix + - ошибка + - проблема + - отладка + - исправление + - решение + security: + - security + - authentication + - authorization + - encryption + - vulnerability + - безопасность + - аутентификация + - авторизация + - шифрование + - уязвимость + performance: + - performance + - optimization + - speed + - caching + - efficiency + - производительность + - оптимизация + - скорость + - кэширование + - эффективность + monitoring: + - monitoring + - logging + - metrics + - analytics + - alerting + - мониторинг + - логирование + - метрики + - аналитика + - уведомления + maintenance: + - backup + - migration + - update + - maintenance + - administration + - резервное копирование + - миграция + - обновление + - обслуживание + - администрирование + +# Search boost weights +search_weights: + title: 10 + headings: 5 + content: 1 + tags: 3 + categories: 2 + code: 2 + links: 1 + metadata: 1 + +# Search filters +search_filters: + - type: category + label: "Категория" + options: + - value: "getting_started" + label: "Начало работы" + - value: "api_reference" + label: "API Справочник" + - value: "development" + label: "Разработка" + - value: "deployment" + label: "Деплой" + - value: "troubleshooting" + label: "Решение проблем" + - value: "security" + label: "Безопасность" + - value: "performance" + label: "Производительность" + - value: "monitoring" + label: "Мониторинг" + - value: "maintenance" + label: "Обслуживание" + + - type: language + label: "Язык" + options: + - value: "ru" + label: "Русский" + - value: "en" + label: "English" + - value: "de" + label: "Deutsch" + + - type: difficulty + label: "Сложность" + options: + - value: "beginner" + label: "Начинающий" + - value: "intermediate" + label: "Средний" + - value: "advanced" + label: "Продвинутый" + +# Search suggestions configuration +search_suggestions: + min_length: 2 + max_suggestions: 10 + debounce_delay: 300 + highlight_threshold: 0 + boost_threshold: 0 + tokenizer: '[\s\-,:!=\[\]()"/]+' + separator: '[\s\-,:!=\[\]()"/]+' + +# Search result configuration +search_results: + max_results: 20 + highlight: "auto" + prebuild_index: true + indexing: "full" + search_rank: | + function(tokens, pages) { + const weights = { + title: 10, + headings: 5, + content: 1, + tags: 3, + categories: 2 + }; + + return pages.map(function(page) { + let score = 0; + const title = page.title.toLowerCase(); + const text = page.text.toLowerCase(); + + tokens.forEach(function(token) { + const tokenLower = token.toLowerCase(); + + // Поиск в заголовке + if (title.includes(tokenLower)) { + score += weights.title; + } + + // Поиск в заголовках + if (page.headings) { + page.headings.forEach(function(heading) { + if (heading.toLowerCase().includes(tokenLower)) { + score += weights.headings; + } + }); + } + + // Поиск в контенте + if (text.includes(tokenLower)) { + score += weights.content; + } + + // Поиск в тегах + if (page.tags) { + page.tags.forEach(function(tag) { + if (tag.toLowerCase().includes(tokenLower)) { + score += weights.tags; + } + }); + } + }); + + return { page: page, score: score }; + }).filter(function(item) { + return item.score > 0; + }).sort(function(a, b) { + return b.score - a.score; + }).map(function(item) { + return item.page; + }); + } diff --git a/docs/search-metadata.yml b/docs/search-metadata.yml new file mode 100644 index 0000000..e9ab896 --- /dev/null +++ b/docs/search-metadata.yml @@ -0,0 +1,623 @@ +# Search Metadata for Enhanced Search +# This file contains metadata to improve search functionality + +# Popular search terms and their synonyms +search_synonyms: + api: + - interface + - endpoint + - rest + - webhook + - service + - интерфейс + - апи + - эндпоинт + - сервис + - вебхук + install: + - setup + - installation + - configure + - deployment + - setup guide + - установка + - установить + - настройка + - развертывание + - инсталляция + - конфигурация + config: + - configuration + - settings + - options + - parameters + - preferences + - настройки + - конфигурация + - параметры + - опции + - предпочтения + error: + - exception + - bug + - issue + - problem + - troubleshooting + - debug + - ошибка + - исключение + - баг + - проблема + - отладка + - исправление + - решение проблем + test: + - testing + - unit test + - integration test + - automated test + - test suite + - тестирование + - тест + - проверка + - автоматический тест + - модульный тест + - интеграционный тест + deploy: + - deployment + - publish + - release + - production + - staging + - развертывание + - публикация + - релиз + - продакшн + - продакшен + - стейджинг + security: + - secure + - vulnerability + - safety + - authentication + - authorization + - безопасность + - защита + - уязвимость + - аутентификация + - авторизация + - вход + - логин + performance: + - speed + - optimization + - fast + - efficient + - caching + - производительность + - скорость + - оптимизация + - быстрый + - эффективность + - кэширование + - кеширование + database: + - db + - sql + - mysql + - postgresql + - mongodb + - storage + - база данных + - бд + - хранилище + - датабаза + authentication: + - auth + - login + - user + - password + - oauth + - аутентификация + - авторизация + - вход + - логин + - пользователь + - пароль + authorization: + - permissions + - access + - roles + - rights + - privileges + - права + - доступ + - роли + - привилегии + - разрешения + logging: + - log + - debug + - trace + - monitor + - audit + - логирование + - лог + - отладка + - трассировка + - мониторинг + - аудит + monitoring: + - monitor + - metrics + - analytics + - dashboard + - alerting + - мониторинг + - метрики + - аналитика + - дашборд + - уведомления + - алерты + backup: + - restore + - recovery + - snapshot + - archive + - data protection + - резервная копия + - бэкап + - восстановление + - снимок + - архив + - защита данных + migration: + - upgrade + - update + - version + - migration guide + - changelog + - миграция + - обновление + - версия + - апгрейд + - руководство по миграции + - история изменений + documentation: + - docs + - guide + - manual + - reference + - help + - документация + - документы + - руководство + - справочник + - помощь + - мануал + tutorial: + - guide + - example + - howto + - walkthrough + - step by step + - туториал + - руководство + - пример + - пошаговая инструкция + - как сделать + - инструкция + faq: + - question + - answer + - help + - support + - common issues + - вопросы + - ответы + - помощь + - поддержка + - частые вопросы + - часто задаваемые вопросы + changelog: + - changes + - updates + - version history + - release notes + - what's new + - изменения + - обновления + - история версий + - заметки о релизе + - что нового + - чейнджлог + license: + - licensing + - legal + - terms + - copyright + - mit license + - лицензия + - лицензирование + - правовые + - условия + - авторские права + # Дополнительные русские термины + php: + - пхп + - пхп + - php + mysql: + - мускул + - mysql + - база данных + composer: + - композер + - composer + - менеджер пакетов + git: + - гит + - git + - система контроля версий + docker: + - докер + - docker + - контейнеризация + nginx: + - энжинкс + - nginx + - веб сервер + apache: + - апач + - apache + - веб сервер + linux: + - линукс + - linux + - операционная система + windows: + - виндовс + - windows + - операционная система + macos: + - макос + - macos + - операционная система + javascript: + - джаваскрипт + - javascript + - js + python: + - питон + - python + - язык программирования + nodejs: + - ноде + - nodejs + - node.js + react: + - реакт + - react + - фреймворк + vue: + - vue + - vue.js + - фреймворк + angular: + - ангуляр + - angular + - фреймворк + laravel: + - лаravel + - laravel + - фреймворк + symfony: + - симфони + - symfony + - фреймворк + wordpress: + - вордпресс + - wordpress + - cms + drupal: + - друпал + - drupal + - cms + joomla: + - джумла + - joomla + - cms + +# Popular search queries +popular_searches: + - installation guide + - api documentation + - configuration + - troubleshooting + - examples + - changelog + - faq + - security + - performance + - deployment + - database setup + - authentication + - error handling + - testing + - monitoring + - backup + - migration + - documentation + - tutorial + - license + - руководство по установке + - документация api + - настройка + - решение проблем + - примеры + - история изменений + - частые вопросы + - безопасность + - производительность + - развертывание + - настройка базы данных + - аутентификация + - обработка ошибок + - тестирование + - мониторинг + - резервное копирование + - миграция + - документация + - туториал + - лицензия + # Дополнительные русские популярные запросы + - установка php + - настройка mysql + - composer install + - git команды + - docker контейнер + - nginx конфигурация + - apache настройка + - linux команды + - windows установка + - javascript примеры + - python скрипт + - nodejs сервер + - react компонент + - vue приложение + - angular проект + - laravel миграция + - symfony бандл + - wordpress плагин + - drupal модуль + - joomla расширение + +# Search categories +search_categories: + getting_started: + - install + - setup + - configuration + - quick start + - first steps + - установка + - настройка + - конфигурация + - быстрый старт + - первые шаги + api_reference: + - api + - endpoint + - method + - parameter + - response + - апи + - эндпоинт + - метод + - параметр + - ответ + development: + - development + - coding + - programming + - sdk + - library + - разработка + - программирование + - код + - библиотека + - пакет разработчика + deployment: + - deploy + - production + - hosting + - server + - cloud + - развертывание + - продакшн + - хостинг + - сервер + - облако + troubleshooting: + - error + - problem + - issue + - debug + - fix + - ошибка + - проблема + - отладка + - исправление + - решение + security: + - security + - authentication + - authorization + - encryption + - vulnerability + - безопасность + - аутентификация + - авторизация + - шифрование + - уязвимость + performance: + - performance + - optimization + - speed + - caching + - efficiency + - производительность + - оптимизация + - скорость + - кэширование + - эффективность + monitoring: + - monitoring + - logging + - metrics + - analytics + - alerting + - мониторинг + - логирование + - метрики + - аналитика + - уведомления + maintenance: + - backup + - migration + - update + - maintenance + - administration + - резервное копирование + - миграция + - обновление + - обслуживание + - администрирование + +# Search boost weights +search_weights: + title: 10 + headings: 5 + content: 1 + tags: 3 + categories: 2 + code: 2 + links: 1 + metadata: 1 + +# Search filters +search_filters: + - type: category + label: "Категория" + options: + - value: "getting_started" + label: "Начало работы" + - value: "api_reference" + label: "API Справочник" + - value: "development" + label: "Разработка" + - value: "deployment" + label: "Деплой" + - value: "troubleshooting" + label: "Решение проблем" + - value: "security" + label: "Безопасность" + - value: "performance" + label: "Производительность" + - value: "monitoring" + label: "Мониторинг" + - value: "maintenance" + label: "Обслуживание" + + - type: language + label: "Язык" + options: + - value: "ru" + label: "Русский" + - value: "en" + label: "English" + - value: "de" + label: "Deutsch" + + - type: difficulty + label: "Сложность" + options: + - value: "beginner" + label: "Начинающий" + - value: "intermediate" + label: "Средний" + - value: "advanced" + label: "Продвинутый" + +# Search suggestions configuration +search_suggestions: + min_length: 2 + max_suggestions: 10 + debounce_delay: 300 + highlight_threshold: 0 + boost_threshold: 0 + tokenizer: '[\s\-,:!=\[\]()"/]+' + separator: '[\s\-,:!=\[\]()"/]+' + +# Search result configuration +search_results: + max_results: 20 + highlight: "auto" + prebuild_index: true + indexing: "full" + search_rank: | + function(tokens, pages) { + const weights = { + title: 10, + headings: 5, + content: 1, + tags: 3, + categories: 2 + }; + + return pages.map(function(page) { + let score = 0; + const title = page.title.toLowerCase(); + const text = page.text.toLowerCase(); + + tokens.forEach(function(token) { + const tokenLower = token.toLowerCase(); + + // Поиск в заголовке + if (title.includes(tokenLower)) { + score += weights.title; + } + + // Поиск в заголовках + if (page.headings) { + page.headings.forEach(function(heading) { + if (heading.toLowerCase().includes(tokenLower)) { + score += weights.headings; + } + }); + } + + // Поиск в контенте + if (text.includes(tokenLower)) { + score += weights.content; + } + + // Поиск в тегах + if (page.tags) { + page.tags.forEach(function(tag) { + if (tag.toLowerCase().includes(tokenLower)) { + score += weights.tags; + } + }); + } + }); + + return { page: page, score: score }; + }).filter(function(item) { + return item.score > 0; + }).sort(function(a, b) { + return b.score - a.score; + }).map(function(item) { + return item.page; + }); + } diff --git a/extras/search-metadata.yml b/extras/search-metadata.yml new file mode 100644 index 0000000..e9ab896 --- /dev/null +++ b/extras/search-metadata.yml @@ -0,0 +1,623 @@ +# Search Metadata for Enhanced Search +# This file contains metadata to improve search functionality + +# Popular search terms and their synonyms +search_synonyms: + api: + - interface + - endpoint + - rest + - webhook + - service + - интерфейс + - апи + - эндпоинт + - сервис + - вебхук + install: + - setup + - installation + - configure + - deployment + - setup guide + - установка + - установить + - настройка + - развертывание + - инсталляция + - конфигурация + config: + - configuration + - settings + - options + - parameters + - preferences + - настройки + - конфигурация + - параметры + - опции + - предпочтения + error: + - exception + - bug + - issue + - problem + - troubleshooting + - debug + - ошибка + - исключение + - баг + - проблема + - отладка + - исправление + - решение проблем + test: + - testing + - unit test + - integration test + - automated test + - test suite + - тестирование + - тест + - проверка + - автоматический тест + - модульный тест + - интеграционный тест + deploy: + - deployment + - publish + - release + - production + - staging + - развертывание + - публикация + - релиз + - продакшн + - продакшен + - стейджинг + security: + - secure + - vulnerability + - safety + - authentication + - authorization + - безопасность + - защита + - уязвимость + - аутентификация + - авторизация + - вход + - логин + performance: + - speed + - optimization + - fast + - efficient + - caching + - производительность + - скорость + - оптимизация + - быстрый + - эффективность + - кэширование + - кеширование + database: + - db + - sql + - mysql + - postgresql + - mongodb + - storage + - база данных + - бд + - хранилище + - датабаза + authentication: + - auth + - login + - user + - password + - oauth + - аутентификация + - авторизация + - вход + - логин + - пользователь + - пароль + authorization: + - permissions + - access + - roles + - rights + - privileges + - права + - доступ + - роли + - привилегии + - разрешения + logging: + - log + - debug + - trace + - monitor + - audit + - логирование + - лог + - отладка + - трассировка + - мониторинг + - аудит + monitoring: + - monitor + - metrics + - analytics + - dashboard + - alerting + - мониторинг + - метрики + - аналитика + - дашборд + - уведомления + - алерты + backup: + - restore + - recovery + - snapshot + - archive + - data protection + - резервная копия + - бэкап + - восстановление + - снимок + - архив + - защита данных + migration: + - upgrade + - update + - version + - migration guide + - changelog + - миграция + - обновление + - версия + - апгрейд + - руководство по миграции + - история изменений + documentation: + - docs + - guide + - manual + - reference + - help + - документация + - документы + - руководство + - справочник + - помощь + - мануал + tutorial: + - guide + - example + - howto + - walkthrough + - step by step + - туториал + - руководство + - пример + - пошаговая инструкция + - как сделать + - инструкция + faq: + - question + - answer + - help + - support + - common issues + - вопросы + - ответы + - помощь + - поддержка + - частые вопросы + - часто задаваемые вопросы + changelog: + - changes + - updates + - version history + - release notes + - what's new + - изменения + - обновления + - история версий + - заметки о релизе + - что нового + - чейнджлог + license: + - licensing + - legal + - terms + - copyright + - mit license + - лицензия + - лицензирование + - правовые + - условия + - авторские права + # Дополнительные русские термины + php: + - пхп + - пхп + - php + mysql: + - мускул + - mysql + - база данных + composer: + - композер + - composer + - менеджер пакетов + git: + - гит + - git + - система контроля версий + docker: + - докер + - docker + - контейнеризация + nginx: + - энжинкс + - nginx + - веб сервер + apache: + - апач + - apache + - веб сервер + linux: + - линукс + - linux + - операционная система + windows: + - виндовс + - windows + - операционная система + macos: + - макос + - macos + - операционная система + javascript: + - джаваскрипт + - javascript + - js + python: + - питон + - python + - язык программирования + nodejs: + - ноде + - nodejs + - node.js + react: + - реакт + - react + - фреймворк + vue: + - vue + - vue.js + - фреймворк + angular: + - ангуляр + - angular + - фреймворк + laravel: + - лаravel + - laravel + - фреймворк + symfony: + - симфони + - symfony + - фреймворк + wordpress: + - вордпресс + - wordpress + - cms + drupal: + - друпал + - drupal + - cms + joomla: + - джумла + - joomla + - cms + +# Popular search queries +popular_searches: + - installation guide + - api documentation + - configuration + - troubleshooting + - examples + - changelog + - faq + - security + - performance + - deployment + - database setup + - authentication + - error handling + - testing + - monitoring + - backup + - migration + - documentation + - tutorial + - license + - руководство по установке + - документация api + - настройка + - решение проблем + - примеры + - история изменений + - частые вопросы + - безопасность + - производительность + - развертывание + - настройка базы данных + - аутентификация + - обработка ошибок + - тестирование + - мониторинг + - резервное копирование + - миграция + - документация + - туториал + - лицензия + # Дополнительные русские популярные запросы + - установка php + - настройка mysql + - composer install + - git команды + - docker контейнер + - nginx конфигурация + - apache настройка + - linux команды + - windows установка + - javascript примеры + - python скрипт + - nodejs сервер + - react компонент + - vue приложение + - angular проект + - laravel миграция + - symfony бандл + - wordpress плагин + - drupal модуль + - joomla расширение + +# Search categories +search_categories: + getting_started: + - install + - setup + - configuration + - quick start + - first steps + - установка + - настройка + - конфигурация + - быстрый старт + - первые шаги + api_reference: + - api + - endpoint + - method + - parameter + - response + - апи + - эндпоинт + - метод + - параметр + - ответ + development: + - development + - coding + - programming + - sdk + - library + - разработка + - программирование + - код + - библиотека + - пакет разработчика + deployment: + - deploy + - production + - hosting + - server + - cloud + - развертывание + - продакшн + - хостинг + - сервер + - облако + troubleshooting: + - error + - problem + - issue + - debug + - fix + - ошибка + - проблема + - отладка + - исправление + - решение + security: + - security + - authentication + - authorization + - encryption + - vulnerability + - безопасность + - аутентификация + - авторизация + - шифрование + - уязвимость + performance: + - performance + - optimization + - speed + - caching + - efficiency + - производительность + - оптимизация + - скорость + - кэширование + - эффективность + monitoring: + - monitoring + - logging + - metrics + - analytics + - alerting + - мониторинг + - логирование + - метрики + - аналитика + - уведомления + maintenance: + - backup + - migration + - update + - maintenance + - administration + - резервное копирование + - миграция + - обновление + - обслуживание + - администрирование + +# Search boost weights +search_weights: + title: 10 + headings: 5 + content: 1 + tags: 3 + categories: 2 + code: 2 + links: 1 + metadata: 1 + +# Search filters +search_filters: + - type: category + label: "Категория" + options: + - value: "getting_started" + label: "Начало работы" + - value: "api_reference" + label: "API Справочник" + - value: "development" + label: "Разработка" + - value: "deployment" + label: "Деплой" + - value: "troubleshooting" + label: "Решение проблем" + - value: "security" + label: "Безопасность" + - value: "performance" + label: "Производительность" + - value: "monitoring" + label: "Мониторинг" + - value: "maintenance" + label: "Обслуживание" + + - type: language + label: "Язык" + options: + - value: "ru" + label: "Русский" + - value: "en" + label: "English" + - value: "de" + label: "Deutsch" + + - type: difficulty + label: "Сложность" + options: + - value: "beginner" + label: "Начинающий" + - value: "intermediate" + label: "Средний" + - value: "advanced" + label: "Продвинутый" + +# Search suggestions configuration +search_suggestions: + min_length: 2 + max_suggestions: 10 + debounce_delay: 300 + highlight_threshold: 0 + boost_threshold: 0 + tokenizer: '[\s\-,:!=\[\]()"/]+' + separator: '[\s\-,:!=\[\]()"/]+' + +# Search result configuration +search_results: + max_results: 20 + highlight: "auto" + prebuild_index: true + indexing: "full" + search_rank: | + function(tokens, pages) { + const weights = { + title: 10, + headings: 5, + content: 1, + tags: 3, + categories: 2 + }; + + return pages.map(function(page) { + let score = 0; + const title = page.title.toLowerCase(); + const text = page.text.toLowerCase(); + + tokens.forEach(function(token) { + const tokenLower = token.toLowerCase(); + + // Поиск в заголовке + if (title.includes(tokenLower)) { + score += weights.title; + } + + // Поиск в заголовках + if (page.headings) { + page.headings.forEach(function(heading) { + if (heading.toLowerCase().includes(tokenLower)) { + score += weights.headings; + } + }); + } + + // Поиск в контенте + if (text.includes(tokenLower)) { + score += weights.content; + } + + // Поиск в тегах + if (page.tags) { + page.tags.forEach(function(tag) { + if (tag.toLowerCase().includes(tokenLower)) { + score += weights.tags; + } + }); + } + }); + + return { page: page, score: score }; + }).filter(function(item) { + return item.score > 0; + }).sort(function(a, b) { + return b.score - a.score; + }).map(function(item) { + return item.page; + }); + } diff --git a/mkdocs.yml b/mkdocs.yml index 6806bee..1abfd37 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -8,189 +8,296 @@ site_description: "Сайт с документациями, который по copyright: "Документация защищена лицензией MIT license" nav: - - Главная: index.md - - Пользование сайтом: - - Как покупать: site/how-to-buy.md - - Тех. Поддержка: site/how-to-ask-for-support.md - - Инструкции: - - Composer: dev/composer.md - - Crowdin: dev/crowdin.md - - Инструкция по установке: dev/install_instructions.md - - Инструкция по установке PHP intl: dev/php_intl.md - - Бесплатные разработки: - - DB Manager: - - Установка: dev/db_manager/install.md - - RePost: - - Главная: dev/repost/index.md - - DLE Faker: - - Установка: dev/dle_faker/install.md - - Настройка: dev/dle_faker/settings.md - - Теги для оформления шаблонов: - - Общие: dev/dle_faker/tag_for_all.md - - Для пользователей: dev/dle_faker/tag_for_users.md - - Пля новостей: dev/dle_faker/tag_for_news.md - - Генератор: - - Генерация пользователей: dev/dle_faker/gen_users.md - - Генерация новостей: dev/dle_faker/gen_news.md - - Функционал парсера данных: dev/dle_faker/parser.md - - История изменений: dev/dle_faker/changelog.md - - Telegram Posting (Не поддерживается!): - - Установка: dev/telegramposting/install.md - - Подключение в сторонние разработки: dev/telegramposting/custom_add.md - - Теги для оформления шаблонов: dev/telegramposting/template_tags.md - - Настройка бота: dev/telegramposting/bot.md - - Версии изменений: dev/telegramposting/changelog.md - - MH Admin: - - Установка: dev/mhadmin/install.md - - Для пользователей: - - Настройка: dev/mhadmin/frontend/manage.md - - Для разработчиков: - - Генератор модулей: dev/mhadmin/new_module.md - - Главный файл в папке inc: dev/mhadmin/backend/inc_main.md - - Модуль главной страницы: dev/mhadmin/backend/module.md - - Генератор языковых файлов: dev/mhadmin/generate_languages.md - - Back-End: - - Главная: dev/mhadmin/backend/index.md - - База данных: - - BasisModel: dev/mhadmin/backend/classes/BasisModel.md - - BasisRepository: dev/mhadmin/backend/classes/BasisRepository.md - - MhDB: dev/mhadmin/backend/classes/MhDB.md - - Классы: - - Admin: dev/mhadmin/backend/classes/Admin.md - - CacheControl: dev/mhadmin/backend/classes/CacheControl.md - - ComposerAction: dev/mhadmin/backend/classes/ComposerAction.md - - DataManager: dev/mhadmin/backend/classes/DataManager.md - - LogGenerator: dev/mhadmin/backend/classes/LogGenerator.md - - MhAjax: dev/mhadmin/backend/classes/MhAjax.md - - MhLog: dev/mhadmin/backend/classes/MhLog.md - - MhLogRepository: dev/mhadmin/backend/classes/MhLogRepository.md - - MhTranslation: dev/mhadmin/backend/classes/MhTranslation.md - - TwigFilter: dev/mhadmin/backend/classes/TwigFilter.md - - Ответы на запросы AJAX: - - AjaxAbstractResponse: dev/mhadmin/backend/classes/AjaxAbstractResponse.md - - ErrorResponseAjax: dev/mhadmin/backend/classes/ErrorResponseAjax.md - - SuccessResponseAjax: dev/mhadmin/backend/classes/SuccessResponseAjax.md - - Твиг (Twig) дополнения: - - AdminUrlExtension: dev/mhadmin/backend/classes/AdminUrlExtension.md - - DateTimeFormatter: dev/mhadmin/backend/classes/DateTimeFormatter.md - - DeclineExtension: dev/mhadmin/backend/classes/DeclineExtension.md - - MobileDetectExtension: dev/mhadmin/backend/classes/MobileDetectExtension.md - - TextLimiter: dev/mhadmin/backend/classes/TextLimiter.md - - Типы: - - AdminLink: dev/mhadmin/backend/classes/AdminLink.md - - Author: dev/mhadmin/backend/classes/Author.md - - BreadCrumb: dev/mhadmin/backend/classes/BreadCrumb.md - - Трейты: - - AssetsChecker: dev/mhadmin/backend/classes/AssetsChecker.md - - DataLoader: dev/mhadmin/backend/classes/DataLoader.md - - DleData: dev/mhadmin/backend/classes/DleData.md - - UpdatesChecker: dev/mhadmin/backend/classes/UpdatesChecker.md - - Front-End: - - Шаблоны: dev/mhadmin/frontend/templates.md - - История изменений: dev/mhadmin/changelog.md - - MyStatus: dev/mystatus.md - - Release Status: dev/releasestatus.md - - Schema.Org: dev/schema.md - - Пользовательские теги: dev/usertags.md - - Webmaster Verification: dev/webmaster-verification.md - - XF Lists: dev/xflist.md - - XF Select: dev/xfselect.md - - Хаки: - - Страницы как на КиноПоиске: dev/hook-pages-like-kp.md - - Подсчёт символов в короткой новости: dev/hook-shortstory-signs-count.md - - Платные разработки: - - Шаблон SeasonVar: - - Установка: dev/paid-seasonvar/install.md - - Automatic Related: dev/paid-seasonvar/related.md - - F.A.Q.: dev/paid-seasonvar/faq.md - - Курс валют: dev/paid-currencies_rate.md - - Последние новости списком: dev/paid-lastnews.md + - Главная: index.md + - Пользование сайтом: + - Как покупать: site/how-to-buy.md + - Тех. Поддержка: site/how-to-ask-for-support.md + - Инструкции: + - Composer: dev/composer.md + - Crowdin: dev/crowdin.md + - Инструкция по установке: dev/install_instructions.md + - Инструкция по установке PHP intl: dev/php_intl.md + - Работа со сторонними API (Wrapper): + - NotKinopoisk PHP Wrapper: + - Главная: dev/notkinopoiskphp/index.md + - Клиент: dev/notkinopoiskphp/client.md + - Абстрактные классы: dev/notkinopoiskphp/abstract-classes.md + - Интерфейсы: + - Оглавление: dev/notkinopoiskphp/interfaces/index.md + - ModelInterface: dev/notkinopoiskphp/interfaces/model-interface.md + - ResponseInterface: dev/notkinopoiskphp/interfaces/response-interface.md + - Сервисы: + - Оглавление: dev/notkinopoiskphp/services/index.md + - FilmService: dev/notkinopoiskphp/services/film-service.md + - PersonService: dev/notkinopoiskphp/services/person-service.md + - MediaService: dev/notkinopoiskphp/services/media-service.md + - UserService: dev/notkinopoiskphp/services/user-service.md + - Модели: + - Оглавление: dev/notkinopoiskphp/models/index.md + - Film: dev/notkinopoiskphp/models/film.md + - Person: dev/notkinopoiskphp/models/person.md + - Staff: dev/notkinopoiskphp/models/staff.md + - Review: dev/notkinopoiskphp/models/review.md + - Fact: dev/notkinopoiskphp/models/fact.md + - Image: dev/notkinopoiskphp/models/image.md + - Video: dev/notkinopoiskphp/models/video.md + - BoxOffice: dev/notkinopoiskphp/models/box-office.md + - Distribution: dev/notkinopoiskphp/models/distribution.md + - Award: dev/notkinopoiskphp/models/award.md + - Genre: dev/notkinopoiskphp/models/genre.md + - Country: dev/notkinopoiskphp/models/country.md + - Season: dev/notkinopoiskphp/models/season.md + - Episode: dev/notkinopoiskphp/models/episode.md + - PersonFilm: dev/notkinopoiskphp/models/person-film.md + - PersonSpouse: dev/notkinopoiskphp/models/person-spouse.md + - FilmSearchResult: dev/notkinopoiskphp/models/film-search-result.md + - FilmCollection: dev/notkinopoiskphp/models/film-collection.md + - RelatedFilm: dev/notkinopoiskphp/models/related-film.md + - Premiere: dev/notkinopoiskphp/models/premiere.md + - UserVote: dev/notkinopoiskphp/models/user-vote.md + - ExternalSource: dev/notkinopoiskphp/models/external-source.md + - Filters: dev/notkinopoiskphp/models/filters.md + - Перечисления (Enums): + - Оглавление: dev/notkinopoiskphp/enums/index.md + - AccountType: dev/notkinopoiskphp/enums/account-type.md + - ContentType: dev/notkinopoiskphp/enums/content-type.md + - DistributionSubType: dev/notkinopoiskphp/enums/distribution-sub-type.md + - ImageType: dev/notkinopoiskphp/enums/image-type.md + - VideoSite: dev/notkinopoiskphp/enums/video-site.md + - BoxOfficeType: dev/notkinopoiskphp/enums/box-office-type.md + - DistributionType: dev/notkinopoiskphp/enums/distribution-type.md + - FactType: dev/notkinopoiskphp/enums/fact-type.md + - ProductionStatus: dev/notkinopoiskphp/enums/production-status.md + - ReviewType: dev/notkinopoiskphp/enums/review-type.md + - ReviewOrder: dev/notkinopoiskphp/enums/review-order.md + - FilmOrder: dev/notkinopoiskphp/enums/film-order.md + - CollectionType: dev/notkinopoiskphp/enums/collection-type.md + - RelationType: dev/notkinopoiskphp/enums/relation-type.md + - ProfessionKey: dev/notkinopoiskphp/enums/profession-key.md + - Sex: dev/notkinopoiskphp/enums/sex.md + - Month: dev/notkinopoiskphp/enums/month.md + - ApiVersion: dev/notkinopoiskphp/enums/api-version.md + - Ответы API: + - Оглавление: dev/notkinopoiskphp/responses/index.md + - DefaultResponse: dev/notkinopoiskphp/responses/default-response.md + - PaginatedResponse: dev/notkinopoiskphp/responses/paginated-response.md + - KeywordSearchResponse: dev/notkinopoiskphp/responses/keyword-search-response.md + - Исключения: + - Оглавление: dev/notkinopoiskphp/exceptions/index.md + - ApiException: dev/notkinopoiskphp/exceptions/api-exception.md + - InvalidApiKeyException: dev/notkinopoiskphp/exceptions/invalid-api-key-exception.md + - RateLimitException: dev/notkinopoiskphp/exceptions/rate-limit-exception.md + - ResourceNotFoundException: dev/notkinopoiskphp/exceptions/resource-not-found-exception.md + - Навигационная карта: dev/notkinopoiskphp/navigation-map.md + - Бесплатные разработки: + - DB Manager: + - Установка: dev/db_manager/install.md + - RePost: + - Главная: dev/repost/index.md + - DLE Faker: + - Установка: dev/dle_faker/install.md + - Настройка: dev/dle_faker/settings.md + - Теги для оформления шаблонов: + - Общие: dev/dle_faker/tag_for_all.md + - Для пользователей: dev/dle_faker/tag_for_users.md + - Пля новостей: dev/dle_faker/tag_for_news.md + - Генератор: + - Генерация пользователей: dev/dle_faker/gen_users.md + - Генерация новостей: dev/dle_faker/gen_news.md + - Функционал парсера данных: dev/dle_faker/parser.md + - История изменений: dev/dle_faker/changelog.md + - Telegram Posting (Не поддерживается!): + - Установка: dev/telegramposting/install.md + - Подключение в сторонние разработки: dev/telegramposting/custom_add.md + - Теги для оформления шаблонов: dev/telegramposting/template_tags.md + - Настройка бота: dev/telegramposting/bot.md + - Версии изменений: dev/telegramposting/changelog.md + - MH Admin: + - Установка: dev/mhadmin/install.md + - Для пользователей: + - Настройка: dev/mhadmin/frontend/manage.md + - Для разработчиков: + - Генератор модулей: dev/mhadmin/new_module.md + - Главный файл в папке inc: dev/mhadmin/backend/inc_main.md + - Модуль главной страницы: dev/mhadmin/backend/index.md + - Генератор языковых файлов: dev/mhadmin/generate_languages.md + - Back-End: + - Главная: dev/mhadmin/backend/index.md + - База данных: + - BasisModel: dev/mhadmin/backend/classes/BasisModel.md + - BasisRepository: dev/mhadmin/backend/classes/BasisRepository.md + - MhDB: dev/mhadmin/backend/classes/MhDB.md + - Классы: + - Admin: dev/mhadmin/backend/classes/Admin.md + - CacheControl: dev/mhadmin/backend/classes/CacheControl.md + - ComposerAction: dev/mhadmin/backend/classes/ComposerAction.md + - DataManager: dev/mhadmin/backend/classes/DataManager.md + - LogGenerator: dev/mhadmin/backend/classes/LogGenerator.md + - MhAjax: dev/mhadmin/backend/classes/MhAjax.md + - MhLog: dev/mhadmin/backend/classes/MhLog.md + - MhLogRepository: dev/mhadmin/backend/classes/MhLogRepository.md + - MhTranslation: dev/mhadmin/backend/classes/MhTranslation.md + - TwigFilter: dev/mhadmin/backend/classes/TwigFilter.md + - Ответы на запросы AJAX: + - AjaxAbstractResponse: dev/mhadmin/backend/classes/AjaxAbstractResponse.md + - ErrorResponseAjax: dev/mhadmin/backend/classes/ErrorResponseAjax.md + - SuccessResponseAjax: dev/mhadmin/backend/classes/SuccessResponseAjax.md + - Твиг (Twig) дополнения: + - AdminUrlExtension: dev/mhadmin/backend/classes/AdminUrlExtension.md + - DateTimeFormatter: dev/mhadmin/backend/classes/DateTimeFormatter.md + - DeclineExtension: dev/mhadmin/backend/classes/DeclineExtension.md + - MobileDetectExtension: dev/mhadmin/backend/classes/MobileDetectExtension.md + - TextLimiter: dev/mhadmin/backend/classes/TextLimiter.md + - Типы: + - AdminLink: dev/mhadmin/backend/classes/AdminLink.md + - Author: dev/mhadmin/backend/classes/Author.md + - BreadCrumb: dev/mhadmin/backend/classes/BreadCrumb.md + - Трейты: + - AssetsChecker: dev/mhadmin/backend/classes/AssetsChecker.md + - DataLoader: dev/mhadmin/backend/classes/DataLoader.md + - DleData: dev/mhadmin/backend/classes/DleData.md + - UpdatesChecker: dev/mhadmin/backend/classes/UpdatesChecker.md + - Front-End: + - Шаблоны: dev/mhadmin/frontend/templates.md + - История изменений: dev/mhadmin/changelog.md + - MyStatus: dev/mystatus.md + - Release Status: dev/releasestatus.md + - Schema.Org: dev/schema.md + - Пользовательские теги: dev/usertags.md + - Webmaster Verification: dev/webmaster-verification.md + - XF Lists: dev/xflist.md + - XF Select: dev/xfselect.md + - Хаки: + - Страницы как на КиноПоиске: dev/hook-pages-like-kp.md + - Подсчёт символов в короткой новости: dev/hook-shortstory-signs-count.md + - Платные разработки: + - Шаблон SeasonVar: + - Установка: dev/paid-seasonvar/install.md + - Automatic Related: dev/paid-seasonvar/related.md + - F.A.Q.: dev/paid-seasonvar/faq.md + - Курс валют: dev/paid-currencies_rate.md + - Последние новости списком: dev/paid-lastnews.md theme: - name: material - colorscheme: monokai - language: ru - features: - - content.code.annotate - - content.code.copy - - content.code.select - - navigation.instant - - navigation.tracking - # - navigation.tabs - # - navigation.tabs.sticky - - navigation.sections - - toc.follow - - navigation.top - palette: - primary: indigo + name: material + colorscheme: monokai + language: ru + features: + - content.code.annotate + - content.code.copy + - content.code.select + - navigation.instant + - navigation.tracking + # - navigation.tabs + # - navigation.tabs.sticky + - navigation.sections + - toc.follow + - navigation.top + palette: + primary: indigo + extra_css: + - extras/css/styles.css + - extras/css/search-enhancement.css + extra_javascript: + - extras/js/search-enhancement.js plugins: - - git-revision-date - - git-revision-date-localized: - enable_creation_date: true - - tags - - search: - lang: - - ru - - de - - en - - autorefs: - resolve_closest: true - - minify: - minify_html: true - minify_js: true - minify_css: true - htmlmin_opts: - remove_comments: true - - redirects: - redirect_maps: - 'dev/paid-seasonvar/00_install.md': 'dev/paid-seasonvar/install.md' - 'dev/paid-seasonvar/01_related.md': 'dev/paid-seasonvar/related.md' - 'dev/paid-seasonvar/02_faq.md': 'dev/paid-seasonvar/faq.md' - 'dev/telegramposting/00_install.md': 'dev/telegramposting/install.md' - 'dev/telegramposting/01_changelog.md': 'dev/telegramposting/changelog.md' + - git-revision-date + - tags + - search: + lang: + - ru + - de + - en + separator: '[\s\-,:!=\[\]()"/]+' + - autorefs: + resolve_closest: true + - minify: + minify_html: true + minify_js: true + minify_css: true + htmlmin_opts: + remove_comments: true + - redirects: + redirect_maps: + "dev/paid-seasonvar/00_install.md": "dev/paid-seasonvar/install.md" + "dev/paid-seasonvar/01_related.md": "dev/paid-seasonvar/related.md" + "dev/paid-seasonvar/02_faq.md": "dev/paid-seasonvar/faq.md" + "dev/telegramposting/00_install.md": "dev/telegramposting/install.md" + "dev/telegramposting/01_changelog.md": "dev/telegramposting/changelog.md" + - awesome-nav + - section-index + # - pdf-export # Temporarily disabled due to WeasyPrint dependencies + - git-revision-date-localized: + enable_creation_date: true + type: datetime + timezone: Europe/Berlin + - autolinks + - table-reader + - mermaid2 + - glightbox: + touchNavigation: true + loop: true + - import-statement + - markdownextradata + - meta-manager markdown_extensions: - - abbr - - attr_list - - md_in_html - - admonition - - pymdownx.details - - pymdownx.highlight: - linenums: true - anchor_linenums: true - use_pygments: true - auto_title: true - # - pymdownx.inlinehilite - - pymdownx.snippets - - pymdownx.superfences - - pymdownx.tabbed: - alternate_style: true - - tables - - footnotes - - pymdownx.critic - - pymdownx.caret - - pymdownx.keys - - pymdownx.mark - - pymdownx.tilde - - md_in_html - - toc: - permalink: true - toc_depth: 3 + - abbr + - attr_list + - md_in_html + - admonition + - pymdownx.details + - pymdownx.highlight: + linenums: true + anchor_linenums: true + use_pygments: true + auto_title: true + # - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:mermaid2.fence_mermaid_custom + - pymdownx.tabbed: + alternate_style: true + - tables + - footnotes + - pymdownx.critic + - pymdownx.caret + - pymdownx.keys + - pymdownx.mark + - pymdownx.tilde + - md_in_html + - toc: + permalink: true + toc_depth: 3 extra: - homepage: https://devcraft.club - analytics: - provider: google - property: G-6B531VYCBD - version: - provider: mike - generator: false - social: - - icon: fontawesome/solid/paper-plane - link: https://t.me/maharder - name: Telegram + homepage: https://devcraft.club + analytics: + provider: google + property: G-6B531VYCBD + version: + provider: mike + generator: false + social: + - icon: fontawesome/solid/paper-plane + link: https://t.me/maharder + name: Telegram + + search_metadata_file: docs/search-metadata.yml + search_synonyms: true + search_categories: true + search_weights: true + + +extra_javascript: + - extras/js/search-metadata-loader.js + - extras/js/search-enhancement.js extra_css: - - extras/css/all.min.css - - extras/css/styles.css \ No newline at end of file + - extras/css/all.min.css + - extras/css/search-enhancement.css + - extras/css/styles.css diff --git a/requirements.txt b/requirements.txt index 19daf62..4f9677a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,8 @@ jaraco-collections jaraco-functools jaraco.collections mike -mkdocs-awesome-pages-plugin +mkdocs-autorefs +mkdocs-awesome-nav mkdocs-git-revision-date-localized-plugin mkdocs-git-revision-date-plugin mkdocs-material From 0d4d4936b0505dcd9d3f7e15d38bb9861e90afe9 Mon Sep 17 00:00:00 2001 From: Maxim Harder Date: Fri, 25 Jul 2025 18:37:19 +0200 Subject: [PATCH 5/8] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20UPDATE=20=D0=98=D1=81?= =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D1=8F=D0=B5=D1=82=20=D1=84=D0=BE?= =?UTF-8?q?=D1=80=D0=BC=D0=B0=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=B8=20=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=82?= =?UTF-8?q?=D1=83=D1=80=D1=83=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавляет навигационные файлы для новых разделов документации - Исправляет ссылки и форматирование в markdown файлах - Обновляет стили CSS для корректного отображения статусов - Оптимизирует отступы и выравнивание в шаблонах и стилях - Добавляет скрипт установки зависимостей с принудительной переустановкой --- docs/.nav.yml | 5 + docs/dev/.nav.yml | 29 ++ docs/dev/composer.md | 23 +- docs/dev/crowdin.md | 21 +- docs/dev/db_manager/.nav.yml | 3 + docs/dev/db_manager/install.md | 9 + docs/dev/dle_faker/.nav.yml | 13 + docs/dev/dle_faker/changelog.md | 2 +- docs/dev/dle_faker/install.md | 7 + docs/dev/mhadmin/.nav.yml | 13 + docs/dev/mhadmin/backend/.nav.yml | 37 ++ docs/dev/mhadmin/backend/classes/MhDB.md | 2 +- docs/dev/mhadmin/changelog.md | 42 +-- docs/dev/mhadmin/frontend/.nav.yml | 4 + docs/dev/mhadmin/install.md | 7 + docs/dev/mystatus.md | 373 ++++++++++--------- docs/dev/notkinopoiskphp/.nav.yml | 12 + docs/dev/notkinopoiskphp/enums/.nav.yml | 6 + docs/dev/notkinopoiskphp/exceptions/.nav.yml | 6 + docs/dev/notkinopoiskphp/index.md | 22 +- docs/dev/notkinopoiskphp/interfaces/.nav.yml | 6 + docs/dev/notkinopoiskphp/models/.nav.yml | 6 + docs/dev/notkinopoiskphp/navigation-map.md | 2 +- docs/dev/notkinopoiskphp/responses/.nav.yml | 6 + docs/dev/notkinopoiskphp/services/.nav.yml | 6 + docs/dev/paid-seasonvar/.nav.yml | 5 + docs/dev/php_intl.md | 5 + docs/dev/releasestatus.md | 28 +- docs/dev/repost/.nav.yml | 3 + docs/dev/repost/index.md | 7 + docs/dev/schema.md | 250 ++++++++----- docs/dev/telegramposting/.nav.yml | 7 + docs/dev/telegramposting/install.md | 7 + docs/dev/usertags.md | 23 +- docs/dev/webmaster-verification.md | 6 + docs/dev/xflist.md | 6 + docs/dev/xfselect.md | 6 + docs/index.md | 16 +- docs/site/.nav.yml | 4 + docs/tags.md | 5 + install_deps.sh | 2 + mkdocs.yml | 217 ++--------- requirements.txt | 24 ++ 43 files changed, 789 insertions(+), 494 deletions(-) create mode 100644 docs/dev/.nav.yml create mode 100644 docs/dev/db_manager/.nav.yml create mode 100644 docs/dev/dle_faker/.nav.yml create mode 100644 docs/dev/mhadmin/.nav.yml create mode 100644 docs/dev/mhadmin/backend/.nav.yml create mode 100644 docs/dev/mhadmin/frontend/.nav.yml create mode 100644 docs/dev/notkinopoiskphp/.nav.yml create mode 100644 docs/dev/notkinopoiskphp/enums/.nav.yml create mode 100644 docs/dev/notkinopoiskphp/exceptions/.nav.yml create mode 100644 docs/dev/notkinopoiskphp/interfaces/.nav.yml create mode 100644 docs/dev/notkinopoiskphp/models/.nav.yml create mode 100644 docs/dev/notkinopoiskphp/responses/.nav.yml create mode 100644 docs/dev/notkinopoiskphp/services/.nav.yml create mode 100644 docs/dev/paid-seasonvar/.nav.yml create mode 100644 docs/dev/repost/.nav.yml create mode 100644 docs/dev/telegramposting/.nav.yml create mode 100644 docs/site/.nav.yml create mode 100644 docs/tags.md create mode 100755 install_deps.sh diff --git a/docs/.nav.yml b/docs/.nav.yml index e69de29..7c4774c 100644 --- a/docs/.nav.yml +++ b/docs/.nav.yml @@ -0,0 +1,5 @@ +nav: + - Главная: index.md + - Список тегов: tags.md + - site + - dev \ No newline at end of file diff --git a/docs/dev/.nav.yml b/docs/dev/.nav.yml new file mode 100644 index 0000000..662f29c --- /dev/null +++ b/docs/dev/.nav.yml @@ -0,0 +1,29 @@ +title: Разработки +nav: + - Инструкции: + - Composer: composer.md + - Crowdin: crowdin.md + - Инструкция по установке: install_instructions.md + - Инструкция по установке PHP intl: php_intl.md + - API Wrappers: + - KinopoiskUnoffical PHP: notkinopoiskphp/ + - Бесплатные разработки: + - DB Manager: db_manager/ + - RePost: repost/ + - DLE Faker: dle_faker/ + - Telegram Posting: telegramposting/ + - MH Admin: mhadmin/ + - MyStatus: mystatus.md + - Release Status: releasestatus.md + - Schema.Org: schema.md + - Пользовательские теги: usertags.md + - Webmaster Verification: webmaster-verification.md + - XF Lists: xflist.md + - XF Select: xfselect.md + - Хаки: + - Страницы как на КиноПоиске: hook-pages-like-kp.md + - Подсчёт символов в короткой новости: hook-shortstory-signs-count.md + - Платные разработки: + - Шаблон SeasonVar: paid-seasonvar/ + - Курс валют: paid-currencies_rate.md + - Последние новости списком: paid-lastnews.md \ No newline at end of file diff --git a/docs/dev/composer.md b/docs/dev/composer.md index fb9fa6e..314e8ff 100644 --- a/docs/dev/composer.md +++ b/docs/dev/composer.md @@ -1,6 +1,23 @@ +--- +tags: + - PHP + - Инструмент + - Инструкция + - composer +title: "Composer - DevCraft Документации" +description: "Инструкция по использованию Composer для управления зависимостями в проектах DevCraft." +keywords: "Composer, PHP, зависимости, DevCraft" +author: "Maxim Harder" +og:title: "Composer - управление зависимостями" +og:description: "Инструкция по использованию Composer для проектов DevCraft" +og:image: "https://devcraft.club/assets/images/logo.png" +twitter:title: "Composer - управление зависимостями" +twitter:description: "Инструкция по использованию Composer для проектов DevCraft" +--- + # Работа с композером (Composer) -Начиная с ~~*...не помню...*~~ версии как в DLE, так и у меня в админке я начал использовать композер для получения данных. А так-как многие жаловались на то, что вспомогательные файлы "очень" тяжёлые и их очень много, то я и решил устанавливать их уже по мере установки самой MH Admin. Но, не всегда это работает, к сожалению. Поэтому и решил добавить инстркуцию сюда. Начнём с азов. +Начиная с ~~_...не помню..._~~ версии как в DLE, так и у меня в админке я начал использовать композер для получения данных. А так-как многие жаловались на то, что вспомогательные файлы "очень" тяжёлые и их очень много, то я и решил устанавливать их уже по мере установки самой MH Admin. Но, не всегда это работает, к сожалению. Поэтому и решил добавить инстркуцию сюда. Начнём с азов. ## Установка @@ -16,7 +33,6 @@ Для композера есть ряд инструкций: [для линукса и мака](https://getcomposer.org/doc/00-intro.md#installation-linux-unix-macos) и [для винды](https://getcomposer.org/doc/00-intro.md#installation-windows). На винде часто может не распознавать композер в терминале. Обойтись можно и файлом [composer.phar](https://getcomposer.org/download/). - ## Установка зависимостей Переходим по пути `engine/inc/maharder/admin`. И в терминале запускаем `composer update`. Благодаря файлу **composer.json**, скрипт сам установит что и куда нужно. @@ -37,11 +53,10 @@ Fatal error: Uncaught RuntimeException: Ошибка (2): No composer.lock file 2. Загружаем его в корень сайта (структура папок сохранена). !!! info "Внимание!" - Все дальнейшие действия с композером выполняются при помощи флажка `--ignore-platform-req=php`. +Все дальнейшие действия с композером выполняются при помощи флажка `--ignore-platform-req=php`. Пример: ```bash composer update --ignore-platform-req=php ``` - diff --git a/docs/dev/crowdin.md b/docs/dev/crowdin.md index 617173f..bed3034 100644 --- a/docs/dev/crowdin.md +++ b/docs/dev/crowdin.md @@ -1,3 +1,19 @@ +--- +tags: + - Инструкция + - crowdin + - Локализация +title: "Crowdin - DevCraft Документации" +description: "Инструкция по работе с Crowdin для локализации проектов DevCraft." +keywords: "Crowdin, локализация, переводы, DevCraft" +author: "Maxim Harder" +og:title: "Crowdin - локализация проектов" +og:description: "Инструкция по работе с Crowdin для локализации" +og:image: "https://devcraft.club/assets/images/logo.png" +twitter:title: "Crowdin - локализация проектов" +twitter:description: "Инструкция по работе с Crowdin для локализации" +--- + # Система локализаций Crowdin Crowdin - платформа переводов локализаций. Именно эту платформу использую я для переводов. @@ -13,13 +29,14 @@ Crowdin - платформа переводов локализаций. Имен Переходим в проект и в адресной строке копируем последнее значение. Пример: + - **Исходная ссылка**: https://crowdin.com/project/ **dle_faker** - **Последнее значение**: dle_faker -Название проекта: **dle_faker** + Название проекта: **dle_faker** ## Как получить ID статистики? -Для этого нужно установить / добавить в проект дополнение **[Badges & Status Images](https://crowdin.com/store/apps/bds)**. +Для этого нужно установить / добавить в проект дополнение **[Badges & Status Images](https://crowdin.com/store/apps/bds)**. Переходим в Tools > Badges & Status Images и включаем его. ![Badges & Status Images](./assets/crowdin_tools.png) diff --git a/docs/dev/db_manager/.nav.yml b/docs/dev/db_manager/.nav.yml new file mode 100644 index 0000000..a67355e --- /dev/null +++ b/docs/dev/db_manager/.nav.yml @@ -0,0 +1,3 @@ +title: DB Manager +nav: + - Установка: install.md \ No newline at end of file diff --git a/docs/dev/db_manager/install.md b/docs/dev/db_manager/install.md index 8a34310..8e33142 100644 --- a/docs/dev/db_manager/install.md +++ b/docs/dev/db_manager/install.md @@ -1,3 +1,12 @@ +--- +tags: + - DLE + - Плагин + - PHP + - База данных + - бэкап +title: DB Manager +--- # DB Manager Данный проект предназначался изначально для того, чтобы без проблем экспортировать данные с внешними ключами (Foreign Keys). Стандартный метод экспорта (от Sypex Dumper) к сожалению игнорирует данные ключи. Из-за чего возникает проблема с восстановлением данных. А поскольку я часто использую эти ключи, то частенько получаю сообщения об этом. Посему было решено написать простой плагин, который позволяет быстро экспортировать данные с внешними ключами. diff --git a/docs/dev/dle_faker/.nav.yml b/docs/dev/dle_faker/.nav.yml new file mode 100644 index 0000000..62035ce --- /dev/null +++ b/docs/dev/dle_faker/.nav.yml @@ -0,0 +1,13 @@ +title: DLE Faker +nav: + - Установка: install.md + - Настройка: settings.md + - Теги для оформления шаблонов: + - Общие: tag_for_all.md + - Для пользователей: tag_for_users.md + - Для новостей: tag_for_news.md + - Генератор: + - Генерация пользователей: gen_users.md + - Генерация новостей: gen_news.md + - Функционал парсера данных: parser.md + - История изменений: changelog.md \ No newline at end of file diff --git a/docs/dev/dle_faker/changelog.md b/docs/dev/dle_faker/changelog.md index cce0c2f..be3c9d0 100644 --- a/docs/dev/dle_faker/changelog.md +++ b/docs/dev/dle_faker/changelog.md @@ -1,4 +1,4 @@ # История изменений -## 173.1.0 ++++ 173.1.0 Инициальная версия, с исходным функционалом \ No newline at end of file diff --git a/docs/dev/dle_faker/install.md b/docs/dev/dle_faker/install.md index 24ec756..5076056 100644 --- a/docs/dev/dle_faker/install.md +++ b/docs/dev/dle_faker/install.md @@ -1,3 +1,10 @@ +--- +tags: + - Faker + - PHP + - DLE + - Генератор +--- # DLE Faker **Ссылка на разработку**: [ Перейти к разработке](https://devcraft.club/downloads/dle-faker.29/) diff --git a/docs/dev/mhadmin/.nav.yml b/docs/dev/mhadmin/.nav.yml new file mode 100644 index 0000000..0246ad1 --- /dev/null +++ b/docs/dev/mhadmin/.nav.yml @@ -0,0 +1,13 @@ +title: MH Admin +nav: + - Установка: install.md + - Для пользователей: + - Настройка: frontend/manage.md + - Для разработчиков: + - Генератор модулей: new_module.md + - Главный файл в папке inc: backend/inc_main.md + - Модуль главной страницы: backend/index.md + - Генератор языковых файлов: generate_languages.md + - Back-End: backend/ + - Front-End: frontend/ + - История изменений: changelog.md \ No newline at end of file diff --git a/docs/dev/mhadmin/backend/.nav.yml b/docs/dev/mhadmin/backend/.nav.yml new file mode 100644 index 0000000..d5458fe --- /dev/null +++ b/docs/dev/mhadmin/backend/.nav.yml @@ -0,0 +1,37 @@ +title: Back-End +nav: + - Главная: index.md + - База данных: + - BasisModel: classes/BasisModel.md + - BasisRepository: classes/BasisRepository.md + - MhDB: classes/MhDB.md + - Классы: + - Admin: classes/Admin.md + - CacheControl: classes/CacheControl.md + - ComposerAction: classes/ComposerAction.md + - DataManager: classes/DataManager.md + - LogGenerator: classes/LogGenerator.md + - MhAjax: classes/MhAjax.md + - MhLog: classes/MhLog.md + - MhLogRepository: classes/MhLogRepository.md + - MhTranslation: classes/MhTranslation.md + - TwigFilter: classes/TwigFilter.md + - Ответы на запросы AJAX: + - AjaxAbstractResponse: classes/AjaxAbstractResponse.md + - ErrorResponseAjax: classes/ErrorResponseAjax.md + - SuccessResponseAjax: classes/SuccessResponseAjax.md + - Твиг (Twig) дополнения: + - AdminUrlExtension: classes/AdminUrlExtension.md + - DateTimeFormatter: classes/DateTimeFormatter.md + - DeclineExtension: classes/DeclineExtension.md + - MobileDetectExtension: classes/MobileDetectExtension.md + - TextLimiter: classes/TextLimiter.md + - Типы: + - AdminLink: classes/AdminLink.md + - Author: classes/Author.md + - BreadCrumb: classes/BreadCrumb.md + - Трейты: + - AssetsChecker: classes/AssetsChecker.md + - DataLoader: classes/DataLoader.md + - DleData: classes/DleData.md + - UpdatesChecker: classes/UpdatesChecker.md diff --git a/docs/dev/mhadmin/backend/classes/MhDB.md b/docs/dev/mhadmin/backend/classes/MhDB.md index a60e262..bf0a8d8 100644 --- a/docs/dev/mhadmin/backend/classes/MhDB.md +++ b/docs/dev/mhadmin/backend/classes/MhDB.md @@ -106,7 +106,7 @@ DatabaseConfig Генерируется с помощью метода `generateManager`. **Тип: -** +** DatabaseManager **Подробности:** diff --git a/docs/dev/mhadmin/changelog.md b/docs/dev/mhadmin/changelog.md index 9382714..728d7b7 100644 --- a/docs/dev/mhadmin/changelog.md +++ b/docs/dev/mhadmin/changelog.md @@ -1,18 +1,18 @@ # История изменений -## 173.3.3 ++/- 173.3.3 * **[FIX]** Исправлена работа с composer * **[FIX]** Исправлена проблема цикличности в классе переводов -## 173.3.2 ++/- 173.3.2 * **[FIX]** По неопределённой причине закинул старую версию поверх новой при мёрдже -## 173.3.1 ++/- 173.3.1 * **[FIX]** Исправлена загрузка скриптов при помощи composer * **[FIX]** Исправлены мелкие ошибки в коде * **[FIX]** Добавлены placeholder для стилей и скриптов -## 173.3.0 ++++ 173.3.0 - **[NEW]** Добавлена мультиязычная поддержка, перевод можно осуществить при помощи сервиса Crowdin. Ссылка в подвале каждого модуля. - **[NEW]** Добавлена возможность выводов логов на отдельной странице. - **[NEW]** Добавлено моделирование таблиц в базе данных при помощи Cycle ORM. Это поможет в дальнейшем создавать модули с более сложной структурой и работу с данными. @@ -27,29 +27,29 @@ - **[DELETE]** Удалены устаревшие и ненужные методы логирования. - **[DELETE]** Удалена замена иконок в админпанели DLE. -## 2.0.7 ++++ 2.0.7 * **[NEW]** Добавлен функционал проверки обновления плагина * **[UPDATE]** Изменён подход к некоторым классам * **[FIX]** Вернул поддержку PHP 7.2 * **[FIX]** Вернул поддержку PHP >= 8 -## 2.0.6.1 ++/- 2.0.6.1 * **[FIX]** Удалены остаточные файлы (кеш, конфигурации, ...) * **[FIX]** Вернул нужные, но удалённые файлы -## 2.0.6 ++++ 2.0.6 * **[NEW]** Добавлена функция отправки логов в телеграм [БЕТА] * **[FIX]** Исправил ошибку работы логирования -## 2.0.5 ++/- 2.0.5 * **[FIX]** Исправил работу моделей * **[FIX]** Исправил обработку кеша * **[FIX]** Почистил папки -## 2.0.4 ++/- 2.0.4 * **[FIX]** Обновил файлы для генерации модуля -## 2.0.3 ++++ 2.0.3 * **[NEW]** Добавлена возможность пользователю самому решать использовать ли в админпанеле обновлённые иконки или нет * **[NEW]** Добавлена возможность пользователю самому решать подключать в админке глобальную кнопку по очистке кеша или нет * **[NEW]** Дальнейшая работа с базой данных будет происходить только через созданные мною модели. Для этого будет использоваться функционал классов Model и Table @@ -60,11 +60,11 @@ * **[FIX]** Продокументировал классы с небольшими объяснениями, что делает какая функция и для чего используется та или иная переменная * **[DEL]** Удалён кошелёк WMR -## 2.0.2 ++/- 2.0.2 * **FIX**: Добавлена иконка * **FIX**: Исправлен файл AJAX -## 2.0.1 +--- 2.0.1 * В связи с некоторыми обстоятельствами пришлось отказаться от i18n модуля. Возможно он выйдет отдельным модулем. * Обновление зависимостей до минимально требуемой версии PHP 7.2 * Улучшена реструктуризация классов @@ -73,7 +73,7 @@ * Fontawesome были обновлены до 6ой версии * Добавлена проверка на целостность файлов и их обновления -## 2.0.0 ++++ 2.0.0 * Полностью новая админпанель, которая не зависит от оформления и функционала самой DLE * За основу панели был взят движок Twig, создавать свои шаблоны будет проще * Очищать кеш в админке стало проще - кнопка была выведена в ряд с "Добавить новость" и "Редактировать новости" @@ -86,35 +86,35 @@ * i18n: поддерживается машинный перевод через сервис RapidApi * i18n: динамичное использование, не требующее массивов данных - достаточно указать текст -## 1.7.1 ++/- 1.7.1 *небольшой фикс в языковом файле -## 1.7 ++/- 1.7 * Обновление иконок от fontawesome глобально * Обновление многих функций * Добавление языковых файлов (в дальнейшем будет проще локализовать модули) * Добавлен немецкий язык к оболочке -## 1.6 ++/- 1.6 * Фикс -## 1.5 ++++ 1.5 * Новые модальные и всплывающие окна * Улучшены некоторые функции * Добавлен скрипт DateTimePicker * Добавлены несколько украшательств -## 1.4 ++++ 1.4 * Вывод категорий -## 1.3 ++++ 1.3 * Автоматический вывод доп. полей в админке * Автоматический вывод пользователей * Исправлены баги в JS -## 1.1 ++++ 1.1 * Обновление до актуальных версий DLE * Мелкие правки -## 1.0.0 ++++ 1.0.0 * Основной релиз \ No newline at end of file diff --git a/docs/dev/mhadmin/frontend/.nav.yml b/docs/dev/mhadmin/frontend/.nav.yml new file mode 100644 index 0000000..b35c5de --- /dev/null +++ b/docs/dev/mhadmin/frontend/.nav.yml @@ -0,0 +1,4 @@ +title: Front-End +nav: + - Шаблоны: templates.md + - Настройка: manage.md \ No newline at end of file diff --git a/docs/dev/mhadmin/install.md b/docs/dev/mhadmin/install.md index c8fa7e3..b5e7ed6 100644 --- a/docs/dev/mhadmin/install.md +++ b/docs/dev/mhadmin/install.md @@ -1,3 +1,10 @@ +--- +tags: + - DLE + - PHP + - Плагин + - админпанель +--- # MH Admin **Ссылка на diff --git a/docs/dev/mystatus.md b/docs/dev/mystatus.md index ccf9c5e..ff34c0a 100644 --- a/docs/dev/mystatus.md +++ b/docs/dev/mystatus.md @@ -1,10 +1,24 @@ +--- +tags: + - DLE + - Плагин +title: "MyStatus - DevCraft Документации" +description: "Документация по модулю MyStatus для DLE. Отображение статуса пользователей." +keywords: "MyStatus, DLE, статус, пользователи, модуль" +author: "Maxim Harder" +og:title: "MyStatus - модуль статуса пользователей" +og:description: "Документация по модулю MyStatus для DLE" +og:image: "https://devcraft.club/assets/images/logo.png" +twitter:title: "MyStatus - модуль статуса пользователей" +twitter:description: "Документация по модулю MyStatus для DLE" +--- + # MyStatus - Статус сериалов **Ссылка на разработку**: [ Перейти к разработке](https://devcraft.club/downloads/mystatus.5/) **Версия модификации**: 1.0.2.3 - ## Установка - Залить файлы из папки uploads в корень сайт @@ -12,7 +26,8 @@ - В шаблонах откройте файл **fullstory.tpl** и пропишите в самом низу файла ```html -{include file="engine/modules/mystatus.php?mysid={myshows}&title={title}&news_id={news-id}"} +{include +file="engine/modules/mystatus.php?mysid={myshows}&title={title}&news_id={news-id}"} ``` В нужное место ставим @@ -36,10 +51,10 @@ ```html
- -
- -
+ +
+ +
``` @@ -198,221 +213,220 @@ $tpl->set( '{status}', $statusname ); - Открываем файл со стилями шаблона и в самый низ прописываем это ```css - - -.status, .status.normal { - float: right; - margin-top: -35px; - font-style: normal; - opacity: 0.6; - color: #000; - transition: all 0.3s; - font-weight: 700; +.status, +.status.normal { + float: right; + margin-top: -35px; + font-style: normal; + opacity: 0.6; + color: #000; + transition: all 0.3s; + font-weight: 700; } .status:hover { - opacity: 1; - cursor: pointer; - animation: shake linear 0.5s; - animation-iteration-count: 1; - transform-origin: 50% 0%; - -webkit-animation: shake linear 0.5s; - -webkit-animation-iteration-count: 1; - -webkit-transform-origin: 50% 0%; - -moz-animation: shake linear 0.5s; - -moz-animation-iteration-count: 1; - -moz-transform-origin: 50% 0%; - -o-animation: shake linear 0.5s; - -o-animation-iteration-count: 1; - -o-transform-origin: 50% 0%; - -ms-animation: shake linear 0.5s; - -ms-animation-iteration-count: 1; - -ms-transform-origin: 50% 0%; + opacity: 1; + cursor: pointer; + animation: shake linear 0.5s; + animation-iteration-count: 1; + transform-origin: 50% 0%; + -webkit-animation: shake linear 0.5s; + -webkit-animation-iteration-count: 1; + -webkit-transform-origin: 50% 0%; + -moz-animation: shake linear 0.5s; + -moz-animation-iteration-count: 1; + -moz-transform-origin: 50% 0%; + -o-animation: shake linear 0.5s; + -o-animation-iteration-count: 1; + -o-transform-origin: 50% 0%; + -ms-animation: shake linear 0.5s; + -ms-animation-iteration-count: 1; + -ms-transform-origin: 50% 0%; } .status.returningseries { - color: #ebffdd; - text-shadow: 0 0 1px black, 0 0 2px #5cad21, 0 0 3px #549e1e, 0 0 4px #3d8806; + color: #ebffdd; + text-shadow: 0 0 1px black, 0 0 2px #5cad21, 0 0 3px #549e1e, 0 0 4px #3d8806; } .status.canceledended { - color: #ffd4d4; - text-shadow: 0 0 1px black, 0 0 2px #ff0000, 0 0 3px #bf2727, 0 0 4px #792323; + color: #ffd4d4; + text-shadow: 0 0 1px black, 0 0 2px #ff0000, 0 0 3px #bf2727, 0 0 4px #792323; } .status.onhiatus { - color: #ffecbf; - text-shadow: 0 0 1px black, 0 0 2px #ffb300, 0 0 3px #cc971a, 0 0 4px #daa321; + color: #ffecbf; + text-shadow: 0 0 1px black, 0 0 2px #ffb300, 0 0 3px #cc971a, 0 0 4px #daa321; } .status.tbdonthebubble { - color: #e8faff; - text-shadow: 0 0 1px black, 0 0 2px #1bc1ff, 0 0 3px #4ae7ea, 0 0 4px #277890; + color: #e8faff; + text-shadow: 0 0 1px black, 0 0 2px #1bc1ff, 0 0 3px #4ae7ea, 0 0 4px #277890; } .status.pilotordered { - color: #e0ebff; - text-shadow: 0 0 1px black, 0 0 2px #0058ff, 0 0 3px #275dc5, 0 0 4px #93aee0; + color: #e0ebff; + text-shadow: 0 0 1px black, 0 0 2px #0058ff, 0 0 3px #275dc5, 0 0 4px #93aee0; } .status.newseries { - color: #fae2ff; - text-shadow: 0 0 1px black, 0 0 2px #d600ff, 0 0 3px #89319a, 0 0 4px #631a71; + color: #fae2ff; + text-shadow: 0 0 1px black, 0 0 2px #d600ff, 0 0 3px #89319a, 0 0 4px #631a71; } @keyframes shake { - 0% { - transform: rotate(0deg); - } - 20% { - transform: rotate(15deg); - } - 40% { - transform: rotate(-10deg); - } - 60% { - transform: rotate(5deg); - } - 80% { - transform: rotate(-5deg); - } - 100% { - transform: rotate(0deg); - } + 0% { + transform: rotate(0deg); + } + 20% { + transform: rotate(15deg); + } + 40% { + transform: rotate(-10deg); + } + 60% { + transform: rotate(5deg); + } + 80% { + transform: rotate(-5deg); + } + 100% { + transform: rotate(0deg); + } } @-moz-keyframes shake { - 0% { - -moz-transform: rotate(0deg); - } - 20% { - -moz-transform: rotate(15deg); - } - 40% { - -moz-transform: rotate(-10deg); - } - 60% { - -moz-transform: rotate(5deg); - } - 80% { - -moz-transform: rotate(-5deg); - } - 100% { - -moz-transform: rotate(0deg); - } + 0% { + -moz-transform: rotate(0deg); + } + 20% { + -moz-transform: rotate(15deg); + } + 40% { + -moz-transform: rotate(-10deg); + } + 60% { + -moz-transform: rotate(5deg); + } + 80% { + -moz-transform: rotate(-5deg); + } + 100% { + -moz-transform: rotate(0deg); + } } @-webkit-keyframes shake { - 0% { - -webkit-transform: rotate(0deg); - } - 20% { - -webkit-transform: rotate(15deg); - } - 40% { - -webkit-transform: rotate(-10deg); - } - 60% { - -webkit-transform: rotate(5deg); - } - 80% { - -webkit-transform: rotate(-5deg); - } - 100% { - -webkit-transform: rotate(0deg); - } + 0% { + -webkit-transform: rotate(0deg); + } + 20% { + -webkit-transform: rotate(15deg); + } + 40% { + -webkit-transform: rotate(-10deg); + } + 60% { + -webkit-transform: rotate(5deg); + } + 80% { + -webkit-transform: rotate(-5deg); + } + 100% { + -webkit-transform: rotate(0deg); + } } @-o-keyframes shake { - 0% { - -o-transform: rotate(0deg); - } - 20% { - -o-transform: rotate(15deg); - } - 40% { - -o-transform: rotate(-10deg); - } - 60% { - -o-transform: rotate(5deg); - } - 80% { - -o-transform: rotate(-5deg); - } - 100% { - -o-transform: rotate(0deg); - } + 0% { + -o-transform: rotate(0deg); + } + 20% { + -o-transform: rotate(15deg); + } + 40% { + -o-transform: rotate(-10deg); + } + 60% { + -o-transform: rotate(5deg); + } + 80% { + -o-transform: rotate(-5deg); + } + 100% { + -o-transform: rotate(0deg); + } } @-ms-keyframes shake { - 0% { - -ms-transform: rotate(0deg); - } - 20% { - -ms-transform: rotate(15deg); - } - 40% { - -ms-transform: rotate(-10deg); - } - 60% { - -ms-transform: rotate(5deg); - } - 80% { - -ms-transform: rotate(-5deg); - } - 100% { - -ms-transform: rotate(0deg); - } + 0% { + -ms-transform: rotate(0deg); + } + 20% { + -ms-transform: rotate(15deg); + } + 40% { + -ms-transform: rotate(-10deg); + } + 60% { + -ms-transform: rotate(5deg); + } + 80% { + -ms-transform: rotate(-5deg); + } + 100% { + -ms-transform: rotate(0deg); + } } @-o-keyframes animationFrames { - 0% { - -o-transform: rotate(0deg); - } - 20% { - -o-transform: rotate(15deg); - } - 40% { - -o-transform: rotate(-10deg); - } - 60% { - -o-transform: rotate(5deg); - } - 80% { - -o-transform: rotate(-5deg); - } - 100% { - -o-transform: rotate(0deg); - } + 0% { + -o-transform: rotate(0deg); + } + 20% { + -o-transform: rotate(15deg); + } + 40% { + -o-transform: rotate(-10deg); + } + 60% { + -o-transform: rotate(5deg); + } + 80% { + -o-transform: rotate(-5deg); + } + 100% { + -o-transform: rotate(0deg); + } } @-ms-keyframes animationFrames { - 0% { - -ms-transform: rotate(0deg); - } - 20% { - -ms-transform: rotate(15deg); - } - 40% { - -ms-transform: rotate(-10deg); - } - 60% { - -ms-transform: rotate(5deg); - } - 80% { - -ms-transform: rotate(-5deg); - } - 100% { - -ms-transform: rotate(0deg); - } + 0% { + -ms-transform: rotate(0deg); + } + 20% { + -ms-transform: rotate(15deg); + } + 40% { + -ms-transform: rotate(-10deg); + } + 60% { + -ms-transform: rotate(5deg); + } + 80% { + -ms-transform: rotate(-5deg); + } + 100% { + -ms-transform: rotate(0deg); + } } ``` - Создаём доп. поле -**Название поля**, **Описание поля** и **Категория** на своё усмотрение -**Тип поля**: Список -**Значение по умолчанию**: -Для DLE 11 и выше можно сделать так + **Название поля**, **Описание поля** и **Категория** на своё усмотрение + **Тип поля**: Список + **Значение по умолчанию**: + Для DLE 11 и выше можно сделать так ``` returningseries|Снимаетсяnewseries|Новинка @@ -433,6 +447,7 @@ tbdonthebubble ``` ## Пример подключения + ```HTML {include file="engine/modules/mystatus.php?mysid={myshows}&title={title}&news_id={news-id}"} ``` @@ -441,9 +456,8 @@ tbdonthebubble - **title** - к этому тегу подключается любая комбинация названия. Желательно иметь такую комбинацию: Русское название English Name (ГОД) - **news_id** - это определяющее самой новости - !!! warning "Внимание!" - Не будет работать, если подключать доп. поля в режиме перелинковки. Для этого нужно создавать и/ли определять доп. поля по новой +Не будет работать, если подключать доп. поля в режиме перелинковки. Для этого нужно создавать и/ли определять доп. поля по новой ## Доп. поля как перекрёстные ссылки @@ -451,14 +465,12 @@ tbdonthebubble Открываем **/engine/modules/show.full.php** и ищем - ```php $tpl->set( '{statuss}', $xfieldsdata[$mystatus_cfg['xfield']] ); ``` и выше или ниже ставим - ```php if($xfieldsdata['НАЗВАНИЕ_ДОП_ПОЛЯ'] != "" || !empty($xfieldsdata['НАЗВАНИЕ_ДОП_ПОЛЯ'])) { $ОПРЕДЕЛИТЕЛЬНОЕ_НАЗВАНИЕ = $xfieldsdata['НАЗВАНИЕ_ДОП_ПОЛЯ'] @@ -474,4 +486,5 @@ $tpl->set( '{НАЗВАНИЕ}', $ОПРЕДЕЛИТЕЛЬНОЕ_НАЗВАНИ Названия все на латинском! ## Установка на 13.х -Для этого достаточно установить архив из папки 13.х, залить файлы и папки engine и templates из папки 12.х в корень сайта и проследовать установке начиная с пункта шаблонов. \ No newline at end of file + +Для этого достаточно установить архив из папки 13.х, залить файлы и папки engine и templates из папки 12.х в корень сайта и проследовать установке начиная с пункта шаблонов. diff --git a/docs/dev/notkinopoiskphp/.nav.yml b/docs/dev/notkinopoiskphp/.nav.yml new file mode 100644 index 0000000..9f81990 --- /dev/null +++ b/docs/dev/notkinopoiskphp/.nav.yml @@ -0,0 +1,12 @@ +title: KinopoiskUnofficialTech PHP Wrapper +nav: + - Главная: index.md + - Клиент: client.md + - Абстрактные классы: abstract-classes.md + - Интерфейсы: interfaces/ + - Сервисы: services/ + - Модели: models/ + - Перечисления (Enums): enums/ + - Ответы API: responses/ + - Исключения: exceptions/ + - Навигационная карта: navigation-map.md \ No newline at end of file diff --git a/docs/dev/notkinopoiskphp/enums/.nav.yml b/docs/dev/notkinopoiskphp/enums/.nav.yml new file mode 100644 index 0000000..22a978e --- /dev/null +++ b/docs/dev/notkinopoiskphp/enums/.nav.yml @@ -0,0 +1,6 @@ +title: Перечисления (Enums) +nav: + - Оглавление: index.md + - "*" +sort: + type: alphabetical diff --git a/docs/dev/notkinopoiskphp/exceptions/.nav.yml b/docs/dev/notkinopoiskphp/exceptions/.nav.yml new file mode 100644 index 0000000..af4d5d0 --- /dev/null +++ b/docs/dev/notkinopoiskphp/exceptions/.nav.yml @@ -0,0 +1,6 @@ +title: Исключения (Exceptions) +nav: + - Оглавление: index.md + - "*" +sort: + type: alphabetical diff --git a/docs/dev/notkinopoiskphp/index.md b/docs/dev/notkinopoiskphp/index.md index b05aaf3..d75be9a 100644 --- a/docs/dev/notkinopoiskphp/index.md +++ b/docs/dev/notkinopoiskphp/index.md @@ -1,3 +1,21 @@ +--- +tags: + - PHP + - API + - Плагин + - Kinopoisk + - Wrapper +title: "NotKinopoiskPHP - DevCraft Документации" +description: "PHP wrapper для API КиноПоиска. Полная документация по использованию NotKinopoiskPHP." +keywords: "NotKinopoiskPHP, КиноПоиск, API, PHP, wrapper, kinopoiskunofficial.tech" +author: "Maxim Harder" +og:title: "NotKinopoiskPHP - PHP wrapper для КиноПоиска" +og:description: "PHP wrapper для API КиноПоиска с полной документацией" +og:image: "https://devcraft.club/assets/images/logo.png" +twitter:title: "NotKinopoiskPHP - PHP wrapper для КиноПоиска" +twitter:description: "PHP wrapper для API КиноПоиска с полной документацией" +--- + # NotKinopoisk PHP Library - Документация Полная документация PHP библиотеки для работы с Kinopoisk Unofficial API. @@ -7,7 +25,7 @@ ### 🚀 Быстрый старт - [Основной клиент](./client.md) - Главный класс для работы с API -- [Примеры использования](./https://github.com/DevCraftClub/NotKinopoiskPHP/tree/main/examples) - Готовые примеры кода +- [Примеры использования](https://github.com/DevCraftClub/NotKinopoiskPHP/tree/main/examples) - Готовые примеры кода - [Карта навигации](./navigation-map.md) - Интерактивная карта всей документации ### 📦 Основные компоненты @@ -247,5 +265,5 @@ composer phpcs ## 🔗 Полезные ссылки - [GitHub репозиторий](https://github.com/DevCraftClub/NotKinopoiskPHP/) -- [Kinopoisk Unofficial API](./https://kinopoiskapiunofficial.tech/) +- [Kinopoisk Unofficial API](https://kinopoiskapiunofficial.tech/) - [Composer](https://getcomposer.org/) diff --git a/docs/dev/notkinopoiskphp/interfaces/.nav.yml b/docs/dev/notkinopoiskphp/interfaces/.nav.yml new file mode 100644 index 0000000..a1d149f --- /dev/null +++ b/docs/dev/notkinopoiskphp/interfaces/.nav.yml @@ -0,0 +1,6 @@ +title: Интерфейсы (Interfaces) +nav: + - Оглавление: index.md + - "*" +sort: + type: alphabetical \ No newline at end of file diff --git a/docs/dev/notkinopoiskphp/models/.nav.yml b/docs/dev/notkinopoiskphp/models/.nav.yml new file mode 100644 index 0000000..82c9a10 --- /dev/null +++ b/docs/dev/notkinopoiskphp/models/.nav.yml @@ -0,0 +1,6 @@ +title: Модели (Models) +nav: + - Оглавление: index.md + - "*" +sort: + type: alphabetical \ No newline at end of file diff --git a/docs/dev/notkinopoiskphp/navigation-map.md b/docs/dev/notkinopoiskphp/navigation-map.md index ecbcdbd..027901d 100644 --- a/docs/dev/notkinopoiskphp/navigation-map.md +++ b/docs/dev/notkinopoiskphp/navigation-map.md @@ -103,7 +103,7 @@ - **[Главная страница](./index.md)** - Обзор библиотеки и быстрый старт - **[Основной клиент](./client.md)** - Главный класс для работы с API -- **[Примеры использования](./https://github.com/DevCraftClub/NotKinopoiskPHP/tree/main/examples)** - Готовые примеры кода +- **[Примеры использования](https://github.com/DevCraftClub/NotKinopoiskPHP/tree/main/examples)** - Готовые примеры кода ### 📦 Основные компоненты diff --git a/docs/dev/notkinopoiskphp/responses/.nav.yml b/docs/dev/notkinopoiskphp/responses/.nav.yml new file mode 100644 index 0000000..845201a --- /dev/null +++ b/docs/dev/notkinopoiskphp/responses/.nav.yml @@ -0,0 +1,6 @@ +title: Ответы API (Responses) +nav: + - Оглавление: index.md + - "*" +sort: + type: alphabetical diff --git a/docs/dev/notkinopoiskphp/services/.nav.yml b/docs/dev/notkinopoiskphp/services/.nav.yml new file mode 100644 index 0000000..f7437de --- /dev/null +++ b/docs/dev/notkinopoiskphp/services/.nav.yml @@ -0,0 +1,6 @@ +title: Сервисы (Services) +nav: + - Оглавление: index.md + - "*" +sort: + type: alphabetical diff --git a/docs/dev/paid-seasonvar/.nav.yml b/docs/dev/paid-seasonvar/.nav.yml new file mode 100644 index 0000000..5eb8bc0 --- /dev/null +++ b/docs/dev/paid-seasonvar/.nav.yml @@ -0,0 +1,5 @@ +title: Шаблон SeasonVar +nav: + - Установка: install.md + - Automatic Related: related.md + - F.A.Q.: faq.md \ No newline at end of file diff --git a/docs/dev/php_intl.md b/docs/dev/php_intl.md index b2e22d0..af85b51 100644 --- a/docs/dev/php_intl.md +++ b/docs/dev/php_intl.md @@ -1,3 +1,8 @@ +--- +tags: + - Инструкция + - PHP +--- # Установка PHP intl (php-intl) Для корректной эксплуатации админки, требуется активировать / установить дополнение PHP intl. diff --git a/docs/dev/releasestatus.md b/docs/dev/releasestatus.md index 5966c7b..a2d4a75 100644 --- a/docs/dev/releasestatus.md +++ b/docs/dev/releasestatus.md @@ -1,3 +1,18 @@ +--- +title: "Release Status - DevCraft Документации" +description: "Документация по модулю Release Status для DLE. Управление статусами релизов." +keywords: "Release Status, DLE, релизы, статусы, модуль" +author: "Maxim Harder" +og:title: "Release Status - модуль статусов релизов" +og:description: "Документация по модулю Release Status для DLE" +og:image: "https://devcraft.club/assets/images/logo.png" +twitter:title: "Release Status - модуль статусов релизов" +twitter:description: "Документация по модулю Release Status для DLE" +tags: + - DLE + - Плагин +--- + # ReleaseStatus **Ссылка на разработку**: [ Перейти к разработке](https://devcraft.club/downloads/releasestatus.6/) @@ -13,23 +28,23 @@ ```html ``` ```html - + ``` -- В этом же файле, или любом другом файле шаблона, прописываем это для вывода блока +- В этом же файле, или любом другом файле шаблона, прописываем это для вывода блока ```html {include file="engine/modules/releasestatus.php"} @@ -37,10 +52,9 @@ - На файл engine/data/releasestatus.php выставить права 666 - **Теги для release_block.tpl** -- {image}, {image-1}, {image-*} - При условии, что изображения выводятся из короткой или полной новости +- {image}, {image-1}, {image-\*} - При условии, что изображения выводятся из короткой или полной новости - {poster} - При условии, если изображение выводится из доп. поля - {title} - Выводит название в зависимости от вывода, настроенного в настройках - {type} - Выводит тип релиза, полнометражку или сериал diff --git a/docs/dev/repost/.nav.yml b/docs/dev/repost/.nav.yml new file mode 100644 index 0000000..471af16 --- /dev/null +++ b/docs/dev/repost/.nav.yml @@ -0,0 +1,3 @@ +title: Re Post - Пересылка новостей +nav: + - Оглавление: index.md \ No newline at end of file diff --git a/docs/dev/repost/index.md b/docs/dev/repost/index.md index 8f52919..5c70373 100644 --- a/docs/dev/repost/index.md +++ b/docs/dev/repost/index.md @@ -1,3 +1,10 @@ +--- +title: DLE Re:: Post +tags: + - PHP + - DLE + - В разработке +--- # DLE Re: Post Этот плагин основан на [Telegram Posting](../telegramposting/install.md). Он позволяет пересылать сообщения в социальные сети. Переработав старый плагин, можно динамически создавать несколько шаблонов и настраивать их под разные контексты. Во время разработки я подумал, что было бы здорово дополнить этот плагин различными дополнениями (социальными сетями). Теоретически, его можно настроить на отправку на различные сайты, а также в социальные сети. diff --git a/docs/dev/schema.md b/docs/dev/schema.md index 78afbec..d780a65 100644 --- a/docs/dev/schema.md +++ b/docs/dev/schema.md @@ -1,3 +1,21 @@ +--- +title: "Schema.Org - DevCraft Документации" +description: "Документация по интеграции Schema.Org разметки в проекты DevCraft." +keywords: "Schema.Org, разметка, SEO, структурированные данные" +author: "Maxim Harder" +og:title: "Schema.Org разметка" +og:description: "Документация по интеграции Schema.Org разметки" +og:image: "https://devcraft.club/assets/images/logo.png" +twitter:title: "Schema.Org разметка" +twitter:description: "Документация по интеграции Schema.Org разметки" +tags: + - DLE + - PHP + - Плагин + - Schema.org + - SEO +--- + # SCHEMA.ORG **Видеомикроразметка на DLE** @@ -43,11 +61,20 @@ UPDATE `dle_post` SET `xfields`=REPLACE(`xfields`,'СтароеНазвание' ```html
- - - - [xfgiven_trailer][/xfgiven_trailer] - [xfgiven_video][/xfgiven_video] + + + + [xfgiven_trailer][/xfgiven_trailer] [xfgiven_video][/xfgiven_video] +
``` а в конец @@ -59,24 +86,35 @@ UPDATE `dle_post` SET `xfields`=REPLACE(`xfields`,'СтароеНазвание' А тут ```html - + ``` я использовал [это](http://shop.sandev.pro/post/19.html). Если вы не можете себе этого позволить или не хотите обновить движок, то меняем на это ```html - + ``` **Х** меняем на ID категории, которая предназначена для "взрослых" (к.п. Жанр "Эротика"). А если и это вас не устраивает, то вот: ```html - + ``` ```html -[xfgiven_trailer][/xfgiven_trailer] -[xfgiven_video][/xfgiven_video] +[xfgiven_trailer][/xfgiven_trailer] [xfgiven_video][/xfgiven_video] ``` - **trailer**- это название поля, куда вводится ссылка на трейлер. Меняем на своё. @@ -94,13 +132,13 @@ UPDATE `dle_post` SET `xfields`=REPLACE(`xfields`,'СтароеНазвание' Можно и так сделать, в самом начале документа после ```html -
+
``` добавляем ```html - + ``` Однако, при этом теряется описание ссылки, посему выбирать вам. @@ -127,13 +165,20 @@ UPDATE `dle_post` SET `xfields`=REPLACE(`xfields`,'СтароеНазвание' ID с кинопоиска ```html -Открыть на кинопоиске +Открыть на кинопоиске ``` Постер ```html - + ``` Описание @@ -153,31 +198,46 @@ ID с кинопоиска чтобы обработать рейтинг (рейтинг в звёзды!), делаем следующее ```html -
- - [rating]
Рейтинг:
{rating}
(голосов: {vote-num})
[/rating] -
+
+ + [rating] +
Рейтинг:
+ {rating} +
(голосов: {vote-num})
+ [/rating] +
``` сделаем сразу разметку для видеофайла (рекомендуется трейлер). После ```html -
+
``` ```html
- - - - - - - - - -
+ + + + + + + + + +
``` поясню: @@ -193,21 +253,22 @@ ID с кинопоиска К тегу бади добавляем начальную структуру, должно выглядеть так ```html - + ``` ## **Комментарии** -Комментарии (ваш-сайт.ру/templates/шаблон/comments.tpl)** +Комментарии (ваш-сайт.ру/templates/шаблон/comments.tpl)\*\* Этот шаг опционален, не столь важен, но всё же рекомендую. В начало ```html
- - - {rating} + + + {rating} +
``` в конец @@ -226,7 +287,14 @@ ID с кинопоиска К аватарке добавляем значение image, должно выглядеть где-то так: ```html -Аватарка {login}'a +Аватарка {login}'a ``` сам комментарий так-же оборачиваем: @@ -238,24 +306,36 @@ ID с кинопоиска если используете рейтинг "нравится" и "не нравится", то оберните его следующим образом (тестировалось на дле 11) ```html -
- -
-
- {rating} - {rating} - [negative-comment][/negative-comment] - [positive-comment]{rating}[/positive-comment] - [neutral-comment]{rating}[/neutral-comment] - [negative-comment]{rating}[/negative-comment] - [positive-comment]0[/positive-comment] - [neutral-comment]0[/neutral-comment] -
-
[rating-minus]-[/rating-minus]
-
{rating}
-
[rating-plus]+[/rating-plus]
-
-
+
+ +
+
+ {rating} + {rating} + [negative-comment][/negative-comment] [positive-comment]{rating}[/positive-comment] [neutral-comment]{rating}[/neutral-comment] [negative-comment]{rating}[/negative-comment] [positive-comment]0[/positive-comment] [neutral-comment]0[/neutral-comment] +
+
[rating-minus]-[/rating-minus]
+
{rating}
+
[rating-plus]+[/rating-plus]
+
+
``` ## **Хлебные крошки** @@ -271,14 +351,12 @@ ID с кинопоиска в самый низ добавляем (если у вас шаблон на основе бутстрапа 3, то пропускаем шаг) ```css -ol[typeof=BreadcrumbList] -{ - display:inline-block; - list-style:none!important; +ol[typeof="BreadcrumbList"] { + display: inline-block; + list-style: none !important; } -ol[typeof=BreadcrumbList] > li -{ - display:inline-block; +ol[typeof="BreadcrumbList"] > li { + display: inline-block; } ``` @@ -288,14 +366,14 @@ ol[typeof=BreadcrumbList] > li ```php if ($config['speedbar'] AND !$view_template ) { - + $s_navigation = "" . $config['short_title'] . ""; if( $config['start_site'] == 3 AND $_SERVER['QUERY_STRING'] == "" AND !$_POST['do']) $titl_e = ""; if (intval($category_id)) $s_navigation .= " {$config['speedbar_separator']} " . get_breadcrumbcategories ( intval($category_id), $config['speedbar_separator'] ); elseif ($do == 'tags') { - + if ($config['allow_alt_url']) $s_navigation .= " {$config['speedbar_separator']} " . $lang['tag_cloud'] . " {$config['speedbar_separator']} " . $tag; else $s_navigation .= " {$config['speedbar_separator']} " . $lang['tag_cloud'] . " {$config['speedbar_separator']} " . $tag; @@ -305,15 +383,15 @@ if ($config['speedbar'] AND !$view_template ) { else { if ( isset($_GET['cstart']) AND intval($_GET['cstart']) > 1 ){ - + $page_extra = " {$config['speedbar_separator']} ".$lang['news_site']." ".intval($_GET['cstart']); - + } else $page_extra = ''; $s_navigation .= $page_extra; } - + $tpl->load_template ( 'speedbar.tpl' ); $tpl->set ( '{speedbar}', '' . stripslashes ( $s_navigation ) . '' ); $tpl->compile ( 'speedbar' ); @@ -326,7 +404,7 @@ if ($config['speedbar'] AND !$view_template ) { ```php if ($config['speedbar'] AND !$view_template ) { - + $s_navigation = "
  • " . $config['short_title'] . "
  • "; if( $config['start_site'] == 3 AND $_SERVER['QUERY_STRING'] == "" AND !$_POST['do']) $titl_e = ""; @@ -337,7 +415,7 @@ if ($config['speedbar'] AND !$view_template ) { $s_navigation .= " {$config['speedbar_separator']} " . $cat_breadcrumb[0]; } elseif ($do == 'tags') { - + if ($config['allow_alt_url']) $s_navigation .= " {$config['speedbar_separator']}
  • " . $lang['tag_cloud'] . "
  • {$config['speedbar_separator']}
  • " . $tag . "
  • "; else $s_navigation .= " {$config['speedbar_separator']}
  • " . $lang['tag_cloud'] . "
  • {$config['speedbar_separator']}
  • " . $tag . "
  • "; @@ -367,13 +445,13 @@ if ($config['speedbar'] AND !$view_template ) { if ( isset($_GET['cstart']) AND intval($_GET['cstart']) > 1 ){ $cat_breadcrumb[1]++; $page_extra = " {$config['speedbar_separator']}
  • ".$lang['news_site']." ".intval($_GET['cstart']) . "
  • "; - + } else $page_extra = ''; $s_navigation .= $page_extra; } - + $tpl->load_template ( 'speedbar.tpl' ); $tpl->set ( '{speedbar}', '
      ' . stripslashes ( $s_navigation ) . '
    ' ); $tpl->compile ( 'speedbar' ); @@ -386,28 +464,28 @@ if ($config['speedbar'] AND !$view_template ) { ```php function get_breadcrumbcategories($id, $separator="»") { - + global $cat_info, $config, $PHP_SELF; - + if( ! $id ) return; - + $parent_id = $cat_info[$id]['parentid']; - + if( $config['allow_alt_url'] ) $list = "{$cat_info[$id]['name']}"; else $list = "{$cat_info[$id]['name']}"; - + while ( $parent_id ) { - + if( $config['allow_alt_url'] ) $list = "{$cat_info[$parent_id]['name']}" . " {$separator} " . $list; else $list = "{$cat_info[$parent_id]['name']}" . " {$separator} " . $list; - + $parent_id = $cat_info[$parent_id]['parentid']; - if($parent_id) { + if($parent_id) { if( $cat_info[$parent_id]['parentid'] == $cat_info[$parent_id]['id'] ) break; - } + } } - + return $list; } ``` @@ -416,11 +494,11 @@ function get_breadcrumbcategories($id, $separator="»") { ``` function get_breadcrumbcategories($id, $separator="»") { - + global $cat_info, $config, $PHP_SELF, $dle_module; - + if( ! $id ) return; - + $parent_id = $cat_info[$id]['parentid']; $first_id_p = $parent_id; $i = 1; @@ -431,7 +509,7 @@ function get_breadcrumbcategories($id, $separator="»") { if($parent_id) if( $cat_info[$parent_id]['parentid'] == $cat_info[$parent_id]['id'] ) break; } - + $i += 1; $parent_id = $first_id_p; if($parent_id == 0) @@ -477,9 +555,9 @@ function get_breadcrumbcategories($id, $separator="»") { $list = "
  • {$cat_info[$parent_id]['name']}
  • " . " {$separator} " . $list; } $parent_id = $cat_info[$parent_id]['parentid']; - if($parent_id) { + if($parent_id) { if( $cat_info[$parent_id]['parentid'] == $cat_info[$parent_id]['id'] ) break; - } + } } return $list . "|" . $id_i; } diff --git a/docs/dev/telegramposting/.nav.yml b/docs/dev/telegramposting/.nav.yml new file mode 100644 index 0000000..fd1b38f --- /dev/null +++ b/docs/dev/telegramposting/.nav.yml @@ -0,0 +1,7 @@ +title: Telegram Posting (Не поддерживается!) +nav: + - Установка: install.md + - Подключение в сторонние разработки: custom_add.md + - Теги для оформления шаблонов: template_tags.md + - Настройка бота: bot.md + - Версии изменений: changelog.md \ No newline at end of file diff --git a/docs/dev/telegramposting/install.md b/docs/dev/telegramposting/install.md index 7bb0a43..7f1cc7e 100644 --- a/docs/dev/telegramposting/install.md +++ b/docs/dev/telegramposting/install.md @@ -1,3 +1,10 @@ +--- +tags: + - Не поддерживается + - PHP + - DLE + - Плагин +--- # Telegram Posting (ПЛАГИН НЕ ПОДДЕРЖИВАЕТСЯ) !!! danger "ПЛАГИН НЕ ПОДДЕРЖИВАЕТСЯ" diff --git a/docs/dev/usertags.md b/docs/dev/usertags.md index 4f1bbbf..464d349 100644 --- a/docs/dev/usertags.md +++ b/docs/dev/usertags.md @@ -1,3 +1,20 @@ +--- +title: "Пользовательские теги - DevCraft Документации" +description: "Документация по системе пользовательских тегов в проектах DevCraft." +keywords: "пользовательские теги, теги, система тегов, DevCraft" +author: "Maxim Harder" +og:title: "Пользовательские теги" +og:description: "Документация по системе пользовательских тегов" +og:image: "https://devcraft.club/assets/images/logo.png" +twitter:title: "Пользовательские теги" +twitter:description: "Документация по системе пользовательских тегов" +tags: + - DLE + - PHP + - Плагин + - Теги +--- + # Пользовательские теги **Ссылка на @@ -25,11 +42,11 @@ all|Уведомлять обо всём none|Не присылать уведомления + - **Добавить на страницу регистрации?:** Да (на ваше усмотрение) - **Поле может быть изменено пользователем?:** Да - **Сделать это поле личным?:** Да - 3. В админпанеле настройте скрипт под себя. 4. Открываем файл шаблона полной новости (**fullstory.tpl**) и в любое место добавляем следующую строку: @@ -43,7 +60,6 @@ - **modal** - для вывода модального окна - **functions** - для вывода функций - 6. Ещё можно дописать параметр **nameN**. Так будут называться ключевые функции для окон и кнопок. 7. Внешний вид всего настраивается в 3ёх шаблонах, что находятся в папке **ШАБЛОН/modules/tagsadd**. Настраивайте под себя. Важный аспект в файле с модальным окном: если изменяете его, то помните, что форме нужны те-же названия полей и @@ -53,7 +69,6 @@ - **{name}** - по умолчанию tagsadd. Глобальное название кнопок и функций - **{button}** - текст кнопки. Указывается в настройках - 9. **Поддерживаемые теги в шаблонах:** modal.tpl - **{name}** - по умолчанию tagsadd. Глобальное название кнопок и функций @@ -61,10 +76,8 @@ - **{news-id}** - ID Новости - **{user-id}** - ID текущего пользователя - 10. **Поддерживаемые теги в шаблонах:** js.tpl - **{name}** - по умолчанию tagsadd. Глобальное название кнопок и функций - **{AJAX}** - ссылка на папку site.ru/engine/ajax - **{THEME}** - актуальная папка шаблона сайта - diff --git a/docs/dev/webmaster-verification.md b/docs/dev/webmaster-verification.md index cd038be..3cb514f 100644 --- a/docs/dev/webmaster-verification.md +++ b/docs/dev/webmaster-verification.md @@ -1,3 +1,9 @@ +--- +tags: + - DLE + - PHP + - Плагин +--- # Webmaster Verification **Ссылка на diff --git a/docs/dev/xflist.md b/docs/dev/xflist.md index 98b6198..3a8766e 100644 --- a/docs/dev/xflist.md +++ b/docs/dev/xflist.md @@ -1,3 +1,9 @@ +--- +tags: + - DLE + - PHP + - Хак +--- # XF List Generator **Ссылка на diff --git a/docs/dev/xfselect.md b/docs/dev/xfselect.md index 8ecc5f2..a81c2f9 100644 --- a/docs/dev/xfselect.md +++ b/docs/dev/xfselect.md @@ -1,3 +1,9 @@ +--- +tags: + - DLE + - PHP + - Хак +--- # XF Select **Ссылка на diff --git a/docs/index.md b/docs/index.md index d190345..19c750e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,3 +1,17 @@ +--- +tags: + - Общее +title: "DevCraft Документации - Главная" +description: "Документация DevCraft - разработки для DLE и других CMS. Исходники всегда доступны на GitHub." +keywords: "DevCraft, DLE, CMS, документация, разработка" +author: "Maxim Harder" +og:title: "DevCraft Документации" +og:description: "Документация DevCraft - разработки для DLE и других CMS" +og:image: "https://devcraft.club/assets/images/logo.png" +twitter:title: "DevCraft Документации" +twitter:description: "Документация DevCraft - разработки для DLE и других CMS" +--- + # Добро пожаловать Этот сайт работает на простом HTML, CSS и JS. @@ -8,6 +22,6 @@ Всегда актуальная информация: -* Telegram: [@MaHarder](https://t.me/MaHarder) +- Telegram: [@DevCraft Support](https://t.me/devcraftclub_support) Я всегда отвечаю в телеграме, может дольше обычного, но отвечаю. diff --git a/docs/site/.nav.yml b/docs/site/.nav.yml new file mode 100644 index 0000000..353dd49 --- /dev/null +++ b/docs/site/.nav.yml @@ -0,0 +1,4 @@ +title: Пользование сайтом +nav: + - Как покупать: how-to-buy.md + - Тех. Поддержка: how-to-ask-for-support.md \ No newline at end of file diff --git a/docs/tags.md b/docs/tags.md new file mode 100644 index 0000000..28bd238 --- /dev/null +++ b/docs/tags.md @@ -0,0 +1,5 @@ +# Ключевые слова + +Перечень ключевых слов в документации: + +[TAGS] \ No newline at end of file diff --git a/install_deps.sh b/install_deps.sh new file mode 100755 index 0000000..bc7f03c --- /dev/null +++ b/install_deps.sh @@ -0,0 +1,2 @@ +./freeze.sh +pip install --force-reinstall --no-cache-dir -r requirements.txt diff --git a/mkdocs.yml b/mkdocs.yml index 1abfd37..d2f48d0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -7,176 +7,9 @@ site_author: Maxim Harder site_description: "Сайт с документациями, который построен на Python и системе MKDocs. Исходники всегда будут доступны на GitHub." copyright: "Документация защищена лицензией MIT license" -nav: - - Главная: index.md - - Пользование сайтом: - - Как покупать: site/how-to-buy.md - - Тех. Поддержка: site/how-to-ask-for-support.md - - Инструкции: - - Composer: dev/composer.md - - Crowdin: dev/crowdin.md - - Инструкция по установке: dev/install_instructions.md - - Инструкция по установке PHP intl: dev/php_intl.md - - Работа со сторонними API (Wrapper): - - NotKinopoisk PHP Wrapper: - - Главная: dev/notkinopoiskphp/index.md - - Клиент: dev/notkinopoiskphp/client.md - - Абстрактные классы: dev/notkinopoiskphp/abstract-classes.md - - Интерфейсы: - - Оглавление: dev/notkinopoiskphp/interfaces/index.md - - ModelInterface: dev/notkinopoiskphp/interfaces/model-interface.md - - ResponseInterface: dev/notkinopoiskphp/interfaces/response-interface.md - - Сервисы: - - Оглавление: dev/notkinopoiskphp/services/index.md - - FilmService: dev/notkinopoiskphp/services/film-service.md - - PersonService: dev/notkinopoiskphp/services/person-service.md - - MediaService: dev/notkinopoiskphp/services/media-service.md - - UserService: dev/notkinopoiskphp/services/user-service.md - - Модели: - - Оглавление: dev/notkinopoiskphp/models/index.md - - Film: dev/notkinopoiskphp/models/film.md - - Person: dev/notkinopoiskphp/models/person.md - - Staff: dev/notkinopoiskphp/models/staff.md - - Review: dev/notkinopoiskphp/models/review.md - - Fact: dev/notkinopoiskphp/models/fact.md - - Image: dev/notkinopoiskphp/models/image.md - - Video: dev/notkinopoiskphp/models/video.md - - BoxOffice: dev/notkinopoiskphp/models/box-office.md - - Distribution: dev/notkinopoiskphp/models/distribution.md - - Award: dev/notkinopoiskphp/models/award.md - - Genre: dev/notkinopoiskphp/models/genre.md - - Country: dev/notkinopoiskphp/models/country.md - - Season: dev/notkinopoiskphp/models/season.md - - Episode: dev/notkinopoiskphp/models/episode.md - - PersonFilm: dev/notkinopoiskphp/models/person-film.md - - PersonSpouse: dev/notkinopoiskphp/models/person-spouse.md - - FilmSearchResult: dev/notkinopoiskphp/models/film-search-result.md - - FilmCollection: dev/notkinopoiskphp/models/film-collection.md - - RelatedFilm: dev/notkinopoiskphp/models/related-film.md - - Premiere: dev/notkinopoiskphp/models/premiere.md - - UserVote: dev/notkinopoiskphp/models/user-vote.md - - ExternalSource: dev/notkinopoiskphp/models/external-source.md - - Filters: dev/notkinopoiskphp/models/filters.md - - Перечисления (Enums): - - Оглавление: dev/notkinopoiskphp/enums/index.md - - AccountType: dev/notkinopoiskphp/enums/account-type.md - - ContentType: dev/notkinopoiskphp/enums/content-type.md - - DistributionSubType: dev/notkinopoiskphp/enums/distribution-sub-type.md - - ImageType: dev/notkinopoiskphp/enums/image-type.md - - VideoSite: dev/notkinopoiskphp/enums/video-site.md - - BoxOfficeType: dev/notkinopoiskphp/enums/box-office-type.md - - DistributionType: dev/notkinopoiskphp/enums/distribution-type.md - - FactType: dev/notkinopoiskphp/enums/fact-type.md - - ProductionStatus: dev/notkinopoiskphp/enums/production-status.md - - ReviewType: dev/notkinopoiskphp/enums/review-type.md - - ReviewOrder: dev/notkinopoiskphp/enums/review-order.md - - FilmOrder: dev/notkinopoiskphp/enums/film-order.md - - CollectionType: dev/notkinopoiskphp/enums/collection-type.md - - RelationType: dev/notkinopoiskphp/enums/relation-type.md - - ProfessionKey: dev/notkinopoiskphp/enums/profession-key.md - - Sex: dev/notkinopoiskphp/enums/sex.md - - Month: dev/notkinopoiskphp/enums/month.md - - ApiVersion: dev/notkinopoiskphp/enums/api-version.md - - Ответы API: - - Оглавление: dev/notkinopoiskphp/responses/index.md - - DefaultResponse: dev/notkinopoiskphp/responses/default-response.md - - PaginatedResponse: dev/notkinopoiskphp/responses/paginated-response.md - - KeywordSearchResponse: dev/notkinopoiskphp/responses/keyword-search-response.md - - Исключения: - - Оглавление: dev/notkinopoiskphp/exceptions/index.md - - ApiException: dev/notkinopoiskphp/exceptions/api-exception.md - - InvalidApiKeyException: dev/notkinopoiskphp/exceptions/invalid-api-key-exception.md - - RateLimitException: dev/notkinopoiskphp/exceptions/rate-limit-exception.md - - ResourceNotFoundException: dev/notkinopoiskphp/exceptions/resource-not-found-exception.md - - Навигационная карта: dev/notkinopoiskphp/navigation-map.md - - Бесплатные разработки: - - DB Manager: - - Установка: dev/db_manager/install.md - - RePost: - - Главная: dev/repost/index.md - - DLE Faker: - - Установка: dev/dle_faker/install.md - - Настройка: dev/dle_faker/settings.md - - Теги для оформления шаблонов: - - Общие: dev/dle_faker/tag_for_all.md - - Для пользователей: dev/dle_faker/tag_for_users.md - - Пля новостей: dev/dle_faker/tag_for_news.md - - Генератор: - - Генерация пользователей: dev/dle_faker/gen_users.md - - Генерация новостей: dev/dle_faker/gen_news.md - - Функционал парсера данных: dev/dle_faker/parser.md - - История изменений: dev/dle_faker/changelog.md - - Telegram Posting (Не поддерживается!): - - Установка: dev/telegramposting/install.md - - Подключение в сторонние разработки: dev/telegramposting/custom_add.md - - Теги для оформления шаблонов: dev/telegramposting/template_tags.md - - Настройка бота: dev/telegramposting/bot.md - - Версии изменений: dev/telegramposting/changelog.md - - MH Admin: - - Установка: dev/mhadmin/install.md - - Для пользователей: - - Настройка: dev/mhadmin/frontend/manage.md - - Для разработчиков: - - Генератор модулей: dev/mhadmin/new_module.md - - Главный файл в папке inc: dev/mhadmin/backend/inc_main.md - - Модуль главной страницы: dev/mhadmin/backend/index.md - - Генератор языковых файлов: dev/mhadmin/generate_languages.md - - Back-End: - - Главная: dev/mhadmin/backend/index.md - - База данных: - - BasisModel: dev/mhadmin/backend/classes/BasisModel.md - - BasisRepository: dev/mhadmin/backend/classes/BasisRepository.md - - MhDB: dev/mhadmin/backend/classes/MhDB.md - - Классы: - - Admin: dev/mhadmin/backend/classes/Admin.md - - CacheControl: dev/mhadmin/backend/classes/CacheControl.md - - ComposerAction: dev/mhadmin/backend/classes/ComposerAction.md - - DataManager: dev/mhadmin/backend/classes/DataManager.md - - LogGenerator: dev/mhadmin/backend/classes/LogGenerator.md - - MhAjax: dev/mhadmin/backend/classes/MhAjax.md - - MhLog: dev/mhadmin/backend/classes/MhLog.md - - MhLogRepository: dev/mhadmin/backend/classes/MhLogRepository.md - - MhTranslation: dev/mhadmin/backend/classes/MhTranslation.md - - TwigFilter: dev/mhadmin/backend/classes/TwigFilter.md - - Ответы на запросы AJAX: - - AjaxAbstractResponse: dev/mhadmin/backend/classes/AjaxAbstractResponse.md - - ErrorResponseAjax: dev/mhadmin/backend/classes/ErrorResponseAjax.md - - SuccessResponseAjax: dev/mhadmin/backend/classes/SuccessResponseAjax.md - - Твиг (Twig) дополнения: - - AdminUrlExtension: dev/mhadmin/backend/classes/AdminUrlExtension.md - - DateTimeFormatter: dev/mhadmin/backend/classes/DateTimeFormatter.md - - DeclineExtension: dev/mhadmin/backend/classes/DeclineExtension.md - - MobileDetectExtension: dev/mhadmin/backend/classes/MobileDetectExtension.md - - TextLimiter: dev/mhadmin/backend/classes/TextLimiter.md - - Типы: - - AdminLink: dev/mhadmin/backend/classes/AdminLink.md - - Author: dev/mhadmin/backend/classes/Author.md - - BreadCrumb: dev/mhadmin/backend/classes/BreadCrumb.md - - Трейты: - - AssetsChecker: dev/mhadmin/backend/classes/AssetsChecker.md - - DataLoader: dev/mhadmin/backend/classes/DataLoader.md - - DleData: dev/mhadmin/backend/classes/DleData.md - - UpdatesChecker: dev/mhadmin/backend/classes/UpdatesChecker.md - - Front-End: - - Шаблоны: dev/mhadmin/frontend/templates.md - - История изменений: dev/mhadmin/changelog.md - - MyStatus: dev/mystatus.md - - Release Status: dev/releasestatus.md - - Schema.Org: dev/schema.md - - Пользовательские теги: dev/usertags.md - - Webmaster Verification: dev/webmaster-verification.md - - XF Lists: dev/xflist.md - - XF Select: dev/xfselect.md - - Хаки: - - Страницы как на КиноПоиске: dev/hook-pages-like-kp.md - - Подсчёт символов в короткой новости: dev/hook-shortstory-signs-count.md - - Платные разработки: - - Шаблон SeasonVar: - - Установка: dev/paid-seasonvar/install.md - - Automatic Related: dev/paid-seasonvar/related.md - - F.A.Q.: dev/paid-seasonvar/faq.md - - Курс валют: dev/paid-currencies_rate.md - - Последние новости списком: dev/paid-lastnews.md +# Навигация автоматически генерируется плагином awesome-nav +# на основе структуры папок в docs/ +# Для кастомизации можно использовать файлы .nav.yml в папках theme: name: material @@ -193,23 +26,33 @@ theme: - navigation.sections - toc.follow - navigation.top + - search.suggest + - search.highlight + - search.share + - header.autohide palette: primary: indigo - extra_css: - - extras/css/styles.css - - extras/css/search-enhancement.css - extra_javascript: - - extras/js/search-enhancement.js + consent: + title: Cookie сайта + description: > # (1) + Мы используем файлы cookie для распознавания ваших повторных посещений и предпочтений, а также + для оценки эффективности нашей документации и того, находят ли пользователи + то, что ищут. Давая своё согласие, вы помогаете нам + улучшать нашу документацию. plugins: - - git-revision-date - - tags - search: lang: - ru - de - en separator: '[\s\-,:!=\[\]()"/]+' + - localsearch + - exclude + - exclude-search + - git-revision-date + - tags: + tags_file: tags.md - autorefs: resolve_closest: true - minify: @@ -227,25 +70,34 @@ plugins: "dev/telegramposting/01_changelog.md": "dev/telegramposting/changelog.md" - awesome-nav - section-index - # - pdf-export # Temporarily disabled due to WeasyPrint dependencies - git-revision-date-localized: enable_creation_date: true type: datetime timezone: Europe/Berlin - autolinks - - table-reader - mermaid2 - glightbox: touchNavigation: true loop: true - import-statement - - markdownextradata - meta-manager + - git-authors + - enumerate-headings: + toc_depth: 3 + increment_across_pages: false + - htmlproofer + - link-marker + - neoteroi.mkdocsoad: + use_pymdownx: true markdown_extensions: - abbr - attr_list - md_in_html + - markdown_version_annotations: + version_added_title: "\\1: Добавлен новый функционал" + version_changed_title: "\\1: Изменения в существующем функционале" + version_removed_title: "\\1: Удаление функционала или очистка проекта" - admonition - pymdownx.details - pymdownx.highlight: @@ -292,10 +144,9 @@ extra: search_categories: true search_weights: true - extra_javascript: - - extras/js/search-metadata-loader.js - - extras/js/search-enhancement.js + - extras/js/search-metadata-loader.js + - extras/js/search-enhancement.js extra_css: - extras/css/all.min.css diff --git a/requirements.txt b/requirements.txt index 4f9677a..b1c79b2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,21 +1,45 @@ backports.tarfile +brotli cairosvg +codespell docopt inflect jaraco-collections jaraco-functools jaraco.collections +jsonschema2md +markdown-version-annotations mike +mkdocs-autolinks-plugin mkdocs-autorefs mkdocs-awesome-nav +mkdocs-enumerate-headings-plugin +mkdocs-exclude +mkdocs-exclude-search +mkdocs-git-authors-plugin mkdocs-git-revision-date-localized-plugin mkdocs-git-revision-date-plugin +mkdocs-glightbox +mkdocs-htmlproofer-plugin +mkdocs-import-statement-plugin +mkdocs-link-marker +mkdocs-localsearch mkdocs-material +mkdocs-mermaid2-plugin +mkdocs-meta-manager mkdocs-minify-plugin mkdocs-redirects mkdocs-section-index +mkdocs-spellcheck +neoteroi-mkdocs +pandas pip-chill +pygtrie regex shtab +symspellpy +tabulate tomli +weasyprint yarg +zopfli From a0a206e30defec8f36a964359a30d8e9d38d78cf Mon Sep 17 00:00:00 2001 From: Maxim Harder Date: Fri, 25 Jul 2025 18:40:34 +0200 Subject: [PATCH 6/8] =?UTF-8?q?=F0=9F=93=9D=20DOCS=20=D0=A3=D0=B4=D0=B0?= =?UTF-8?q?=D0=BB=D1=8F=D1=8E=20=D1=83=D1=81=D1=82=D0=B0=D1=80=D0=B5=D0=B2?= =?UTF-8?q?=D1=88=D1=83=D1=8E=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=B0=D1=86=D0=B8=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Удалены файлы docs/dev/crowdin.md и docs/dev/releasestatus.md - Очистка репозитория от неактуальной документации для упрощения поддержки --- docs/dev/crowdin.md | 2 +- docs/dev/releasestatus.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/dev/crowdin.md b/docs/dev/crowdin.md index bed3034..dd2937f 100644 --- a/docs/dev/crowdin.md +++ b/docs/dev/crowdin.md @@ -41,4 +41,4 @@ Crowdin - платформа переводов локализаций. Имен ![Badges & Status Images](./assets/crowdin_tools.png) После включения, скроллим вниз и в окне с кодами копируем код статистики. -![Код статистики](./assets/crowdin_stats_code.png) \ No newline at end of file +![Код статистики](./assets/crowdin_stats_code.png) diff --git a/docs/dev/releasestatus.md b/docs/dev/releasestatus.md index a2d4a75..f0e0f0b 100644 --- a/docs/dev/releasestatus.md +++ b/docs/dev/releasestatus.md @@ -76,4 +76,4 @@ tags:
    -
    \ No newline at end of file +
    From 14c87dddb1deb0dc17f811dd9696f4f4e7627d0c Mon Sep 17 00:00:00 2001 From: Maxim Harder Date: Fri, 25 Jul 2025 19:29:41 +0200 Subject: [PATCH 7/8] =?UTF-8?q?=F0=9F=93=9D=20DOCS=20=D0=94=D0=BE=D0=B1?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D1=8F=D0=B5=D1=82=20=D0=BC=D0=B5=D1=82=D0=B0?= =?UTF-8?q?=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D0=B5=20=D0=B8=20=D0=BE=D0=BF?= =?UTF-8?q?=D0=B8=D1=81=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B2=20=D0=B4=D0=BE?= =?UTF-8?q?=D0=BA=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=86=D0=B8=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлены frontmatter блоки с тегами, описаниями, ключевыми словами и авторами для улучшения SEO и структурирования документации - Обновлены файлы документации по моделям, сервисам, исключениям и плагинам - Исправлены опечатки и улучшена читаемость в инструкциях и описаниях - Обновлены ссылки и изображения для корректного отображения в соцсетях и предпросмотре страниц --- docs/dev/.nav.yml | 2 +- docs/dev/composer.md | 4 +- docs/dev/crowdin.md | 4 +- docs/dev/currencies_rate.md | 270 ------------------ docs/dev/dle_faker/changelog.md | 17 ++ docs/dev/dle_faker/gen_news.md | 17 ++ docs/dev/dle_faker/gen_users.md | 17 ++ docs/dev/dle_faker/install.md | 1 + docs/dev/dle_faker/parser.md | 18 ++ docs/dev/dle_faker/settings.md | 18 ++ docs/dev/dle_faker/tag_for_all.md | 17 ++ docs/dev/dle_faker/tag_for_news.md | 17 ++ docs/dev/dle_faker/tag_for_users.md | 17 ++ docs/dev/hook-pages-like-kp.md | 16 ++ docs/dev/hook-shortstory-signs-count.md | 16 ++ docs/dev/install_instructions.md | 14 + docs/dev/lastnews.md | 35 --- docs/dev/mhadmin/backend/classes/Admin.md | 17 ++ docs/dev/mhadmin/backend/classes/AdminLink.md | 17 ++ .../backend/classes/AdminUrlExtension.md | 17 ++ .../backend/classes/AjaxAbstractResponse.md | 17 ++ .../mhadmin/backend/classes/AssetsChecker.md | 19 +- docs/dev/mhadmin/backend/classes/Author.md | 17 ++ .../dev/mhadmin/backend/classes/BasisModel.md | 17 ++ .../backend/classes/BasisRepository.md | 17 ++ .../dev/mhadmin/backend/classes/BreadCrumb.md | 17 ++ .../mhadmin/backend/classes/CacheControl.md | 17 ++ .../mhadmin/backend/classes/ComposerAction.md | 17 ++ .../dev/mhadmin/backend/classes/DataLoader.md | 17 ++ .../mhadmin/backend/classes/DataManager.md | 17 ++ .../backend/classes/DateTimeFormatter.md | 17 ++ .../backend/classes/DeclineExtension.md | 17 ++ docs/dev/mhadmin/backend/classes/DleData.md | 17 ++ .../backend/classes/ErrorResponseAjax.md | 17 ++ .../mhadmin/backend/classes/LogGenerator.md | 17 ++ docs/dev/mhadmin/backend/classes/MhAjax.md | 17 ++ docs/dev/mhadmin/backend/classes/MhDB.md | 17 ++ docs/dev/mhadmin/backend/classes/MhLog.md | 17 ++ .../backend/classes/MhLogRepository.md | 17 ++ .../mhadmin/backend/classes/MhTranslation.md | 17 ++ .../backend/classes/MobileDetectExtension.md | 17 ++ .../backend/classes/SuccessResponseAjax.md | 17 ++ .../mhadmin/backend/classes/TextLimiter.md | 17 ++ .../dev/mhadmin/backend/classes/TwigFilter.md | 17 ++ .../mhadmin/backend/classes/UpdatesChecker.md | 17 ++ docs/dev/mhadmin/backend/inc_main.md | 17 ++ docs/dev/mhadmin/backend/index.md | 17 ++ docs/dev/mhadmin/changelog.md | 17 ++ docs/dev/mhadmin/frontend/manage.md | 17 ++ docs/dev/mhadmin/frontend/templates.md | 17 ++ docs/dev/mhadmin/generate_languages.md | 17 ++ docs/dev/mhadmin/new_module.md | 17 ++ docs/dev/mystatus.md | 6 +- docs/dev/notkinopoiskphp/abstract-classes.md | 16 ++ docs/dev/notkinopoiskphp/client.md | 16 ++ .../dev/notkinopoiskphp/enums/account-type.md | 16 ++ docs/dev/notkinopoiskphp/enums/api-version.md | 16 ++ .../notkinopoiskphp/enums/box-office-type.md | 16 ++ .../notkinopoiskphp/enums/collection-type.md | 16 ++ .../dev/notkinopoiskphp/enums/content-type.md | 16 ++ .../enums/distribution-sub-type.md | 16 ++ .../enums/distribution-type.md | 16 ++ docs/dev/notkinopoiskphp/enums/fact-type.md | 16 ++ docs/dev/notkinopoiskphp/enums/film-order.md | 16 ++ docs/dev/notkinopoiskphp/enums/image-type.md | 16 ++ docs/dev/notkinopoiskphp/enums/index.md | 16 ++ docs/dev/notkinopoiskphp/enums/month.md | 16 ++ .../enums/production-status.md | 16 ++ .../notkinopoiskphp/enums/profession-key.md | 16 ++ .../notkinopoiskphp/enums/relation-type.md | 16 ++ .../dev/notkinopoiskphp/enums/review-order.md | 16 ++ docs/dev/notkinopoiskphp/enums/review-type.md | 16 ++ docs/dev/notkinopoiskphp/enums/sex.md | 16 ++ docs/dev/notkinopoiskphp/enums/video-site.md | 16 ++ .../exceptions/api-exception.md | 16 ++ docs/dev/notkinopoiskphp/exceptions/index.md | 16 ++ .../exceptions/invalid-api-key-exception.md | 16 ++ .../exceptions/kp-validation-exception.md | 16 ++ .../exceptions/rate-limit-exception.md | 16 ++ .../resource-not-found-exception.md | 16 ++ docs/dev/notkinopoiskphp/index.md | 4 +- docs/dev/notkinopoiskphp/interfaces/index.md | 16 ++ .../interfaces/model-interface.md | 16 ++ .../interfaces/response-interface.md | 16 ++ .../notkinopoiskphp/models/api-key-info.md | 16 ++ .../notkinopoiskphp/models/api-key-qouta.md | 16 ++ docs/dev/notkinopoiskphp/models/award.md | 16 ++ docs/dev/notkinopoiskphp/models/box-office.md | 16 ++ docs/dev/notkinopoiskphp/models/country.md | 16 ++ .../notkinopoiskphp/models/distribution.md | 16 ++ docs/dev/notkinopoiskphp/models/episode.md | 16 ++ .../notkinopoiskphp/models/external-source.md | 16 ++ docs/dev/notkinopoiskphp/models/fact.md | 16 ++ .../notkinopoiskphp/models/film-collection.md | 16 ++ .../models/film-search-result.md | 16 ++ docs/dev/notkinopoiskphp/models/film.md | 16 ++ docs/dev/notkinopoiskphp/models/filters.md | 16 ++ docs/dev/notkinopoiskphp/models/genre.md | 16 ++ docs/dev/notkinopoiskphp/models/image.md | 16 ++ docs/dev/notkinopoiskphp/models/index.md | 16 ++ docs/dev/notkinopoiskphp/models/media-post.md | 16 ++ .../models/person-by-name-result.md | 16 ++ .../dev/notkinopoiskphp/models/person-film.md | 16 ++ .../notkinopoiskphp/models/person-spouse.md | 16 ++ docs/dev/notkinopoiskphp/models/person.md | 16 ++ docs/dev/notkinopoiskphp/models/premiere.md | 16 ++ .../notkinopoiskphp/models/related-film.md | 16 ++ docs/dev/notkinopoiskphp/models/review.md | 16 ++ docs/dev/notkinopoiskphp/models/season.md | 16 ++ docs/dev/notkinopoiskphp/models/staff.md | 16 ++ docs/dev/notkinopoiskphp/models/user-vote.md | 16 ++ docs/dev/notkinopoiskphp/models/video.md | 16 ++ docs/dev/notkinopoiskphp/navigation-map.md | 16 ++ .../responses/budget-response.md | 16 ++ .../responses/default-response.md | 16 ++ docs/dev/notkinopoiskphp/responses/index.md | 16 ++ .../responses/keyword-search-response.md | 16 ++ .../responses/movie-staff-response.md | 16 ++ .../responses/paginated-response.md | 16 ++ .../responses/review-response.md | 16 ++ .../responses/sequel-prequel-response.md | 16 ++ .../responses/simple-response.md | 16 ++ .../services/abstract-service.md | 16 ++ .../notkinopoiskphp/services/film-service.md | 16 ++ docs/dev/notkinopoiskphp/services/index.md | 16 ++ .../notkinopoiskphp/services/media-service.md | 16 ++ .../services/person-service.md | 16 ++ .../notkinopoiskphp/services/user-service.md | 16 ++ docs/dev/paid-currencies_rate.md | 19 +- docs/dev/paid-lastnews.md | 60 ++-- docs/dev/paid-seasonvar/faq.md | 17 ++ docs/dev/paid-seasonvar/install.md | 17 ++ docs/dev/paid-seasonvar/related.md | 17 ++ docs/dev/releasestatus.md | 22 +- docs/dev/repost/.nav.yml | 2 +- docs/dev/repost/index.md | 2 +- docs/dev/schema.md | 21 +- docs/dev/telegramposting/bot.md | 17 ++ docs/dev/telegramposting/changelog.md | 69 +++-- docs/dev/telegramposting/custom_add.md | 17 ++ docs/dev/telegramposting/template_tags.md | 17 ++ docs/dev/usertags.md | 6 +- docs/dev/webmaster-verification.md | 2 +- docs/dev/xflist.md | 4 +- docs/dev/xfselect.md | 4 +- docs/index.md | 2 +- docs/site/how-to-ask-for-support.md | 16 ++ docs/site/how-to-buy.md | 22 +- docs/tags.md | 14 + mkdocs.yml | 3 - requirements.txt | 2 - 151 files changed, 2254 insertions(+), 409 deletions(-) delete mode 100644 docs/dev/currencies_rate.md delete mode 100644 docs/dev/lastnews.md diff --git a/docs/dev/.nav.yml b/docs/dev/.nav.yml index 662f29c..c065bfa 100644 --- a/docs/dev/.nav.yml +++ b/docs/dev/.nav.yml @@ -9,7 +9,7 @@ nav: - KinopoiskUnoffical PHP: notkinopoiskphp/ - Бесплатные разработки: - DB Manager: db_manager/ - - RePost: repost/ + - "Re: Post": repost/ - DLE Faker: dle_faker/ - Telegram Posting: telegramposting/ - MH Admin: mhadmin/ diff --git a/docs/dev/composer.md b/docs/dev/composer.md index 314e8ff..5c7701c 100644 --- a/docs/dev/composer.md +++ b/docs/dev/composer.md @@ -4,13 +4,13 @@ tags: - Инструмент - Инструкция - composer -title: "Composer - DevCraft Документации" +title: "Composer - Инструкция по использованию Composer" description: "Инструкция по использованию Composer для управления зависимостями в проектах DevCraft." keywords: "Composer, PHP, зависимости, DevCraft" author: "Maxim Harder" og:title: "Composer - управление зависимостями" og:description: "Инструкция по использованию Composer для проектов DevCraft" -og:image: "https://devcraft.club/assets/images/logo.png" +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" twitter:title: "Composer - управление зависимостями" twitter:description: "Инструкция по использованию Composer для проектов DevCraft" --- diff --git a/docs/dev/crowdin.md b/docs/dev/crowdin.md index dd2937f..ab821f3 100644 --- a/docs/dev/crowdin.md +++ b/docs/dev/crowdin.md @@ -3,13 +3,13 @@ tags: - Инструкция - crowdin - Локализация -title: "Crowdin - DevCraft Документации" +title: "Crowdin - Система локализаций" description: "Инструкция по работе с Crowdin для локализации проектов DevCraft." keywords: "Crowdin, локализация, переводы, DevCraft" author: "Maxim Harder" og:title: "Crowdin - локализация проектов" og:description: "Инструкция по работе с Crowdin для локализации" -og:image: "https://devcraft.club/assets/images/logo.png" +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" twitter:title: "Crowdin - локализация проектов" twitter:description: "Инструкция по работе с Crowdin для локализации" --- diff --git a/docs/dev/currencies_rate.md b/docs/dev/currencies_rate.md deleted file mode 100644 index 088e017..0000000 --- a/docs/dev/currencies_rate.md +++ /dev/null @@ -1,270 +0,0 @@ -# Цены по курсу валют - -**Ссылка на разработку**: [ Перейти к разработке](https://devcraft.club/shop/cena-po-kursu-valjut.4/) - -**Версия модификации**: 3.1.1 - -## **Установка** - -- Для установки достаточно закинуть в корень сайта все файлы и загрузить файл с настройками (**kurscbr.xml**) в админпанеле через менеджер плагинов. -- Для версий DLE ниже |- Создайте доп. поле с типом списка со следующими значениями: - -``` -AED|AED - Дирхам Арабских Эмиратов -ALL|ALL - Лек -AMD|AMD - Драма -ANG|ANG - Нидерландский Антильский Гульден -AOA|AOA - Кванза -ARS|ARS - Аргентинское Песо -AUD|AUD - Австралийский Доллар -AZN|AZN - Азербайджанский Манат -BBD|BBD - Барбадосский Доллар -BDT|BDT - Така -BGN|BGN - Болгарский Лев -BHD|BHD - Бахрейнский Динар -BRL|BRL - Бразильский Реал -BSD|BSD - Багамский Доллар -BWP|BWP - Ботсваны Пула -BYN|BYN - Белорусский Рубль -CAD|CAD - Канадский Доллар -CHF|CHF - Швейцарский Франк -CLP|CLP - Чилийское Песо -CNY|CNY - Китайский Юань -COP|COP - Колумбийское Песо -CZK|CZK - Чешская Крона -DKK|DKK - Датская Крона -DOP|DOP - Доминиканское Песо -EGP|EGP - Египетский Фунт -ETB|ETB - Эфиопский Быр -EUR|EUR - Евро -FJD|FJD - Фиджийский Доллар -GBP|GBP - Британский Фунт -GEL|GEL - Грузинский Лари -GHS|GHS - Ганский Седи -GTQ|GTQ - Гватемальский Кетсаль -HKD|HKD - Гонконгский Доллар -HNL|HNL - Гондурасская Лемпира -HRK|HRK - Хорватская Куна -HUF|HUF - Венгерский Форинт -IDR|IDR - Индонезийская Рупия -ILS|ILS - Новый Израильский Шекель -INR|INR - Индийская Рупия -IQD|IQD - Иракский Динар -IRR|IRR - Иранский Риал -ISK|ISK - Исландская Крона -JMD|JMD - Ямайский Доллар -JOD|JOD - Иорданский Динар -JPY|JPY - Японская Иена -KES|KES - Кенийский Шиллинг -KHR|KHR - Камбоджийский Риель -KRW|KRW - Южнокорейская Вона -KWD|KWD - Кувейтский Динар -KZT|KZT - Казахстанский Тенге -LAK|LAK - Лаосский Кип -LBP|LBP - Ливанский Фунт -LKR|LKR - Шри-Ланкийская Рупия -MAD|MAD - Марокканский Дирхам -MDL|MDL - Молдавский Лей -MKD|MKD - Денар Республики Македония -MMK|MMK - Мьянманский Чат -MUR|MUR - Маврикийская Рупия -MXN|MXN - Мексиканское Песо -MYR|MYR - Малайзийский Ринггит -NAD|NAD - Доллар Намибии -NGN|NGN - Найра -NOK|NOK - Норвежская Крона -NZD|NZD - Новозеландский Доллар -OMR|OMR - Оманский Риал -PAB|PAB - Бальбоа -PEN|PEN - Перуанский Соль -PGK|PGK - Папуа-Новой Гвинеи Кина -PHP|PHP - Филиппинское Песо -PKR|PKR - Пакистанская Рупия -PLN|PLN - Злотый -PYG|PYG - Парагвайский Гуарани -QAR|QAR - Катарский Риал -RON|RON - Румынский Лей -RSD|RSD - Сербский Динар -RUB|RUB - Рубль -SAR|SAR - Саудовский Риял -SCR|SCR - Сейшельская Рупия -SEK|SEK - Шведская Крона -SGD|SGD - Сингапурский Доллар -THB|THB - Бат -TJS|TJS - Таджикских Сомони -TND|TND - Тунисский Динар -TRY|TRY - Турецкая Лира -TTD|TTD - Тринидад И Тобаго Доллар -TWD|TWD - Новый Тайваньский Доллар -TZS|TZS - Танзанийский Шиллинг -UAH|UAH - Украинская Гривна -USD|USD - Доллар -UYU|UYU - Уругвайское Песо -UZS|UZS - Узбекский Сом -VEF|VEF - Боливар -VND|VND - Донг -XAF|XAF - Франк Кфа Beac -XCD|XCD - Восточно-Карибский Доллар -XOF|XOF - Западно-Африканский Франк Кфа Фран -XPF|XPF - Франк Кфп -ZAR|ZAR - Южноафриканский Рэнд -ZMW|ZMW - Замбийская Квача -``` - -- Регистрируемся на сайте сервиса: [exchangerate-api](https://www.exchangerate-api.com/) -- Настраиваем всё в админпанеле -- Удаляем **install.php** с корня сайта - - -## **Теги и использование** - -### main.tpl -Для подключения **на главной** (main.tpl) достаточно этой строчки: - -```HTML -{include file="engine/modules/kurscbr.php?mod=site&from=XXX"} -``` - -Вместо **ХХХ** впишите свою валюту. От этой валюты будет идти расчёт. Все доступные валюты можно узнать ниже. В шаблоне можно указывать след. теги: - -|Тег| Описание | -|---|--------------------------------------------------------| -|**{cur_XXX}**| выводит курс по соотношению изначальной валюты | -|**{cur_id_XXX}**| выводит идентификатор валюты в виде **USD, EUR, RUB** | - - -### full.tpl -Для шаблона **в полной новости** (full.tpl) доступны следующие теги: - -|Тег| Описание | -|---|------------------------------------------------------------------------------------------------------------------------------------------| -|**{cur_XXX}**| выводит окончательную цену по курсу | -|**{cur_id_XXX}**| выводит идентификатор валюты в виде **USD, EUR, RUB** или название валюты в виде **Доллар, Рубль, Евро**. Смотря как вы настроили у себя | -|**{curs_XXX}**| выводит курс к изначальной валюте. | -|**{price_ori}**| выводит исходную цену | -|**{valuta_ori}**| выводит исходную валюту | - - -### fullstory.tpl -Чтобы подключить модуль в шаблоне с полной новостью (fullstory.tpl) добавляем строчку: - -```HTML -{include file="engine/modules/kurscbr.php?news_id={news-id}&save=CUR:field"} -``` - - -- Параметр **news_id** **обязателен** к использованию. Он указывает ID новости. -- Параметр **save** указывается тогда, когда нужно сохранить отформатирование значение в определённое поле. К примеру: основная цена в евро, то в этом поле будет сохранена отконвертированная сумма. -- Вместо **CUR**указываем нужную **валюту** -- вместо **field** - **доп. поле**. Эти значения разделяются двоеточием (**:**). Если надо сохранить в несколько полей, то используйте **запятую** в качестве разделителя. - -```HTML -{include file="engine/modules/kurscbr.php?news_id={news-id}&save=EUR:eur_price,USD:usd:price"} -``` - -## Доступные к использованию валюты - -**На данный момент API поддерживает эти валюты:** - -|Идентификатор|Описание| -|-------------|--------| -|AED|Дирхам Арабских Эмиратов| -|ALL|Лек| -|AMD|Драма| -|ANG|Нидерландский Антильский Гульден| -|AOA|Кванза| -|ARS|Аргентинское Песо| -|AUD|Австралийский Доллар| -|AZN|Азербайджанский Манат| -|BBD|Барбадосский Доллар| -|BGN|Болгарский Лев| -|BHD|Бахрейнский Динар| -|BRL|Бразильский Реал| -|BSD|Багамский Доллар| -|BWP|Ботсваны Пула| -|BYN|Белорусский Рубль| -|CAD|Канадский Доллар| -|CHF|Швейцарский Франк| -|CLP|Чилийское Песо| -|CNY|Китайский Юань| -|COP|Колумбийское Песо| -|CZK|Чешская Крона| -|DKK|Датская Крона| -|DOP|Доминиканское Песо| -|EGP|Египетский Фунт| -|ETB|Эфиопский Быр| -|EUR|Евро| -|FJD|Фиджийский Доллар| -|GBP|Британский Фунт| -|GEL|Грузинский Лари| -|GHS|Ганский Седи| -|GTQ|Гватемальский Кетсаль| -|HKD|Гонконгский Доллар| -|HNL|Гондурасская Лемпира| -|HRK|Хорватская Куна| -|HUF|Венгерский Форинт| -|IDR|Индонезийская Рупия| -|ILS|Новый Израильский Шекель| -|INR|Индийская Рупия| -|IQD|Иракский Динар| -|IRR|Иранский Риал| -|ISK|Исландская Крона| -|JMD|Ямайский Доллар| -|JOD|Иорданский Динар| -|JPY|Японская Иена| -|KES|Кенийский Шиллинг| -|KHR|Камбоджийский Риель| -|KRW|Южнокорейская Вона| -|KWD|Кувейтский Динар| -|KZT|Казахстанский Тенге| -|LAK|Лаосский Кип| -|LBP|Ливанский Фунт| -|LKR|Шри-Ланкийская Рупия| -|MAD|Марокканский Дирхам| -|MDL|Молдавский Лей| -|MKD|Денар Республики Македония| -|MMK|Мьянманский Чат| -|MUR|Маврикийская Рупия| -|MXN|Мексиканское Песо| -|MYR|Малайзийский Ринггит| -|NAD|Доллар Намибии| -|NGN|Найра| -|NOK|Норвежская Крона| -|NZD|Новозеландский Доллар| -|OMR|Оманский Риал| -|PAB|Бальбоа| -|PEN|Перуанский Соль| -|PGK|Папуа-Новой Гвинеи Кина| -|PHP|Филиппинское Песо| -|PKR|Пакистанская Рупия| -|PLN|Злотый| -|PYG|Парагвайский Гуарани| -|QAR|Катарский Риал| -|RON|Румынский Лей| -|RSD|Сербский Динар| -|RUB|Рубль| -|SAR|Саудовский Риял| -|SCR|Сейшельская Рупия| -|SEK|Шведская Крона| -|SGD|Сингапурский Доллар| -|THB|Бат| -|TJS|Таджикских Сомони| -|TND|Тунисский Динар| -|TRY|Турецкая Лира| -|TTD|Тринидад И Тобаго Доллар| -|TWD|Новый Тайваньский Доллар| -|TZS|Танзанийский Шиллинг| -|UAH|Украинская Гривна| -|USD|Доллар| -|UYU|Уругвайское Песо| -|UZS|Узбекский Сом| -|VEF|Боливар| -|VND|Донг| -|XAF|Франк Кфа Beac| -|XCD|Восточно-Карибский Доллар| -|XOF|Западно-Африканский Франк Кфа Фран| -|XPF|Франк Кфп| -|ZAR|Южноафриканский Рэнд| -|ZMW|Замбийская Квача| - -Если описание какой-либо валюты неверно - пишите \ No newline at end of file diff --git a/docs/dev/dle_faker/changelog.md b/docs/dev/dle_faker/changelog.md index be3c9d0..5e40a1f 100644 --- a/docs/dev/dle_faker/changelog.md +++ b/docs/dev/dle_faker/changelog.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Генератор +title: "История изменений - DevCraft Документации" +description: "Документация по плагину история изменений для DLE." +keywords: "PHP, DLE, Плагин, Генератор, история изменений, DevCraft, документация" +author: "Maxim Harder" +og:title: "История изменений" +og:description: "Документация по плагину история изменений для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "История изменений" +twitter:description: "Документация по плагину история изменений для DLE." +--- + # История изменений +++ 173.1.0 diff --git a/docs/dev/dle_faker/gen_news.md b/docs/dev/dle_faker/gen_news.md index cc743f8..59a7d91 100644 --- a/docs/dev/dle_faker/gen_news.md +++ b/docs/dev/dle_faker/gen_news.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Генератор +title: "Генератор новостей - DevCraft Документации" +description: "Документация по плагину генератор новостей для DLE." +keywords: "PHP, DLE, Плагин, Генератор, генератор новостей, DevCraft, документация" +author: "Maxim Harder" +og:title: "Генератор новостей" +og:description: "Документация по плагину генератор новостей для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Генератор новостей" +twitter:description: "Документация по плагину генератор новостей для DLE." +--- + # Генератор новостей Генератор новостей работает по следующему принципу: diff --git a/docs/dev/dle_faker/gen_users.md b/docs/dev/dle_faker/gen_users.md index a51c8f1..5cf8e28 100644 --- a/docs/dev/dle_faker/gen_users.md +++ b/docs/dev/dle_faker/gen_users.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Генератор +title: "Генератор пользователей - DevCraft Документации" +description: "Документация по плагину генератор пользователей для DLE." +keywords: "PHP, DLE, Плагин, Генератор, генератор пользователей, DevCraft, документация" +author: "Maxim Harder" +og:title: "Генератор пользователей" +og:description: "Документация по плагину генератор пользователей для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Генератор пользователей" +twitter:description: "Документация по плагину генератор пользователей для DLE." +--- + # Генератор пользователей В отличие от генератора новостей, генератор пользователей не имеет шаблонов. И операция генерации настраивается с нуля. diff --git a/docs/dev/dle_faker/install.md b/docs/dev/dle_faker/install.md index 5076056..48f4849 100644 --- a/docs/dev/dle_faker/install.md +++ b/docs/dev/dle_faker/install.md @@ -4,6 +4,7 @@ tags: - PHP - DLE - Генератор +title: Установка - DLE Faker --- # DLE Faker diff --git a/docs/dev/dle_faker/parser.md b/docs/dev/dle_faker/parser.md index 02e2203..24f54a1 100644 --- a/docs/dev/dle_faker/parser.md +++ b/docs/dev/dle_faker/parser.md @@ -1,3 +1,21 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Генератор + - Парсер +title: "Парсинг данных с шаблонов - DLE Faker" +description: "Документация по плагину парсинг данных с шаблонов для DLE." +keywords: "PHP, DLE, Плагин, Генератор, парсинг данных с шаблонов, DevCraft, документация" +author: "Maxim Harder" +og:title: "Парсинг данных с шаблонов" +og:description: "Документация по плагину парсинг данных с шаблонов для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Парсинг данных с шаблонов" +twitter:description: "Документация по плагину парсинг данных с шаблонов для DLE." +--- + # Парсинг данных с шаблонов Процесс обработки шаблонов происходит при AJAX запросе. В данном случае за этот процесс отвечает файл: `upload/engine/ajax/maharder/dle_faker/parse_content.php`. diff --git a/docs/dev/dle_faker/settings.md b/docs/dev/dle_faker/settings.md index 6d32a25..deb0632 100644 --- a/docs/dev/dle_faker/settings.md +++ b/docs/dev/dle_faker/settings.md @@ -1,3 +1,21 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Генератор + - Настройки +title: "Настройки - DLE Faker" +description: "Документация по плагину настройки для DLE." +keywords: "PHP, DLE, Плагин, Генератор, настройки, DevCraft, документация" +author: "Maxim Harder" +og:title: "Настройки" +og:description: "Документация по плагину настройки для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Настройки" +twitter:description: "Документация по плагину настройки для DLE." +--- + # Настройки Настройки плагина являются главной страницей. Они обязательны к заполнению, ибо значений по умолчанию нет! diff --git a/docs/dev/dle_faker/tag_for_all.md b/docs/dev/dle_faker/tag_for_all.md index 4e105ce..524e15e 100644 --- a/docs/dev/dle_faker/tag_for_all.md +++ b/docs/dev/dle_faker/tag_for_all.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Генератор +title: "Теги для всех шаблонов - DLE Faker" +description: "Документация по плагину теги для всех шаблонов для DLE." +keywords: "PHP, DLE, Плагин, Генератор, теги для всех шаблонов, DevCraft, документация" +author: "Maxim Harder" +og:title: "Теги для всех шаблонов" +og:description: "Документация по плагину теги для всех шаблонов для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Теги для всех шаблонов" +twitter:description: "Документация по плагину теги для всех шаблонов для DLE." +--- + # Теги для всех шаблонов Данные теги будут доступны для всех шаблонов, поскольку они универсальны. diff --git a/docs/dev/dle_faker/tag_for_news.md b/docs/dev/dle_faker/tag_for_news.md index 4083fb2..021625a 100644 --- a/docs/dev/dle_faker/tag_for_news.md +++ b/docs/dev/dle_faker/tag_for_news.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Генератор +title: "Теги для шаблонов новостей - DLE Faker" +description: "Документация по плагину теги для шаблонов новостей для DLE." +keywords: "PHP, DLE, Плагин, Генератор, теги для шаблонов новостей, DevCraft, документация" +author: "Maxim Harder" +og:title: "Теги для шаблонов новостей" +og:description: "Документация по плагину теги для шаблонов новостей для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Теги для шаблонов новостей" +twitter:description: "Документация по плагину теги для шаблонов новостей для DLE." +--- + # Теги для шаблонов новостей Эти теги используются в шаблоне новостей. Любые другие теги будут игнорироваться. diff --git a/docs/dev/dle_faker/tag_for_users.md b/docs/dev/dle_faker/tag_for_users.md index 90284fe..0b11d56 100644 --- a/docs/dev/dle_faker/tag_for_users.md +++ b/docs/dev/dle_faker/tag_for_users.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Генератор +title: "Теги для шаблона пользователей - DLE Faker" +description: "Документация по плагину теги для шаблона пользователей для DLE." +keywords: "PHP, DLE, Плагин, Генератор, теги для шаблона пользователей, DevCraft, документация" +author: "Maxim Harder" +og:title: "Теги для шаблона пользователей" +og:description: "Документация по плагину теги для шаблона пользователей для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Теги для шаблона пользователей" +twitter:description: "Документация по плагину теги для шаблона пользователей для DLE." +--- + # Теги для шаблона пользователей Эти теги используются в шаблоне генерации пользователей. Любые другие теги будут игнорироваться. diff --git a/docs/dev/hook-pages-like-kp.md b/docs/dev/hook-pages-like-kp.md index badc279..e6abe03 100644 --- a/docs/dev/hook-pages-like-kp.md +++ b/docs/dev/hook-pages-like-kp.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - DLE + - Хак +title: "Отдельные страницы как на кинопоиске" +description: "Хак для DLE: отдельные страницы как на кинопоиске." +keywords: "PHP, DLE, Хак, отдельные страницы как на кинопоиске, DevCraft, документация" +author: "Maxim Harder" +og:title: "Отдельные страницы как на кинопоиске" +og:description: "Хак для DLE: отдельные страницы как на кинопоиске." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Отдельные страницы как на кинопоиске" +twitter:description: "Хак для DLE: отдельные страницы как на кинопоиске." +--- + # Отдельные страницы как на кинопоиске _**Для чего это вообще нужно?**_ diff --git a/docs/dev/hook-shortstory-signs-count.md b/docs/dev/hook-shortstory-signs-count.md index 6bad4c8..584575b 100644 --- a/docs/dev/hook-shortstory-signs-count.md +++ b/docs/dev/hook-shortstory-signs-count.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - DLE + - Хак +title: "Количество символов краткой новости" +description: "Хак для DLE: количество символов краткой новости." +keywords: "PHP, DLE, Хак, количество символов краткой новости, DevCraft, документация" +author: "Maxim Harder" +og:title: "Количество символов краткой новости" +og:description: "Хак для DLE: количество символов краткой новости." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Количество символов краткой новости" +twitter:description: "Хак для DLE: количество символов краткой новости." +--- + # Количество символов краткой новости **Установка** diff --git a/docs/dev/install_instructions.md b/docs/dev/install_instructions.md index 793f559..f5d5b16 100644 --- a/docs/dev/install_instructions.md +++ b/docs/dev/install_instructions.md @@ -1,3 +1,17 @@ +--- +tags: + - Инструкция +title: "Установка (практически) всех плагинов" +description: "Инструкция по установка (практически) всех плагинов." +keywords: "Инструкция, установка (практически) всех плагинов, DevCraft, документация" +author: "Maxim Harder" +og:title: "Установка (практически) всех плагинов" +og:description: "Инструкция по установка (практически) всех плагинов." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Установка (практически) всех плагинов" +twitter:description: "Инструкция по установка (практически) всех плагинов." +--- + # Установка (практически) всех плагинов Поскольку установка лишь в нескольких случаях отличаются, то копировать одно и тоже во все плагины не вижу смысла. diff --git a/docs/dev/lastnews.md b/docs/dev/lastnews.md deleted file mode 100644 index a550b63..0000000 --- a/docs/dev/lastnews.md +++ /dev/null @@ -1,35 +0,0 @@ -# LastNews списком аля seasonvar - -**Ссылка на разработку**: [ Перейти к разработке](https://devcraft.club/shop/lastnews-spiskom.2/) - -**Версия модификации**: 1.1 - - -## **Пример подключения** - -```HTML -{include file="engine/modules/lastnews.php?day=2&cache=yes&limit=15&order=date&sort=asc&temp=lastnews&temp2=storylines&c_id=5,6,7&von=heute&bis=gestern"} -``` - -## **Пояснения** - -| Тег | Описание | -| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| **c_id** | Выбираем нужные ID категорий.**По умолчанию выбраны все**. | -| **day** | Этот параметр для вывода нескольких блоков.**По умолчанию 1**. | -| **cache** | Включает и выключает кеш блока.**По умолчанию включён**. Можно выбирать либо yes, либо no. | -| **limit** | Указывает кол-во выводимых новостей.**По умолчанию 100**. | -| **order** | Это стандартная функция и работает так-же. Однако меняет порядок новостей в блоке по дате, а не сами даты.**По умолчанию стоит date.** | -| **sort** | Сортирует новости либо по убыванию, либо по возрастанию. Можно использовать**ТОЛЬКО**DESC и ASC! **По умолчанию стоит DESC**. | -| **temp** | Указываем имя шаблона для блока. Шаблон должен лежать в папке с темой сайта. Не указывайте окончания tpl!**По умолчанию это lastnews**. | -| **temp2** | Указываем шаблон списка, что выводится в блоке. Действует так-же, как и с temp.**по умолчанию это lastnews_story**. | -| **von, bis** | Указываем диапазон даты с какого по какое должно показать новости. von это значение для "от", а bis - " до". Указав эти параметры, нельзя использовать параметр datum. Что к чему - ниже! | -| **datum** | Указываем определённую дату и выводим новости за сей день! | -| **Вариации даты** | -**heute**: это сегодняшняя дата, выводит новости, что были добавлены именно сегодня, вне зависимости от даты | -| | -**gestern**: вчерашняя дата, т.е. новости за вчера | -| | -**vorgestern**: позавчерашняя дата | -| | -**daym3**: позапозавчерашняя дата, т.е. от сегодня отсчитываем три дня | -| | -**daym4**: от сегодня отсчитываем четыре дня | -| | -**daym5**: от сегодня отсчитываем пять дней | -| | -**daym6**: от сегодня отсчитываем шесть дней | -| | -**любая определённая дата**: в формате Y-m-d (год-месяц-день, пр: 2016-07-14) | diff --git a/docs/dev/mhadmin/backend/classes/Admin.md b/docs/dev/mhadmin/backend/classes/Admin.md index 519dc2d..eeff4d2 100644 --- a/docs/dev/mhadmin/backend/classes/Admin.md +++ b/docs/dev/mhadmin/backend/classes/Admin.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Класс: Admin - MH Admin" +description: "Документация по плагину класс: admin для DLE." +keywords: "PHP, DLE, Плагин, Админка, класс: admin, DevCraft, документация" +author: "Maxim Harder" +og:title: "Класс: Admin" +og:description: "Документация по плагину класс: admin для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Класс: Admin" +twitter:description: "Документация по плагину класс: admin для DLE." +--- + # Класс: Admin ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/AdminLink.md b/docs/dev/mhadmin/backend/classes/AdminLink.md index df7feba..420bbe8 100644 --- a/docs/dev/mhadmin/backend/classes/AdminLink.md +++ b/docs/dev/mhadmin/backend/classes/AdminLink.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Класс: AdminLink - MH Admin" +description: "Документация по плагину класс: adminlink для DLE." +keywords: "PHP, DLE, Плагин, Админка, класс: adminlink, DevCraft, документация" +author: "Maxim Harder" +og:title: "Класс: AdminLink" +og:description: "Документация по плагину класс: adminlink для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Класс: AdminLink" +twitter:description: "Документация по плагину класс: adminlink для DLE." +--- + # Класс: AdminLink ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/AdminUrlExtension.md b/docs/dev/mhadmin/backend/classes/AdminUrlExtension.md index f5dbb0d..f48e64a 100644 --- a/docs/dev/mhadmin/backend/classes/AdminUrlExtension.md +++ b/docs/dev/mhadmin/backend/classes/AdminUrlExtension.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Класс: AdminUrlExtension - MH Admin" +description: "Документация по плагину класс: adminurlextension для DLE." +keywords: "PHP, DLE, Плагин, Админка, класс: adminurlextension, DevCraft, документация" +author: "Maxim Harder" +og:title: "Класс: AdminUrlExtension" +og:description: "Документация по плагину класс: adminurlextension для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Класс: AdminUrlExtension" +twitter:description: "Документация по плагину класс: adminurlextension для DLE." +--- + # Класс: AdminUrlExtension ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/AjaxAbstractResponse.md b/docs/dev/mhadmin/backend/classes/AjaxAbstractResponse.md index caab932..2441be0 100644 --- a/docs/dev/mhadmin/backend/classes/AjaxAbstractResponse.md +++ b/docs/dev/mhadmin/backend/classes/AjaxAbstractResponse.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Абстрактный класс: AjaxAbstractResponse - MH Admin" +description: "Документация по плагину абстрактный класс: ajaxabstractresponse для DLE." +keywords: "PHP, DLE, Плагин, Админка, абстрактный класс: ajaxabstractresponse, DevCraft, документация" +author: "Maxim Harder" +og:title: "Абстрактный класс: AjaxAbstractResponse" +og:description: "Документация по плагину абстрактный класс: ajaxabstractresponse для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Абстрактный класс: AjaxAbstractResponse" +twitter:description: "Документация по плагину абстрактный класс: ajaxabstractresponse для DLE." +--- + # Абстрактный класс: AjaxAbstractResponse ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/AssetsChecker.md b/docs/dev/mhadmin/backend/classes/AssetsChecker.md index fa1b7a0..4e8c474 100644 --- a/docs/dev/mhadmin/backend/classes/AssetsChecker.md +++ b/docs/dev/mhadmin/backend/classes/AssetsChecker.md @@ -1,4 +1,21 @@ -# Класс: \AssetsChecker +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Класс: AssetsChecker - MH Admin" +description: "Документация по плагину класс: AssetsChecker для DLE." +keywords: "PHP, DLE, Плагин, Админка, класс: AssetsChecker, DevCraft, документация" +author: "Maxim Harder" +og:title: "Класс: AssetsChecker" +og:description: "Документация по плагину класс: AssetsChecker для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Класс: AssetsChecker" +twitter:description: "Документация по плагину класс: AssetsChecker для DLE." +--- + +# Класс: AssetsChecker ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/Author.md b/docs/dev/mhadmin/backend/classes/Author.md index b784777..17ff344 100644 --- a/docs/dev/mhadmin/backend/classes/Author.md +++ b/docs/dev/mhadmin/backend/classes/Author.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Класс: Author - MH Admin" +description: "Документация по плагину класс: author для DLE." +keywords: "PHP, DLE, Плагин, Админка, класс: author, DevCraft, документация" +author: "Maxim Harder" +og:title: "Класс: Author" +og:description: "Документация по плагину класс: author для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Класс: Author" +twitter:description: "Документация по плагину класс: author для DLE." +--- + # Класс: Author ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/BasisModel.md b/docs/dev/mhadmin/backend/classes/BasisModel.md index 96f5f53..b6f46dc 100644 --- a/docs/dev/mhadmin/backend/classes/BasisModel.md +++ b/docs/dev/mhadmin/backend/classes/BasisModel.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Абстрактный класс: BasisModel - MH Admin" +description: "Документация по плагину абстрактный класс: basismodel для DLE." +keywords: "PHP, DLE, Плагин, Админка, абстрактный класс: basismodel, DevCraft, документация" +author: "Maxim Harder" +og:title: "Абстрактный класс: BasisModel" +og:description: "Документация по плагину абстрактный класс: basismodel для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Абстрактный класс: BasisModel" +twitter:description: "Документация по плагину абстрактный класс: basismodel для DLE." +--- + # Абстрактный класс: BasisModel ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/BasisRepository.md b/docs/dev/mhadmin/backend/classes/BasisRepository.md index fce26d4..b96a407 100644 --- a/docs/dev/mhadmin/backend/classes/BasisRepository.md +++ b/docs/dev/mhadmin/backend/classes/BasisRepository.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Класс: BasisRepository - MH Admin" +description: "Документация по плагину класс: basisrepository для DLE." +keywords: "PHP, DLE, Плагин, Админка, класс: basisrepository, DevCraft, документация" +author: "Maxim Harder" +og:title: "Класс: BasisRepository" +og:description: "Документация по плагину класс: basisrepository для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Класс: BasisRepository" +twitter:description: "Документация по плагину класс: basisrepository для DLE." +--- + # Класс: BasisRepository ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/BreadCrumb.md b/docs/dev/mhadmin/backend/classes/BreadCrumb.md index 6a343d3..a160320 100644 --- a/docs/dev/mhadmin/backend/classes/BreadCrumb.md +++ b/docs/dev/mhadmin/backend/classes/BreadCrumb.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Класс: BreadCrumb - MH Admin" +description: "Документация по плагину класс: breadcrumb для DLE." +keywords: "PHP, DLE, Плагин, Админка, класс: breadcrumb, DevCraft, документация" +author: "Maxim Harder" +og:title: "Класс: BreadCrumb" +og:description: "Документация по плагину класс: breadcrumb для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Класс: BreadCrumb" +twitter:description: "Документация по плагину класс: breadcrumb для DLE." +--- + # Класс: BreadCrumb ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/CacheControl.md b/docs/dev/mhadmin/backend/classes/CacheControl.md index 84203c0..f9458e9 100644 --- a/docs/dev/mhadmin/backend/classes/CacheControl.md +++ b/docs/dev/mhadmin/backend/classes/CacheControl.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Абстрактный класс: CacheControl - MH Admin" +description: "Документация по плагину абстрактный класс: cachecontrol для DLE." +keywords: "PHP, DLE, Плагин, Админка, абстрактный класс: cachecontrol, DevCraft, документация" +author: "Maxim Harder" +og:title: "Абстрактный класс: CacheControl" +og:description: "Документация по плагину абстрактный класс: cachecontrol для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Абстрактный класс: CacheControl" +twitter:description: "Документация по плагину абстрактный класс: cachecontrol для DLE." +--- + # Абстрактный класс: CacheControl ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/ComposerAction.md b/docs/dev/mhadmin/backend/classes/ComposerAction.md index 163dff7..8efe777 100644 --- a/docs/dev/mhadmin/backend/classes/ComposerAction.md +++ b/docs/dev/mhadmin/backend/classes/ComposerAction.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Абстрактный класс: ComposerAction - MH Admin" +description: "Документация по плагину абстрактный класс: composeraction для DLE." +keywords: "PHP, DLE, Плагин, Админка, абстрактный класс: composeraction, DevCraft, документация" +author: "Maxim Harder" +og:title: "Абстрактный класс: ComposerAction" +og:description: "Документация по плагину абстрактный класс: composeraction для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Абстрактный класс: ComposerAction" +twitter:description: "Документация по плагину абстрактный класс: composeraction для DLE." +--- + # Абстрактный класс: ComposerAction --- diff --git a/docs/dev/mhadmin/backend/classes/DataLoader.md b/docs/dev/mhadmin/backend/classes/DataLoader.md index 946af39..566147f 100644 --- a/docs/dev/mhadmin/backend/classes/DataLoader.md +++ b/docs/dev/mhadmin/backend/classes/DataLoader.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Класс: DataLoader - MH Admin" +description: "Документация по плагину класс: dataloader для DLE." +keywords: "PHP, DLE, Плагин, Админка, класс: dataloader, DevCraft, документация" +author: "Maxim Harder" +og:title: "Класс: DataLoader" +og:description: "Документация по плагину класс: dataloader для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Класс: DataLoader" +twitter:description: "Документация по плагину класс: dataloader для DLE." +--- + # Класс: DataLoader ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/DataManager.md b/docs/dev/mhadmin/backend/classes/DataManager.md index 9d6fffb..fa25ccf 100644 --- a/docs/dev/mhadmin/backend/classes/DataManager.md +++ b/docs/dev/mhadmin/backend/classes/DataManager.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Абстрактный класс: DataManager - MH Admin" +description: "Документация по плагину абстрактный класс: datamanager для DLE." +keywords: "PHP, DLE, Плагин, Админка, абстрактный класс: datamanager, DevCraft, документация" +author: "Maxim Harder" +og:title: "Абстрактный класс: DataManager" +og:description: "Документация по плагину абстрактный класс: datamanager для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Абстрактный класс: DataManager" +twitter:description: "Документация по плагину абстрактный класс: datamanager для DLE." +--- + # Абстрактный класс: DataManager ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/DateTimeFormatter.md b/docs/dev/mhadmin/backend/classes/DateTimeFormatter.md index 40570a4..a5b0afc 100644 --- a/docs/dev/mhadmin/backend/classes/DateTimeFormatter.md +++ b/docs/dev/mhadmin/backend/classes/DateTimeFormatter.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Класс: DateTimeFormatter - MH Admin" +description: "Документация по плагину класс: datetimeformatter для DLE." +keywords: "PHP, DLE, Плагин, Админка, класс: datetimeformatter, DevCraft, документация" +author: "Maxim Harder" +og:title: "Класс: DateTimeFormatter" +og:description: "Документация по плагину класс: datetimeformatter для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Класс: DateTimeFormatter" +twitter:description: "Документация по плагину класс: datetimeformatter для DLE." +--- + # Класс: DateTimeFormatter ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/DeclineExtension.md b/docs/dev/mhadmin/backend/classes/DeclineExtension.md index d049121..5f38f1f 100644 --- a/docs/dev/mhadmin/backend/classes/DeclineExtension.md +++ b/docs/dev/mhadmin/backend/classes/DeclineExtension.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Класс: DeclineExtension - MH Admin" +description: "Документация по плагину класс: declineextension для DLE." +keywords: "PHP, DLE, Плагин, Админка, класс: declineextension, DevCraft, документация" +author: "Maxim Harder" +og:title: "Класс: DeclineExtension" +og:description: "Документация по плагину класс: declineextension для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Класс: DeclineExtension" +twitter:description: "Документация по плагину класс: declineextension для DLE." +--- + # Класс: DeclineExtension ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/DleData.md b/docs/dev/mhadmin/backend/classes/DleData.md index ef0f78e..acb22ac 100644 --- a/docs/dev/mhadmin/backend/classes/DleData.md +++ b/docs/dev/mhadmin/backend/classes/DleData.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Класс: DleData - MH Admin" +description: "Документация по плагину класс: dledata для DLE." +keywords: "PHP, DLE, Плагин, Админка, класс: dledata, DevCraft, документация" +author: "Maxim Harder" +og:title: "Класс: DleData" +og:description: "Документация по плагину класс: dledata для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Класс: DleData" +twitter:description: "Документация по плагину класс: dledata для DLE." +--- + # Класс: DleData ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/ErrorResponseAjax.md b/docs/dev/mhadmin/backend/classes/ErrorResponseAjax.md index b7021a1..917fa42 100644 --- a/docs/dev/mhadmin/backend/classes/ErrorResponseAjax.md +++ b/docs/dev/mhadmin/backend/classes/ErrorResponseAjax.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Класс: ErrorResponseAjax - MH Admin" +description: "Документация по плагину класс: errorresponseajax для DLE." +keywords: "PHP, DLE, Плагин, Админка, класс: errorresponseajax, DevCraft, документация" +author: "Maxim Harder" +og:title: "Класс: ErrorResponseAjax" +og:description: "Документация по плагину класс: errorresponseajax для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Класс: ErrorResponseAjax" +twitter:description: "Документация по плагину класс: errorresponseajax для DLE." +--- + # Класс: ErrorResponseAjax ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/LogGenerator.md b/docs/dev/mhadmin/backend/classes/LogGenerator.md index c76eb23..f7addcc 100644 --- a/docs/dev/mhadmin/backend/classes/LogGenerator.md +++ b/docs/dev/mhadmin/backend/classes/LogGenerator.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Абстрактный класс: LogGenerator - MH Admin" +description: "Документация по плагину абстрактный класс: loggenerator для DLE." +keywords: "PHP, DLE, Плагин, Админка, абстрактный класс: loggenerator, DevCraft, документация" +author: "Maxim Harder" +og:title: "Абстрактный класс: LogGenerator" +og:description: "Документация по плагину абстрактный класс: loggenerator для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Абстрактный класс: LogGenerator" +twitter:description: "Документация по плагину абстрактный класс: loggenerator для DLE." +--- + # Абстрактный класс: LogGenerator ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/MhAjax.md b/docs/dev/mhadmin/backend/classes/MhAjax.md index 01995a2..84abeac 100644 --- a/docs/dev/mhadmin/backend/classes/MhAjax.md +++ b/docs/dev/mhadmin/backend/classes/MhAjax.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Класс: MhAjax - MH Admin" +description: "Документация по плагину класс: mhajax для DLE." +keywords: "PHP, DLE, Плагин, Админка, класс: mhajax, DevCraft, документация" +author: "Maxim Harder" +og:title: "Класс: MhAjax" +og:description: "Документация по плагину класс: mhajax для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Класс: MhAjax" +twitter:description: "Документация по плагину класс: mhajax для DLE." +--- + # Класс: MhAjax ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/MhDB.md b/docs/dev/mhadmin/backend/classes/MhDB.md index bf0a8d8..6ead133 100644 --- a/docs/dev/mhadmin/backend/classes/MhDB.md +++ b/docs/dev/mhadmin/backend/classes/MhDB.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Класс: MhDB - MH Admin" +description: "Документация по плагину класс: mhdb для DLE." +keywords: "PHP, DLE, Плагин, Админка, класс: mhdb, DevCraft, документация" +author: "Maxim Harder" +og:title: "Класс: MhDB" +og:description: "Документация по плагину класс: mhdb для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Класс: MhDB" +twitter:description: "Документация по плагину класс: mhdb для DLE." +--- + # Класс: MhDB ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/MhLog.md b/docs/dev/mhadmin/backend/classes/MhLog.md index 84ab7a9..94baba7 100644 --- a/docs/dev/mhadmin/backend/classes/MhLog.md +++ b/docs/dev/mhadmin/backend/classes/MhLog.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Класс: MhLog - MH Admin" +description: "Документация по плагину класс: mhlog для DLE." +keywords: "PHP, DLE, Плагин, Админка, класс: mhlog, DevCraft, документация" +author: "Maxim Harder" +og:title: "Класс: MhLog" +og:description: "Документация по плагину класс: mhlog для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Класс: MhLog" +twitter:description: "Документация по плагину класс: mhlog для DLE." +--- + # Класс: MhLog ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/MhLogRepository.md b/docs/dev/mhadmin/backend/classes/MhLogRepository.md index f4f258d..527efaa 100644 --- a/docs/dev/mhadmin/backend/classes/MhLogRepository.md +++ b/docs/dev/mhadmin/backend/classes/MhLogRepository.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Класс: MhLogRepository - MH Admin" +description: "Документация по плагину класс: mhlogrepository для DLE." +keywords: "PHP, DLE, Плагин, Админка, класс: mhlogrepository, DevCraft, документация" +author: "Maxim Harder" +og:title: "Класс: MhLogRepository" +og:description: "Документация по плагину класс: mhlogrepository для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Класс: MhLogRepository" +twitter:description: "Документация по плагину класс: mhlogrepository для DLE." +--- + # Класс: MhLogRepository ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/MhTranslation.md b/docs/dev/mhadmin/backend/classes/MhTranslation.md index 419c8c5..e179f36 100644 --- a/docs/dev/mhadmin/backend/classes/MhTranslation.md +++ b/docs/dev/mhadmin/backend/classes/MhTranslation.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Абстрактный класс: MhTranslation - MH Admin" +description: "Документация по плагину абстрактный класс: mhtranslation для DLE." +keywords: "PHP, DLE, Плагин, Админка, абстрактный класс: mhtranslation, DevCraft, документация" +author: "Maxim Harder" +og:title: "Абстрактный класс: MhTranslation" +og:description: "Документация по плагину абстрактный класс: mhtranslation для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Абстрактный класс: MhTranslation" +twitter:description: "Документация по плагину абстрактный класс: mhtranslation для DLE." +--- + # Абстрактный класс: MhTranslation ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/MobileDetectExtension.md b/docs/dev/mhadmin/backend/classes/MobileDetectExtension.md index 50651a6..ddff7c3 100644 --- a/docs/dev/mhadmin/backend/classes/MobileDetectExtension.md +++ b/docs/dev/mhadmin/backend/classes/MobileDetectExtension.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Класс: MobileDetectExtension - MH Admin" +description: "Документация по плагину класс: mobiledetectextension для DLE." +keywords: "PHP, DLE, Плагин, Админка, класс: mobiledetectextension, DevCraft, документация" +author: "Maxim Harder" +og:title: "Класс: MobileDetectExtension" +og:description: "Документация по плагину класс: mobiledetectextension для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Класс: MobileDetectExtension" +twitter:description: "Документация по плагину класс: mobiledetectextension для DLE." +--- + # Класс: MobileDetectExtension ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/SuccessResponseAjax.md b/docs/dev/mhadmin/backend/classes/SuccessResponseAjax.md index 752b60c..b207ab4 100644 --- a/docs/dev/mhadmin/backend/classes/SuccessResponseAjax.md +++ b/docs/dev/mhadmin/backend/classes/SuccessResponseAjax.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Класс: SuccessResponseAjax - MH Admin" +description: "Документация по плагину класс: successresponseajax для DLE." +keywords: "PHP, DLE, Плагин, Админка, класс: successresponseajax, DevCraft, документация" +author: "Maxim Harder" +og:title: "Класс: SuccessResponseAjax" +og:description: "Документация по плагину класс: successresponseajax для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Класс: SuccessResponseAjax" +twitter:description: "Документация по плагину класс: successresponseajax для DLE." +--- + # Класс: SuccessResponseAjax ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/TextLimiter.md b/docs/dev/mhadmin/backend/classes/TextLimiter.md index 65a8c23..b7f76ed 100644 --- a/docs/dev/mhadmin/backend/classes/TextLimiter.md +++ b/docs/dev/mhadmin/backend/classes/TextLimiter.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Класс: TextLimiter - MH Admin" +description: "Документация по плагину класс: textlimiter для DLE." +keywords: "PHP, DLE, Плагин, Админка, класс: textlimiter, DevCraft, документация" +author: "Maxim Harder" +og:title: "Класс: TextLimiter" +og:description: "Документация по плагину класс: textlimiter для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Класс: TextLimiter" +twitter:description: "Документация по плагину класс: textlimiter для DLE." +--- + # Класс: TextLimiter ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/TwigFilter.md b/docs/dev/mhadmin/backend/classes/TwigFilter.md index 30a9aea..5353cae 100644 --- a/docs/dev/mhadmin/backend/classes/TwigFilter.md +++ b/docs/dev/mhadmin/backend/classes/TwigFilter.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Класс: TwigFilter - MH Admin" +description: "Документация по плагину класс: twigfilter для DLE." +keywords: "PHP, DLE, Плагин, Админка, класс: twigfilter, DevCraft, документация" +author: "Maxim Harder" +og:title: "Класс: TwigFilter" +og:description: "Документация по плагину класс: twigfilter для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Класс: TwigFilter" +twitter:description: "Документация по плагину класс: twigfilter для DLE." +--- + # Класс: TwigFilter ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/classes/UpdatesChecker.md b/docs/dev/mhadmin/backend/classes/UpdatesChecker.md index fd9981e..1454b59 100644 --- a/docs/dev/mhadmin/backend/classes/UpdatesChecker.md +++ b/docs/dev/mhadmin/backend/classes/UpdatesChecker.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Класс: UpdatesChecker - MH Admin" +description: "Документация по плагину класс: updateschecker для DLE." +keywords: "PHP, DLE, Плагин, Админка, класс: updateschecker, DevCraft, документация" +author: "Maxim Harder" +og:title: "Класс: UpdatesChecker" +og:description: "Документация по плагину класс: updateschecker для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Класс: UpdatesChecker" +twitter:description: "Документация по плагину класс: updateschecker для DLE." +--- + # Класс: UpdatesChecker ## Краткое содержание: diff --git a/docs/dev/mhadmin/backend/inc_main.md b/docs/dev/mhadmin/backend/inc_main.md index 45f44b0..dfd653c 100644 --- a/docs/dev/mhadmin/backend/inc_main.md +++ b/docs/dev/mhadmin/backend/inc_main.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Конфигурация модуля - MH Admin" +description: "Документация по плагину конфигурация модуля для DLE." +keywords: "PHP, DLE, Плагин, Админка, конфигурация модуля, DevCraft, документация" +author: "Maxim Harder" +og:title: "Конфигурация модуля" +og:description: "Документация по плагину конфигурация модуля для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Конфигурация модуля" +twitter:description: "Документация по плагину конфигурация модуля для DLE." +--- + # Конфигурация модуля Самый главный файл настроек в админ панеле находится по пути `engine/inc/module.php`. Альтернатив, к сожалению, пока что нет. Можно, конечно сделать как при AJAX. Но, пожалуйста не надо. diff --git a/docs/dev/mhadmin/backend/index.md b/docs/dev/mhadmin/backend/index.md index 62ed012..84a2f24 100644 --- a/docs/dev/mhadmin/backend/index.md +++ b/docs/dev/mhadmin/backend/index.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Содержание классов для разработки в Back-End - MH Admin" +description: "Документация по плагину содержание классов для разработки в back-end для DLE." +keywords: "PHP, DLE, Плагин, Админка, содержание классов для разработки в back-end, DevCraft, документация" +author: "Maxim Harder" +og:title: "Содержание классов для разработки в Back-End" +og:description: "Документация по плагину содержание классов для разработки в back-end для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Содержание классов для разработки в Back-End" +twitter:description: "Документация по плагину содержание классов для разработки в back-end для DLE." +--- + # Содержание классов для разработки в Back-End ## Трейты diff --git a/docs/dev/mhadmin/changelog.md b/docs/dev/mhadmin/changelog.md index 728d7b7..a2f87a0 100644 --- a/docs/dev/mhadmin/changelog.md +++ b/docs/dev/mhadmin/changelog.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "История изменений - MH Admin" +description: "Документация по плагину история изменений для DLE." +keywords: "PHP, DLE, Плагин, Админка, история изменений, DevCraft, документация" +author: "Maxim Harder" +og:title: "История изменений" +og:description: "Документация по плагину история изменений для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "История изменений" +twitter:description: "Документация по плагину история изменений для DLE." +--- + # История изменений +/- 173.3.3 diff --git a/docs/dev/mhadmin/frontend/manage.md b/docs/dev/mhadmin/frontend/manage.md index dc9d02a..ce9dbfe 100644 --- a/docs/dev/mhadmin/frontend/manage.md +++ b/docs/dev/mhadmin/frontend/manage.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Настройка - MH Admin" +description: "Документация по плагину настройка для DLE." +keywords: "PHP, DLE, Плагин, Админка, настройка, DevCraft, документация" +author: "Maxim Harder" +og:title: "Настройка" +og:description: "Документация по плагину настройка для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Настройка" +twitter:description: "Документация по плагину настройка для DLE." +--- + # Настройка Административная панель работает на основе [Fomantic UI](https://fomantic-ui.com) diff --git a/docs/dev/mhadmin/frontend/templates.md b/docs/dev/mhadmin/frontend/templates.md index 9f9e55c..390b9bb 100644 --- a/docs/dev/mhadmin/frontend/templates.md +++ b/docs/dev/mhadmin/frontend/templates.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Шаблоны - MH Admin" +description: "Документация по плагину шаблоны для DLE." +keywords: "PHP, DLE, Плагин, Админка, шаблоны, DevCraft, документация" +author: "Maxim Harder" +og:title: "Шаблоны" +og:description: "Документация по плагину шаблоны для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Шаблоны" +twitter:description: "Документация по плагину шаблоны для DLE." +--- + # Шаблоны Все шаблоны расположены по пути `engine/inc/maharder/admin/templates` и `engine/inc/maharder/_templates/ваш_модуль`. Для оформления используется шаблонизатор [Twig](https://twig.symfony.com/doc/3.x/templates.html) с некоторыми дополнениями. diff --git a/docs/dev/mhadmin/generate_languages.md b/docs/dev/mhadmin/generate_languages.md index dc597b4..b091430 100644 --- a/docs/dev/mhadmin/generate_languages.md +++ b/docs/dev/mhadmin/generate_languages.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Генератор языковых файлов - MH Admin" +description: "Документация по плагину генератор языковых файлов для DLE." +keywords: "PHP, DLE, Плагин, Админка, генератор языковых файлов, DevCraft, документация" +author: "Maxim Harder" +og:title: "Генератор языковых файлов" +og:description: "Документация по плагину генератор языковых файлов для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Генератор языковых файлов" +twitter:description: "Документация по плагину генератор языковых файлов для DLE." +--- + # Генератор языковых файлов Проект задумывался для упрощённого вывода переводимых фраз в отдельный файл, который можно будет при помощи сервиса Crowdin перевести. diff --git a/docs/dev/mhadmin/new_module.md b/docs/dev/mhadmin/new_module.md index 04065af..db07e51 100644 --- a/docs/dev/mhadmin/new_module.md +++ b/docs/dev/mhadmin/new_module.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Админка +title: "Генератор модуля - MH Admin" +description: "Документация по плагину генератор модуля для DLE." +keywords: "PHP, DLE, Плагин, Админка, генератор модуля, DevCraft, документация" +author: "Maxim Harder" +og:title: "Генератор модуля" +og:description: "Документация по плагину генератор модуля для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Генератор модуля" +twitter:description: "Документация по плагину генератор модуля для DLE." +--- + # Генератор модуля Данный функционал был разработан для облегчённого добавления структуры разработки административной панели плагина. diff --git a/docs/dev/mystatus.md b/docs/dev/mystatus.md index ff34c0a..4336ee2 100644 --- a/docs/dev/mystatus.md +++ b/docs/dev/mystatus.md @@ -2,20 +2,20 @@ tags: - DLE - Плагин -title: "MyStatus - DevCraft Документации" +title: "MyStatus - Статус сериалов" description: "Документация по модулю MyStatus для DLE. Отображение статуса пользователей." keywords: "MyStatus, DLE, статус, пользователи, модуль" author: "Maxim Harder" og:title: "MyStatus - модуль статуса пользователей" og:description: "Документация по модулю MyStatus для DLE" -og:image: "https://devcraft.club/assets/images/logo.png" +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" twitter:title: "MyStatus - модуль статуса пользователей" twitter:description: "Документация по модулю MyStatus для DLE" --- # MyStatus - Статус сериалов -**Ссылка на разработку**: [ Перейти к разработке](https://devcraft.club/downloads/mystatus.5/) +**Ссылка на разработку**: [Перейти к разработке](https://devcraft.club/downloads/mystatus.5/) **Версия модификации**: 1.0.2.3 diff --git a/docs/dev/notkinopoiskphp/abstract-classes.md b/docs/dev/notkinopoiskphp/abstract-classes.md index c02f025..1e9babd 100644 --- a/docs/dev/notkinopoiskphp/abstract-classes.md +++ b/docs/dev/notkinopoiskphp/abstract-classes.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Плагин +title: "Абстрактные классы - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по абстрактные классы. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Плагин, абстрактные классы, DevCraft, документация" +author: "Maxim Harder" +og:title: "Абстрактные классы" +og:description: "Документация по абстрактные классы. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Абстрактные классы" +twitter:description: "Документация по абстрактные классы. Часть API wrapper для КиноПоиска." +--- + # Абстрактные классы Абстрактные базовые классы библиотеки NotKinopoisk PHP Wrapper. diff --git a/docs/dev/notkinopoiskphp/client.md b/docs/dev/notkinopoiskphp/client.md index 1c8afa3..5bd1865 100644 --- a/docs/dev/notkinopoiskphp/client.md +++ b/docs/dev/notkinopoiskphp/client.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Плагин +title: "Client - Основной клиент - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по client - основной клиент. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Плагин, client - основной клиент, DevCraft, документация" +author: "Maxim Harder" +og:title: "Client - Основной клиент" +og:description: "Документация по client - основной клиент. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Client - Основной клиент" +twitter:description: "Документация по client - основной клиент. Часть API wrapper для КиноПоиска." +--- + # Client - Основной клиент Основной клиент для работы с Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/enums/account-type.md b/docs/dev/notkinopoiskphp/enums/account-type.md index aa4d33b..4426e1a 100644 --- a/docs/dev/notkinopoiskphp/enums/account-type.md +++ b/docs/dev/notkinopoiskphp/enums/account-type.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Перечисления +title: "AccountType - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по accounttype. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Перечисления, accounttype, DevCraft, документация" +author: "Maxim Harder" +og:title: "AccountType" +og:description: "Документация по accounttype. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "AccountType" +twitter:description: "Документация по accounttype. Часть API wrapper для КиноПоиска." +--- + # AccountType ## Описание diff --git a/docs/dev/notkinopoiskphp/enums/api-version.md b/docs/dev/notkinopoiskphp/enums/api-version.md index 864e191..be07568 100644 --- a/docs/dev/notkinopoiskphp/enums/api-version.md +++ b/docs/dev/notkinopoiskphp/enums/api-version.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Перечисления +title: "ApiVersion - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по apiversion. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Перечисления, apiversion, DevCraft, документация" +author: "Maxim Harder" +og:title: "ApiVersion" +og:description: "Документация по apiversion. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "ApiVersion" +twitter:description: "Документация по apiversion. Часть API wrapper для КиноПоиска." +--- + # ApiVersion Перечисление версий API Kinopoisk Unofficial. diff --git a/docs/dev/notkinopoiskphp/enums/box-office-type.md b/docs/dev/notkinopoiskphp/enums/box-office-type.md index 0ef713d..ae2bb0c 100644 --- a/docs/dev/notkinopoiskphp/enums/box-office-type.md +++ b/docs/dev/notkinopoiskphp/enums/box-office-type.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Перечисления +title: "BoxOfficeType - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по boxofficetype. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Перечисления, boxofficetype, DevCraft, документация" +author: "Maxim Harder" +og:title: "BoxOfficeType" +og:description: "Документация по boxofficetype. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "BoxOfficeType" +twitter:description: "Документация по boxofficetype. Часть API wrapper для КиноПоиска." +--- + # BoxOfficeType Типы кассовых сборов в Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/enums/collection-type.md b/docs/dev/notkinopoiskphp/enums/collection-type.md index f7894da..a29b791 100644 --- a/docs/dev/notkinopoiskphp/enums/collection-type.md +++ b/docs/dev/notkinopoiskphp/enums/collection-type.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Перечисления +title: "CollectionType - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по collectiontype. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Перечисления, collectiontype, DevCraft, документация" +author: "Maxim Harder" +og:title: "CollectionType" +og:description: "Документация по collectiontype. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "CollectionType" +twitter:description: "Документация по collectiontype. Часть API wrapper для КиноПоиска." +--- + # CollectionType Типы коллекций фильмов в Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/enums/content-type.md b/docs/dev/notkinopoiskphp/enums/content-type.md index 079e276..0a3d205 100644 --- a/docs/dev/notkinopoiskphp/enums/content-type.md +++ b/docs/dev/notkinopoiskphp/enums/content-type.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Перечисления +title: "ContentType - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по contenttype. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Перечисления, contenttype, DevCraft, документация" +author: "Maxim Harder" +og:title: "ContentType" +og:description: "Документация по contenttype. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "ContentType" +twitter:description: "Документация по contenttype. Часть API wrapper для КиноПоиска." +--- + # ContentType Типы контента в Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/enums/distribution-sub-type.md b/docs/dev/notkinopoiskphp/enums/distribution-sub-type.md index 05e3389..ede4b92 100644 --- a/docs/dev/notkinopoiskphp/enums/distribution-sub-type.md +++ b/docs/dev/notkinopoiskphp/enums/distribution-sub-type.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Перечисления +title: "DistributionSubType - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по distributionsubtype. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Перечисления, distributionsubtype, DevCraft, документация" +author: "Maxim Harder" +og:title: "DistributionSubType" +og:description: "Документация по distributionsubtype. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "DistributionSubType" +twitter:description: "Документация по distributionsubtype. Часть API wrapper для КиноПоиска." +--- + # DistributionSubType ## Описание diff --git a/docs/dev/notkinopoiskphp/enums/distribution-type.md b/docs/dev/notkinopoiskphp/enums/distribution-type.md index 473e18e..cab495c 100644 --- a/docs/dev/notkinopoiskphp/enums/distribution-type.md +++ b/docs/dev/notkinopoiskphp/enums/distribution-type.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Перечисления +title: "DistributionType - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по distributiontype. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Перечисления, distributiontype, DevCraft, документация" +author: "Maxim Harder" +og:title: "DistributionType" +og:description: "Документация по distributiontype. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "DistributionType" +twitter:description: "Документация по distributiontype. Часть API wrapper для КиноПоиска." +--- + # DistributionType Типы проката в Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/enums/fact-type.md b/docs/dev/notkinopoiskphp/enums/fact-type.md index 09f2a0b..a724c65 100644 --- a/docs/dev/notkinopoiskphp/enums/fact-type.md +++ b/docs/dev/notkinopoiskphp/enums/fact-type.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Перечисления +title: "FactType - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по facttype. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Перечисления, facttype, DevCraft, документация" +author: "Maxim Harder" +og:title: "FactType" +og:description: "Документация по facttype. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "FactType" +twitter:description: "Документация по facttype. Часть API wrapper для КиноПоиска." +--- + # FactType Типы фактов в Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/enums/film-order.md b/docs/dev/notkinopoiskphp/enums/film-order.md index a00579e..cb8ed07 100644 --- a/docs/dev/notkinopoiskphp/enums/film-order.md +++ b/docs/dev/notkinopoiskphp/enums/film-order.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Перечисления +title: "FilmOrder - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по filmorder. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Перечисления, filmorder, DevCraft, документация" +author: "Maxim Harder" +og:title: "FilmOrder" +og:description: "Документация по filmorder. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "FilmOrder" +twitter:description: "Документация по filmorder. Часть API wrapper для КиноПоиска." +--- + # FilmOrder Порядок сортировки фильмов в Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/enums/image-type.md b/docs/dev/notkinopoiskphp/enums/image-type.md index ade8558..6d8ac55 100644 --- a/docs/dev/notkinopoiskphp/enums/image-type.md +++ b/docs/dev/notkinopoiskphp/enums/image-type.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Перечисления +title: "ImageType - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по imagetype. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Перечисления, imagetype, DevCraft, документация" +author: "Maxim Harder" +og:title: "ImageType" +og:description: "Документация по imagetype. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "ImageType" +twitter:description: "Документация по imagetype. Часть API wrapper для КиноПоиска." +--- + # ImageType Типы изображений в Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/enums/index.md b/docs/dev/notkinopoiskphp/enums/index.md index e585b8e..587300e 100644 --- a/docs/dev/notkinopoiskphp/enums/index.md +++ b/docs/dev/notkinopoiskphp/enums/index.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Перечисления +title: "Перечисления (Enums) - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по перечисления (enums). Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Перечисления, перечисления (enums), DevCraft, документация" +author: "Maxim Harder" +og:title: "Перечисления (Enums)" +og:description: "Документация по перечисления (enums). Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Перечисления (Enums)" +twitter:description: "Документация по перечисления (enums). Часть API wrapper для КиноПоиска." +--- + # Перечисления (Enums) Перечисления для определения констант и типов в Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/enums/month.md b/docs/dev/notkinopoiskphp/enums/month.md index 82e432b..5656b45 100644 --- a/docs/dev/notkinopoiskphp/enums/month.md +++ b/docs/dev/notkinopoiskphp/enums/month.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Перечисления +title: "Month - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по month. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Перечисления, month, DevCraft, документация" +author: "Maxim Harder" +og:title: "Month" +og:description: "Документация по month. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Month" +twitter:description: "Документация по month. Часть API wrapper для КиноПоиска." +--- + # Month Enum месяцев года для API запросов. diff --git a/docs/dev/notkinopoiskphp/enums/production-status.md b/docs/dev/notkinopoiskphp/enums/production-status.md index 51cb98e..bcb57f2 100644 --- a/docs/dev/notkinopoiskphp/enums/production-status.md +++ b/docs/dev/notkinopoiskphp/enums/production-status.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Перечисления +title: "ProductionStatus - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по productionstatus. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Перечисления, productionstatus, DevCraft, документация" +author: "Maxim Harder" +og:title: "ProductionStatus" +og:description: "Документация по productionstatus. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "ProductionStatus" +twitter:description: "Документация по productionstatus. Часть API wrapper для КиноПоиска." +--- + # ProductionStatus ## Описание diff --git a/docs/dev/notkinopoiskphp/enums/profession-key.md b/docs/dev/notkinopoiskphp/enums/profession-key.md index faa8516..2ae4887 100644 --- a/docs/dev/notkinopoiskphp/enums/profession-key.md +++ b/docs/dev/notkinopoiskphp/enums/profession-key.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Перечисления +title: "ProfessionKey - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по professionkey. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Перечисления, professionkey, DevCraft, документация" +author: "Maxim Harder" +og:title: "ProfessionKey" +og:description: "Документация по professionkey. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "ProfessionKey" +twitter:description: "Документация по professionkey. Часть API wrapper для КиноПоиска." +--- + # ProfessionKey Enum профессий персоны в фильме. diff --git a/docs/dev/notkinopoiskphp/enums/relation-type.md b/docs/dev/notkinopoiskphp/enums/relation-type.md index 50e8774..4464278 100644 --- a/docs/dev/notkinopoiskphp/enums/relation-type.md +++ b/docs/dev/notkinopoiskphp/enums/relation-type.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Перечисления +title: "RelationType - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по relationtype. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Перечисления, relationtype, DevCraft, документация" +author: "Maxim Harder" +og:title: "RelationType" +og:description: "Документация по relationtype. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "RelationType" +twitter:description: "Документация по relationtype. Часть API wrapper для КиноПоиска." +--- + # RelationType Типы связи между фильмами в Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/enums/review-order.md b/docs/dev/notkinopoiskphp/enums/review-order.md index 66e7c92..b82c9d6 100644 --- a/docs/dev/notkinopoiskphp/enums/review-order.md +++ b/docs/dev/notkinopoiskphp/enums/review-order.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Перечисления +title: "ReviewOrder - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по revieworder. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Перечисления, revieworder, DevCraft, документация" +author: "Maxim Harder" +og:title: "ReviewOrder" +og:description: "Документация по revieworder. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "ReviewOrder" +twitter:description: "Документация по revieworder. Часть API wrapper для КиноПоиска." +--- + # ReviewOrder Enum типов сортировки отзывов в Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/enums/review-type.md b/docs/dev/notkinopoiskphp/enums/review-type.md index cb89991..2bd0c89 100644 --- a/docs/dev/notkinopoiskphp/enums/review-type.md +++ b/docs/dev/notkinopoiskphp/enums/review-type.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Перечисления +title: "ReviewType - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по reviewtype. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Перечисления, reviewtype, DevCraft, документация" +author: "Maxim Harder" +og:title: "ReviewType" +og:description: "Документация по reviewtype. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "ReviewType" +twitter:description: "Документация по reviewtype. Часть API wrapper для КиноПоиска." +--- + # ReviewType Типы рецензий в Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/enums/sex.md b/docs/dev/notkinopoiskphp/enums/sex.md index 7380e4a..df33403 100644 --- a/docs/dev/notkinopoiskphp/enums/sex.md +++ b/docs/dev/notkinopoiskphp/enums/sex.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Перечисления +title: "Sex - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по sex. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Перечисления, sex, DevCraft, документация" +author: "Maxim Harder" +og:title: "Sex" +og:description: "Документация по sex. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Sex" +twitter:description: "Документация по sex. Часть API wrapper для КиноПоиска." +--- + # Sex Пол из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/enums/video-site.md b/docs/dev/notkinopoiskphp/enums/video-site.md index 006038f..8b3e9af 100644 --- a/docs/dev/notkinopoiskphp/enums/video-site.md +++ b/docs/dev/notkinopoiskphp/enums/video-site.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Перечисления +title: "VideoSite - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по videosite. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Перечисления, videosite, DevCraft, документация" +author: "Maxim Harder" +og:title: "VideoSite" +og:description: "Документация по videosite. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "VideoSite" +twitter:description: "Документация по videosite. Часть API wrapper для КиноПоиска." +--- + # VideoSite Типы видео-сайтов в Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/exceptions/api-exception.md b/docs/dev/notkinopoiskphp/exceptions/api-exception.md index a0fc3cd..e2041d7 100644 --- a/docs/dev/notkinopoiskphp/exceptions/api-exception.md +++ b/docs/dev/notkinopoiskphp/exceptions/api-exception.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Исключения +title: "ApiException - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по apiexception. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Исключения, apiexception, DevCraft, документация" +author: "Maxim Harder" +og:title: "ApiException" +og:description: "Документация по apiexception. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "ApiException" +twitter:description: "Документация по apiexception. Часть API wrapper для КиноПоиска." +--- + # ApiException Базовое исключение для всех ошибок, связанных с Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/exceptions/index.md b/docs/dev/notkinopoiskphp/exceptions/index.md index a054ec5..2a3308e 100644 --- a/docs/dev/notkinopoiskphp/exceptions/index.md +++ b/docs/dev/notkinopoiskphp/exceptions/index.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Исключения +title: "Исключения - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по исключения. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Исключения, исключения, DevCraft, документация" +author: "Maxim Harder" +og:title: "Исключения" +og:description: "Документация по исключения. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Исключения" +twitter:description: "Документация по исключения. Часть API wrapper для КиноПоиска." +--- + # Исключения Классы исключений для обработки ошибок в Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/exceptions/invalid-api-key-exception.md b/docs/dev/notkinopoiskphp/exceptions/invalid-api-key-exception.md index d70217e..3a9f853 100644 --- a/docs/dev/notkinopoiskphp/exceptions/invalid-api-key-exception.md +++ b/docs/dev/notkinopoiskphp/exceptions/invalid-api-key-exception.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Исключения +title: "InvalidApiKeyException - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по invalidapikeyexception. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Исключения, invalidapikeyexception, DevCraft, документация" +author: "Maxim Harder" +og:title: "InvalidApiKeyException" +og:description: "Документация по invalidapikeyexception. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "InvalidApiKeyException" +twitter:description: "Документация по invalidapikeyexception. Часть API wrapper для КиноПоиска." +--- + # InvalidApiKeyException Исключение для неверного или отсутствующего API ключа. diff --git a/docs/dev/notkinopoiskphp/exceptions/kp-validation-exception.md b/docs/dev/notkinopoiskphp/exceptions/kp-validation-exception.md index 14e7a94..c519718 100644 --- a/docs/dev/notkinopoiskphp/exceptions/kp-validation-exception.md +++ b/docs/dev/notkinopoiskphp/exceptions/kp-validation-exception.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Исключения +title: "KpValidationException - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по kpvalidationexception. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Исключения, kpvalidationexception, DevCraft, документация" +author: "Maxim Harder" +og:title: "KpValidationException" +og:description: "Документация по kpvalidationexception. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "KpValidationException" +twitter:description: "Документация по kpvalidationexception. Часть API wrapper для КиноПоиска." +--- + # KpValidationException ## Описание diff --git a/docs/dev/notkinopoiskphp/exceptions/rate-limit-exception.md b/docs/dev/notkinopoiskphp/exceptions/rate-limit-exception.md index 0c9ac48..248f7e8 100644 --- a/docs/dev/notkinopoiskphp/exceptions/rate-limit-exception.md +++ b/docs/dev/notkinopoiskphp/exceptions/rate-limit-exception.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Исключения +title: "RateLimitException - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по ratelimitexception. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Исключения, ratelimitexception, DevCraft, документация" +author: "Maxim Harder" +og:title: "RateLimitException" +og:description: "Документация по ratelimitexception. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "RateLimitException" +twitter:description: "Документация по ratelimitexception. Часть API wrapper для КиноПоиска." +--- + # RateLimitException Исключение для превышения лимитов запросов к API. diff --git a/docs/dev/notkinopoiskphp/exceptions/resource-not-found-exception.md b/docs/dev/notkinopoiskphp/exceptions/resource-not-found-exception.md index 16e7c3d..46b50b1 100644 --- a/docs/dev/notkinopoiskphp/exceptions/resource-not-found-exception.md +++ b/docs/dev/notkinopoiskphp/exceptions/resource-not-found-exception.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Исключения +title: "ResourceNotFoundException - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по resourcenotfoundexception. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Исключения, resourcenotfoundexception, DevCraft, документация" +author: "Maxim Harder" +og:title: "ResourceNotFoundException" +og:description: "Документация по resourcenotfoundexception. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "ResourceNotFoundException" +twitter:description: "Документация по resourcenotfoundexception. Часть API wrapper для КиноПоиска." +--- + # ResourceNotFoundException Исключение для ненайденных ресурсов в API. diff --git a/docs/dev/notkinopoiskphp/index.md b/docs/dev/notkinopoiskphp/index.md index d75be9a..84f916f 100644 --- a/docs/dev/notkinopoiskphp/index.md +++ b/docs/dev/notkinopoiskphp/index.md @@ -5,13 +5,13 @@ tags: - Плагин - Kinopoisk - Wrapper -title: "NotKinopoiskPHP - DevCraft Документации" +title: "NotKinopoiskPHP - KinopoiskUnofficialTech PHP Wrapper" description: "PHP wrapper для API КиноПоиска. Полная документация по использованию NotKinopoiskPHP." keywords: "NotKinopoiskPHP, КиноПоиск, API, PHP, wrapper, kinopoiskunofficial.tech" author: "Maxim Harder" og:title: "NotKinopoiskPHP - PHP wrapper для КиноПоиска" og:description: "PHP wrapper для API КиноПоиска с полной документацией" -og:image: "https://devcraft.club/assets/images/logo.png" +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" twitter:title: "NotKinopoiskPHP - PHP wrapper для КиноПоиска" twitter:description: "PHP wrapper для API КиноПоиска с полной документацией" --- diff --git a/docs/dev/notkinopoiskphp/interfaces/index.md b/docs/dev/notkinopoiskphp/interfaces/index.md index a34ecb2..7f683f2 100644 --- a/docs/dev/notkinopoiskphp/interfaces/index.md +++ b/docs/dev/notkinopoiskphp/interfaces/index.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Интерфейсы +title: "Интерфейсы - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по интерфейсы. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Интерфейсы, интерфейсы, DevCraft, документация" +author: "Maxim Harder" +og:title: "Интерфейсы" +og:description: "Документация по интерфейсы. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Интерфейсы" +twitter:description: "Документация по интерфейсы. Часть API wrapper для КиноПоиска." +--- + # Интерфейсы Базовые интерфейсы для определения контрактов в Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/interfaces/model-interface.md b/docs/dev/notkinopoiskphp/interfaces/model-interface.md index ebd5575..fb18782 100644 --- a/docs/dev/notkinopoiskphp/interfaces/model-interface.md +++ b/docs/dev/notkinopoiskphp/interfaces/model-interface.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Интерфейсы +title: "ModelInterface - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по modelinterface. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Интерфейсы, modelinterface, DevCraft, документация" +author: "Maxim Harder" +og:title: "ModelInterface" +og:description: "Документация по modelinterface. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "ModelInterface" +twitter:description: "Документация по modelinterface. Часть API wrapper для КиноПоиска." +--- + # ModelInterface Интерфейс для моделей с возможностью преобразования в/из массива. diff --git a/docs/dev/notkinopoiskphp/interfaces/response-interface.md b/docs/dev/notkinopoiskphp/interfaces/response-interface.md index 42d9287..689175a 100644 --- a/docs/dev/notkinopoiskphp/interfaces/response-interface.md +++ b/docs/dev/notkinopoiskphp/interfaces/response-interface.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Интерфейсы +title: "ResponseInterface - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по responseinterface. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Интерфейсы, responseinterface, DevCraft, документация" +author: "Maxim Harder" +og:title: "ResponseInterface" +og:description: "Документация по responseinterface. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "ResponseInterface" +twitter:description: "Документация по responseinterface. Часть API wrapper для КиноПоиска." +--- + # ResponseInterface Интерфейс для объектов ответов Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/models/api-key-info.md b/docs/dev/notkinopoiskphp/models/api-key-info.md index 82549e7..cd40d60 100644 --- a/docs/dev/notkinopoiskphp/models/api-key-info.md +++ b/docs/dev/notkinopoiskphp/models/api-key-info.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "ApiKeyInfo - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по apikeyinfo. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, apikeyinfo, DevCraft, документация" +author: "Maxim Harder" +og:title: "ApiKeyInfo" +og:description: "Документация по apikeyinfo. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "ApiKeyInfo" +twitter:description: "Документация по apikeyinfo. Часть API wrapper для КиноПоиска." +--- + # ApiKeyInfo ## Описание diff --git a/docs/dev/notkinopoiskphp/models/api-key-qouta.md b/docs/dev/notkinopoiskphp/models/api-key-qouta.md index 0cffd2e..7e69fe8 100644 --- a/docs/dev/notkinopoiskphp/models/api-key-qouta.md +++ b/docs/dev/notkinopoiskphp/models/api-key-qouta.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "ApiKeyQouta - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по apikeyqouta. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, apikeyqouta, DevCraft, документация" +author: "Maxim Harder" +og:title: "ApiKeyQouta" +og:description: "Документация по apikeyqouta. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "ApiKeyQouta" +twitter:description: "Документация по apikeyqouta. Часть API wrapper для КиноПоиска." +--- + # ApiKeyQouta ## Описание diff --git a/docs/dev/notkinopoiskphp/models/award.md b/docs/dev/notkinopoiskphp/models/award.md index ea119d4..2f9e998 100644 --- a/docs/dev/notkinopoiskphp/models/award.md +++ b/docs/dev/notkinopoiskphp/models/award.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "Award - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по award. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, award, DevCraft, документация" +author: "Maxim Harder" +og:title: "Award" +og:description: "Документация по award. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Award" +twitter:description: "Документация по award. Часть API wrapper для КиноПоиска." +--- + # Award Модель награды из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/models/box-office.md b/docs/dev/notkinopoiskphp/models/box-office.md index 6d3f2d6..36126c3 100644 --- a/docs/dev/notkinopoiskphp/models/box-office.md +++ b/docs/dev/notkinopoiskphp/models/box-office.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "BoxOffice - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по boxoffice. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, boxoffice, DevCraft, документация" +author: "Maxim Harder" +og:title: "BoxOffice" +og:description: "Документация по boxoffice. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "BoxOffice" +twitter:description: "Документация по boxoffice. Часть API wrapper для КиноПоиска." +--- + # BoxOffice Модель кассовых сборов из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/models/country.md b/docs/dev/notkinopoiskphp/models/country.md index 8a53628..47c2186 100644 --- a/docs/dev/notkinopoiskphp/models/country.md +++ b/docs/dev/notkinopoiskphp/models/country.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "Country - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по country. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, country, DevCraft, документация" +author: "Maxim Harder" +og:title: "Country" +og:description: "Документация по country. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Country" +twitter:description: "Документация по country. Часть API wrapper для КиноПоиска." +--- + # Country Модель страны из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/models/distribution.md b/docs/dev/notkinopoiskphp/models/distribution.md index edf5c82..d3de9eb 100644 --- a/docs/dev/notkinopoiskphp/models/distribution.md +++ b/docs/dev/notkinopoiskphp/models/distribution.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "Distribution - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по distribution. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, distribution, DevCraft, документация" +author: "Maxim Harder" +og:title: "Distribution" +og:description: "Документация по distribution. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Distribution" +twitter:description: "Документация по distribution. Часть API wrapper для КиноПоиска." +--- + # Distribution Модель данных о прокате фильма из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/models/episode.md b/docs/dev/notkinopoiskphp/models/episode.md index f486a8b..23f4835 100644 --- a/docs/dev/notkinopoiskphp/models/episode.md +++ b/docs/dev/notkinopoiskphp/models/episode.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "Episode - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по episode. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, episode, DevCraft, документация" +author: "Maxim Harder" +og:title: "Episode" +og:description: "Документация по episode. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Episode" +twitter:description: "Документация по episode. Часть API wrapper для КиноПоиска." +--- + # Episode Модель эпизода сериала из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/models/external-source.md b/docs/dev/notkinopoiskphp/models/external-source.md index d48cac5..e432f9d 100644 --- a/docs/dev/notkinopoiskphp/models/external-source.md +++ b/docs/dev/notkinopoiskphp/models/external-source.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "ExternalSource - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по externalsource. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, externalsource, DevCraft, документация" +author: "Maxim Harder" +og:title: "ExternalSource" +og:description: "Документация по externalsource. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "ExternalSource" +twitter:description: "Документация по externalsource. Часть API wrapper для КиноПоиска." +--- + # ExternalSource Модель внешнего источника из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/models/fact.md b/docs/dev/notkinopoiskphp/models/fact.md index 421195d..3ad1bbf 100644 --- a/docs/dev/notkinopoiskphp/models/fact.md +++ b/docs/dev/notkinopoiskphp/models/fact.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "Fact - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по fact. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, fact, DevCraft, документация" +author: "Maxim Harder" +og:title: "Fact" +og:description: "Документация по fact. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Fact" +twitter:description: "Документация по fact. Часть API wrapper для КиноПоиска." +--- + # Fact Модель факта из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/models/film-collection.md b/docs/dev/notkinopoiskphp/models/film-collection.md index bfd4b14..da014ed 100644 --- a/docs/dev/notkinopoiskphp/models/film-collection.md +++ b/docs/dev/notkinopoiskphp/models/film-collection.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "FilmCollection - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по filmcollection. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, filmcollection, DevCraft, документация" +author: "Maxim Harder" +og:title: "FilmCollection" +og:description: "Документация по filmcollection. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "FilmCollection" +twitter:description: "Документация по filmcollection. Часть API wrapper для КиноПоиска." +--- + # FilmCollection Модель элемента коллекции фильмов из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/models/film-search-result.md b/docs/dev/notkinopoiskphp/models/film-search-result.md index 4b1f274..f1ce2fa 100644 --- a/docs/dev/notkinopoiskphp/models/film-search-result.md +++ b/docs/dev/notkinopoiskphp/models/film-search-result.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "FilmSearchResult - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по filmsearchresult. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, filmsearchresult, DevCraft, документация" +author: "Maxim Harder" +og:title: "FilmSearchResult" +og:description: "Документация по filmsearchresult. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "FilmSearchResult" +twitter:description: "Документация по filmsearchresult. Часть API wrapper для КиноПоиска." +--- + # FilmSearchResult Модель результата поиска фильма из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/models/film.md b/docs/dev/notkinopoiskphp/models/film.md index 34aa146..e611b1e 100644 --- a/docs/dev/notkinopoiskphp/models/film.md +++ b/docs/dev/notkinopoiskphp/models/film.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "Film - Модель фильма - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по film - модель фильма. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, film - модель фильма, DevCraft, документация" +author: "Maxim Harder" +og:title: "Film - Модель фильма" +og:description: "Документация по film - модель фильма. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Film - Модель фильма" +twitter:description: "Документация по film - модель фильма. Часть API wrapper для КиноПоиска." +--- + # Film - Модель фильма Модель фильма из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/models/filters.md b/docs/dev/notkinopoiskphp/models/filters.md index b705602..99575f1 100644 --- a/docs/dev/notkinopoiskphp/models/filters.md +++ b/docs/dev/notkinopoiskphp/models/filters.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "Filters - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по filters. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, filters, DevCraft, документация" +author: "Maxim Harder" +og:title: "Filters" +og:description: "Документация по filters. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Filters" +twitter:description: "Документация по filters. Часть API wrapper для КиноПоиска." +--- + # Filters Модель фильтров из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/models/genre.md b/docs/dev/notkinopoiskphp/models/genre.md index 77734fb..13d57d7 100644 --- a/docs/dev/notkinopoiskphp/models/genre.md +++ b/docs/dev/notkinopoiskphp/models/genre.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "Genre - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по genre. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, genre, DevCraft, документация" +author: "Maxim Harder" +og:title: "Genre" +og:description: "Документация по genre. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Genre" +twitter:description: "Документация по genre. Часть API wrapper для КиноПоиска." +--- + # Genre Модель жанра из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/models/image.md b/docs/dev/notkinopoiskphp/models/image.md index c3e37c4..29fb48a 100644 --- a/docs/dev/notkinopoiskphp/models/image.md +++ b/docs/dev/notkinopoiskphp/models/image.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "Image - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по image. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, image, DevCraft, документация" +author: "Maxim Harder" +og:title: "Image" +og:description: "Документация по image. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Image" +twitter:description: "Документация по image. Часть API wrapper для КиноПоиска." +--- + # Image Модель изображения из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/models/index.md b/docs/dev/notkinopoiskphp/models/index.md index 3b76ac1..f5bfa9b 100644 --- a/docs/dev/notkinopoiskphp/models/index.md +++ b/docs/dev/notkinopoiskphp/models/index.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "Модели данных - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по модели данных. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, модели данных, DevCraft, документация" +author: "Maxim Harder" +og:title: "Модели данных" +og:description: "Документация по модели данных. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Модели данных" +twitter:description: "Документация по модели данных. Часть API wrapper для КиноПоиска." +--- + # Модели данных Модели для представления данных из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/models/media-post.md b/docs/dev/notkinopoiskphp/models/media-post.md index 9c5b995..4af312e 100644 --- a/docs/dev/notkinopoiskphp/models/media-post.md +++ b/docs/dev/notkinopoiskphp/models/media-post.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "MediaPost - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по mediapost. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, mediapost, DevCraft, документация" +author: "Maxim Harder" +og:title: "MediaPost" +og:description: "Документация по mediapost. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "MediaPost" +twitter:description: "Документация по mediapost. Часть API wrapper для КиноПоиска." +--- + # MediaPost ## Описание diff --git a/docs/dev/notkinopoiskphp/models/person-by-name-result.md b/docs/dev/notkinopoiskphp/models/person-by-name-result.md index bf2cbdf..3d08508 100644 --- a/docs/dev/notkinopoiskphp/models/person-by-name-result.md +++ b/docs/dev/notkinopoiskphp/models/person-by-name-result.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "PersonByNameResult - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по personbynameresult. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, personbynameresult, DevCraft, документация" +author: "Maxim Harder" +og:title: "PersonByNameResult" +og:description: "Документация по personbynameresult. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "PersonByNameResult" +twitter:description: "Документация по personbynameresult. Часть API wrapper для КиноПоиска." +--- + # PersonByNameResult ## Описание diff --git a/docs/dev/notkinopoiskphp/models/person-film.md b/docs/dev/notkinopoiskphp/models/person-film.md index a32ba5b..f7e2d87 100644 --- a/docs/dev/notkinopoiskphp/models/person-film.md +++ b/docs/dev/notkinopoiskphp/models/person-film.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "PersonFilm - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по personfilm. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, personfilm, DevCraft, документация" +author: "Maxim Harder" +og:title: "PersonFilm" +og:description: "Документация по personfilm. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "PersonFilm" +twitter:description: "Документация по personfilm. Часть API wrapper для КиноПоиска." +--- + # PersonFilm Модель фильма персоны из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/models/person-spouse.md b/docs/dev/notkinopoiskphp/models/person-spouse.md index 6cac1e3..45fe137 100644 --- a/docs/dev/notkinopoiskphp/models/person-spouse.md +++ b/docs/dev/notkinopoiskphp/models/person-spouse.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "PersonSpouse - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по personspouse. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, personspouse, DevCraft, документация" +author: "Maxim Harder" +og:title: "PersonSpouse" +og:description: "Документация по personspouse. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "PersonSpouse" +twitter:description: "Документация по personspouse. Часть API wrapper для КиноПоиска." +--- + # PersonSpouse Модель супруга персоны из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/models/person.md b/docs/dev/notkinopoiskphp/models/person.md index a7ecce0..aa0d217 100644 --- a/docs/dev/notkinopoiskphp/models/person.md +++ b/docs/dev/notkinopoiskphp/models/person.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "Person - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по person. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, person, DevCraft, документация" +author: "Maxim Harder" +og:title: "Person" +og:description: "Документация по person. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Person" +twitter:description: "Документация по person. Часть API wrapper для КиноПоиска." +--- + # Person Модель персоны из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/models/premiere.md b/docs/dev/notkinopoiskphp/models/premiere.md index dd03e29..b5a3855 100644 --- a/docs/dev/notkinopoiskphp/models/premiere.md +++ b/docs/dev/notkinopoiskphp/models/premiere.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "Premiere - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по premiere. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, premiere, DevCraft, документация" +author: "Maxim Harder" +og:title: "Premiere" +og:description: "Документация по premiere. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Premiere" +twitter:description: "Документация по premiere. Часть API wrapper для КиноПоиска." +--- + # Premiere Модель премьеры из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/models/related-film.md b/docs/dev/notkinopoiskphp/models/related-film.md index 4746183..1e78fd0 100644 --- a/docs/dev/notkinopoiskphp/models/related-film.md +++ b/docs/dev/notkinopoiskphp/models/related-film.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "RelatedFilm - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по relatedfilm. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, relatedfilm, DevCraft, документация" +author: "Maxim Harder" +og:title: "RelatedFilm" +og:description: "Документация по relatedfilm. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "RelatedFilm" +twitter:description: "Документация по relatedfilm. Часть API wrapper для КиноПоиска." +--- + # RelatedFilm Модель связанного фильма из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/models/review.md b/docs/dev/notkinopoiskphp/models/review.md index a9d8a9b..2296111 100644 --- a/docs/dev/notkinopoiskphp/models/review.md +++ b/docs/dev/notkinopoiskphp/models/review.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "Review - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по review. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, review, DevCraft, документация" +author: "Maxim Harder" +og:title: "Review" +og:description: "Документация по review. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Review" +twitter:description: "Документация по review. Часть API wrapper для КиноПоиска." +--- + # Review Модель рецензии из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/models/season.md b/docs/dev/notkinopoiskphp/models/season.md index b278060..aa17522 100644 --- a/docs/dev/notkinopoiskphp/models/season.md +++ b/docs/dev/notkinopoiskphp/models/season.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "Season - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по season. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, season, DevCraft, документация" +author: "Maxim Harder" +og:title: "Season" +og:description: "Документация по season. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Season" +twitter:description: "Документация по season. Часть API wrapper для КиноПоиска." +--- + # Season Модель сезона сериала из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/models/staff.md b/docs/dev/notkinopoiskphp/models/staff.md index c288290..ee4ddf1 100644 --- a/docs/dev/notkinopoiskphp/models/staff.md +++ b/docs/dev/notkinopoiskphp/models/staff.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "Staff - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по staff. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, staff, DevCraft, документация" +author: "Maxim Harder" +og:title: "Staff" +og:description: "Документация по staff. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Staff" +twitter:description: "Документация по staff. Часть API wrapper для КиноПоиска." +--- + # Staff Модель персонала фильма из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/models/user-vote.md b/docs/dev/notkinopoiskphp/models/user-vote.md index 59f9fe4..fd1baf7 100644 --- a/docs/dev/notkinopoiskphp/models/user-vote.md +++ b/docs/dev/notkinopoiskphp/models/user-vote.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "UserVote - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по uservote. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, uservote, DevCraft, документация" +author: "Maxim Harder" +og:title: "UserVote" +og:description: "Документация по uservote. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "UserVote" +twitter:description: "Документация по uservote. Часть API wrapper для КиноПоиска." +--- + # UserVote Модель пользовательского голоса. diff --git a/docs/dev/notkinopoiskphp/models/video.md b/docs/dev/notkinopoiskphp/models/video.md index 235fdbd..5fff32d 100644 --- a/docs/dev/notkinopoiskphp/models/video.md +++ b/docs/dev/notkinopoiskphp/models/video.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Модели +title: "Video - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по video. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Модели, video, DevCraft, документация" +author: "Maxim Harder" +og:title: "Video" +og:description: "Документация по video. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Video" +twitter:description: "Документация по video. Часть API wrapper для КиноПоиска." +--- + # Video Модель видео из Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/navigation-map.md b/docs/dev/notkinopoiskphp/navigation-map.md index 027901d..be9e962 100644 --- a/docs/dev/notkinopoiskphp/navigation-map.md +++ b/docs/dev/notkinopoiskphp/navigation-map.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Плагин +title: "Карта навигации документации - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по карта навигации документации. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Плагин, карта навигации документации, DevCraft, документация" +author: "Maxim Harder" +og:title: "Карта навигации документации" +og:description: "Документация по карта навигации документации. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Карта навигации документации" +twitter:description: "Документация по карта навигации документации. Часть API wrapper для КиноПоиска." +--- + # Карта навигации документации Интерактивная карта навигации по всей документации NotKinopoisk PHP Library. diff --git a/docs/dev/notkinopoiskphp/responses/budget-response.md b/docs/dev/notkinopoiskphp/responses/budget-response.md index 0a5d96a..904523c 100644 --- a/docs/dev/notkinopoiskphp/responses/budget-response.md +++ b/docs/dev/notkinopoiskphp/responses/budget-response.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Ответы +title: "BudgetResponse - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по budgetresponse. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Ответы, budgetresponse, DevCraft, документация" +author: "Maxim Harder" +og:title: "BudgetResponse" +og:description: "Документация по budgetresponse. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "BudgetResponse" +twitter:description: "Документация по budgetresponse. Часть API wrapper для КиноПоиска." +--- + # BudgetResponse ## Описание diff --git a/docs/dev/notkinopoiskphp/responses/default-response.md b/docs/dev/notkinopoiskphp/responses/default-response.md index cb2e3dc..20cf724 100644 --- a/docs/dev/notkinopoiskphp/responses/default-response.md +++ b/docs/dev/notkinopoiskphp/responses/default-response.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Ответы +title: "DefaultResponse - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по defaultresponse. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Ответы, defaultresponse, DevCraft, документация" +author: "Maxim Harder" +og:title: "DefaultResponse" +og:description: "Документация по defaultresponse. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "DefaultResponse" +twitter:description: "Документация по defaultresponse. Часть API wrapper для КиноПоиска." +--- + # DefaultResponse Базовый класс для ответов Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/responses/index.md b/docs/dev/notkinopoiskphp/responses/index.md index 16887d2..6664cf8 100644 --- a/docs/dev/notkinopoiskphp/responses/index.md +++ b/docs/dev/notkinopoiskphp/responses/index.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Ответы +title: "Ответы API - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по ответы api. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Ответы, ответы api, DevCraft, документация" +author: "Maxim Harder" +og:title: "Ответы API" +og:description: "Документация по ответы api. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Ответы API" +twitter:description: "Документация по ответы api. Часть API wrapper для КиноПоиска." +--- + # Ответы API Классы для представления ответов от Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/responses/keyword-search-response.md b/docs/dev/notkinopoiskphp/responses/keyword-search-response.md index ff685df..d7250b9 100644 --- a/docs/dev/notkinopoiskphp/responses/keyword-search-response.md +++ b/docs/dev/notkinopoiskphp/responses/keyword-search-response.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Ответы +title: "KeywordSearchResponse - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по keywordsearchresponse. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Ответы, keywordsearchresponse, DevCraft, документация" +author: "Maxim Harder" +og:title: "KeywordSearchResponse" +og:description: "Документация по keywordsearchresponse. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "KeywordSearchResponse" +twitter:description: "Документация по keywordsearchresponse. Часть API wrapper для КиноПоиска." +--- + # KeywordSearchResponse Ответ на поиск фильмов по ключевому слову. diff --git a/docs/dev/notkinopoiskphp/responses/movie-staff-response.md b/docs/dev/notkinopoiskphp/responses/movie-staff-response.md index c5f66c6..5c5cfd5 100644 --- a/docs/dev/notkinopoiskphp/responses/movie-staff-response.md +++ b/docs/dev/notkinopoiskphp/responses/movie-staff-response.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Ответы +title: "MovieStaffResponse - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по moviestaffresponse. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Ответы, moviestaffresponse, DevCraft, документация" +author: "Maxim Harder" +og:title: "MovieStaffResponse" +og:description: "Документация по moviestaffresponse. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "MovieStaffResponse" +twitter:description: "Документация по moviestaffresponse. Часть API wrapper для КиноПоиска." +--- + # MovieStaffResponse ## Описание diff --git a/docs/dev/notkinopoiskphp/responses/paginated-response.md b/docs/dev/notkinopoiskphp/responses/paginated-response.md index 8c1dd74..df927ec 100644 --- a/docs/dev/notkinopoiskphp/responses/paginated-response.md +++ b/docs/dev/notkinopoiskphp/responses/paginated-response.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Ответы +title: "PaginatedResponse - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по paginatedresponse. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Ответы, paginatedresponse, DevCraft, документация" +author: "Maxim Harder" +og:title: "PaginatedResponse" +og:description: "Документация по paginatedresponse. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "PaginatedResponse" +twitter:description: "Документация по paginatedresponse. Часть API wrapper для КиноПоиска." +--- + # PaginatedResponse Пагинированный ответ API с поддержкой навигации по страницам. diff --git a/docs/dev/notkinopoiskphp/responses/review-response.md b/docs/dev/notkinopoiskphp/responses/review-response.md index 0c6ac1f..8d805a9 100644 --- a/docs/dev/notkinopoiskphp/responses/review-response.md +++ b/docs/dev/notkinopoiskphp/responses/review-response.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Ответы +title: "ReviewResponse - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по reviewresponse. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Ответы, reviewresponse, DevCraft, документация" +author: "Maxim Harder" +og:title: "ReviewResponse" +og:description: "Документация по reviewresponse. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "ReviewResponse" +twitter:description: "Документация по reviewresponse. Часть API wrapper для КиноПоиска." +--- + # ReviewResponse ## Описание diff --git a/docs/dev/notkinopoiskphp/responses/sequel-prequel-response.md b/docs/dev/notkinopoiskphp/responses/sequel-prequel-response.md index c5e86e5..c4d38ef 100644 --- a/docs/dev/notkinopoiskphp/responses/sequel-prequel-response.md +++ b/docs/dev/notkinopoiskphp/responses/sequel-prequel-response.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Ответы +title: "SequelPrequelResponse - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по sequelprequelresponse. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Ответы, sequelprequelresponse, DevCraft, документация" +author: "Maxim Harder" +og:title: "SequelPrequelResponse" +og:description: "Документация по sequelprequelresponse. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "SequelPrequelResponse" +twitter:description: "Документация по sequelprequelresponse. Часть API wrapper для КиноПоиска." +--- + # SequelPrequelResponse ## Описание diff --git a/docs/dev/notkinopoiskphp/responses/simple-response.md b/docs/dev/notkinopoiskphp/responses/simple-response.md index 40993fa..6576757 100644 --- a/docs/dev/notkinopoiskphp/responses/simple-response.md +++ b/docs/dev/notkinopoiskphp/responses/simple-response.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Ответы +title: "SimpleResponse - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по simpleresponse. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Ответы, simpleresponse, DevCraft, документация" +author: "Maxim Harder" +og:title: "SimpleResponse" +og:description: "Документация по simpleresponse. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "SimpleResponse" +twitter:description: "Документация по simpleresponse. Часть API wrapper для КиноПоиска." +--- + # SimpleResponse ## Описание diff --git a/docs/dev/notkinopoiskphp/services/abstract-service.md b/docs/dev/notkinopoiskphp/services/abstract-service.md index 092837d..bb190f0 100644 --- a/docs/dev/notkinopoiskphp/services/abstract-service.md +++ b/docs/dev/notkinopoiskphp/services/abstract-service.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Сервисы +title: "AbstractService - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по abstractservice. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Сервисы, abstractservice, DevCraft, документация" +author: "Maxim Harder" +og:title: "AbstractService" +og:description: "Документация по abstractservice. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "AbstractService" +twitter:description: "Документация по abstractservice. Часть API wrapper для КиноПоиска." +--- + # AbstractService ## Описание diff --git a/docs/dev/notkinopoiskphp/services/film-service.md b/docs/dev/notkinopoiskphp/services/film-service.md index 56e20b7..6ef5ac7 100644 --- a/docs/dev/notkinopoiskphp/services/film-service.md +++ b/docs/dev/notkinopoiskphp/services/film-service.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Сервисы +title: "FilmService - Сервис для работы с фильмами - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по filmservice - сервис для работы с фильмами. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Сервисы, filmservice - сервис для работы с фильмами, DevCraft, документация" +author: "Maxim Harder" +og:title: "FilmService - Сервис для работы с фильмами" +og:description: "Документация по filmservice - сервис для работы с фильмами. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "FilmService - Сервис для работы с фильмами" +twitter:description: "Документация по filmservice - сервис для работы с фильмами. Часть API wrapper для КиноПоиска." +--- + # FilmService - Сервис для работы с фильмами Сервис для работы с фильмами в Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/services/index.md b/docs/dev/notkinopoiskphp/services/index.md index 315eea2..7431bec 100644 --- a/docs/dev/notkinopoiskphp/services/index.md +++ b/docs/dev/notkinopoiskphp/services/index.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Сервисы +title: "Сервисы - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по сервисы. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Сервисы, сервисы, DevCraft, документация" +author: "Maxim Harder" +og:title: "Сервисы" +og:description: "Документация по сервисы. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Сервисы" +twitter:description: "Документация по сервисы. Часть API wrapper для КиноПоиска." +--- + # Сервисы Сервисы для работы с различными компонентами Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/services/media-service.md b/docs/dev/notkinopoiskphp/services/media-service.md index 66eb65d..78cafb0 100644 --- a/docs/dev/notkinopoiskphp/services/media-service.md +++ b/docs/dev/notkinopoiskphp/services/media-service.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Сервисы +title: "MediaService - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по mediaservice. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Сервисы, mediaservice, DevCraft, документация" +author: "Maxim Harder" +og:title: "MediaService" +og:description: "Документация по mediaservice. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "MediaService" +twitter:description: "Документация по mediaservice. Часть API wrapper для КиноПоиска." +--- + # MediaService Сервис для работы с медиа контентом в Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/services/person-service.md b/docs/dev/notkinopoiskphp/services/person-service.md index 9516210..b98a09e 100644 --- a/docs/dev/notkinopoiskphp/services/person-service.md +++ b/docs/dev/notkinopoiskphp/services/person-service.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Сервисы +title: "PersonService - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по personservice. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Сервисы, personservice, DevCraft, документация" +author: "Maxim Harder" +og:title: "PersonService" +og:description: "Документация по personservice. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "PersonService" +twitter:description: "Документация по personservice. Часть API wrapper для КиноПоиска." +--- + # PersonService Сервис для работы с персонами в Kinopoisk API. diff --git a/docs/dev/notkinopoiskphp/services/user-service.md b/docs/dev/notkinopoiskphp/services/user-service.md index b913870..7d120c6 100644 --- a/docs/dev/notkinopoiskphp/services/user-service.md +++ b/docs/dev/notkinopoiskphp/services/user-service.md @@ -1,3 +1,19 @@ +--- +tags: + - PHP + - API + - Сервисы +title: "UserService - KinopoiskUnofficialTech PHP Wrapper" +description: "Документация по userservice. Часть API wrapper для КиноПоиска." +keywords: "PHP, API, Сервисы, userservice, DevCraft, документация" +author: "Maxim Harder" +og:title: "UserService" +og:description: "Документация по userservice. Часть API wrapper для КиноПоиска." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "UserService" +twitter:description: "Документация по userservice. Часть API wrapper для КиноПоиска." +--- + # UserService Сервис для работы с пользовательскими данными в Kinopoisk API. diff --git a/docs/dev/paid-currencies_rate.md b/docs/dev/paid-currencies_rate.md index 088e017..6ab06f8 100644 --- a/docs/dev/paid-currencies_rate.md +++ b/docs/dev/paid-currencies_rate.md @@ -1,6 +1,23 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Платно +title: "Цены по курсу валют" +description: "Документация по плагину цены по курсу валют для DLE." +keywords: "PHP, DLE, Плагин, Платно, цены по курсу валют, DevCraft, документация" +author: "Maxim Harder" +og:title: "Цены по курсу валют" +og:description: "Документация по плагину цены по курсу валют для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Цены по курсу валют" +twitter:description: "Документация по плагину цены по курсу валют для DLE." +--- + # Цены по курсу валют -**Ссылка на разработку**: [ Перейти к разработке](https://devcraft.club/shop/cena-po-kursu-valjut.4/) +**Ссылка на разработку**: [Перейти к разработке](https://devcraft.club/shop/cena-po-kursu-valjut.4/) **Версия модификации**: 3.1.1 diff --git a/docs/dev/paid-lastnews.md b/docs/dev/paid-lastnews.md index a550b63..4d2645b 100644 --- a/docs/dev/paid-lastnews.md +++ b/docs/dev/paid-lastnews.md @@ -1,6 +1,24 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Платно + - Не поддерживается +title: "LastNews списком аля seasonvar" +description: "Документация по плагину lastnews списком аля seasonvar для DLE." +keywords: "PHP, DLE, Плагин, Платно, lastnews списком аля seasonvar, DevCraft, документация" +author: "Maxim Harder" +og:title: "LastNews списком аля seasonvar" +og:description: "Документация по плагину lastnews списком аля seasonvar для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "LastNews списком аля seasonvar" +twitter:description: "Документация по плагину lastnews списком аля seasonvar для DLE." +--- + # LastNews списком аля seasonvar -**Ссылка на разработку**: [ Перейти к разработке](https://devcraft.club/shop/lastnews-spiskom.2/) +**Ссылка на разработку**: [Перейти к разработке](https://devcraft.club/shop/lastnews-spiskom.2/) **Версия модификации**: 1.1 @@ -13,23 +31,23 @@ ## **Пояснения** -| Тег | Описание | -| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| **c_id** | Выбираем нужные ID категорий.**По умолчанию выбраны все**. | -| **day** | Этот параметр для вывода нескольких блоков.**По умолчанию 1**. | -| **cache** | Включает и выключает кеш блока.**По умолчанию включён**. Можно выбирать либо yes, либо no. | -| **limit** | Указывает кол-во выводимых новостей.**По умолчанию 100**. | -| **order** | Это стандартная функция и работает так-же. Однако меняет порядок новостей в блоке по дате, а не сами даты.**По умолчанию стоит date.** | -| **sort** | Сортирует новости либо по убыванию, либо по возрастанию. Можно использовать**ТОЛЬКО**DESC и ASC! **По умолчанию стоит DESC**. | -| **temp** | Указываем имя шаблона для блока. Шаблон должен лежать в папке с темой сайта. Не указывайте окончания tpl!**По умолчанию это lastnews**. | -| **temp2** | Указываем шаблон списка, что выводится в блоке. Действует так-же, как и с temp.**по умолчанию это lastnews_story**. | -| **von, bis** | Указываем диапазон даты с какого по какое должно показать новости. von это значение для "от", а bis - " до". Указав эти параметры, нельзя использовать параметр datum. Что к чему - ниже! | -| **datum** | Указываем определённую дату и выводим новости за сей день! | -| **Вариации даты** | -**heute**: это сегодняшняя дата, выводит новости, что были добавлены именно сегодня, вне зависимости от даты | -| | -**gestern**: вчерашняя дата, т.е. новости за вчера | -| | -**vorgestern**: позавчерашняя дата | -| | -**daym3**: позапозавчерашняя дата, т.е. от сегодня отсчитываем три дня | -| | -**daym4**: от сегодня отсчитываем четыре дня | -| | -**daym5**: от сегодня отсчитываем пять дней | -| | -**daym6**: от сегодня отсчитываем шесть дней | -| | -**любая определённая дата**: в формате Y-m-d (год-месяц-день, пр: 2016-07-14) | +| Тег | Описание | +|-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **c_id** | Выбираем нужные ID категорий. **По умолчанию выбраны все**. | +| **day** | Этот параметр для вывода нескольких блоков. **По умолчанию 1**. | +| **cache** | Включает и выключает кеш блока. **По умолчанию включён**. Можно выбирать либо yes, либо no. | +| **limit** | Указывает кол-во выводимых новостей. **По умолчанию 100**. | +| **order** | Это стандартная функция и работает так-же. Однако меняет порядок новостей в блоке по дате, а не сами даты. **По умолчанию стоит date.** | +| **sort** | Сортирует новости либо по убыванию, либо по возрастанию. Можно использовать **ТОЛЬКО** DESC и ASC! **По умолчанию стоит DESC**. | +| **temp** | Указываем имя шаблона для блока. Шаблон должен лежать в папке с темой сайта. Не указывайте окончания tpl! **По умолчанию это lastnews**. | +| **temp2** | Указываем шаблон списка, что выводится в блоке. Действует так-же, как и с temp. **по умолчанию это lastnews_story**. | +| **von, bis** | Указываем диапазон даты с какого по какое должно показать новости. von это значение для "от", а bis - " до". Указав эти параметры, нельзя использовать параметр datum. Что к чему - ниже! | +| **datum** | Указываем определённую дату и выводим новости за сей день! | +| **Вариации даты** @span | - **heute**: это сегодняшняя дата, выводит новости, что были добавлены именно сегодня, вне зависимости от даты | +| | -**gestern**: вчерашняя дата, т.е. новости за вчера | +| | -**vorgestern**: позавчерашняя дата | +| | -**daym3**: позапозавчерашняя дата, т.е. от сегодня отсчитываем три дня | +| | -**daym4**: от сегодня отсчитываем четыре дня | +| | -**daym5**: от сегодня отсчитываем пять дней | +| | -**daym6**: от сегодня отсчитываем шесть дней | +| | -**любая определённая дата**: в формате Y-m-d (год-месяц-день, пр: 2016-07-14) | diff --git a/docs/dev/paid-seasonvar/faq.md b/docs/dev/paid-seasonvar/faq.md index 8bf273a..33e67d3 100644 --- a/docs/dev/paid-seasonvar/faq.md +++ b/docs/dev/paid-seasonvar/faq.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Платно +title: "FAQ - SeasonVar" +description: "Документация по плагину faq для DLE." +keywords: "PHP, DLE, Плагин, Платно, faq, DevCraft, документация" +author: "Maxim Harder" +og:title: "FAQ" +og:description: "Документация по плагину faq для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "FAQ" +twitter:description: "Документация по плагину faq для DLE." +--- + # FAQ Ответы на часто задаваемые вопросы, которые мне задавали после приобретения diff --git a/docs/dev/paid-seasonvar/install.md b/docs/dev/paid-seasonvar/install.md index 901f683..b7ece0a 100644 --- a/docs/dev/paid-seasonvar/install.md +++ b/docs/dev/paid-seasonvar/install.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Платно +title: "Seasonvar - SeasonVar" +description: "Документация по плагину seasonvar для DLE." +keywords: "PHP, DLE, Плагин, Платно, seasonvar, DevCraft, документация" +author: "Maxim Harder" +og:title: "Seasonvar" +og:description: "Документация по плагину seasonvar для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Seasonvar" +twitter:description: "Документация по плагину seasonvar для DLE." +--- + # Seasonvar Адаптивный шаблон для DLE diff --git a/docs/dev/paid-seasonvar/related.md b/docs/dev/paid-seasonvar/related.md index 04f53be..6111554 100644 --- a/docs/dev/paid-seasonvar/related.md +++ b/docs/dev/paid-seasonvar/related.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Платно +title: "AutomaticRelated - SeasonVar" +description: "Документация по плагину automaticrelated для DLE." +keywords: "PHP, DLE, Плагин, Платно, automaticrelated, DevCraft, документация" +author: "Maxim Harder" +og:title: "AutomaticRelated" +og:description: "Документация по плагину automaticrelated для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "AutomaticRelated" +twitter:description: "Документация по плагину automaticrelated для DLE." +--- + # AutomaticRelated **Автор**: Gameer diff --git a/docs/dev/releasestatus.md b/docs/dev/releasestatus.md index f0e0f0b..c77585d 100644 --- a/docs/dev/releasestatus.md +++ b/docs/dev/releasestatus.md @@ -5,17 +5,18 @@ keywords: "Release Status, DLE, релизы, статусы, модуль" author: "Maxim Harder" og:title: "Release Status - модуль статусов релизов" og:description: "Документация по модулю Release Status для DLE" -og:image: "https://devcraft.club/assets/images/logo.png" +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" twitter:title: "Release Status - модуль статусов релизов" twitter:description: "Документация по модулю Release Status для DLE" tags: - DLE - Плагин + - Не поддерживается --- # ReleaseStatus -**Ссылка на разработку**: [ Перейти к разработке](https://devcraft.club/downloads/releasestatus.6/) +**Ссылка на разработку**: [ Перейти к разработке](https://devcraft.club/downloads/releasestatus.6/) **Версия модификации**: 1.0.0 @@ -23,22 +24,7 @@ tags: - Залить файлы из папки uploads в корень сайта (уделите внимание папке Default в папке templates) - Запустите файл install.php в корне вашего сайта, а затем удалите -- В шаблонах откройте файл main.tpl и пропишите до - -```html - - - -``` +- В шаблонах откройте файл main.tpl и добавьте стиль ```html diff --git a/docs/dev/repost/.nav.yml b/docs/dev/repost/.nav.yml index 471af16..b2b5d3d 100644 --- a/docs/dev/repost/.nav.yml +++ b/docs/dev/repost/.nav.yml @@ -1,3 +1,3 @@ -title: Re Post - Пересылка новостей +title: "Re: Post - Пересылка новостей" nav: - Оглавление: index.md \ No newline at end of file diff --git a/docs/dev/repost/index.md b/docs/dev/repost/index.md index 5c70373..1977587 100644 --- a/docs/dev/repost/index.md +++ b/docs/dev/repost/index.md @@ -1,5 +1,5 @@ --- -title: DLE Re:: Post +title: "DLE Re: Post" tags: - PHP - DLE diff --git a/docs/dev/schema.md b/docs/dev/schema.md index d780a65..8bf3574 100644 --- a/docs/dev/schema.md +++ b/docs/dev/schema.md @@ -5,7 +5,7 @@ keywords: "Schema.Org, разметка, SEO, структурированные author: "Maxim Harder" og:title: "Schema.Org разметка" og:description: "Документация по интеграции Schema.Org разметки" -og:image: "https://devcraft.club/assets/images/logo.png" +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" twitter:title: "Schema.Org разметка" twitter:description: "Документация по интеграции Schema.Org разметки" tags: @@ -14,6 +14,7 @@ tags: - Плагин - Schema.org - SEO + - Не поддерживается --- # SCHEMA.ORG @@ -25,15 +26,15 @@ tags: Для начала, я советую просмотреть саму структуру построения: ** [Movie - schema.org](http://schema.org/Movie)**. Для себя я выбрал следующие поля: - **contributor**или **countryOfOrigin**(лучше первое) - Страна -- **copyrightyear**- Год -- **genre**- Жанр (если у вас категории являются жанром, то пропускаем) -- **director**- Режиссёр creator - Сценарист -- **musicBy**- Композитор -- **editor**- Монтаж -- **producer**- Продюсер -- **provider**- Оператор -- **translator**- Перевод / озвучание -- **actor**- Актёры +- **copyrightyear** - Год +- **genre** - Жанр (если у вас категории являются жанром, то пропускаем) +- **director** - Режиссёр creator - Сценарист +- **musicBy** - Композитор +- **editor** - Монтаж +- **producer** - Продюсер +- **provider** - Оператор +- **translator** - Перевод / озвучание +- **actor** - Актёры !!! warning "Внимание!" Нужно (!) создать следующие поля со следующими названиями (одно поле с функцией "Использовать при желании (можно оставить поле пуcтым)" и "Использовать значения полей как перекрестные гиперссылки ")! diff --git a/docs/dev/telegramposting/bot.md b/docs/dev/telegramposting/bot.md index 06c9890..2bb52c3 100644 --- a/docs/dev/telegramposting/bot.md +++ b/docs/dev/telegramposting/bot.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Устарело +title: "Настройка бота - Telegram Posting" +description: "Документация по плагину настройка бота для DLE." +keywords: "PHP, DLE, Плагин, Устарело, настройка бота, DevCraft, документация" +author: "Maxim Harder" +og:title: "Настройка бота" +og:description: "Документация по плагину настройка бота для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Настройка бота" +twitter:description: "Документация по плагину настройка бота для DLE." +--- + # Настройка бота Довольно частый вопрос, на который приходится давать ответ 😂. diff --git a/docs/dev/telegramposting/changelog.md b/docs/dev/telegramposting/changelog.md index b78302c..b78e9ff 100644 --- a/docs/dev/telegramposting/changelog.md +++ b/docs/dev/telegramposting/changelog.md @@ -1,6 +1,23 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Устарело +title: "Изменения в версиях - Telegram Posting" +description: "Документация по плагину изменения в версиях для DLE." +keywords: "PHP, DLE, Плагин, Устарело, изменения в версиях, DevCraft, документация" +author: "Maxim Harder" +og:title: "Изменения в версиях" +og:description: "Документация по плагину изменения в версиях для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Изменения в версиях" +twitter:description: "Документация по плагину изменения в версиях для DLE." +--- + # Изменения в версиях -## 1.7.3 ++/- 1.7.3 * **[UPDATE]** Обновление кода до версии MHAdmin 2.0.7 * **[NEW]** Добавлена возможность назначать отдельно разделители тегов, хештегов и категорий * **[FIX]** Исправлена совместимость с версией PHP 7.2 @@ -9,14 +26,14 @@ * **[FIX]** Исправлена заявленная ошибка: https://devcraft.club/tickets/jazykovoj-kod-ru_ru-ne-najden-i-mysql-error.4/ * **[FIX]** Исправлена заявленная ошибка: обработка данных тега [telegram_media_ -## 1.7.2.2 ++/- 1.7.2.2 * **[FIX]** Тег {category} исправлен * **[FIX]** Тег {link-category} исправлен -##1.7.2.1 ++/- 1.7.2.1 * **[FIX]** Поправлен вывод категорий в виде хештегов -## 1.7.2 ++++ 1.7.2 * **[NEW]** Добавлен тег {category-hashtag} - отвечающий за вывод категории новости в виде хештегов. * **[NEW]** Добавлен тег {link-category} - отвечающий за вывод категории новости в виде ссылок на вашем сайте. * **[NEW]** Добавлен тег {category} - отвечающий за вывод категории новости в виде текста. @@ -29,13 +46,13 @@ * **[FIX]** Исправлена заявленная ошибка на сайте: https://devcraft.club/tickets/modul-telegram-posting-rabotaet-s-oshibkami.2/' * **[FIX]** Исправлена ошибка отправки лишь одного шаблона в телеграм -## 1.7.0 ++++ 1.7.0 * **[NEW]** К редактору шаблонов была добавлена возможность копирования из других шаблонов * **[NEW]** К редактору шаблонов была добавлена возможность поиска и вставки возможных тегов с их описанием (макс. 800 символов) * **[UPDATE]** Редактор сообщений был упрощён * **[FIX]** Исправлен парсинг содержимого, переставил местами обработчики -## 1.6.8 ++++ 1.6.8 * **[NEW]** Добавлена конвертация WebP в JPG/PNG * **[NEW]** Добавлена настройка задержки отправки новостей в телеграм при работе с кроном * **[NEW]** Добавлена настройка удаления неверно добавленных новостей в отложенные сообщения @@ -45,7 +62,7 @@ * **[FIX]** Устранена ошибка отправки новости по крону, функционал переработан * **[FIX]** Исправлен установщик -## 1.6.7 ++/- 1.6.7 - FIX: Теперь можно указывать прочие зависимости (забыл добавить их в массив) - FIX: Обработка текста перенесена уже в новую функцию, поскольку урезались нужные теги для обработки данных @@ -53,20 +70,20 @@ - FIX: Исправлена работа с кроном. Временный штамп не отправлялся в базу данных - NEW: Добавлен вывод списка с ожидаемыми новостями на отправку -## 1.6.6 ++/- 1.6.6 - Логирование не работает на PHP 7.4, поэтому для таких случаев сделан вывод в браузер - FIX: Убраны дубли - FIX: Исправлена "копипаста" -## 1.6.5 ++/- 1.6.5 - FIX: Для файлов в доп. полях которые сохраняются как [attachment...] была сделана обработка (упустил из виду) - NEW: Максимальная длина сообщения отправляемого в телеграм была установлена, вшита в код. Это - 1024 символов, включая пробелы. Если длина сообщения равна или больше 1024 символов, то отправляются 1021 символа и троеточие в конце. - FIX: Сбор изображений из базы данных был исправлен - FIX: Миниатюры теперь генерируются из списка всех изображений -## 1.6.4 ++/- 1.6.4 - FIX: Исправлена отправка данные, если указана внутренняя ссылка без домена, а-ля /uploads/... - Добавлено кеширование данных на запросы в базу данных @@ -74,7 +91,7 @@ - FIX: Исправлен запрос в базу данных на новость (забыл закрывающую скобку поставить) - Что-бы включить логирование отправки данных, достаточно в файле repost.class.php заменить $logs = 0 на $logs = 1 -## 1.6.3 ++/- 1.6.3 - FIX: Добавление аудио из доп. полей в общий массив - FIX: Добавление видео из доп. полей в общий массив @@ -82,52 +99,52 @@ - FIX: Исправлена отправка файлов со сторонних источников - FIX: Исправлена отправка текстовых сообщений -## 1.6.2 ++/- 1.6.2 - FIX: Отправка сообщений из настроек - FIX: Сохранение зависимостей (скрипт начинал работать после подключения второй зависимости, из-за чего вызывал ошибку 504) - FIX: Если файл указан как ссылка (простое текстовое поле), то он пропускался. Теперь, если файл находится не на сервере, то пропускает проверку, а указывается как ссылка. - FIX: Убраны дубли, из-за чего движок ругался -## 1.6.1 ++/- 1.6.1 - Фикс файла version.php. При мёрдже файл был сохранён не верно -## 1.6.0 ++/- 1.6.0 - Добавлена поддержка медиа контента. Можно выбирать одно из двух. - Добавлена поддержка парсинга всех стандртных тегов DLE - Исправлена проблема с отрпавкой данных из HTML редактора -## 1.5.1 ++/- 1.5.1 - Небольшой фикс касательно поиска названия группы / канала - Созданы значения по умолчанию для крона, теперь, даже если он и не настроен, телеграм будет получать сообщения по крону -## 1.5 ++/- 1.5 - Обновлено для версии DLE 14.x - Добавлена возможность выводить лимитированные описания - Исправлен поиск чата - Исправлены мелкие баги и недочёты -## 1.4 ++++ 1.4 - Добавлена поддержка SOCKS5 прокси. -## 1.3.3 ++/- 1.3.3 - Небольшой фикс со стилями. -## 1.3.2 ++/- 1.3.2 - Небольшой фикс по отправке сообщений. -## 1.3.1 ++/- 1.3.1 - Исправлена ошибка созданная гитом -## 1.3 ++++ 1.3 - Добавлена возможность отправлять в телеграм при добавлении с сайта - Добавлены новые теги для шаблонов: %categories% (выводит все категории через разделитель указанный в настройках движка) и %category_links% (выводит так же категории, только ссылками) @@ -136,28 +153,28 @@ - Исправлено пару багов - Облегчённая версия -## 1.2.1 ++++ 1.2.1 - Версия для DLE 13 и выше -## 1.2 ++++ 1.2 - Мелкие правки - Добавлена возможность использовать прокси (актуально для сайтов размещённых в РФ) - Добавлена возможность отправки сообщений по крону - Последняя версия для DLE 12.x (Добавлена конвертация текстав нужную кодировку) -## 1.1 ++/- 1.1 - Исправлена ошибка со считыванием данных - Добавлены новые теги для шаблонов: %full_descr% и %short_descr% - Тег %descr% будет заменён на %short_descr% -## 1.01 ++/- 1.01 - Исправлена ошибка с доп. полями -## 1.0 ++++ 1.0 - Базовая версия - Отправка сообщений при добавлении новости diff --git a/docs/dev/telegramposting/custom_add.md b/docs/dev/telegramposting/custom_add.md index 5ce921d..9549121 100644 --- a/docs/dev/telegramposting/custom_add.md +++ b/docs/dev/telegramposting/custom_add.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Устарело +title: "Подключение в сторонние разработки - Telegram Posting" +description: "Документация по плагину подключение в сторонние разработки для DLE." +keywords: "PHP, DLE, Плагин, Устарело, подключение в сторонние разработки, DevCraft, документация" +author: "Maxim Harder" +og:title: "Подключение в сторонние разработки" +og:description: "Документация по плагину подключение в сторонние разработки для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Подключение в сторонние разработки" +twitter:description: "Документация по плагину подключение в сторонние разработки для DLE." +--- + # Подключение в сторонние разработки Данная функция была разработана специально для того, чтобы модуль можно было легко встроить в сторонюю разработку, которая добавляет новости не штатными средствами. Это означает, если хотите, чтобы новость добавлялась в телеграм при помощи граббера/парсера, то обращайтесь с этой страницей к автору этой разработки. diff --git a/docs/dev/telegramposting/template_tags.md b/docs/dev/telegramposting/template_tags.md index eafe568..036ba75 100644 --- a/docs/dev/telegramposting/template_tags.md +++ b/docs/dev/telegramposting/template_tags.md @@ -1,3 +1,20 @@ +--- +tags: + - PHP + - DLE + - Плагин + - Устарело +title: "Теги для оформления шаблонов - Telegram Posting" +description: "Документация по плагину теги для оформления шаблонов для DLE." +keywords: "PHP, DLE, Плагин, Устарело, теги для оформления шаблонов, DevCraft, документация" +author: "Maxim Harder" +og:title: "Теги для оформления шаблонов" +og:description: "Документация по плагину теги для оформления шаблонов для DLE." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Теги для оформления шаблонов" +twitter:description: "Документация по плагину теги для оформления шаблонов для DLE." +--- + # Теги для оформления шаблонов |Теги|Описание| diff --git a/docs/dev/usertags.md b/docs/dev/usertags.md index 464d349..c6ef1bd 100644 --- a/docs/dev/usertags.md +++ b/docs/dev/usertags.md @@ -5,7 +5,7 @@ keywords: "пользовательские теги, теги, система author: "Maxim Harder" og:title: "Пользовательские теги" og:description: "Документация по системе пользовательских тегов" -og:image: "https://devcraft.club/assets/images/logo.png" +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" twitter:title: "Пользовательские теги" twitter:description: "Документация по системе пользовательских тегов" tags: @@ -18,7 +18,7 @@ tags: # Пользовательские теги **Ссылка на -разработку**: [ Перейти к разработке](https://devcraft.club/downloads/polzovatelskie-tegi.12/) +разработку**: [Перейти к разработке](https://devcraft.club/downloads/polzovatelskie-tegi.12/) **Версия модификации**: 2.0.0 @@ -26,7 +26,7 @@ tags: 1. Для установки достаточно закинуть в корень сайта все файлы и запустить скрипт установки ( сайт.ру/install.php?action=install). -1. В админпанеле устанавливаем для пользователей доп. поле: +1. В админпанели устанавливаем для пользователей доп. поле: - **Название:** любое - **Описание:** любое diff --git a/docs/dev/webmaster-verification.md b/docs/dev/webmaster-verification.md index 3cb514f..6eef583 100644 --- a/docs/dev/webmaster-verification.md +++ b/docs/dev/webmaster-verification.md @@ -7,7 +7,7 @@ tags: # Webmaster Verification **Ссылка на -разработку**: [ Перейти к разработке](https://devcraft.club/downloads/webmaster-verification.21/) +разработку**: [Перейти к разработке](https://devcraft.club/downloads/webmaster-verification.21/) **Версия модификации**: 1.0.0 diff --git a/docs/dev/xflist.md b/docs/dev/xflist.md index 3a8766e..d8ae6af 100644 --- a/docs/dev/xflist.md +++ b/docs/dev/xflist.md @@ -3,11 +3,13 @@ tags: - DLE - PHP - Хак + - Доп. поля + - xfields --- # XF List Generator **Ссылка на -разработку**: [ Перейти к разработке](https://devcraft.club/downloads/xf-list-generator.19/) +разработку**: [Перейти к разработке](https://devcraft.club/downloads/xf-list-generator.19/) **Версия модификации**: 1.1.0 diff --git a/docs/dev/xfselect.md b/docs/dev/xfselect.md index a81c2f9..1ce7166 100644 --- a/docs/dev/xfselect.md +++ b/docs/dev/xfselect.md @@ -3,11 +3,13 @@ tags: - DLE - PHP - Хак + - Доп. поля + - xfields --- # XF Select **Ссылка на -разработку**: [ Перейти к разработке](https://devcraft.club/downloads/xf-select-vyvod-pravilnogo-znachenija.18/) +разработку**: [Перейти к разработке](https://devcraft.club/downloads/xf-select-vyvod-pravilnogo-znachenija.18/) **Версия модификации**: 1.0.0 diff --git a/docs/index.md b/docs/index.md index 19c750e..947ee45 100644 --- a/docs/index.md +++ b/docs/index.md @@ -7,7 +7,7 @@ keywords: "DevCraft, DLE, CMS, документация, разработка" author: "Maxim Harder" og:title: "DevCraft Документации" og:description: "Документация DevCraft - разработки для DLE и других CMS" -og:image: "https://devcraft.club/assets/images/logo.png" +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" twitter:title: "DevCraft Документации" twitter:description: "Документация DevCraft - разработки для DLE и других CMS" --- diff --git a/docs/site/how-to-ask-for-support.md b/docs/site/how-to-ask-for-support.md index d5a072c..c52e11d 100644 --- a/docs/site/how-to-ask-for-support.md +++ b/docs/site/how-to-ask-for-support.md @@ -1,3 +1,19 @@ +--- +tags: + - Общее + - Инструкция + - Сайт +title: "Создание запроса на тех. поддержку" +description: "Инструкция по создание запроса на тех. поддержку." +keywords: "Общее, Инструкция, создание запроса на тех. поддержку, DevCraft, документация" +author: "Maxim Harder" +og:title: "Создание запроса на тех. поддержку" +og:description: "Инструкция по создание запроса на тех. поддержку." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Создание запроса на тех. поддержку" +twitter:description: "Инструкция по создание запроса на тех. поддержку." +--- + # Создание запроса на тех. поддержку На данные меры пришлось пойти из-за того, что люди не любят читать. Чтобы упростить лень, пришлось создать такой способ. Повторные вопросы в темах и ЛС будут игнорироваться. diff --git a/docs/site/how-to-buy.md b/docs/site/how-to-buy.md index 7a4ecce..eefe72f 100644 --- a/docs/site/how-to-buy.md +++ b/docs/site/how-to-buy.md @@ -1,12 +1,28 @@ +--- +tags: + - Общее + - Инструкция + - Сайт +title: "Как совершать покупки?" +description: "Инструкция по как совершать покупки на сайте" +keywords: "Общее, Инструкция, покупки, DevCraft, документация" +author: "Maxim Harder" +og:title: "Как совершать покупки?" +og:description: "Инструкция по как совершать покупки?." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Как совершать покупки?" +twitter:description: "Инструкция по как совершать покупки?." +--- + # Как совершать покупки? ## Доступные платёжные системы На данный момент к сайту подключены следующие системы оплаты: -* DigiSeller (Oplata.info) - Через неё можно оплатить разными системами -* PayPal EUR - Платёжная система с валютой Евро -* YooMoney - Оплата рублями +- DigiSeller (Oplata.info) - Через неё можно оплатить разными системами +- PayPal EUR - Платёжная система с валютой Евро +- YooMoney - Оплата рублями Так-же можно произвести оплату переводом на одну из систем указанных в пометке о добровольной поддержке. Информация находится внизу [страницы с пользовательским соглашением](https://devcraft.club/pages/licence-agreement/). Затем просто нужно сообщить через ЛС свой ник на сайте и каким способом была произведена оплата. diff --git a/docs/tags.md b/docs/tags.md index 28bd238..6c23dcc 100644 --- a/docs/tags.md +++ b/docs/tags.md @@ -1,3 +1,17 @@ +--- +tags: + - Общее +title: "Ключевые слова - DevCraft Документации" +description: "Документация по ключевые слова." +keywords: "Общее, ключевые слова, DevCraft, документация" +author: "Maxim Harder" +og:title: "Ключевые слова" +og:description: "Документация по ключевые слова." +og:image: "https://devcraft.club/data/assets/logo_default/devcraftx2.png" +twitter:title: "Ключевые слова" +twitter:description: "Документация по ключевые слова." +--- + # Ключевые слова Перечень ключевых слов в документации: diff --git a/mkdocs.yml b/mkdocs.yml index d2f48d0..4c8d283 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -82,9 +82,6 @@ plugins: - import-statement - meta-manager - git-authors - - enumerate-headings: - toc_depth: 3 - increment_across_pages: false - htmlproofer - link-marker - neoteroi.mkdocsoad: diff --git a/requirements.txt b/requirements.txt index b1c79b2..8414c6d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,6 @@ mike mkdocs-autolinks-plugin mkdocs-autorefs mkdocs-awesome-nav -mkdocs-enumerate-headings-plugin mkdocs-exclude mkdocs-exclude-search mkdocs-git-authors-plugin @@ -30,7 +29,6 @@ mkdocs-meta-manager mkdocs-minify-plugin mkdocs-redirects mkdocs-section-index -mkdocs-spellcheck neoteroi-mkdocs pandas pip-chill From 11c12af07834d337ceda6b8ea8fae2b913c78b29 Mon Sep 17 00:00:00 2001 From: Maxim Harder Date: Fri, 25 Jul 2025 19:32:30 +0200 Subject: [PATCH 8/8] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20UPDATE=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=BD=D1=84=D0=B8=D0=B3=D1=83=D1=80=D0=B0=D1=86=D0=B8=D0=B8=20?= =?UTF-8?q?=D0=B8=20workflow=20=D0=B4=D0=BB=D1=8F=20=D1=81=D0=B1=D0=BE?= =?UTF-8?q?=D1=80=D0=BA=D0=B8=20=D0=B8=20=D0=B1=D0=B5=D0=B7=D0=BE=D0=BF?= =?UTF-8?q?=D0=B0=D1=81=D0=BD=D0=BE=D1=81=D1=82=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Обновлен mkdocs.yml: исправлен плагин tags для корректной работы - Изменена команда сборки документации в deploy.yml с --strict на -c для оптимизации процесса - Обновлен action upload-artifact в security.yml с версии v3 на v4 для улучшения стабильности загрузки артефактов --- .github/workflows/deploy.yml | 2 +- .github/workflows/security.yml | 2 +- mkdocs.yml | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index e3bd2a3..d105719 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -29,7 +29,7 @@ jobs: - name: Build documentation run: | - mkdocs build --strict + mkdocs build -c mkdocs gh-deploy --force - name: Deploy to GitHub Pages diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 5ac7e85..f1aae59 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -54,7 +54,7 @@ jobs: fi - name: Upload security reports - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: always() with: name: security-reports diff --git a/mkdocs.yml b/mkdocs.yml index 4c8d283..3e7b70f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -51,8 +51,7 @@ plugins: - exclude - exclude-search - git-revision-date - - tags: - tags_file: tags.md + - tags - autorefs: resolve_closest: true - minify: