Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions .github/workflows/code-quality.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ jobs:
- name: Setup PHP with Xdebug
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
coverage: xdebug
php-version: '8.4'

- name: Install dependencies with composer
run: composer install --no-ansi --no-interaction --no-progress && composer require barryvdh/laravel-debugbar spatie/laravel-typescript-transformer --dev
Expand All @@ -51,11 +50,10 @@ jobs:
with:
fetch-depth: 0

- name: Setup PHP with Xdebug
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
coverage: xdebug
php-version: '8.4'

- name: Install dependencies with composer
run: composer install --no-ansi --no-interaction --no-progress && composer require barryvdh/laravel-debugbar spatie/laravel-typescript-transformer --dev
Expand Down
8 changes: 7 additions & 1 deletion src/Bag/Casts/CollectionOf.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Bag\Exceptions\BagNotFoundException;
use Bag\Exceptions\InvalidBag;
use Bag\Exceptions\InvalidCollection;
use Bag\Values\Optional;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Collection as LaravelCollection;
use Override;
Expand Down Expand Up @@ -44,7 +45,12 @@ public function set(Collection $propertyTypes, string $propertyName, LaravelColl
throw new InvalidCollection(sprintf('The property "%s" must be a subclass of %s', $propertyName, LaravelCollection::class));
}

return $propertyType::make($properties->get($propertyName))->map(function (mixed $item) {
$value = $properties->get($propertyName);
if ($value instanceof Optional) {
return $value;
}

return $propertyType::make($value)->map(function (mixed $item) {
if ($item instanceof Bag && $item::class === $this->valueClassname) {
return clone $item;
}
Expand Down
7 changes: 6 additions & 1 deletion src/Bag/Casts/DateTime.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Bag\Casts;

use Bag\Collection;
use Bag\Values\Optional;
use Carbon\Exceptions\InvalidFormatException;
use DateMalformedStringException;
use DateTimeImmutable;
Expand Down Expand Up @@ -40,14 +41,18 @@ public function get(string $propertyName, LaravelCollection $properties): mixed
/**
* @param Collection<ReflectionNamedType> $propertyTypes
* @param LaravelCollection<array-key,mixed> $properties
* @return T|null
* @return T|Optional|null
* @throws DateMalformedStringException
*/
#[Override]
public function set(Collection $propertyTypes, string $propertyName, LaravelCollection $properties): mixed
{
$value = $properties->get($propertyName);

if ($value instanceof Optional) {
return $value;
}

if ($propertyTypes->contains('null') && $value === null) {
return null;
}
Expand Down
7 changes: 6 additions & 1 deletion src/Bag/Casts/MagicCast.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use BackedEnum;
use Bag\Bag;
use Bag\Collection;
use Bag\Values\Optional;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use DateTimeImmutable;
Expand All @@ -19,13 +20,17 @@
class MagicCast implements CastsPropertySet
{
/**
* @param Collection<string> $propertyTypes
* @param Collection<string|Optional> $propertyTypes
*/
#[Override]
public function set(Collection $propertyTypes, string $propertyName, LaravelCollection $properties): mixed
{
$value = $properties->get($propertyName);

if ($value instanceof Optional) {
return $value;
}

// Find the correct type for the property
$valueType = get_debug_type($value);

Expand Down
5 changes: 5 additions & 0 deletions src/Bag/Casts/MoneyFromMinor.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use BackedEnum;
use Bag\Collection;
use Bag\Values\Optional;
use Brick\Math\BigNumber;
use Brick\Money\Exception\UnknownCurrencyException;
use Brick\Money\Money as BrickMoney;
Expand All @@ -26,6 +27,10 @@ public function set(Collection $propertyTypes, string $propertyName, LaravelColl
/** @var BigNumber|float|int|string $amount */
$amount = $properties->get($propertyName);

if ($amount instanceof Optional) {
return $amount;
}

if ($amount instanceof BrickMoney) {
return $amount;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Bag/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public function splice($offset, $length = null, $replacement = []): void

/**
* @inheritDoc
* @phpstan-ignore method.childReturnType
* @phpstan-ignore selfOut.type, method.childReturnType
*/
#[Override]
public function transform(callable $callback): void
Expand Down
8 changes: 8 additions & 0 deletions tests/Feature/Casts/CollectionOfTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
declare(strict_types=1);

use Bag\Casts\CollectionOf;
use Bag\Values\Optional;
use Tests\Fixtures\Collections\BagWithCollectionCollection;
use Tests\Fixtures\Values\BagWithCustomCollectionOf;
use Tests\Fixtures\Values\BagWithLaravelCollectionOf;
use Tests\Fixtures\Values\BagWithOptionalCollection;
use Tests\Fixtures\Values\TestBag;

covers(CollectionOf::class);
Expand Down Expand Up @@ -47,3 +49,9 @@

expect($bag->bags)->toBeInstanceOf(BagWithCollectionCollection::class)->toContainOnlyInstancesOf(TestBag::class);
});

test('it handles optionals', function () {
$bag = BagWithOptionalCollection::from([]);

expect($bag->bags)->toBeInstanceOf(Optional::class);
});
20 changes: 20 additions & 0 deletions tests/Fixtures/Values/BagWithOptionalCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace Tests\Fixtures\Values;

use Bag\Attributes\Cast;
use Bag\Bag;
use Bag\Casts\CollectionOf;
use Bag\Values\Optional;
use Illuminate\Support\Collection;

readonly class BagWithOptionalCollection extends Bag
{
public function __construct(
#[Cast(CollectionOf::class, TestBag::class)]
public Optional|Collection $bags,
) {
}
}
9 changes: 9 additions & 0 deletions tests/Unit/Casts/DateTimeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
declare(strict_types=1);
use Bag\Casts\DateTime;
use Bag\Collection;
use Bag\Values\Optional;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Carbon\Exceptions\InvalidFormatException;
Expand Down Expand Up @@ -137,3 +138,11 @@
expect($datetime)->toBeNull()
->and($cast->get('test', collect(['test' => null])))->toBeNull();
});

test('it can be optional', function () {
$cast = new DateTime();

$datetime = $cast->set(Collection::wrap([CarbonImmutable::class, 'null']), 'test', collect(['test' => new Optional()]));

expect($datetime)->toBeInstanceOf(Optional::class);
});
10 changes: 10 additions & 0 deletions tests/Unit/Casts/MagicCastTest.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
<?php

declare(strict_types=1);

use Bag\Casts\MagicCast;
use Bag\Collection;
use Bag\Values\Optional;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Illuminate\Database\Eloquent\Model;
Expand Down Expand Up @@ -164,3 +166,11 @@
expect($result)->toBeInstanceOf($model::class)
->and($result->id)->toBe(1);
});

test('it can be optional', function () {
$cast = new MagicCast();

$datetime = $cast->set(Collection::wrap(['string']), 'test', collect(['test' => new Optional()]));

expect($datetime)->toBeInstanceOf(Optional::class);
});
11 changes: 11 additions & 0 deletions tests/Unit/Casts/MoneyFromMajorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
declare(strict_types=1);
use Bag\Casts\MoneyFromMajor;
use Bag\Collection;
use Bag\Values\Optional;
use Brick\Money\Exception\UnknownCurrencyException;
use Brick\Money\Money;
use Laravel\SerializableClosure\Support\ReflectionClosure;
Expand Down Expand Up @@ -91,3 +92,13 @@
$cast = new MoneyFromMajor(currency: CurrencyAlpha3::US_Dollar, locale: 'en_GB');
expect($cast->get('test', collect(['test' => Money::of(100, 'USD')])))->toBe('US$100.00');
});

test('it can be optional', function () {
$cast = new MoneyFromMajor(currency: CurrencyAlpha3::US_Dollar);

$type = Collection::wrap((new ReflectionClosure(fn (MoneyFromMajor $type) => true))->getParameters()[0]->getType());

$money = $cast->set($type, 'test', collect(['test' => new Optional()]));

expect($money)->toBeInstanceOf(Optional::class);
});
11 changes: 11 additions & 0 deletions tests/Unit/Casts/MoneyFromMinorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
declare(strict_types=1);
use Bag\Casts\MoneyFromMinor;
use Bag\Collection;
use Bag\Values\Optional;
use Brick\Money\Exception\UnknownCurrencyException;
use Brick\Money\Money;
use Laravel\SerializableClosure\Support\ReflectionClosure;
Expand Down Expand Up @@ -91,3 +92,13 @@
$cast = new MoneyFromMinor(currency: CurrencyAlpha3::US_Dollar, locale: 'en_GB');
expect($cast->get('test', collect(['test' => Money::of(100, 'USD')])))->toBe('US$100.00');
});

test('it can be optional', function () {
$cast = new MoneyFromMinor(currency: CurrencyAlpha3::US_Dollar);

$type = Collection::wrap((new ReflectionClosure(fn (MoneyFromMinor $type) => true))->getParameters()[0]->getType());

$money = $cast->set($type, 'test', collect(['test' => new Optional()]));

expect($money)->toBeInstanceOf(Optional::class);
});