PHP 对 TMDB (TheMovieDatabase) API v3 的封装。支持两种方法,一种是使用仓储、模型和工厂进行建模,另一种是直接通过数组访问来自 The Movie Database 的原始数据。

4.1.3 2022-11-04 19:25 UTC

README

License License Build Status Build Status codecov PHP Total Downloads

使用最小、正常和开发依赖进行测试。

买我一杯咖啡,或者一瓶啤酒 :-)

我的肚子会感激你的捐赠!

主要功能

  • 电影数据库的数组实现(原始)
  • 使用仓储的电影数据库模型实现
  • 一个 ImageHelper 类,帮助构建图像 URL 或 HTML 元素。

注意 PHP 新手

如果你是 PHP 新手,并开始一个项目来学习,我建议 你跳到安装部分,然后遵循仅为你准备的 快速入门

我建议你在之后更广泛地了解所有这些 PSR 标准意味着什么,以及它们对 PHP 社区有什么作用 :-).

PSR 兼容性

我们尽量为该库的最终用户提供尽可能多的选项,因此 4.0 更新中已做出修改,以引入我们能够实现的 PSR 兼容性。您带来您首选的符合 PSR 标准的依赖项,注册监听器,我们处理其余部分。

框架实现

安装

安装 composer

在我们可以安装 API 库之前,您需要安装一组依赖项,以提供以下实现。

您必须自行满足的依赖项

  • 例如,对于 PSR-7: HTTP 消息接口,例如 nyholm/psr7
  • 例如,对于 PSR-14: 事件调度器,例如 symfony/event-dispatcher
  • 例如,对于 PSR-17: HTTP 工厂,例如 nyholm/psr7
  • 例如,对于 PSR-18: HTTP 客户端,例如 guzzlehttp/guzzle

我强烈建议实现可选的缓存实现

在使用缓存时,请确保在 composer 中也包含 php-http/cache-plugin,此插件为我们处理逻辑,因此我们不必重新发明轮子。然而,您也可以选择实现自己的缓存监听器,或将其缓存逻辑添加到您选择的 http 客户端中。

composer require php-http/cache-plugin:^1.7

尽管 themoviedb.org 自 2019 年底已禁用 请求速率限制,但我仍建议启用缓存,以使您的应用程序运行得更顺畅。因此,之前的版本中的 427 重试订阅者不再存在。

  • 例如,对于 PSR-6: 缓存接口,例如 symfony/cache
  • 例如,对于 PSR-16: 简单缓存,使用 PSR-6 适配器(例如 symfony/cache),然后使用 PSR-16 到 PSR-6 适配器

这不仅会使您的应用程序更响应,在我们可以从缓存中加载时加载,还会减少我们需要发送的请求数量。

可选依赖项

  • 例如,对于 PSR-3: 日志接口,例如 monolog/monolog

安装 php-tmdb/api

如果满足上述要求,您就可以安装库了。

composer require php-tmdb/api:^4

包含 Composer 的自动加载器

require_once dirname(__DIR__).'/vendor/autoload.php';

要使用提供的示例,请将 examples/apikey.php.dist 复制到 examples/apikey.php 并更改设置。

对 PSR 标准或 composer 不熟悉?

如果您来这里是想开始一个有趣的项目来学习,那么上面提到的内容可能会有些令人畏惧。

别担心!这里的文档也考虑到了初学者。

我们还提供了一些示例,位于 examples/ 文件夹中。

要开始;

composer require php-tmdb/api:^4 symfony/event-dispatcher guzzlehttp/guzzle symfony/cache monolog/monolog nyholm/psr7

现在我们已经安装了所有需要的软件,让我们开始设置以能够使用该库。

快速设置

查看下面的设置文件,并查看 示例 文件夹,例如 examples/movies/api/get.phpexamples/movies/api/get.php 文件。

构造客户端

如果您选择与之前建议的不同实现,显然所有即将到来的文档都不会匹配。根据您的依赖项进行调整,我们将遵循之前的示例。

通用API使用

如果您正在寻找一个简单的数组入口点,API命名空间是您需要的地方,但是我们建议您使用仓库和模型的功能。

use Tmdb\Client;

$client = new Client();
$movie = $client->getMoviesApi()->getMovie(550);

如果您想提供任何其他查询参数。

use Tmdb\Client;

$client = new Client();
$movie = $client->getMoviesApi()->getMovie(550, ['language' => 'en']);

对于所有后续调用,请查看提供的单元测试示例,或者API类本身。

模型使用

该库还可以以面向对象的方式使用,我认为这是做事情的首选方式。

不是直接调用客户端,而是将客户端传递给众多仓库中的一个,然后在上面做一些工作。

use Tmdb\Repository\MovieRepository;
use Tmdb\Client;

$client = new Client();
$repository = new MovieRepository($client);
$movie = $repository->load(87421);

echo $movie->getTitle();

仓库还包含其他通过API命名空间可用的API方法。

use Tmdb\Repository\MovieRepository;
use Tmdb\Client;

$client = new Client();
$repository = new MovieRepository($client);
$topRated = $repository->getTopRated(['page' => 3]);
// or
$popular = $repository->getPopular();

对于所有后续调用,请查看提供的单元测试示例,或者模型本身。

事件分发

在库内部,我们可以分发以下事件,通过使用事件监听器,您可以修改某些行为。

HTTP客户端异常

  • Tmdb\Event\HttpClientExceptionEvent
    • 允许在错误可以纠正的情况下仍然设置成功的响应,通过在您的监听器中调用$event->isPropagated(),这需要您提供PSR-7响应对象并将其设置$event->setResponse($response)

TMDB API异常

  • Tmdb\Event\TmdbExceptionEvent
    • 允许在错误可以纠正的情况下仍然设置成功的响应,通过在您的监听器中调用$event->isPropagated(),这需要您提供PSR-7响应对象并将其设置$event->setResponse($response)

实体化

  • Tmdb\Event\BeforeHydrationEvent允许在实体化之前修改响应数据。
    • 即使将event_listener_handles_hydration选项设置为false,此事件仍将被抛出,这允许例如记录器仍然生成记录。
  • Tmdb\Event\AfterHydrationEvent允许修改返回的最终主题。

当前的事件调度器实现会导致大量的开销,您可能根本不希望这样做。

未来我们将进一步研究这个问题以进行改进,现在我们还有更大的事情要做。

4.0版本开始,默认禁用了实体化事件。

要重新启用此功能,我们建议仅对需要修改数据的模型使用它;

use Tmdb\Client;

$client = new Client([
    'hydration' => [
        'event_listener_handles_hydration' => true,
        'only_for_specified_models' => [
            Tmdb\Model\Movie::class
        ]
    ]
]);

如果已应用此配置,请确保您使用的事件调度器了解我们的HydrationListener

use Symfony\Component\EventDispatcher\EventDispatcher;
use Tmdb\Event\HydrationEvent;
use Tmdb\Event\Listener\HydrationListener;

$eventDispatcher = new EventDispatcher();
$hydrationListener = new HydrationListener($eventDispatcher);
$eventDispatcher->addListener(HydrationEvent::class, $hydrationListener);

如果您在未指定任何模型的情况下重新启用此功能,所有实体化将通过事件监听器完成。

请求与响应

  • Tmdb\Event\BeforeRequestEvent
    • 允许在发送之前修改PSR-7请求数据。
    • 允许早期响应行为(例如缓存),通过在您的监听器中调用$event->isPropagated(),这需要您提供PSR-7响应对象并将其设置$event->setResponse($response)
  • Tmdb\Event\ResponseEvent
    • 包含Request对象。
    • 允许在实体化之前修改PSR-7响应,这需要您提供PSR-7响应对象并将其设置$event->setResponse($response)
    • 允许最终用户实现自己的缓存或对给定响应执行的其他任何操作。

事件监听器

我们有几个可选的事件监听器,您可以将它们添加到提供附加功能。

缓存

不是构造默认的RequestListener,而是使用Psr6CachedRequestListener构造客户端。

use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Tmdb\Event\Listener\Psr6CachedRequestListener;
use Tmdb\Repository\MovieRepository;
use Tmdb\Client;

$client = new Client();

$cache = new FilesystemAdapter('php-tmdb', 86400, __DIR__ . '/cache');
$requestListener = new Psr6CachedRequestListener(
    $client->getHttpClient(),
    $client->getEventDispatcher(),
    $cache,
    $client->getHttpClient()->getPsr17StreamFactory(),
    []
);

$repository = new MovieRepository($client);
$popular = $repository->getPopular();

当前的实现将在未来再次改变,它可能涉及到监听器注册的小变化,或者在没有你察觉的情况下发生。 我们目前基于 php-http/cache-plugin,它会引入我们不真正使用的额外依赖。由于缓存本身就是一个相当大的主题,目前我们选择了“快速而简单的方法”。

日志记录

日志记录被分为几个监听器,这样你可以决定你想记录什么,或者不记录什么。所有这些监听器都支持写入自定义格式的消息。请参阅位于 Tmdb\Formatter 命名空间的相关接口和类。

你还可以传递任何PSR-3兼容的日志记录器代替monolog。

Tmdb\Event\Listener\Logger\LogApiErrorListener

use Monolog\Logger;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Tmdb\Event\Listener\Logger\LogApiErrorListener;
use Tmdb\Event\TmdbExceptionEvent;
use Tmdb\Formatter\TmdbApiException\SimpleTmdbApiExceptionFormatter;

$eventDispatcher = new EventDispatcher();
$apiErrorListener = new LogApiErrorListener(
    new Logger(),
    new SimpleTmdbApiExceptionFormatter()
);

$eventDispatcher->addListener(TmdbExceptionEvent::class, $apiErrorListener);

这将记录当响应已经成功接收,但响应指示请求未成功时抛出的异常。

[2021-01-01 13:24:14] php-tmdb.CRITICAL: Critical API exception: 7 Invalid API key: You must be granted a valid key. [] []

Tmdb\Event\Listener\Logger\LogHttpMessageListener

use Symfony\Component\EventDispatcher\EventDispatcher;
use Tmdb\Event\BeforeRequestEvent;
use Tmdb\Event\HttpClientExceptionEvent;
use Tmdb\Event\Listener\Logger\LogHttpMessageListener;
use Tmdb\Event\ResponseEvent;
use Tmdb\Formatter\HttpMessage\SimpleHttpMessageFormatter;

$eventDispatcher = new EventDispatcher();
$requestLoggerListener = new LogHttpMessageListener(
    new Monolog\Logger(),
    new SimpleHttpMessageFormatter()
);

$eventDispatcher->addListener(BeforeRequestEvent::class, $requestLoggerListener);
$eventDispatcher->addListener(ResponseEvent::class, $requestLoggerListener);
$eventDispatcher->addListener(HttpClientExceptionEvent::class, $requestLoggerListener);

这将记录出入站请求和响应。

[2021-01-01 13:11:18] php-tmdb.INFO: Sending request: GET https://api.themoviedb.org/3/company/1?include_adult=true&language=en-US&region=us 1.1 {"length":0,"has_session_token":false} []
[2021-01-01 13:11:18] php-tmdb.INFO: Received response: 200 OK 1.1 {"status_code":200,"length":223} []

在发生任何其他PSR-18客户端异常(例如连接错误)的情况下,这些也将被写入日志。

[2021-01-01 13:36:39] php-tmdb.INFO: Sending request: GET https://api.themoviedb.org/3/company/1?include_adult=true&language=en-US&region=us 1.1 {"length":0,"has_session_token":false} []
[2021-01-01 13:36:39] php-tmdb.CRITICAL: Critical http client error: 0 cURL error 7: Failed to connect to api.themoviedb.org port 443: Connection refused (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) {"request":"https://api.themoviedb.org/3/company/1?include_adult=true&language=en-US&region=us"} []

Tmdb\Event\Listener\Logger\LogHydrationListener

use Symfony\Component\EventDispatcher\EventDispatcher;
use Tmdb\Event\BeforeHydrationEvent;
use Tmdb\Event\Listener\Logger\LogHydrationListener;
use Tmdb\Formatter\Hydration\SimpleHydrationFormatter;

$eventDispatcher = new EventDispatcher();
$hydrationLoggerListener = new LogHydrationListener(
    new Monolog\Logger(),
    new SimpleHydrationFormatter(),
    false // set to true if you wish to add the json data passed for each hydration, do not use this in production!
);

$eventDispatcher->addListener(BeforeHydrationEvent::class, $hydrationLoggerListener);

这将记录带有(可选)数据的模型的水化,这对于调试很有用。

[2021-01-01 13:11:18] php-tmdb.DEBUG: Hydrating model "Tmdb\Model\Image\LogoImage". {"data":{"file_path":"/o86DbpburjxrqAzEDhXZcyE8pDb.png"},"data_size":49} []
[2021-01-01 13:11:18] php-tmdb.DEBUG: Hydrating model "Tmdb\Model\Company". {"data":{"description":"","headquarters":"San Francisco, California","homepage":"https://www.lucasfilm.com/","id":1,"logo_path":"/o86DbpburjxrqAzEDhXZcyE8pDb.png","name":"Lucasfilm Ltd.","origin_country":"US","parent_company":null},"data_size":227} []

对于带有大量附加数据的调用,这很快就会在日志文件中变成大量输出,我建议只在必要时使用。

在生产环境中不要启用水化数据转储,这将生成大量日志。.

成人过滤器

要启用包含被认为为“成人”的结果,请添加以下监听器。

use Symfony\Component\EventDispatcher\EventDispatcher;
use Tmdb\Event\BeforeRequestEvent;
use Tmdb\Event\Listener\Request\AdultFilterRequestListener;

$eventDispatcher = new EventDispatcher();
$adultFilterListener = new AdultFilterRequestListener(true);

$eventDispatcher->addListener(BeforeRequestEvent::class, $adultFilterListener);

语言过滤器

要启用根据语言过滤内容,请添加以下监听器。

use Symfony\Component\EventDispatcher\EventDispatcher;
use Tmdb\Event\BeforeRequestEvent;
use Tmdb\Event\Listener\Request\LanguageFilterRequestListener;

$eventDispatcher = new EventDispatcher();
$languageFilterListener = new LanguageFilterRequestListener('nl-NL');

$eventDispatcher->addListener(BeforeRequestEvent::class, $languageFilterListener);

地区过滤器

要启用根据地区过滤内容,请添加以下监听器。

use Symfony\Component\EventDispatcher\EventDispatcher;
use Tmdb\Event\BeforeRequestEvent;
use Tmdb\Event\Listener\Request\RegionFilterRequestListener;

$eventDispatcher = new EventDispatcher();
$regionFilterListener = new RegionFilterRequestListener('nl');

$eventDispatcher->addListener(BeforeRequestEvent::class, $regionFilterListener);

访客会话

如果你想使用访客会话,你需要在客户端明确指定这一点。

use Tmdb\Client;
use Tmdb\Token\Session\GuestSessionToken;

$client = new Client();
$client->setGuestSessionToken(new GuestSessionToken('my_token'));

// Now you can make calls in the guest sessions namespace. 

图像辅助工具

存在一个 ImageHelper 类来处理图像,这需要加载配置。

use Tmdb\Client;
use Tmdb\Helper\ImageHelper;
use Tmdb\Model\Image;
use Tmdb\Repository\ConfigurationRepository;

$client = new Client();
$image = new Image();
$configRepository = new ConfigurationRepository($client);
$config = $configRepository->load();

$imageHelper = new ImageHelper($config);

echo $imageHelper->getHtml($image, 'w154', 154, 80);

集合过滤

我们还提供了一些简单的过滤任何集合的方法,但是请注意,你可以通过使用闭包轻松实现自己的过滤器。

use Tmdb\Model\Movie;
use Tmdb\Model\Image\PosterImage;

$movie = new Movie();

foreach($movie->getImages()->filter(
        function($key, $value){
            return $value instanceof PosterImage;
        }
    ) as $image) {
        // do something with all poster images
}

然而,这些基本过滤器已经在 Images 集合对象中得到了涵盖。

use Tmdb\Model\Movie;

/** @var $movie Movie **/
$backdrop = $movie
    ->getImages()
    ->filterBackdrops()
;

还有更多提供过滤器的集合,但在使用过程中你会找到这些。

泛型集合和结果集合

  • GenericCollection 包含任何对象集合(例如电影集合)。
  • ResultCollectionGenericCollection 的扩展,并从结果集继承了响应参数(页面、总页面数、总结果数),这可以用于创建分页。