uxf/core

维护者

详细信息

gitlab.com/uxf/core

源代码

问题

安装数: 16,634

依赖: 10

建议者: 1

安全: 0

星标: 0

分支: 0

3.50.6 2024-09-27 07:48 UTC

README

安装

$ composer require uxf/core

自定义类型

# time + date
$a = new UXF\Core\Type\Date('2020-10-30');
$b = new UXF\Core\Type\DateTime('2020-10-30 10:00:11');
$c = new UXF\Core\Type\Time('10:00:00');

# money
$x = UXF\Core\Type\Currency::CZK
$y = UXF\Core\Type\Money::of(666, $y);

# phone
$z = UXF\Core\Type\Phone::of('+420777666777');

# email
$z = UXF\Core\Type\Email::of('info@uxf.cz');

# national identification number for CZ - NinCze
$o = UXF\Core\Type\NationalIdentificationNumberCze::of('930201/3545');

# bank account number for CZ - BanCze
$p = UXF\Core\Type\BankAccountNumberCze::of('123/0300');

FromBody & FromQuery & FromHeader

use UXF\Core\Http\Request\FromBody;
use UXF\Core\Http\Request\FromQuery;
use UXF\Core\Http\Request\FromHeader;

class TestController
{
    public function __invoke(
        #[FromBody] TestRequestBody $body,
        #[FromQuery] TestRequestQuery $query,
        #[FromHeader] TestRequestHeader $header,
    ) {
        ...
    }
}

FromBody 数组

use UXF\Core\Http\Request\FromBody;

class TestController
{
    /**
     * @param TestRequestBody[] $body
     */
    public function __invoke(#[FromBody(TestRequestBody::class)] array $body)
    {
        ...
    }
}

PATCH 方法

class TestPatchRequestBody
{
    public function __controller(
        public readonly string | null | NotSet $string = new NotSet(),
    ) {
    }
}

use UXF\Core\Http\Request\FromBody;

class TestController
{
    public function __invoke(#[FromBody] TestPatchRequestBody $body)
    {
        if (!$body->string instanceof NotSet) {
            ...
        }
    }
}

实体

Controller::__invoke 方法中的实体参数(uri 路径参数)通过实体主标识符(或通过由 #[Entity('uuid')] 指定的自定义属性)解析

use UXF\Core\Http\Request\Entity;

#[ORM\Entity]
class File
{
    #[ORM\Column, ORM\Id]
    public int $id;

    #[ORM\Column(type: 'uuid', unique: true)]
    public UuidInterface $uuid;
}

class TestController
{
    // eg. GET /4f94e4b0-e31a-4070-9ae0-59b32006d911/1
    #[Route('/{file1}/{file2}')]
    public function __invoke(#[Entity('uuid')] File $file1, File $file2)
    {
        ...
    }
}

使用 HydratorMap (Union) 的响应

use UXF\Hydrator\Attribute\HydratorMap;

// interface
#[HydratorMap(property: 'type', matrix: [
    'o' => Orienteering::class,
    'p' => Paragliding::class,
])]
interface ActivityResponse
{
}

// children
class OrienteeringResponse implements ActivityResponse
{
    public function __construct(
        public readonly int $card,
        public readonly string $type = 'o',
    ) {
    }
}

class ParaglidingResponse implements ActivityResponse
{
    public function __construct(
        public readonly string $glider,
        public readonly string $type = 'p',
    ) {
    }
}

// usage
class ClubController
{
    public function __invoke(): ActivityResponse
    {
        return new ParaglidingResponse('GIN');
    }
}

UXF\Core\Http\ResponseModifierInterface - 修改 symfony 响应

use Symfony\Component\HttpFoundation\Response;
use UXF\Core\Http\ResponseModifierInterface;

/**
 * @implements ResponseModifierInterface<array<string, string>>
 */
final class ModifiedResponseController implements ResponseModifierInterface
{
    /**
     * @return array<string, string>
     */
    public function __invoke(): array
    {
        return [];
    }

    public static function modifyResponse(Response $response, mixed $data): void
    {
        $response->setStatusCode(509);
        $response->headers->set('X-Test', 'hello');
    }
}

Doctrine UNACCENT 扩展和 SearchHelper

use Doctrine\ORM\EntityManagerInterface;
use UXF\Core\Utils\SearchHelper;
use Project\Entity\Foo;

class SearchService
{
    public function __construct(private EntityManagerInterface $entityManager)
    {
    }

    public function findByTerm(string $term): array
    {
        return $this->entityManager->createQueryBuilder()
            ->select('e')
            ->from(Foo::class, 'e')
            ->where('UNACCENT(e.fulltext) LIKE :term')
            ->setParameter('term', SearchHelper::unaccentLike($term))
            ->getQuery()->getResult();
    }
}

工具类

#[UXF\Core\Attribute\Internal]

# phpstan.neon
includes:
    - vendor/uxf/core/config/extension.neon
use UXF\Core\Attribute\Internal;

final readonly class Funny
{
    #[Internal(FunnyService::class)]
    public function setState(State $state): void
    {
        ...
    }
}

UXF\Core\Type\Money

$a = Money::of(1.50, Currency::CZK);
$b = Money::of('1.40', Currency::CZK);

// +
$c = $a->plus($b);

// -
$c = $a->minus($b);

// *
$c = $a->multipliedBy($b);

// /
$c = $a->dividedBy($b);

// (int)
$c = $a->amountInt();

// (float)
$c = $a->amountFloat();

// (bool)
$c = $a->equals($b);

UXF\Core\Utils\Lst

// find
Lst::from([$a, $b])->find(fn (X $a): bool => $a->id = 1); // $a

// map
Lst::from([$a, $b])->map(fn (X $x): Y => $x->y); // Lst[$x, $y] 

// filter
Lst::from([$a, $b])->filter(fn (X $a): bool => $a->id > 1); // Lst[$b]

// sort
Lst::from([$a, $b])->sort(fn (X $a, X $b): int => $a <=> $b); // Lst[$b, $a]

// unique
Lst::from([$a, $b, $b])->unique(); // Lst[$a, $b]

// concat
Lst::from([$a, $b])->concat(Lst::from([$c, $d])); // Lst[$a, $b, $c, $d]

// push
Lst::from([$a, $b])->push($c); // Lst[$a, $b, $c]

// unshift
Lst::from([$a, $b])->unshift($c); // Lst[$c, $a, $b]

// slice
Lst::from([$a, $b, $c])->slice(1, 1); // Lst[$b]

// join
Lst::from([1, 2, 3])->join(', '); // '1, 2, 3'

// aggregate
Lst::from([1, 2, 3])->aggregate(0, fn (int $sum, int $item) => $sum + $item)); // 6

// forEach
Lst::from([1, 2, 3])->forEach(fn (int $item) => $test->counter += $item); // void

// dictionary
Lst::from([$a, $b])->dictionary(fn (X $a) => $a->key, fn (X $a) => $a->name); // ['key1' => 'value1', 'key2' => 'value2']

// groupBy
Lst::from([$a, $b, $c])->groupBy(fn (X $a) => $a->key, fn (X $a) => $a); // ['key1' => [$a], 'key2' => [$b, $c]]

// first
Lst::from([$a, $b])->first(); // $a
Lst::from([])->first(); // null

// last
Lst::from([$a, $b])->last(); // $b
Lst::from([])->last(); // null

// count
Lst::from([$a, $b])->count(); // 2

// isEmpty
Lst::from([$a, $b])->isEmpty(); // false

// contains
Lst::from([$a, $b])->contains($a); // true

// getValues
Lst::from([$a, $b])->getValues(); // [$a, $b]

// full
$values = Lst::from([
    Language::create(10, 'Z'),
    Language::create(11, 'A'),
    Language::create(12, 'C'),
    Language::create(13, 'A'),
    Language::create(14, 'D'),
])
    ->filter(fn (Language $l) => $l->getId() >= 12)
    ->sort(fn (Language $a, Language $b) => $a->getName() <=> $b->getName())
    ->map(fn (Language $l) => $l->getName())
    ->unique()
    ->getValues();