Skip to content

Commit

Permalink
Adding tests + tweaks & fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
weaverryan committed Aug 17, 2023
1 parent 21d3a18 commit 7c3f666
Show file tree
Hide file tree
Showing 14 changed files with 384 additions and 5 deletions.
30 changes: 28 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,32 @@ Then this library is for you!
Define a "mapper" class:

```php
TODO
use App\Entity\Dragon;
use App\DTO\DragonDTO;

#[AsMapper(from: Dragon::class, to: DragonDTO::class)]
class DragonEntityToDtoMapper implements MapperInterface
{
public function init(object $from, string $toClass, array $context): object
{
$entity = $from;
$dto = new DragonDTO();
$dto->id = $entity->getId();

return $dto;
}

public function populate(object $from, object $to, array $context): object
{
$dto = $from;
$entity = $to;

$dto->name = $entity->getName();
$dto->firePower = $entity->getFirePower();

return $entity;
}
}
```

Then... map!
Expand All @@ -18,7 +43,7 @@ $dragonDTO = $microMapper->map($dragon, DragonDTO::class);
```

MicroMapper is similar to other data mappers, like
[jane-php/automapper](https://github.com/janephp/automapper), except less
[jane-php/automapper](https://github.com/janephp/automapper), except... less
impressive! Jane's Automapper is awesome and handles a lot of heavy lifting.
With MicroMapper, *you* do the heavy lifting. Let's review with a table!

Expand Down Expand Up @@ -49,3 +74,4 @@ TODO

- property accessor for doctrine relations
- nested objects
- manual setup
2 changes: 1 addition & 1 deletion src/Bundle/DependencyInjection/MicroMapperCompilerPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
*/
class MicroMapperCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
public function process(ContainerBuilder $container): void
{
$mapperConfigDefinitions = [];
foreach ($container->findTaggedServiceIds('micro_mapper.mapper') as $id => $tags) {
Expand Down
7 changes: 6 additions & 1 deletion src/MicroMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,15 @@ class MicroMapper implements MicroMapperInterface
/**
* @param MapperConfig[] $mapperConfigs
*/
public function __construct(private array $mapperConfigs)
public function __construct(private array $mapperConfigs = [])
{
}

public function addMapperConfig(MapperConfig $mapperConfig): void
{
$this->mapperConfigs[] = $mapperConfig;
}

public function map(object $from, string $toClass, array $context = []): object
{
$this->currentDepth++;
Expand Down
2 changes: 1 addition & 1 deletion src/SymfonycastsMicroMapperBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ protected function createContainerExtension(): ?ExtensionInterface
return new MicroMapperExtension();
}

public function build(ContainerBuilder $container)
public function build(ContainerBuilder $container): void
{
$container->addCompilerPass(new MicroMapperCompilerPass());
}
Expand Down
31 changes: 31 additions & 0 deletions tests/IntegrationTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace Symfonycasts\MicroMapper\Tests;

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfonycasts\MicroMapper\MicroMapperInterface;
use Symfonycasts\MicroMapper\Tests\fixtures\DinoRegion;
use Symfonycasts\MicroMapper\Tests\fixtures\DinoRegionDto;
use Symfonycasts\MicroMapper\Tests\fixtures\Dinosaur;

class IntegrationTest extends KernelTestCase
{
public function testBundleIntegration()
{
$region = new DinoRegion();
$region->id = 1;
$region->name = 'North America';
$region->climate = 'temperate';
$dinosaur1 = new Dinosaur(3, 'Velociraptor', 'mongoliensis');
$dinosaur1->region = $region;
$region->dinosaurs = [$dinosaur1];

$microMapper = self::getContainer()->get('public.micro_mapper');
assert($microMapper instanceof MicroMapperInterface);
$dto = $microMapper->map($region, DinoRegionDto::class);
$this->assertInstanceOf(DinoRegionDto::class, $dto);
$this->assertSame(1, $dto->id);
$this->assertCount(1, $dto->dinosaursMappedShallow);
$this->assertSame(3, $dto->dinosaursMappedShallow[0]->id);
}
}
38 changes: 38 additions & 0 deletions tests/MapperConfigTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace Symfonycasts\MicroMapper\Tests;

use PHPUnit\Framework\TestCase;
use Symfonycasts\MicroMapper\MapperConfig;
use Symfonycasts\MicroMapper\MapperInterface;
use Symfonycasts\MicroMapper\Tests\fixtures\DinoRegion;
use Symfonycasts\MicroMapper\Tests\fixtures\DinoRegionDto;

class MapperConfigTest extends TestCase
{
public function testSupports()
{
$config = new MapperConfig(
fromClass: DinoRegion::class,
toClass: DinoRegionDto::class,
mapper: fn() => $this->createMock(MapperInterface::class),
);

$this->assertTrue($config->supports(new DinoRegion(), DinoRegionDto::class));
$this->assertFalse($config->supports(new \stdClass(), DinoRegionDto::class));
$this->assertFalse($config->supports(new DinoRegion(), \stdClass::class));
$this->assertFalse($config->supports(new DinoRegionDto(), DinoRegion::class));
}

public function testGetMapper()
{
$mockMapper = $this->createMock(MapperInterface::class);
$config = new MapperConfig(
fromClass: DinoRegion::class,
toClass: DinoRegionDto::class,
mapper: fn() => $mockMapper,
);

$this->assertSame($mockMapper, $config->getMapper());
}
}
71 changes: 71 additions & 0 deletions tests/MicroMapperTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

namespace Symfonycasts\MicroMapper\Tests;

use PHPUnit\Framework\TestCase;
use Symfonycasts\MicroMapper\MapperConfig;
use Symfonycasts\MicroMapper\MicroMapper;
use Symfonycasts\MicroMapper\MicroMapperInterface;
use Symfonycasts\MicroMapper\Tests\fixtures\DinoRegion;
use Symfonycasts\MicroMapper\Tests\fixtures\DinoRegionDto;
use Symfonycasts\MicroMapper\Tests\fixtures\DinoRegionToDtoMapper;
use Symfonycasts\MicroMapper\Tests\fixtures\Dinosaur;
use Symfonycasts\MicroMapper\Tests\fixtures\DinosaurDto;
use Symfonycasts\MicroMapper\Tests\fixtures\DinosaurToDtoMapper;

class MicroMapperTest extends TestCase
{
// calls correct mapper
// respects MAX_DEPTH (and only calls init)
// throws on circular reference

public function testMap()
{
$this->createMapper();
$region = new DinoRegion();
$region->id = 1;
$region->name = 'North America';
$region->climate = 'temperate';

$dinosaur1 = new Dinosaur(3, 'Velociraptor', 'mongoliensis');
$dinosaur1->region = $region;
$dinosaur2 = new Dinosaur(4, 'Triceratops', 'horridus');
$dinosaur2->region = $region;
$region->dinosaurs = [$dinosaur1, $dinosaur2];

$dto = $this->createMapper()->map($region, DinoRegionDto::class);
$this->assertInstanceOf(DinoRegionDto::class, $dto);
$this->assertSame(1, $dto->id);
$this->assertSame('North America', $dto->name);
$this->assertSame('temperate', $dto->climate);
$this->assertCount(2, $dto->dinosaursMappedShallow);
$this->assertCount(2, $dto->dinosaursMappedDeep);

// id is mapped for both deep and shallow
$this->assertSame(3, $dto->dinosaursMappedShallow[0]->id);
$this->assertSame(3, $dto->dinosaursMappedDeep[0]->id);
// further properties are only in the deep
$this->assertNull($dto->dinosaursMappedShallow[0]->genus);
$this->assertSame('Velociraptor', $dto->dinosaursMappedDeep[0]->genus);
// the deep will have a region, but it will be shallow
$this->assertSame($dto->dinosaursMappedDeep[0]->region->id, 1);
$this->assertNull($dto->dinosaursMappedDeep[0]->region->name);
}

private function createMapper(): MicroMapperInterface
{
$microMapper = new MicroMapper([]);
$microMapper->addMapperConfig(new MapperConfig(
DinoRegion::class,
DinoRegionDto::class,
fn() => new DinoRegionToDtoMapper($microMapper)
));
$microMapper->addMapperConfig(new MapperConfig(
Dinosaur::class,
DinosaurDto::class,
fn() => new DinosaurToDtoMapper($microMapper)
));

return $microMapper;
}
}
11 changes: 11 additions & 0 deletions tests/fixtures/DinoRegion.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Symfonycasts\MicroMapper\Tests\fixtures;

class DinoRegion
{
public ?int $id = null;
public ?string $name = null;
public ?string $climate = null;
public array $dinosaurs = [];
}
18 changes: 18 additions & 0 deletions tests/fixtures/DinoRegionDto.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Symfonycasts\MicroMapper\Tests\fixtures;

class DinoRegionDto
{
public ?int $id = null;
public ?string $name = null;
public ?string $climate = null;
/**
* @var array DinosaurDto[]
*/
public array $dinosaursMappedShallow = [];
/**
* @var array DinosaurDto[]
*/
public array $dinosaursMappedDeep = [];
}
50 changes: 50 additions & 0 deletions tests/fixtures/DinoRegionToDtoMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace Symfonycasts\MicroMapper\Tests\fixtures;

use Symfonycasts\MicroMapper\AsMapper;
use Symfonycasts\MicroMapper\MapperInterface;
use Symfonycasts\MicroMapper\MicroMapperInterface;

#[AsMapper(from: DinoRegion::class, to: DinoRegionDto::class)]
class DinoRegionToDtoMapper implements MapperInterface
{
public function __construct(private MicroMapperInterface $microMapper)
{

}

public function init(object $from, string $toClass, array $context): object
{
$dto = new $toClass();
$dto->id = $from->id;

return $dto;
}

public function populate(object $from, object $to, array $context): object
{
assert($from instanceof DinoRegion);
assert($to instanceof DinoRegionDto);

$to->name = $from->name;
$to->climate = $from->climate;
$shallowDinosaurDtos = [];
foreach ($from->dinosaurs as $dino) {
$shallowDinosaurDtos[] = $this->microMapper->map($dino, DinosaurDto::class, [
MicroMapperInterface::MAX_DEPTH => 0,
]);
}
$to->dinosaursMappedShallow = $shallowDinosaurDtos;

$deepDinosaurDtos = [];
foreach ($from->dinosaurs as $dino) {
$deepDinosaurDtos[] = $this->microMapper->map($dino, DinosaurDto::class, [
MicroMapperInterface::MAX_DEPTH => 1,
]);
}
$to->dinosaursMappedDeep = $deepDinosaurDtos;

return $to;
}
}
15 changes: 15 additions & 0 deletions tests/fixtures/Dinosaur.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Symfonycasts\MicroMapper\Tests\fixtures;

class Dinosaur
{
public function __construct(
public ?int $id = null,
public ?string $genus = null,
public ?string $species = null,
public ?DinoRegion $region = null,
)
{
}
}
11 changes: 11 additions & 0 deletions tests/fixtures/DinosaurDto.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Symfonycasts\MicroMapper\Tests\fixtures;

class DinosaurDto
{
public ?int $id = null;
public ?string $genus = null;
public ?string $species = null;
public ?DinoRegionDto $region = null;
}
38 changes: 38 additions & 0 deletions tests/fixtures/DinosaurToDtoMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace Symfonycasts\MicroMapper\Tests\fixtures;

use Symfonycasts\MicroMapper\AsMapper;
use Symfonycasts\MicroMapper\MapperInterface;
use Symfonycasts\MicroMapper\MicroMapperInterface;

#[AsMapper(from: Dinosaur::class, to: DinosaurDto::class)]
class DinosaurToDtoMapper implements MapperInterface
{
public function __construct(private MicroMapperInterface $microMapper)
{

}

public function init(object $from, string $toClass, array $context): object
{
$dto = new $toClass();
$dto->id = $from->id;

return $dto;
}

public function populate(object $from, object $to, array $context): object
{
assert($from instanceof Dinosaur);
assert($to instanceof DinosaurDto);

$to->genus = $from->genus;
$to->species = $from->species;
$to->region = $this->microMapper->map($from->region, DinoRegionDto::class, [
MicroMapperInterface::MAX_DEPTH => 0,
]);

return $to;
}
}
Loading

0 comments on commit 7c3f666

Please sign in to comment.