Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 19 additions & 2 deletions src/Extension.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
use Nette\DI\CompilerExtension;
use Nette\DI\Definitions\ServiceDefinition;
use Nette\NotImplementedException;
use Nette\Schema\Expect;
use Nette\Schema\Schema;

class Extension extends CompilerExtension
{
Expand All @@ -20,14 +22,29 @@ class Extension extends CompilerExtension
public function loadConfiguration(): void
{
$mapper = $this->getContainerBuilder()->addDefinition($this->prefix('mapper'));
$mapper->setFactory(Mapper::class);
$mapper->setFactory(Mapper::class, [$this->config['validation']]);

/** @phpstan-ignore foreach.nonIterable */
foreach ($this->config as $tableName => $fieldDefinitions) {
foreach ($this->config['tables'] as $tableName => $fieldDefinitions) {
/** @phpstan-ignore argument.type */
$this->registerORMService($tableName, $fieldDefinitions, $mapper);
}
}

public function getConfigSchema(): Schema
{
return Expect::structure([
'validation' => Expect::bool(false),
'tables' => Expect::arrayOf(
Expect::structure([
'model' => Expect::string(),
'service' => Expect::string(),
])->castTo('array'),
'string'
)->castTo('array')
])->otherItems('mixed')->castTo('array');
}

/**
* @template TModel of Model
* @phpstan-param array{
Expand Down
5 changes: 5 additions & 0 deletions src/Mapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ class Mapper
*/
private array $map = [];

public function __construct(
public readonly bool $validate
) {
}

/**
* @throws Throwable
* @phpstan-param TKey $table
Expand Down
23 changes: 11 additions & 12 deletions src/Model/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,16 @@ public function &__get(string $key): mixed //phpcs:ignore
if (!is_null($value) && isset($docs[$key])) {
$item = $docs[$key];
if ($item['type']->isClass()) {
$returnType = $item['reflection'];
if (isset($returnType)) {
if ($value instanceof ActiveRow) {
if ($returnType->isSubclassOf(self::class)) {
$value = $returnType->newInstance($value->toArray(), $value->getTable());
}
} elseif ($returnType->isSubclassOf(\BackedEnum::class)) {
$value = $returnType->getMethod('tryFrom')->invoke($returnType, $value);
} elseif ($returnType->name === WGS84Point::class) {
$value = $returnType->getMethod('fromBytes')->invoke($returnType, $value);
/** @phpstan-ignore argument.type */
$returnType = new \ReflectionClass($item['type']->getSingleName());
if ($value instanceof ActiveRow) {
if ($returnType->isSubclassOf(self::class)) {
$value = $returnType->newInstance($value->toArray(), $value->getTable());
}
} elseif ($returnType->isSubclassOf(\BackedEnum::class)) {
$value = $returnType->getMethod('tryFrom')->invoke($returnType, $value);
} elseif ($returnType->name === WGS84Point::class) {
$value = $returnType->getMethod('fromBytes')->invoke($returnType, $value);
}
}
}
Expand Down Expand Up @@ -92,9 +91,9 @@ public function getReferencedModel(string $requestedModel): ?self
if ($path !== null) {
foreach ($path as $item) {
$nextModel = $item['type'] === 'property'
/** @phpstan-ignore property.dynamicName */
/** @phpstan-ignore property.dynamicName */
? $newModel->{$item['accessor']}
/** @phpstan-ignore method.dynamicName */
/** @phpstan-ignore method.dynamicName */
: $newModel->{$item['accessor']}();
if (is_null($nextModel)) {
if ($item['nullable']) {
Expand Down
44 changes: 22 additions & 22 deletions src/ModelRelationsParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,18 @@
use ReflectionException;
use ReflectionNamedType;

/**
* @phpstan-type ColumnDoc array{
* type:Type,
* property:string
* }
*/
class ModelRelationsParser
{
/**
* @template TModel of Model
* @phpstan-param ReflectionClass<TModel> $modelReflection
* @phpstan-return array<string,array{
* type:Type,
* reflection:ReflectionClass<object>|null,
* property:string
* }>|null
* @throws ReflectionException
* @phpstan-return array<string,ColumnDoc>|null
*/
public static function parseModelDoc(ReflectionClass $modelReflection): ?array
{
Expand All @@ -40,19 +41,21 @@ public static function parseModelDoc(ReflectionClass $modelReflection): ?array
if ($m !== false && $m !== 0) {
[, $returnString, $property] = $matches;
$returnType = Type::fromString($returnString);
if (!$returnType->isBuiltin() && $returnType->isSimple()) {
if ($returnType->allows('null')) {
$returnType = Type::fromString(
/** @phpstan-ignore argument.type,argument.type */
'?' . Reflection::expandClassName($returnType->getSingleName(), $modelReflection)
);
} else {
$returnType = Type::fromString(
/** @phpstan-ignore argument.type,argument.type */
Reflection::expandClassName($returnType->getSingleName(), $modelReflection)
);
}
}
$properties[$property] = [
'type' => $returnType,
'reflection' => $returnType->isClass()
? new ReflectionClass(
/** @phpstan-ignore argument.type */
Reflection::expandClassName(
/** @phpstan-ignore argument.type */
$returnType->getSingleName(),
/** @phpstan-ignore argument.type */
$modelReflection
)
)
: null,
'property' => $property,
];
}
Expand Down Expand Up @@ -143,10 +146,8 @@ public static function resolveReferencedProperties(ReflectionClass $model): arra
if (!$type->isClass()) {
continue;
}
$itemReflection = new ReflectionClass(
/** @phpstan-ignore argument.type,argument.type,argument.type */
Reflection::expandClassName($type->getSingleName(), $model)
);
/** @phpstan-ignore argument.type */
$itemReflection = new ReflectionClass($type->getSingleName());
if ($itemReflection->isSubclassOf(Model::class)) {
$items[$itemReflection->name] = [
'type' => 'property',
Expand Down Expand Up @@ -183,7 +184,6 @@ public static function getPath(
self::resolveReferencedProperties($model),
self::resolveReferencedMethods($model)
);

if (isset($items[$requestedModel->getName()])) {
return [$items[$requestedModel->getName()]];
}
Expand Down
38 changes: 30 additions & 8 deletions src/Service/Service.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,59 @@
use Fykosak\NetteORM\Model\Model;
use Fykosak\NetteORM\Selection\TypedSelection;
use Fykosak\NetteORM\Types\WGS84Point;
use Fykosak\NetteORM\Validator\Validator;
use Fykosak\NetteORM\Validator\ValidatorException;
use Nette\Database\Explorer;
use Nette\InvalidStateException;

/**
* @template TModel of Model
* @phpstan-type ColumnMeta array{
* name: string,
* table: string,
* nativetype: string,
* size: ?int,
* nullable: bool,
* default: mixed,
* autoincrement: bool,
* primary: bool,
* comment: ?string,
* vendor: array<string, mixed>
* }
*/
abstract class Service
{
/** @phpstan-var array<int,array<string,mixed>> */
/** @phpstan-var list<ColumnMeta> */
private array $columns;

/**
* @param Mapper<string,TModel> $mapper
* @throws \ReflectionException
*/
final public function __construct(
private readonly string $tableName,
public readonly Explorer $explorer,
private readonly Mapper $mapper
) {
if ($this->mapper->validate) {
$validator = new Validator();
$errors = $validator($this);
if (count($errors) > 0) {
throw new ValidatorException($errors);
}
}
}

/**
* @phpstan-return TModel|null
* @phpstan-return Model|null
*/
public function findByPrimary(string|int|null $key): ?Model
{
return isset($key) ? $this->getTable()->get($key) : null;
}

/**
* @phpstan-param TModel $model
* @phpstan-param Model $model
* @throws \PDOException
*/
public function disposeModel(Model $model): void
Expand All @@ -63,9 +85,9 @@ final public function getTable(): TypedSelection
}

/**
* @phpstan-param TModel|null $model
* @phpstan-param Model|null $model
* @phpstan-param array<string,mixed> $data
* @phpstan-return TModel
* @phpstan-return Model
* @throws \PDOException
*/
public function storeModel(array $data, ?Model $model = null): Model
Expand All @@ -89,7 +111,7 @@ final public function getModelClassName(): string

/**
* @throws \InvalidArgumentException
* @phpstan-param TModel $model
* @phpstan-param Model $model
*/
protected function checkType(Model $model): void
{
Expand Down Expand Up @@ -124,9 +146,9 @@ protected function filterData(array $data): array
}

/**
* @phpstan-return array<int,array<string,mixed>>
* @return list<ColumnMeta>
*/
protected function getColumnMetadata(): array
public function getColumnMetadata(): array
{
if (!isset($this->columns)) {
$this->columns = $this->explorer->getConnection()->getDriver()->getColumns($this->tableName);
Expand Down
33 changes: 33 additions & 0 deletions src/Validator/CheckBuildIn.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace Fykosak\NetteORM\Validator;

use Nette\Utils\Type;

class CheckBuildIn implements ValidatorCheck
{
public function validate(
Type $dbType,
Type $docType,
array $metadata,
string $table,
string $column,
array &$errors
): void {
if ($dbType->isBuiltin() || $docType->isBuiltin()) {
if ($dbType->getSingleName() !== $docType->getSingleName()) {
$errors[] = new ValidatorError(
$table,
$column,
sprintf(
'Database type `%s` does not match PHPDoc type `%s`',
$dbType,
$docType,
)
);
}
}
}
}
Loading
Loading