ray-di / ray.mediaquery Goto Github PK
View Code? Open in Web Editor NEWMedia access mapping framework
Home Page: https://packagist.org/packages/ray/media-query
License: MIT License
Media access mapping framework
Home Page: https://packagist.org/packages/ray/media-query
License: MIT License
sqlファイル名のポストフィックスがitem
など、SqlQuery::getRowが呼ばれるケースで、結果セットが空の時にundefined offsetが発生してしまいます。
Ray.MediaQuery/src/SqlQuery.php
Lines 84 to 87 in 4fcc7d1
結果が取得できなかった際の戻り値としては、空配列またはnullを期待していたのですが、意図した動作でしょうか?
戻り値の型宣言でnullable な型を渡すことが出来なくなっている
テストコードの TotoEntityInterface::getItem
の返り値を nullable にすると再現します。
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.
複数行のSQLがpagerでエラー
SELECT *
FROM todo;
上記のSQLでページングをする
現状:
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ファイル名とバインドした値をメッセージに加えています。
I want to use an Entity with a 1-to-many data structure relationship, but it seems that it is currently not possible.
By using php.net:PDO::FETCH_NAMED, it seems that if there are multiple values, they will be stored in an array.
This might also be helpful: phpdelusions.net::FETCH_NAMED
現在Ray.Queryをプロジェクトで使用していますが、Ray.MediaQueryの利用、変更を検討しています。
しかしながらRay.MediaQueryのminバージョンが7.4以上になっており、この点が導入のボトルネックになっています。
minバージョン指定が7.3になるとプロジェクトに導入しやすいため、検討いただけると嬉しいです。
Line 12 in 141570a
エンティティのファクトリーを指定可能にします。
例)
$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;
}
I've juststarted playing with BEAR.Sunday, so please excude my lack of knowledge & possibly understanding.
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…
MediaQueryLogger
);MediaQueryLogger
class is final, so it's not possible to intercept the log()
method call.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();
}
}
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);
}
}
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…
Remove the final
from MediaQueryLogger
;
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.
@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側でそこまで出来ていると記述量が減り助かるなと思った次第です。
下記のようにエンティティクラスを指定すると、DBクエリーをfetchobjectで行い結果をエンティティにハイドレートする。
#[DbQuery('user_list', entity: User::class)]
pros
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.