Giter VIP home page Giter VIP logo

ray.mediaquery's People

Contributors

apple-x-co avatar jingu avatar koriym avatar mstysk avatar naokitsuchiya avatar shotanue avatar yuki777 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

ray.mediaquery's Issues

getRowから呼ばれるクエリ結果が空の場合にundefined offsetが発生する

sqlファイル名のポストフィックスがitemなど、SqlQuery::getRowが呼ばれるケースで、結果セットが空の時にundefined offsetが発生してしまいます。

public function getRow(string $sqlId, array $values = [], int $fetchMode = PDO::FETCH_ASSOC, $fetchArg = '')
{
$rowList = $this->perform($sqlId, $values, $fetchMode, $fetchArg);
$item = $rowList[0];

結果が取得できなかった際の戻り値としては、空配列またはnullを期待していたのですが、意図した動作でしょうか?

戻り値の型宣言でnullable な型を渡すことが出来なくなっている

Bug Report

戻り値の型宣言でnullable な型を渡すことが出来なくなっている

How to reproduce

テストコードの TotoEntityInterface::getItem の返り値を nullable にすると再現します。

  • Ray.MediaQuery: 0.10.0
  • PHP: 8.1.14
Ray.MediaQuery on  HEAD (ef3dda2) via 🐘 v8.1.14
❯ git diff
error: cannot run delta: No such file or directory
diff --git a/tests/Fake/Queries/TodoEntityInterface.php b/tests/Fake/Queries/TodoEntityInterface.php
index 23a5bf3..1f5e349 100644
--- a/tests/Fake/Queries/TodoEntityInterface.php
+++ b/tests/Fake/Queries/TodoEntityInterface.php
@@ -10,7 +10,7 @@
 interface TodoEntityInterface
 {
     #[DbQuery('todo_item')]
-    public function getItem(string $id): Todo;
+    public function getItem(string $id): ?Todo;

     #[DbQuery('todo_list')]
     /**

Ray.MediaQuery on  HEAD (ef3dda2) via 🐘 v8.1.14
❯ ./vendor/bin/phpunit
PHPUnit 9.6.3 by Sebastian Bergmann and contributors.

......E.............................................              52 / 52 (100%)

Time: 00:01.916, Memory: 32.00 MB

There was 1 error:

1) Ray\MediaQuery\DbQueryModuleTest::testEntity
TypeError: Ray\MediaQuery\Queries\TodoEntityInterfaceNull_1464564696::getItem(): Return value must be of type ?Ray\MediaQuery\Entity\Todo, array returned

/pato/to/Ray.MediaQuery/tests/tmp/Ray_MediaQuery_Queries_TodoEntityInterfaceNull_1464564696.php:15
/path/to/Ray.MediaQuery/tests/DbQueryModuleTest.php:144

ERRORS!
Tests: 52, Assertions: 78, Errors: 1.

Multi-row SQL error with pager

Bug Report

複数行のSQLがpagerでエラー

How to reproduce

SELECT *
  FROM todo;

上記のSQLでページングをする

Error message improvement

現状:

SQLのエラーが発生するとPDOExceceptionが発生する (ネイティブのPHP振る舞い)

PDOException : SQLSTATE[HY000]: General error: 1 near "@SELECT": syntax error

このエラーメッセージを改善。

Ray\MediaQuery\Exception\PdoPerformException : SQLSTATE[HY000]: General error: 1 near "@SELECT": syntax error in todo_item.sql with values{"id":"1"}

SQLファイル名とバインドした値をメッセージに加えています。

PHP 7.3 support

現在Ray.Queryをプロジェクトで使用していますが、Ray.MediaQueryの利用、変更を検討しています。

しかしながらRay.MediaQueryのminバージョンが7.4以上になっており、この点が導入のボトルネックになっています。
minバージョン指定が7.3になるとプロジェクトに導入しやすいため、検討いただけると嬉しいです。

"php": "^7.4 || ^8.0",

Entity's factory support

エンティティのファクトリーを指定可能にします。

例)
$idに応じてTeacherクラスとStudentクラスの生成を行います。

ファクトトリーを用意します。

<?php

final class UserEntityFactory
{
    public static function factory(string $id, string $name): Todo
    {
        if (str_starts_with($id, 't') {
            return new Teacher($id, $name);
        }

        return new Student($id, $name);
    }
}

factoryアトリビュートでエンティティのファクトリーを指定します。

<?php

declare(strict_types=1);

namespace Ray\MediaQuery\Fake\Queries;

use Ray\MediaQuery\Annotation\DbQuery;
use Ray\MediaQuery\Entity\Todo;
use Ray\MediaQuery\Factory\TodoEntityFactory;

interface UserInterface
{
    #[DbQuery('todo_item', factory: UserEntityFactory::class)]
    public function getItem(string $id): Teacher|Student;

    #[DbQuery('todo_list', factory: UserEntityFactory::class)]
    /** @return array<Teacher|Student> */
    public function getList(): array;
}

\Ray\MediaQuery\MediaQueryLogger causing problems with Uuids

I've juststarted playing with BEAR.Sunday, so please excude my lack of knowledge & possibly understanding.

Bug Report

I am using (binary) Uuids for Ids in my new app, and whilst I have this working OK for saving the values to the d/b, an error is being throw because the MediaQueryLogger cannot json_encode the binary string.

I thought I could use AOP to intercept the Uuid values being passed to logger, however there are 2 problems with this…

  1. The values being passed are the converted ones, not the original ones, so they'll need 'unconverting' (which I'm doing in a decorator of the MediaQueryLogger);
  2. The MediaQueryLogger class is final, so it's not possible to intercept the log() method call.

How to reproduce

  1. Create the following files & classes…

    # asset_create.sql
    INSERT INTO assets (id, name, createdAt, updatedAt)
    VALUES (:id, :name, :createdAt, :updatedAt);
    <?php
    // src/Resource/App/Assets.php
    
    declare(strict_types=1);
    
    namespace TobyGriffiths\NetWorth\Resource\App;
    
    use BEAR\Package\Annotation\ReturnCreatedResource;
    use BEAR\Resource\ResourceObject;
    use DateTimeInterface;
    use Koriym\HttpConstants\ResponseHeader;
    use Koriym\HttpConstants\StatusCode;
    use TobyGriffiths\NetWorth\Query\AssetCommandInterface;
    use TobyGriffiths\NetWorth\Uuid\UuidFactory;
    
    use function uri_template;
    
    class Assets extends ResourceObject
    {
        public function __construct(
            private UuidFactory $uuidFactory,
            private readonly AssetCommandInterface $command,
            private readonly DateTimeInterface $now,
        ) {
        }
    
        public function onPost(string $name): static
        {
            $id = $this->uuidFactory->uuid7($this->now);
            $this->command->create($id, $name);
    
            $this->code = StatusCode::CREATED;
            $this->headers[ResponseHeader::LOCATION] = uri_template('/assets{?id}', ['id' => $id->toString()]);
    
            return $this;
        }
    }
    <?php
    // src/Uuid/UuidFactory.php
    
    declare(strict_types=1);
    
    namespace TobyGriffiths\NetWorth\Uuid;
    
    use DateTimeImmutable;
    use Ramsey\Uuid\UuidFactory as RamseyUuidFactory;
    
    final readonly class UuidFactory implements UuidFactoryInterface
    {
        public function __construct(
            private RamseyUuidFactory $innerFactory,
        ) {
        }
    
        public function uuid7(DateTimeImmutable|null $dateTime = null): Uuid
        {
            return new Uuid($this->innerFactory->uuid7($dateTime));
        }
    }
    // src/Uuid/Uuid.php
    <?php
    
    declare(strict_types=1);
    
    namespace TobyGriffiths\NetWorth\Uuid;
    
    use Ramsey\Uuid\UuidInterface as RamseyUuidInterface;
    
    final readonly class Uuid
    {
        public function __construct(
            private RamseyUuidInterface $innerUuid,
        ) {
        }
    
        public function toString(): string
        {
            return $this->innerUuid->toString();
        }
    
        public function toScalar(): string
        {
            return $this->innerUuid->getBytes();
        }
    }
  2. Bind the necessary services…

    <?php
    
    declare(strict_types=1);
    
    namespace TobyGriffiths\NetWorth\Module;
    
    use BEAR\Dotenv\Dotenv;
    use BEAR\Package\AbstractAppModule;
    use BEAR\Package\PackageModule;
    use BEAR\Resource\Module\JsonSchemaModule;
    use Ramsey\Uuid\UuidFactory as RamseyUuidFactory;
    use Ramsey\Uuid\UuidFactoryInterface as RamseyUuidFactoryInterface;
    use Ray\AuraSqlModule\AuraSqlModule;
    use Ray\Di\ProviderInterface;
    use Ray\Di\Scope;
    use Ray\IdentityValueModule\IdentityValueModule;
    use Ray\MediaQuery\DbQueryConfig;
    use Ray\MediaQuery\MediaQueryLogger;
    use Ray\MediaQuery\MediaQueryLoggerInterface;
    use Ray\MediaQuery\MediaQueryModule;
    use Ray\MediaQuery\Queries;
    use TobyGriffiths\NetWorth\MediaQuery\BinaryDataMediaQueryLoggerDecorator;
    use TobyGriffiths\NetWorth\Uuid\UuidFactory;
    use TobyGriffiths\NetWorth\Uuid\UuidFactoryInterface;
    
    use function dirname;
    use function getenv;
    
    class AppModule extends AbstractAppModule
    {
        protected function configure(): void
        {
            (new Dotenv())->load(dirname(__DIR__, 2));
    
            $this->overrideMediaQueryLogger();
    
            $this->installDbModules();
    
            $this->bind(UuidFactoryInterface::class)->to(UuidFactory::class)->in(Scope::SINGLETON);
            $this->bind(RamseyUuidFactoryInterface::class)->to(RamseyUuidFactory::class)->in(Scope::SINGLETON);
    
            $this->install(new PackageModule());
        }
    
        private function installDbModules(): void
        {
            $this->install(
                new AuraSqlModule(
                    (string) getenv('DB_DSN'),
                    (string) getenv('DB_USER'),
                    (string) getenv('DB_PW'),
                    (string) getenv('DB_SLAVE'),
                ),
            );
    
            $this->install(
                new MediaQueryModule(
                    Queries::fromDir($this->appMeta->appDir . '/src/Query'),
                    [
                        new DbQueryConfig($this->appMeta->appDir . '/var/sql'),
                    ],
                ),
            );
    
            $this->install(new IdentityValueModule());
    
            $this->install(
                new JsonSchemaModule(
                    $this->appMeta->appDir . '/var/schema/response',
                    $this->appMeta->appDir . '/var/schema/request',
                ),
            );
        }
    
        private function overrideMediaQueryLogger(): void
        {
        // Commenting this fix out to demo the problem
        //
        //    $this
        //        ->bind(MediaQueryLoggerInterface::class)
        //        ->to(BinaryDataMediaQueryLoggerDecorator::class)
        //        ->in(Scope::SINGLETON);
        //    $this->bind(MediaQueryLogger::class);
        }
    }
  3. Call the endpoint…

$ php ./bin/app/php post '/assets?name=wibble'

You can see I've commented out a call to overrideMediaQueryLogger(), which binds the following class to fix the issue, for now…

<?php

declare(strict_types=1);

namespace TobyGriffiths\NetWorth\MediaQuery;

use InvalidArgumentException;
use Ramsey\Uuid\UuidFactory;
use Ray\MediaQuery\MediaQueryLogger as RayMediaQueryLogger;
use Ray\MediaQuery\MediaQueryLoggerInterface;

use function mb_check_encoding;

/**
 * Drop in replacement of Ray\MediaQuery\MediaQueryLogger that can handle binary Uuid values.
 */
final class BinaryDataMediaQueryLoggerDecorator implements MediaQueryLoggerInterface
{
    public function __construct(
        private readonly RayMediaQueryLogger $decorated,
        // We use the concrete class here because that has needed to be bound
        private readonly UuidFactory $uuidFactory,
    ) {
    }

    public function start(): void
    {
        $this->decorated->start();
    }

    /**
     * {@inheritDoc}
     */
    public function log(string $queryId, array $values): void
    {
        foreach ($values as &$value) {
            if (mb_check_encoding($value, 'UTF-8')) {
                continue;
            }

            // Replace this with injected handlers for different types of binary data?
            try {
                $value = $this->uuidFactory
                    ->fromBytes($value)
                    ->toString();
            } catch (InvalidArgumentException) {
                // Don't convert the value, we'll just fail for now.
            }
        }
    }

    public function __toString(): string
    {
        return $this->decorated->__toString();
    }
}

So I have a proposed solution, which I'm happy to PR…

  1. Remove the final from MediaQueryLogger;

  2. Include the original values in the call to MediaQueryLoggerInterface::log(), optional, so as not to break anyone's
    overrides…

    public function log(string $queryId, array $values, array $originalValues = []): void;

I also found it difficult to work out how to decorate the original MediaQueryLogger instance, which is straight pretty
forward in Symfony, so if anyone can enlighten me on that, I can improve my workaround until this has a better fix?

If I can provide any more context info, or test some things, please let me know.

@Pager利用時にも@DbQueryで指定したentityでhydrateしたい

@DbQueryにentityを指定した場合、hydrateできますが、@Pagerを組み合わせるとhydrateできない状況です。

具体的には、以下のコードだとRay\MediaQuery\PagesInterfaceが戻り値になりますが、PagesInterfaceに内包しているレコードは@DbQueryに渡されているentityでのhydrateされず、連想配列になります。

    /**
     * @DbQuery("query", entity="Foo::class")
     * @Pager(perPage="50", template="?page={?page}")
     */
    public function query(): PagesInterface;

呼び出し側で連想配列からマップもできますが、MediaQuery側でそこまで出来ていると記述量が減り助かるなと思った次第です。

Bind entity

下記のようにエンティティクラスを指定すると、DBクエリーをfetchobjectで行い結果をエンティティにハイドレートする。

#[DbQuery('user_list', entity: User::class)]

pros

  • 列を表すのにarray-shape表記をしなくて良い。
  • エンティティクラスではmagicメソッドを使い値を変換することができる。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.