Skip to content
This repository has been archived by the owner on Nov 21, 2023. It is now read-only.

Commit

Permalink
feat: 🎸 Support custom resolvers (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
mpyw authored Nov 27, 2021
1 parent b536b3a commit 3872ed3
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 5 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@ Detect **primary/unique key or constraint violation** errors from `PDOException`
| Laravel | <code>^6.0 &#124;&#124; ^7.0 &#124;&#124; ^8.0 &#124;&#124; ^9.0</code> |
| [mpyw/unique-violation-detector](https://github.com/mpyw/unique-violation-detector) | <code>^1.0</code> |

## Supported Connections

| Database | Connection Class |
|:---|:---|
| MySQL | `Illuminate\Database\MySqlConnection` |
| PostgreSQL | `Illuminate\Database\PostgresConnection` |
| SQLite | `Illuminate\Database\SQLiteConnection` |
| SQLServer | `Illuminate\Database\SqlServerConnection` |

You can also add custom resolvers by one of the following:

- `Mpyw\LaravelUniqueViolationDetector\Facades\Unique::resolverFor()`
- `Mpyw\LaravelUniqueViolationDetector\DetectorDiscoverer::resolverFor()`

## Installing

```
Expand Down
21 changes: 21 additions & 0 deletions src/DetectorDiscoverer.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,29 @@

class DetectorDiscoverer
{
/**
* @var callable[]|string[]
*/
protected static $resolvers = [];

/**
* @param string|callable $factory
* @phpstan-param class-string<ConnectionInterface> $connectionClassName
* @phpstan-param class-string<DetectorInterface>|callable(): DetectorInterface $factory
* @psalm-param class-string<ConnectionInterface> $connectionClassName
* @psalm-param class-string<DetectorInterface>|callable(): DetectorInterface $factory
*/
public static function resolverFor(string $connectionClassName, $factory): void
{
static::$resolvers[$connectionClassName] = $factory;
}

public function discover(ConnectionInterface $connection): DetectorInterface
{
if ($factory = static::$resolvers[\get_class($connection)] ?? null) {
return \is_string($factory) ? new $factory() : $factory();
}

if ($connection instanceof MySqlConnection) {
return new MySQLDetector();
}
Expand Down
15 changes: 15 additions & 0 deletions src/Facades/Unique.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@

namespace Mpyw\LaravelUniqueViolationDetector\Facades;

use Illuminate\Database\ConnectionInterface;
use Illuminate\Support\Facades\Facade;
use Mpyw\LaravelUniqueViolationDetector\Contracts;
use Mpyw\LaravelUniqueViolationDetector\DetectorDiscoverer;
use Mpyw\UniqueViolationDetector\UniqueViolationDetector as DetectorInterface;

/**
* @method static bool violated(\PDOException $e)
Expand All @@ -17,4 +20,16 @@ public static function getFacadeAccessor(): string
{
return Contracts\UniqueViolationDetector::class;
}

/**
* @param string|callable $factory
* @phpstan-param class-string<ConnectionInterface> $connectionClassName
* @phpstan-param class-string<DetectorInterface>|callable(): DetectorInterface $factory
* @psalm-param class-string<ConnectionInterface> $connectionClassName
* @psalm-param class-string<DetectorInterface>|callable(): DetectorInterface $factory
*/
public static function resolverFor(string $connectionClassName, $factory): void
{
DetectorDiscoverer::resolverFor($connectionClassName, $factory);
}
}
16 changes: 12 additions & 4 deletions src/UniqueViolationDetector.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,26 @@
class UniqueViolationDetector implements Contracts\UniqueViolationDetector
{
/**
* @var DetectorInterface
* @var ConnectionInterface
*/
private $detector;
protected $connection;

/**
* @var null|DetectorInterface
*/
protected $detector = null;

public function __construct(ConnectionInterface $connection)
{
$this->detector = (new DetectorDiscoverer())->discover($connection);
$this->connection = $connection;
}

public function violated(PDOException $e): bool
{
return $this->detector->uniqueConstraintViolated($e);
return (
$this->detector
?: ($this->detector = (new DetectorDiscoverer())->discover($this->connection))
)->uniqueConstraintViolated($e);
}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/UniqueViolationDetectorServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class UniqueViolationDetectorServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->bind(Contracts\UniqueViolationDetector::class, UniqueViolationDetector::class);
$this->app->singleton(UniqueViolationDetector::class);
$this->app->alias(UniqueViolationDetector::class, Contracts\UniqueViolationDetector::class);
}
}
39 changes: 39 additions & 0 deletions tests/Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,19 @@

use Illuminate\Database\QueryException;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\SQLiteConnection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Mpyw\LaravelUniqueViolationDetector\DetectorDiscoverer;
use Mpyw\LaravelUniqueViolationDetector\Facades\Unique;
use Mpyw\LaravelUniqueViolationDetector\Tests\Models\Post;
use Mpyw\LaravelUniqueViolationDetector\Tests\Models\User;
use Mpyw\LaravelUniqueViolationDetector\UniqueViolationDetector;
use Mpyw\LaravelUniqueViolationDetector\UniqueViolationDetectorServiceProvider;
use Mpyw\UniqueViolationDetector\MySQLDetector;
use Mpyw\UniqueViolationDetector\SQLiteDetector;
use Orchestra\Testbench\TestCase as BaseTestCase;
use PDO;

class Test extends BaseTestCase
{
Expand Down Expand Up @@ -123,4 +128,38 @@ public function testEnumConstraintNotViolated(): void
$this->assertFalse((new UniqueViolationDetector(DB::connection()))->violated($e));
}
}

/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function testCustomClosureResolver(): void
{
$originalDetector = new class() extends SQLiteDetector {
};

Unique::resolverFor(SQLiteConnection::class, function () use ($originalDetector) {
return $originalDetector;
});

$discoveredDetector = (new DetectorDiscoverer())
->discover(new SQLiteConnection(new PDO('sqlite::memory:')));

$this->assertTrue($discoveredDetector instanceof $originalDetector);
}

/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function testCustomStringResolver(): void
{
// Just for testing
Unique::resolverFor(SQLiteConnection::class, MySQLDetector::class);

$discoveredDetector = (new DetectorDiscoverer())
->discover(new SQLiteConnection(new PDO('sqlite::memory:')));

$this->assertInstanceOf(MySQLDetector::class, $discoveredDetector);
}
}

0 comments on commit 3872ed3

Please sign in to comment.