DevCraft Документации

DefaultResponse

Документация по defaultresponse. Часть API wrapper для КиноПоиска.

Базовый класс для ответов Kinopoisk API.

Описание

DefaultResponse предоставляет базовую функциональность для всех типов ответов API, включая создание из массива данных и валидацию целевого класса. Содержит общие свойства: общее количество элементов и массив элементов.

Основные возможности

  • Хранение общего количества элементов и массива данных
  • Создание экземпляра из массива данных API с валидацией
  • Автоматическое преобразование элементов в указанный класс
  • Проверка существования и совместимости целевого класса

Свойства

  • $total (int) - Общее количество элементов в коллекции (включая не загруженные)
  • $items (array) - Массив элементов данных в текущем ответе

Конструктор

public function __construct(
    public int $total,
    public array $items,
)

Параметры

  • $total (int) - Общее количество элементов в коллекции
  • $items (array) - Массив элементов данных

Исключения

  • \NotKinopoisk\Exception\KpValidationException - Если данные невалидны

Пример использования

// В дочернем классе
public function __construct(int $total, array $films) {
    parent::__construct($total, $films);
}

Методы

fromArray()

Создает экземпляр ответа из массива данных API.

public static function fromArray(array $data, string $cls): self

Параметры

  • $data (array) - Массив данных от API, содержащий 'total' и 'items'
  • $cls (string) - Полное имя класса для преобразования элементов

Возвращаемое значение

  • self - Новый экземпляр текущего класса с преобразованными данными

Исключения

  • KpValidationException - Если указанный класс не существует
  • KpValidationException - Если класс не имеет метода fromArray
  • KpValidationException - Если данные имеют неверный формат

Пример использования

$apiData = [
    'total' => 1000,
    'items' => [
        ['id' => 1, 'name' => 'Film 1'],
        ['id' => 2, 'name' => 'Film 2']
    ]
];

$response = FilmResponse::fromArray($apiData, Film::class);
// $response->items будет содержать массив объектов Film

checkClass()

Валидирует целевой класс для преобразования элементов.

public static function checkClass(string $cls): void

Параметры

  • $cls (string) - Полное имя класса

Исключения

  • KpValidationException - Если класс невалиден

Описание

Проверяет:

  • Существование класса
  • Наличие статического метода fromArray
  • Статичность метода fromArray

first()

Возвращает первый элемент или null, если элементов нет.

public function first(): ?object

Возвращаемое значение

  • object|null - Первый элемент или null

Пример использования

$firstFilm = $response->first();
if ($firstFilm) {
    echo "Первый фильм: " . $firstFilm->getDisplayName();
}

last()

Возвращает последний элемент или null, если элементов нет.

public function last(): ?object

Возвращаемое значение

  • object|null - Последний элемент или null

Пример использования

$lastFilm = $response->last();
if ($lastFilm) {
    echo "Последний фильм: " . $lastFilm->getDisplayName();
}

isEmpty()

Проверяет, пуст ли ответ.

public function isEmpty(): bool

Возвращаемое значение

  • bool - true, если нет элементов

Пример использования

if ($response->isEmpty()) {
    echo "Результаты не найдены";
} else {
    echo "Найдено элементов: " . $response->getCount();
}

toArray()

Преобразует ответ в массив.

public function toArray(): array

Возвращаемое значение

  • array{total: int, items: array} - Массив с данными ответа

Пример использования

$array = $response->toArray();
echo "Всего: {$array['total']}, Загружено: " . count($array['items']);

getCount()

Возвращает количество элементов в текущем ответе.

public function getCount(): int

Возвращаемое значение

  • int - Количество загруженных элементов

Пример использования

echo "Загружено элементов: " . $response->getCount();

Полный пример использования

<?php

require_once 'vendor/autoload.php';

use NotKinopoisk\Client;
use NotKinopoisk\Models\Film;

// Создание клиента
$client = new Client('your-api-key');

// Получение фильмов
$filmService = $client->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

// Функция для фильтрации элементов ответа
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";
}

Создание отчета по ответу

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 = "<!DOCTYPE html>\n<html>\n<head>\n";
        $html .= "<title>{$title}</title>\n";
        $html .= "<style>\n";
        $html .= ".report { max-width: 1200px; margin: 0 auto; padding: 20px; font-family: Arial, sans-serif; }\n";
        $html .= ".section { margin-bottom: 30px; }\n";
        $html .= ".section-title { font-size: 20px; font-weight: bold; margin-bottom: 15px; padding: 10px; background-color: #f8f9fa; border-radius: 5px; }\n";
        $html .= ".item { margin-bottom: 15px; padding: 15px; border: 1px solid #ddd; border-radius: 5px; }\n";
        $html .= ".item-title { font-weight: bold; font-size: 16px; margin-bottom: 10px; }\n";
        $html .= ".item-meta { font-size: 14px; color: #666; margin-bottom: 5px; }\n";
        $html .= ".stats { background-color: #e9ecef; padding: 15px; border-radius: 5px; margin-bottom: 20px; }\n";
        $html .= ".item-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 15px; }\n";
        $html .= ".progress-bar { width: 100%; background-color: #e9ecef; border-radius: 5px; overflow: hidden; margin: 5px 0; }\n";
        $html .= ".progress-fill { height: 20px; background-color: #007bff; transition: width 0.3s ease; }\n";
        $html .= "</style>\n</head>\n<body>\n";
        $html .= "<div class='report'>\n";
        $html .= "<h1>{$title}</h1>\n";

        $stats = $this->getStats();

        // Статистика
        $html .= "<div class='stats'>\n";
        $html .= "<h2>Основная информация</h2>\n";
        $html .= "<p><strong>Общее количество элементов:</strong> {$stats['total']}</p>\n";
        $html .= "<p><strong>Загружено элементов:</strong> {$stats['loaded']}</p>\n";
        $html .= "<p><strong>Ответ пустой:</strong> " . ($stats['isEmpty'] ? 'Да' : 'Нет') . "</p>\n";
        $html .= "<p><strong>Есть еще элементы:</strong> " . ($stats['hasMore'] ? 'Да' : 'Нет') . "</p>\n";

        // Прогресс-бар загрузки
        $html .= "<h3>Процент загрузки</h3>\n";
        $html .= "<div class='progress-bar'>\n";
        $html .= "<div class='progress-fill' style='width: {$stats['loadPercentage']}%'></div>\n";
        $html .= "</div>\n";
        $html .= "<p>{$stats['loadPercentage']}% загружено</p>\n";

        $html .= "</div>\n";

        // Элементы
        if (!$this->response->isEmpty()) {
            $html .= "<div class='section'>\n";
            $html .= "<div class='section-title'>📋 Список элементов</div>\n";
            $html .= "<div class='item-grid'>\n";

            foreach ($this->response->items as $item) {
                $html .= "<div class='item'>\n";
                $html .= "<div class='item-title'>{$item->getDisplayName()}</div>\n";
                if (method_exists($item, 'rating') && $item->rating) {
                    $html .= "<div class='item-meta'>Рейтинг: {$item->rating}</div>\n";
                }
                if (method_exists($item, 'year') && $item->year) {
                    $html .= "<div class='item-meta'>Год: {$item->year}</div>\n";
                }
                $html .= "</div>\n";
            }

            $html .= "</div>\n</div>\n";
        }

        $html .= "</div>\n</body>\n</html>";

        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

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";
    }
}

Связанные классы

On this page