Skip to content

Commit

Permalink
Merge pull request #13 from ray-di/entity-with-constructor
Browse files Browse the repository at this point in the history
Support entity constructor
  • Loading branch information
koriym committed Sep 14, 2021
2 parents 53c3e6a + 307fa8e commit 4fcc7d1
Show file tree
Hide file tree
Showing 13 changed files with 3,000 additions and 1,704 deletions.
36 changes: 30 additions & 6 deletions README.ja.md
Expand Up @@ -116,11 +116,33 @@ interface TodoItemInterface
```php
final class Todo
{
/** @var string */
public $id;
public string $id;
public string $title;
}
```

プロパティをキャメルケースに変換する場合には`CameCaseTrait`を使います。

```php
use Ray\MediaQuery\CamelCaseTrait;

/** @var string */
public $title;
class Invoice
{
use CamelCaseTrait;

public $userName;
}
```

コンストラクタがあると、フェッチしたデータでコールされます。

```php
final class Todo
{
public function __construct(
public string $id,
public string $title
) {}
}
```

Expand Down Expand Up @@ -151,7 +173,7 @@ interface TaskAddInterface
値はSQL実行時やWeb APIリクエスト時に日付フォーマットされた文字列に変換されます。

```sql
INSERT INTO task (title, created_at) VALUES (:title, :createdAt); // 2021-2-14 00:00:00
INSERT INTO task (title, created_at) VALUES (:title, :createdAt); # 2021-2-14 00:00:00
```

値を渡さないとバインドされている現在時刻がインジェクションされます。
Expand Down Expand Up @@ -190,7 +212,7 @@ class UserId implements ToScalarInterface
```

```sql
INSERT INTO memo (user_id, memo) VALUES (:user_id, :memo);
INSERT INTO memo (user_id, memo) VALUES (:user_id, :memo);
```

### パラメーターインジェクション
Expand Down Expand Up @@ -296,6 +318,8 @@ public function testAdd(): void
属性を表すのに[doctrineアノテーション](https://github.com/doctrine/annotations/)[アトリビュート](https://www.php.net/manual/ja/language.attributes.overview.php) どちらも利用できます。 次の2つは同じものです。

```php
use Ray\MediaQuery\Annotation\DbQuery;

#[DbQuery('user_add')]
public function add1(string $id, string $title): void;

Expand Down
36 changes: 30 additions & 6 deletions README.md
Expand Up @@ -127,11 +127,33 @@ interface TodoItemInterface
```php
final class Todo
{
/** @var string */
public $id;
public string $id;
public string $title;
}
```

Use `CameCaseTrait` to convert a property to camelCase.

```php
use Ray\MediaQuery\CamelCaseTrait;

/** @var string */
public $title;
class Invoice
{
use CamelCaseTrait;

public $userName;
}
```

If the entity has a constructor, the constructor will be called with the fetched data.

```php
final class Todo
{
public function __construct(
public string $id,
public string $title
) {}
}
```

Expand Down Expand Up @@ -160,7 +182,7 @@ interface TaskAddInterface
The value will be converted to a date formatted string at SQL execution time or Web API request time.

```sql
INSERT INTO task (title, created_at) VALUES (:title, :createdAt); // 2021-2-14 00:00:00
INSERT INTO task (title, created_at) VALUES (:title, :createdAt); # 2021-2-14 00:00:00
```

If no value is passed, the bound current time will be injected.
Expand Down Expand Up @@ -200,7 +222,7 @@ class UserId implements ToScalarInterface
```

```sql
INSERT INTO memo (user_id, memo) VALUES (:user_id, :memo);
INSERT INTO memo (user_id, memo) VALUES (:user_id, :memo);
```

### Parameter Injection
Expand Down Expand Up @@ -288,6 +310,8 @@ You can use either [doctrine annotations](https://github.com/doctrine/annotation
The next two are the same.

```php
use Ray\MediaQuery\Annotation\DbQuery;

#[DbQuery('user_add')]
public function add1(string $id, string $title): void;

Expand Down
1 change: 1 addition & 0 deletions phpcs.xml
Expand Up @@ -40,6 +40,7 @@
<exclude name="SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming.SuperfluousPrefix"/>
<exclude name="SlevomatCodingStandard.Classes.SuperfluousExceptionNaming.SuperfluousSuffix"/>
<exclude name="SlevomatCodingStandard.Classes.SuperfluousInterfaceNaming.SuperfluousSuffix"/>
<exclude name="SlevomatCodingStandard.Classes.SuperfluousTraitNaming.SuperfluousSuffix"/>
<exclude name="SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable"/>
<exclude name="SlevomatCodingStandard.Commenting.UselessInheritDocComment.UselessInheritDocComment"/>
<exclude name="SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingNativeTypeHint"/>
Expand Down
21 changes: 21 additions & 0 deletions src/CamelCaseTrait.php
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace Ray\MediaQuery;

use function lcfirst;
use function str_replace;
use function ucwords;

trait CamelCaseTrait
{
/**
* @param mixed $value
*/
public function __set(string $name, $value): void
{
$propName = lcfirst(str_replace('_', '', ucwords($name, '_')));
$this->{$propName} = $value;
}
}
24 changes: 21 additions & 3 deletions src/DbQueryInterceptor.php
Expand Up @@ -11,6 +11,7 @@
use Ray\MediaQuery\Annotation\Pager;

use function class_exists;
use function method_exists;
use function substr;

class DbQueryInterceptor implements MethodInterceptor
Expand Down Expand Up @@ -45,14 +46,31 @@ public function invoke(MethodInvocation $invocation)
return $this->getPager($dbQury->id, $values, $pager);
}

$fetchStyle = class_exists($dbQury->entity) ? PDO::FETCH_CLASS : PDO::FETCH_ASSOC;
$fetchStyle = $this->getFetchMode($dbQury);

return $this->sqlQuery($dbQury->id, $values, $fetchStyle, $dbQury->entity);
}

/**
* @param array<string, mixed> $values
* @param int|string|callable $fetchArg
* @return PDO::FETCH_ASSOC|PDO::FETCH_CLASS|PDO::FETCH_FUNC $fetchStyle
*/
private function getFetchMode(DbQuery $dbQuery): int
{
if (! class_exists($dbQuery->entity)) {
return PDO::FETCH_ASSOC;
}

if (method_exists($dbQuery->entity, '__construct')) {
return PDO::FETCH_FUNC;
}

return PDO::FETCH_CLASS;
}

/**
* @param array<string, mixed> $values
* @param PDO::FETCH_ASSOC|PDO::FETCH_CLASS|PDO::FETCH_FUNC $fetchStyle
* @param int|string|callable $fetchArg
*
* @return array<mixed>|object
*/
Expand Down
22 changes: 18 additions & 4 deletions src/SqlQuery.php
Expand Up @@ -14,12 +14,14 @@

use function array_pop;
use function assert;
use function class_exists;
use function explode;
use function file;
use function file_exists;
use function file_get_contents;
use function is_array;
use function is_object;
use function is_string;
use function preg_replace;
use function sprintf;
use function stripos;
Expand Down Expand Up @@ -108,8 +110,9 @@ public function getCount(string $sqlId, array $values): int
}

/**
* @param array<string, mixed> $values
* @param callable|int|string $fetchArg
* @param PDO::FETCH_ASSOC|PDO::FETCH_CLASS|PDO::FETCH_FUNC $fetchModode
* @param array<string, mixed> $values
* @param callable|int|string $fetchArg
*
* @return array<mixed>
*/
Expand All @@ -135,7 +138,8 @@ private function perform(string $sqlId, array $values, int $fetchModode, $fetchA
}

/**
* @param callable|int|string $fetchArg
* @param PDO::FETCH_ASSOC|PDO::FETCH_CLASS|PDO::FETCH_FUNC $fetchModode
* @param callable|int|string $fetchArg
*
* @return array<mixed>
*/
Expand All @@ -146,7 +150,17 @@ private function fetchAll(int $fetchModode, $fetchArg): array
return (array) $this->pdoStatement->fetchAll($fetchModode);
}

return (array) $this->pdoStatement->fetchAll($fetchModode, $fetchArg);
if ($fetchModode === PDO::FETCH_CLASS) {
return (array) $this->pdoStatement->fetchAll($fetchModode, $fetchArg);
}

// PDO::FETCH_FUNC
return (array) $this->pdoStatement->fetchAll(PDO::FETCH_FUNC, /** @param list<mixed> $args */static function (...$args) use ($fetchArg) {
assert(is_string($fetchArg) && class_exists($fetchArg));

/** @psalm-suppress MixedMethodCall */
return new $fetchArg(...$args);
});
}

/**
Expand Down
15 changes: 9 additions & 6 deletions src/SqlQueryInterface.php
Expand Up @@ -10,22 +10,25 @@
interface SqlQueryInterface
{
/**
* @param array<string, mixed> $values
* @param int|string|callable $fetchArg
* @param array<string, mixed> $values
* @param PDO::FETCH_ASSOC|PDO::FETCH_CLASS|PDO::FETCH_FUNC $fetchMode
* @param int|string|callable $fetchArg
*/
public function exec(string $sqlId, array $values = [], int $fetchMode = PDO::FETCH_ASSOC, $fetchArg = ''): void;

/**
* @param array<string, mixed> $values
* @param int|string|callable $fetchArg
* @param array<string, mixed> $values
* @param PDO::FETCH_ASSOC|PDO::FETCH_CLASS|PDO::FETCH_FUNC $fetchMode
* @param int|string|callable $fetchArg
*
* @return array<mixed>|object
*/
public function getRow(string $sqlId, array $values = [], int $fetchMode = PDO::FETCH_ASSOC, $fetchArg = '');

/**
* @param array<string, mixed> $values
* @param int|string|callable $fetchArg
* @param array<string, mixed> $values
* @param PDO::FETCH_ASSOC|PDO::FETCH_CLASS|PDO::FETCH_FUNC $fetchMode
* @param int|string|callable $fetchArg
*
* @return array<array<mixed>>
*/
Expand Down
19 changes: 19 additions & 0 deletions tests/CamelCaseTraitTest.php
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace Ray\MediaQuery;

use PHPUnit\Framework\TestCase;
use Ray\MediaQuery\Entity\Invoice;

class CamelCaseTraitTest extends TestCase
{
public function testRequest(): void
{
$user = new Invoice();
// phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
$user->user_name = '1'; // @phpstan-ignore-line
$this->assertSame($user->userName, '1');
}
}
15 changes: 15 additions & 0 deletions tests/DbQueryModuleTest.php
Expand Up @@ -11,10 +11,12 @@
use Ray\Di\AbstractModule;
use Ray\Di\Injector;
use Ray\MediaQuery\Entity\Todo;
use Ray\MediaQuery\Entity\TodoConstruct;
use Ray\MediaQuery\Queries\PromiseAddInterface;
use Ray\MediaQuery\Queries\PromiseItemInterface;
use Ray\MediaQuery\Queries\PromiseListInterface;
use Ray\MediaQuery\Queries\TodoAddInterface;
use Ray\MediaQuery\Queries\TodoConstcuctEntityInterface;
use Ray\MediaQuery\Queries\TodoEntityInterface;
use Ray\MediaQuery\Queries\TodoItemInterface;
use Ray\MediaQuery\Queries\TodoListInterface;
Expand Down Expand Up @@ -45,6 +47,7 @@ protected function setUp(): void
PromiseItemInterface::class,
PromiseListInterface::class,
TodoEntityInterface::class,
TodoConstcuctEntityInterface::class,
]);
$sqlDir = dirname(__DIR__) . '/tests/sql';
$dbQueryConfig = new DbQueryConfig($sqlDir);
Expand Down Expand Up @@ -125,7 +128,19 @@ public function testEntity(): void
$todoList = $this->injector->getInstance(TodoEntityInterface::class);
$list = $todoList->getlist();
$this->assertInstanceOf(Todo::class, $list[0]);
$this->assertSame('run', $list[0]->title);
$item = $todoList->getItem('1');
$this->assertInstanceOf(Todo::class, $item);
}

public function testEntityWithConstructor(): void
{
/** @var TodoEntityInterface $todoList */
$todoList = $this->injector->getInstance(TodoConstcuctEntityInterface::class);
$list = $todoList->getlist();
$this->assertInstanceOf(TodoConstruct::class, $list[0]);
$this->assertSame('run', $list[0]->title);
$item = $todoList->getItem('1');
$this->assertInstanceOf(TodoConstruct::class, $item);
}
}
15 changes: 15 additions & 0 deletions tests/Fake/Entity/Invoice.php
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace Ray\MediaQuery\Entity;

use Ray\MediaQuery\CamelCaseTrait;

class Invoice
{
use CamelCaseTrait;

/** @var string */
public $userName;
}
20 changes: 20 additions & 0 deletions tests/Fake/Entity/TodoConstruct.php
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace Ray\MediaQuery\Entity;

class TodoConstruct
{
/** @var string */
public $id;

/** @var string */
public $title;

public function __construct(string $id, string $title)
{
$this->id = $id;
$this->title = $title;
}
}

0 comments on commit 4fcc7d1

Please sign in to comment.