Skip to content
This repository has been archived by the owner on Mar 28, 2022. It is now read-only.

Commit

Permalink
Throw more specific exceptions on bind and unbind
Browse files Browse the repository at this point in the history
  • Loading branch information
DASPRiD committed Dec 13, 2016
1 parent 36aaa7c commit bd027cb
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 2 deletions.
17 changes: 17 additions & 0 deletions src/Mapping/Exception/BindFailureException.php
@@ -0,0 +1,17 @@
<?php
declare(strict_types = 1);

namespace DASPRiD\Formidable\Mapping\Exception;

use DomainException;
use Throwable;

final class BindFailureException extends DomainException implements ExceptionInterface
{
use NestedMappingExceptionTrait;

public static function fromBindException(string $mappingKey, Throwable $previous) : self
{
return self::fromException('bind', $mappingKey, $previous);
}
}
44 changes: 44 additions & 0 deletions src/Mapping/Exception/NestedMappingExceptionTrait.php
@@ -0,0 +1,44 @@
<?php
declare(strict_types = 1);

namespace DASPRiD\Formidable\Mapping\Exception;

use Throwable;

trait NestedMappingExceptionTrait
{
/**
* @var string
*/
private $mappingKey;

protected static function fromException(string $verb, string $mappingKey, Throwable $previous) : self
{
if ($previous instanceof self) {
$mappingKey = sprintf('%s.%s', $mappingKey, $previous->mappingKey);
$actualException = $previous->getActualException();
} else {
$actualException = $previous;
}

$exception = new self(
sprintf('Failed to %s %s: %s', $verb, $mappingKey, $actualException->getMessage()),
0,
$previous
);
$exception->mappingKey = $mappingKey;

return $exception;
}

public function getActualException() : Throwable
{
$previous = $this->getPrevious();

if ($previous instanceof self) {
return $previous->getActualException();
}

return $previous;
}
}
17 changes: 17 additions & 0 deletions src/Mapping/Exception/UnbindFailureException.php
@@ -0,0 +1,17 @@
<?php
declare(strict_types = 1);

namespace DASPRiD\Formidable\Mapping\Exception;

use DomainException;
use Throwable;

final class UnbindFailureException extends DomainException implements ExceptionInterface
{
use NestedMappingExceptionTrait;

public static function fromUnbindException(string $mappingKey, Throwable $previous) : self
{
return self::fromException('unbind', $mappingKey, $previous);
}
}
15 changes: 13 additions & 2 deletions src/Mapping/ObjectMapping.php
Expand Up @@ -5,14 +5,17 @@

use DASPRiD\Formidable\Data;
use DASPRiD\Formidable\FormError\FormErrorSequence;
use DASPRiD\Formidable\Mapping\Exception\BindFailureException;
use DASPRiD\Formidable\Mapping\Exception\InvalidMappingException;
use DASPRiD\Formidable\Mapping\Exception\InvalidMappingKeyException;
use DASPRiD\Formidable\Mapping\Exception\InvalidUnapplyResultException;
use DASPRiD\Formidable\Mapping\Exception\MappedClassMismatchException;
use DASPRiD\Formidable\Mapping\Exception\NonExistentMappedClassException;
use DASPRiD\Formidable\Mapping\Exception\NonExistentUnapplyKeyException;
use DASPRiD\Formidable\Mapping\Exception\UnbindFailureException;
use ReflectionClass;
use ReflectionProperty;
use Throwable;

final class ObjectMapping implements MappingInterface
{
Expand Down Expand Up @@ -108,7 +111,11 @@ public function bind(Data $data) : BindResult
$formErrorSequence = new FormErrorSequence();

foreach ($this->mappings as $key => $mapping) {
$bindResult = $mapping->bind($data);
try {
$bindResult = $mapping->bind($data);
} catch (Throwable $e) {
throw BindFailureException::fromBindException($key, $e);
}

if (!$bindResult->isSuccess()) {
$formErrorSequence = $formErrorSequence->merge($bindResult->getFormErrorSequence());
Expand Down Expand Up @@ -147,7 +154,11 @@ public function unbind($value) : Data
throw NonExistentUnapplyKeyException::fromNonExistentUnapplyKey($key);
}

$data = $data->merge($mapping->unbind($values[$key]));
try {
$data = $data->merge($mapping->unbind($values[$key]));
} catch (Throwable $e) {
throw UnbindFailureException::fromUnbindException($key, $e);
}
}

return $data;
Expand Down
42 changes: 42 additions & 0 deletions test/Mapping/Exception/BindFailureExceptionTest.php
@@ -0,0 +1,42 @@
<?php
declare(strict_types = 1);

namespace DASPRiD\FormidableTest\Mapping\Exception;

use DASPRiD\Formidable\Mapping\Exception\BindFailureException;
use Exception;
use PHPUnit_Framework_TestCase as TestCase;

/**
* @covers DASPRiD\Formidable\Mapping\Exception\BindFailureException
* @covers DASPRiD\Formidable\Mapping\Exception\NestedMappingExceptionTrait
*/
class BindFailureExceptionTest extends TestCase
{
public function testFromBindExceptionWithGenericException()
{
$previous = new Exception('test');
$exception = BindFailureException::fromBindException('foo', $previous);

$this->assertSame(
'Failed to bind foo: test',
$exception->getMessage()
);
$this->assertSame($previous, $exception->getPrevious());
}

public function testFromBindExceptionWithNestedBindFailureException()
{
$previous = BindFailureException::fromBindException(
'bar',
BindFailureException::fromBindException('baz', new Exception('test'))
);
$exception = BindFailureException::fromBindException('foo', $previous);

$this->assertSame(
'Failed to bind foo.bar.baz: test',
$exception->getMessage()
);
$this->assertSame($previous, $exception->getPrevious());
}
}
42 changes: 42 additions & 0 deletions test/Mapping/Exception/UnbindFailureExceptionTest.php
@@ -0,0 +1,42 @@
<?php
declare(strict_types = 1);

namespace DASPRiD\FormidableTest\Mapping\Exception;

use DASPRiD\Formidable\Mapping\Exception\UnbindFailureException;
use Exception;
use PHPUnit_Framework_TestCase as TestCase;

/**
* @covers DASPRiD\Formidable\Mapping\Exception\UnbindFailureException
* @covers DASPRiD\Formidable\Mapping\Exception\NestedMappingExceptionTrait
*/
class UnbindFailureExceptionTest extends TestCase
{
public function testFromUnbindExceptionWithGenericException()
{
$previous = new Exception('test');
$exception = UnbindFailureException::fromUnbindException('foo', $previous);

$this->assertSame(
'Failed to unbind foo: test',
$exception->getMessage()
);
$this->assertSame($previous, $exception->getPrevious());
}

public function testFromUnbindExceptionWithNestedBindFailureException()
{
$previous = UnbindFailureException::fromUnbindException(
'bar',
UnbindFailureException::fromUnbindException('baz', new Exception('test'))
);
$exception = UnbindFailureException::fromUnbindException('foo', $previous);

$this->assertSame(
'Failed to unbind foo.bar.baz: test',
$exception->getMessage()
);
$this->assertSame($previous, $exception->getPrevious());
}
}
33 changes: 33 additions & 0 deletions test/Mapping/ObjectMappingTest.php
Expand Up @@ -9,15 +9,18 @@
use DASPRiD\Formidable\Mapping\Constraint\ConstraintInterface;
use DASPRiD\Formidable\Mapping\Constraint\ValidationError;
use DASPRiD\Formidable\Mapping\Constraint\ValidationResult;
use DASPRiD\Formidable\Mapping\Exception\BindFailureException;
use DASPRiD\Formidable\Mapping\Exception\InvalidMappingException;
use DASPRiD\Formidable\Mapping\Exception\InvalidMappingKeyException;
use DASPRiD\Formidable\Mapping\Exception\InvalidUnapplyResultException;
use DASPRiD\Formidable\Mapping\Exception\MappedClassMismatchException;
use DASPRiD\Formidable\Mapping\Exception\NonExistentMappedClassException;
use DASPRiD\Formidable\Mapping\Exception\NonExistentUnapplyKeyException;
use DASPRiD\Formidable\Mapping\Exception\UnbindFailureException;
use DASPRiD\Formidable\Mapping\MappingInterface;
use DASPRiD\Formidable\Mapping\ObjectMapping;
use DASPRiD\FormidableTest\Mapping\TestAsset\SimpleObject;
use Exception;
use PHPUnit_Framework_TestCase as TestCase;
use Prophecy\Argument;
use stdClass;
Expand Down Expand Up @@ -98,6 +101,22 @@ public function testBindInvalidData()
$this->assertSame('bat', iterator_to_array($bindResult->getFormErrorSequence())[0]->getMessage());
}

public function testExceptionOnBind()
{
$data = Data::fromFlatArray(['foo' => 'bar']);

$mapping = $this->prophesize(MappingInterface::class);
$mapping->bind($data)->willThrow(new Exception('test'));
$mapping->withPrefixAndRelativeKey('', 'foo')->willReturn($mapping->reveal());

$objectMapping = new ObjectMapping([
'foo' => $mapping->reveal(),
], SimpleObject::class);

$this->expectException(BindFailureException::class);
$objectMapping->bind($data);
}

public function testBindAppliesConstraints()
{
$constraint = $this->prophesize(ConstraintInterface::class);
Expand Down Expand Up @@ -151,6 +170,20 @@ public function testUnbindObjectWithMissingProperty()
$objectMapping->unbind(new SimpleObject('baz', 'bat'));
}

public function testExceptionOnUnbind()
{
$mapping = $this->prophesize(MappingInterface::class);
$mapping->unbind('bar')->willThrow(new Exception('test'));
$mapping->withPrefixAndRelativeKey('', 'foo')->willReturn($mapping->reveal());

$objectMapping = new ObjectMapping([
'foo' => $mapping->reveal(),
], SimpleObject::class);

$this->expectException(UnbindFailureException::class);
$objectMapping->unbind(new SimpleObject('bar', ''));
}

public function testInvalidUnapplyReturnValue()
{
$objectMapping = new ObjectMapping([], SimpleObject::class, null, function () {
Expand Down

0 comments on commit bd027cb

Please sign in to comment.