vrok/symfony-addons

Symfony 辅助类

安装次数: 3,431

依赖者: 0

建议者: 0

安全: 0

星标: 1

关注者: 3

分支: 1

开放问题: 1

类型:symfony-bundle

2.12.0 2024-07-11 12:44 UTC

README

这是一个库,包含用于与 Symfony 框架结合使用的附加类。

CI Status Coverage Status

邮件辅助器

自动设置发送地址

我们想要替换通过 mailer.yaml 设置发送地址的方法(参见https://symfonycasts.com/screencast/mailer/event-global-recipients),因为这仍然需要为每封邮件设置 FROM 地址,并且也不允许我们设置发送者姓名。

config/services.yaml

    Vrok\SymfonyAddons\EventSubscriber\AutoSenderSubscriber:
        arguments:
            $sender: "%env(MAILER_SENDER)%"

.env[.local]

MAILER_SENDER="Change Me <your@email>"

消息传递辅助器

在消息前后重置记录器

我们希望将属于单个消息的所有日志条目分组,并带有唯一的 UID,在处理完消息后(成功或失败)刷新缓冲记录器,以便立即在日志中看到条目。

config/services.yaml

    # add a UID to the context, same UID for each HTTP request or console command
    # and with the event subscriber also for each message 
    Monolog\Processor\UidProcessor:
        tags:
            - { name: monolog.processor, handler: logstash }

    # resets the UID when a message is received, flushed a buffer after a
    # message was handled. Add this multiple times if you want to flush more
    # channels, e.g. messenger
    app.event.reset_app_logger:
        class: Vrok\SymfonyAddons\EventSubscriber\ResetLoggerSubscriber
        tags:
            - { name: monolog.logger, channel: app }

验证器

AtLeastOneOf

类似于 Symfony 自带的 AtLeastOneOf 约束,但返回的是最后一次失败的验证消息,而不是像 This value should satisfy at least ... 这样的消息。当使用 AtLeastOneBlank 作为第一个约束时,可以用于明显可选的表单字段,此时应只显示简单的消息。
参见 AtLeastOneOfValidatorTest 中的示例。

NoHtml

此验证器尝试检测字符串是否包含 HTML,以允许纯文本。
参见 NoHtmlValidatorTest 中的允许值/禁止值示例。

NoLineBreak

此验证器在验证的字符串中检测到一个或多个换行符时引发违规。
检测 unicode 换行符,参见 NoLineBreaksValidatorTest 获取详细信息。

NoSurroundingWhitespace

此验证器在验证的字符串中检测到尾随或前导空白或换行符时引发违规。
使用正则表达式查找 \s\R,参见 NoSurroundingWhitespaceValidatorTest 获取检测到的字符的详细信息。

PasswordStrength

此验证器通过确定密码字符串的熵来评估给定密码字符串的强度,而不是要求像“必须包含至少一个大写字母、一个数字和一个特殊字符”这样的东西。
可以设置 minStrength 来调整要求。有关计算详情,请参阅 Vrok\SymfonyAddons\Helper\PasswordStrength

PHPUnit 辅助器

使用 ApiPlatformTestCase

此类用于通过指定输入数据和验证响应数据来测试 ApiPlatform 端点。它结合了以下记录的特性,在每次测试前刷新数据库,可选地创建已认证的请求并检查创建的日志/发送的电子邮件/分发的消息。它允许轻松检查预期的响应内容、数据中的允许或禁止键或验证给定的模式。

需要安装 "symfony/browser-kit" 和 "symfony/http-client"(当然还有 ApiPlatform)。

<?php

use Vrok\SymfonyAddons\PHPUnit\ApiPlatformTestCase;

class AuthApiTest extends ApiPlatformTestCase
{
    public function testAuthRequiresPassword(): void
    {
        $this->testOperation([
            'uri'            => '/authentication_token',
            'method'         => 'POST',
            'requestOptions' => ['json' => ['username' => 'fakeuser']],
            'responseCode'   => 400,
            'contentType'    => 'application/json',
            'json'           => [
                'type'   => 'https://tools.ietf.org/html/rfc2616#section-10',
                'title'  => 'An error occurred',
                'detail' => 'The key "password" must be provided.',
            ],
        ]);
    }
}

使用 RefreshDatabaseTrait

为每个测试(重新)创建 DB 架构,删除现有数据并用预定义的固定值填充表。安装 doctrine/doctrine-fixtures-bundle 并创建固定值,特性默认使用 test 组。

只需在测试用例中包含特性并在 setUp 方法中调用 bootKernel()createClient() 即可,例如。

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Vrok\SymfonyAddons\PHPUnit\RefreshDatabaseTrait;

class DatabaseTest extends KernelTestCase
{
    use RefreshDatabaseTrait;

    /**
     * @var \Doctrine\ORM\EntityManager
     */
    private $entityManager;

    protected function setUp(): void
    {
        $kernel = self::bootKernel();

        $this->entityManager = $kernel->getContainer()
            ->get('doctrine')
            ->getManager();
    }

}

可选地定义为此测试类使用哪些固定值

    protected static $fixtureGroups = ['test', 'other'];

支持通过 DB_CLEANUP_METHOD 设置测试后的清理方法。允许的值是 purgedropSchema,更多详细信息请参阅 RefreshDatabaseTrait::$cleanupMethod

使用 AuthenticatedClientTrait

适用于与APIPlatform项目配合使用 lexik/jwt-authentication-bundle。根据用户的唯一邮箱、用户名等创建JWT,并将其添加到测试客户端的头部。

在测试用例中包含特性并调用 createAuthenticatedClient

use ApiPlatform\Core\Bridge\Symfony\Bundle\Test\ApiTestCase;
use Vrok\SymfonyAddons\PHPUnit\AuthenticatedClientTrait;

class ApiTest extends ApiTestCase
{
   use AuthenticatedClientTrait;

   public function testAccess(): void
   {
       $client = static::createAuthenticatedClient([
           'email' => TestFixtures::ADMIN['email']
       ]);

       $iri = $this->findIriBy(User::class, ['id' => 1]);
       $client->request('GET', $iri);
       self::assertResponseIsSuccessful();
   }
}

使用MonologAssertsTrait

适用于使用monolog-bundle的Symfony项目。
需要v3.0或更高版本的 monolog/monolog

在触发应创建日志的操作之前,包含特性并调用 prepareLogger,然后使用 assertLoggerHasMessage 检查是否创建了带有指定消息和严重性的日志记录。

use Monolog\Level;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Vrok\SymfonyAddons\PHPUnit\MonologAssertsTrait;

class LoggerTest extends KernelTestCase
{
   use MonologAssertsTrait;

   public function testLog(): void
   {      
       self::prepareLogger();

       $logger = static::getContainer()->get(LoggerInterface::class);
       $logger->error('Failed to do something');
       
       self::assertLoggerHasMessage('Failed to do something', Level::Error);
   }
}

工作流助手

需要 symfony/workflow

PropertyMarkingStore

可以用于替代默认的 MethodMarkingStore,用于没有Setter/Getter的实体和属性。

workflow.yaml

framework:
  workflows:
    application_state:
      type: state_machine
      marking_store:
        # We need to use a service as there is no option to register a new "type"
        service: workflow.application.marking_store

services.yaml

    # When using the "service" option, all other settings like "property: state"
    # are ignored in the workflow.yaml -> That's why we need a service definition
    # with the correct arguments.
    workflow.application.marking_store:
      class: Vrok\SymfonyAddons\Workflow\PropertyMarkingStore
      arguments: [true, 'state']

WorkflowHelper

允许获取可用转换及其阻塞器的数组,可用于显示用户从当前状态可以执行哪些转换,以及为什么当前转换被阻塞。

    public function __invoke(
        Entity $data
        WorkflowInterface $entityStateMachine,
    ): array
    {
      $result = $data->toArray();
      
      $result['transitions'] = WorkflowHelper::getTransitionList($data, $entityStateMachine);
      
      return $result;
    }
'publish' => [
    'blockers' => [
        TransitionBlocker::UNKNOWN => 'Title is empty!',
    ],
],

Cron事件

将此捆绑包添加到 bundles.php 注册了三个新的CLI命令

    Vrok\SymfonyAddons\VrokSymfonyAddonsBundle::class => ['all' => true],
bin/console cron:hourly
bin/console cron:daily
bin/console cron:monthly

当调用这些命令时,它们会触发一个事件(CronHourlyEventCronDailyEventCronMonthlyEvent),该事件可以被一个或多个事件监听器/订阅者用来进行维护、推送消息到messenger等。执行这些命令的正确性由您负责通过crontab来执行!

use Vrok\SymfonyAddons\Event\CronDailyEvent;

class MyEventSubscriber implements EventSubscriberInterface
    public static function getSubscribedEvents(): array
    {
        return [
            CronDailyEvent::class => [
                ['onCronDaily', 100],
            ],
        ];
    }
}

ApiPlatform过滤器

SimpleSearchFilter

选择包含至少一个指定属性中搜索词的实体(不区分大小写)。所有指定属性类型必须是字符串。

#[ApiFilter(
    filterClass: SimpleSearchFilter::class,
    properties: [
        'description',
        'name',
        'slug',
    ],
    arguments: ['searchParameterName' => 'pattern']
)]

需要CAST,如Doctrine函数所定义,例如通过 vrok/doctrine-addons

doctrine:
  orm:
    dql:
      string_functions:
        CAST: Vrok\DoctrineAddons\ORM\Query\AST\CastFunction

ContainsFilter

仅适用于Postgres:通过使用 @> 运算符,根据它们的jsonb字段过滤实体,如果它们包含搜索参数。例如,用于过滤数组中的数字。

#[ApiFilter(filterClass: ContainsFilter::class, properties: ['numbers'])]

需要CONTAINS,如Doctrine函数所定义,由 vrok/doctrine-addons 提供

doctrine:
  orm:
    dql:
      string_functions:
        CONTAINS: Vrok\DoctrineAddons\ORM\Query\AST\ContainsFunction

JsonExistsFilter

仅适用于Postgres:通过使用 ? 运算符,根据它们的jsonb字段过滤实体,如果它们包含搜索参数。例如,用于过滤用户的角色,以防止在以文本方式搜索时与重叠的角色名称(例如,ROLE_ADMIN和ROLE_ADMIN_BLOG)意外匹配。

#[ApiFilter(filterClass: JsonExistsFilter::class, properties: ['roles'])]

需要JSON_CONTAINS_TEXT,如Doctrine函数所定义,由 vrok/doctrine-addons 提供

doctrine:
  orm:
    dql:
      string_functions:
        JSON_CONTAINS_TEXT: Vrok\DoctrineAddons\ORM\Query\AST\JsonContainsTextFunction

MultipartDecoder

将此捆绑包添加到 bundles.php 注册了 MultipartDecoder,允许处理带有附加数据的文件上传(例如在ApiPlatform中)

    Vrok\SymfonyAddons\VrokSymfonyAddonsBundle::class => ['all' => true],

解码器会自动用于 multipart 请求,并简单返回所有POST参数和上传的文件。要启用此功能,请将 multipart 格式添加到您的 config\api_platform.yaml

api_platform:
    formats:
        multipart: ['multipart/form-data']

FormDecoder

将此捆绑包添加到 bundles.php 注册了 FormDecoder,允许在ApiPlatform中处理HTML表单数据

    Vrok\SymfonyAddons\VrokSymfonyAddonsBundle::class => ['all' => true],

解码器会自动用于 form 请求,并简单返回所有POST参数。要启用此功能,请将 form 格式添加到您的 config\api_platform.yaml

api_platform:
    formats:
      form: ['application/x-www-form-urlencoded']

Twig扩展

将此捆绑包添加到 bundles.phpsymfony/twig-bundle 一起注册新扩展

    Vrok\SymfonyAddons\VrokSymfonyAddonsBundle::class => ['all' => true],

FormatBytes

将字节转换为可读格式(支持高达TiB)。
此扩展自动注册。
在您的Twig模板中

  {{ attachment.filesize|formatBytes }}

输出:9.34 MiB

开发者文档

composer.json require

  • symfony/yaml 是加载捆绑包和测试配置所必需的

composer.json dev

  • doctrine/data-fixtures 由 doctrine-fixtures 扩展自动安装,但我们需要将其版本锁定为最小版本,因为 1.5.2 之前的版本与 DBAL < 3 不兼容(@see doctrine/data-fixtures#370
  • doctrine/doctrine-fixtures-bundle 对于 ApiPlatformTestCase 的测试是必需的
  • symfony/browser-kit 对于 MultipartDecoder 的测试是必需的
  • symfony/mailer 对于 AutoSenderSubscriber 的测试是必需的
  • symfony/doctrine-messenger 对于 ResetLoggerSubscriber 的测试是必需的
  • symfony/monolog-bundle 对于 MonologAssertsTrait 和 ResetLoggerSubscriber 的测试是必需的
  • symfony/phpunit-bridge 必须至少是 v6.2.3,以防止 "调用未定义的方法 Doctrine\Common\Annotations\AnnotationRegistry::registerLoader()"
  • symfony/string 对于 API Platform 的屈折变化器是必需的
  • symfony/twig-bundle 对于 FormatBytesExtension 的测试是必需的
  • symfony/workflow 对于 WorkflowHelper 和 PropertyMarkingStore 的测试是必需的
  • monolog/monolog 至少必须是 v3,以支持 Monolog\Level
  • api-platform/corevrok/doctrine-addons 对于测试 ApiPlatform 过滤器是必需的

待办事项

  • AuthenticatedClientTraitRefreshDatabaseTrait 的测试
  • ApiPlatformTestCase 应该不再使用 AuthenticatedClientTrait,而应该使用自己的 getJWT() 并使 User 类可配置,就像 fixtures 一样。
  • QueryBuilderHelper 的测试
  • 将代码与 ApiPlatform\Doctrine\Orm\Util\QueryBuilderHelper 进行比较