uxf/core

Maintainers

Details

gitlab.com/uxf/core

Source

Issues

Installs: 17 529

Dependents: 9

Suggesters: 1

Security: 0

Stars: 0

Forks: 0

3.56.3 2024-12-18 10:59 UTC

README

Install

$ composer require uxf/core

Custom types

# 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);

# decimal
$d = UXF\Core\Type\Decimal::of(666);

# url
$e = UXF\Core\Type\Url::of('https://google.com/wow?hello[]=1#fragment');

# country
$f = UXF\Core\Type\Country::CZE;

# 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 array

use UXF\Core\Http\Request\FromBody;

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

PATCH method

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) {
            ...
        }
    }
}

Entity

Controller::__invoke method entity arguments (uri path params) are resolved by entity primary identifier (or by custom property specified by #[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)
    {
        ...
    }
}

Response with 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 - modify symfony response

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');
    }
}

Utils

#[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);
$z = Money::zero(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);

// (bool)
$c = $a->isZero();

UXF\Core\Type\Decimal

$a = Decimal::of(1.50);
$b = Decimal::of('1.40');
$z = Decimal::zero();

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

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

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

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

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

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

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

// (bool)
$c = $a->isZero();

UXF\Core\Type\Url

$url = Url::of('https://user:pass@google.com:443/hello?ok&test[]=1#fragment');

/**
# UXF\Core\Type\UrlComponents {
  scheme: "https"
  host: "google.com"
  port: 443
  user: "user"
  pass: "pass"
  path: "/hello"
  query: [
    "ok" => ""
    "test" => [1]
  ]
  fragment: "fragment"
}
*/
$components = $url->getComponents();

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();