Skip to content
Merged
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
5 changes: 4 additions & 1 deletion .github/workflows/ci-mssql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ jobs:
extensions: pdo, pdo_sqlsrv
mssql: 'server:2019-latest'
- php: '8.1'
extensions: pdo, pdo_sqlsrv-5.10.0beta2
extensions: pdo, pdo_sqlsrv-5.11.0
mssql: 'server:2019-latest'
- php: '8.2'
extensions: pdo, pdo_sqlsrv-5.11.0
mssql: 'server:2019-latest'

services:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci-mysql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ jobs:
php-version:
- "8.0"
- "8.1"
- "8.2"

mysql-version:
- "5.7"
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci-pgsql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ jobs:
php-version:
- "8.0"
- "8.1"
- "8.2"

pgsql-version:
- "10"
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ jobs:
php-version:
- "8.0"
- "8.1"
- "8.2"
steps:
- name: Checkout
uses: actions/checkout@v2
Expand Down Expand Up @@ -72,6 +73,7 @@ jobs:
php-version:
- "8.0"
- "8.1"
- "8.2"
steps:
- name: Checkout
uses: actions/checkout@v2
Expand Down
57 changes: 13 additions & 44 deletions .github/workflows/static.yml
Original file line number Diff line number Diff line change
@@ -1,47 +1,16 @@
name: static analysis
on:
pull_request:
push:
branches:
- '*.*'

on: [push, pull_request]
name: static analysis

jobs:
mutation:
name: PHP ${{ matrix.php }}-${{ matrix.os }}

runs-on: ${{ matrix.os }}

strategy:
matrix:
os:
- ubuntu-latest

php:
- "8.0"

steps:
- name: Checkout
uses: actions/checkout@v2.3.4

- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: "${{ matrix.php }}"
tools: composer:v2, cs2pr
coverage: none

- name: Determine composer cache directory
run: echo "COMPOSER_CACHE_DIR=$(composer config cache-dir)" >> $GITHUB_ENV

- name: Cache dependencies installed with composer
uses: actions/cache@v2
with:
path: ${{ env.COMPOSER_CACHE_DIR }}
key: php${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }}
restore-keys: |
php${{ matrix.php }}-composer-
- name: Update composer
run: composer self-update

- name: Install dependencies with composer
run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi

- name: Static analysis
run: vendor/bin/psalm --shepherd --stats --output-format=checkstyle
psalm:
uses: spiral/gh-actions/.github/workflows/psalm.yml@master
with:
os: >-
['ubuntu-latest']
php: >-
['8.1']
7 changes: 4 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"name": "cycle/entity-behavior",
"description": "Provides a collection of attributes that add behaviors to Cycle ORM entities",
"type": "library",
"require": {
"php": ">=8.0",
Expand All @@ -11,10 +12,10 @@
},
"require-dev": {
"cycle/annotated": "^3.0",
"ramsey/uuid": "^4.0",
"ramsey/uuid": "^4.5",
"phpunit/phpunit": "^9.5",
"spiral/tokenizer": "^2.8",
"vimeo/psalm": "^4.13"
"spiral/tokenizer": "^2.8 || ^3.0",
"vimeo/psalm": "^5.11"
},
"license": "MIT",
"autoload": {
Expand Down
2 changes: 2 additions & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
findUnusedBaselineEntry="true"
findUnusedCode="false"
>
<projectFiles>
<directory name="src" />
Expand Down
19 changes: 10 additions & 9 deletions src/OptimisticLock.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@

namespace Cycle\ORM\Entity\Behavior;

use Cycle\Database\ColumnInterface;
use Cycle\Database\Schema\AbstractColumn;
use Cycle\ORM\Entity\Behavior\Schema\BaseModifier;
use Cycle\ORM\Entity\Behavior\Schema\RegistryModifier;
use Cycle\ORM\Entity\Behavior\Exception\BehaviorCompilationException;
use Cycle\ORM\Entity\Behavior\Listener\OptimisticLock as Listener;
use Cycle\Schema\Definition\Field;
use Cycle\Schema\Registry;
use Doctrine\Common\Annotations\Annotation\Enum;
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
Expand Down Expand Up @@ -58,9 +57,9 @@ final class OptimisticLock extends BaseModifier
*/
public function __construct(
private string $field = 'version',
?string $column = null,
/** @Enum({"microtime", "random-string", "increment", "datetime"}) */
#[ExpectedValues(valuesFromClass: Listener::class)]
?string $column = null,
private ?string $rule = null
) {
$this->column = $column;
Expand Down Expand Up @@ -106,12 +105,14 @@ public function render(Registry $registry): void
*
* @throws BehaviorCompilationException
*/
private function computeRule(AbstractColumn $column): string
private function computeRule(Field $field): string
{
return match ($column->getType()) {
ColumnInterface::INT => self::RULE_INCREMENT,
ColumnInterface::STRING => self::RULE_MICROTIME,
'datetime' => self::RULE_DATETIME,
$type = $field->getType();

return match (true) {
RegistryModifier::isIntegerType($type) => self::RULE_INCREMENT,
RegistryModifier::isStringType($type) => self::RULE_MICROTIME,
RegistryModifier::isDatetimeType($type) => self::RULE_DATETIME,
default => throw new BehaviorCompilationException('Failed to compute rule based on column type.')
};
}
Expand All @@ -123,7 +124,7 @@ private function addField(Registry $registry): void
assert($this->column !== null);

$this->rule ??= $fields->has($this->field)
? $this->computeRule($registry->getTableSchema($registry->getEntity($this->role))->column($this->column))
? $this->computeRule($fields->get($this->field))
// rule not set, field not fount
: Listener::DEFAULT_RULE;

Expand Down
51 changes: 47 additions & 4 deletions src/Schema/RegistryModifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@
*/
class RegistryModifier
{
protected const DEFINITION = '/(?P<type>[a-z]+)(?: *\((?P<options>[^\)]+)\))?/i';
protected const INTEGER_TYPES = [
'int',
'smallint',
'tinyint',
'bigint',
'integer',
'tinyInteger',
'smallInteger',
'bigInteger',
];
protected const DATETIME_TYPES = ['datetime', 'datetime2'];
protected const INT_COLUMN = AbstractColumn::INT;
protected const STRING_COLUMN = AbstractColumn::STRING;
protected const DATETIME_COLUMN = 'datetime';
Expand All @@ -38,7 +50,7 @@ public function __construct(Registry $registry, string $role)
public function addDatetimeColumn(string $columnName, string $fieldName): AbstractColumn
{
if ($this->fields->has($fieldName)) {
if (!$this->isType(self::DATETIME_COLUMN, $fieldName, $columnName)) {
if (!static::isDatetimeType($this->fields->get($fieldName)->getType())) {
throw new BehaviorCompilationException(sprintf('Field %s must be of type datetime.', $fieldName));
}
$this->validateColumnName($fieldName, $columnName);
Expand All @@ -57,7 +69,7 @@ public function addDatetimeColumn(string $columnName, string $fieldName): Abstra
public function addIntegerColumn(string $columnName, string $fieldName): AbstractColumn
{
if ($this->fields->has($fieldName)) {
if (!$this->isType(self::INT_COLUMN, $fieldName, $columnName)) {
if (!static::isIntegerType($this->fields->get($fieldName)->getType())) {
throw new BehaviorCompilationException(sprintf('Field %s must be of type integer.', $fieldName));
}
$this->validateColumnName($fieldName, $columnName);
Expand All @@ -73,7 +85,7 @@ public function addIntegerColumn(string $columnName, string $fieldName): Abstrac
public function addStringColumn(string $columnName, string $fieldName): AbstractColumn
{
if ($this->fields->has($fieldName)) {
if (!$this->isType(self::STRING_COLUMN, $fieldName, $columnName)) {
if (!static::isStringType($this->fields->get($fieldName)->getType())) {
throw new BehaviorCompilationException(sprintf('Field %s must be of type string.', $fieldName));
}
$this->validateColumnName($fieldName, $columnName);
Expand All @@ -92,7 +104,7 @@ public function addStringColumn(string $columnName, string $fieldName): Abstract
public function addUuidColumn(string $columnName, string $fieldName): AbstractColumn
{
if ($this->fields->has($fieldName)) {
if (!$this->isType(self::UUID_COLUMN, $fieldName, $columnName)) {
if (!static::isUuidType($this->fields->get($fieldName)->getType())) {
throw new BehaviorCompilationException(sprintf('Field %s must be of type uuid.', $fieldName));
}
$this->validateColumnName($fieldName, $columnName);
Expand Down Expand Up @@ -156,6 +168,9 @@ protected function validateColumnName(string $fieldName, string $columnName): vo
}
}

/**
* @deprecated since v1.2
*/
protected function isType(string $type, string $fieldName, string $columnName): bool
{
if ($type === self::DATETIME_COLUMN) {
Expand All @@ -176,4 +191,32 @@ protected function isType(string $type, string $fieldName, string $columnName):

return $this->table->column($columnName)->getType() === $type;
}

public static function isIntegerType(string $type): bool
{
\preg_match(self::DEFINITION, $type, $matches);

return \in_array($matches['type'], self::INTEGER_TYPES, true);
}

public static function isDatetimeType(string $type): bool
{
\preg_match(self::DEFINITION, $type, $matches);

return \in_array($matches['type'], self::DATETIME_TYPES, true);
}

public static function isStringType(string $type): bool
{
\preg_match(self::DEFINITION, $type, $matches);

return $matches['type'] === 'string';
}

public static function isUuidType(string $type): bool
{
\preg_match(self::DEFINITION, $type, $matches);

return $matches['type'] === 'uuid';
}
}
24 changes: 24 additions & 0 deletions tests/Behavior/Fixtures/OptimisticLock/WithAllParameters.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace Cycle\ORM\Entity\Behavior\Tests\Fixtures\OptimisticLock;

use Cycle\Annotated\Annotation\Column;
use Cycle\Annotated\Annotation\Entity;
use Cycle\ORM\Entity\Behavior\OptimisticLock;

#[Entity]
#[OptimisticLock(
field: 'revision',
column: 'revision_field',
rule: OptimisticLock::RULE_INCREMENT
)]
final class WithAllParameters
{
#[Column(type: 'primary')]
public int $id;

#[Column(type: 'integer', name: 'revision_field')]
public int $revision;
}
1 change: 1 addition & 0 deletions tests/Behavior/Functional/Driver/Common/BaseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ abstract class BaseTest extends TestCase

public static array $config;
protected ?DatabaseManager $dbal = null;
protected ?DriverInterface $driver = null;
private static array $driverCache = [];

public function setUp(): void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Cycle\ORM\Entity\Behavior\Tests\Fixtures\OptimisticLock\Page;
use Cycle\ORM\Entity\Behavior\Tests\Fixtures\OptimisticLock\Post;
use Cycle\ORM\Entity\Behavior\Tests\Fixtures\OptimisticLock\Product;
use Cycle\ORM\Entity\Behavior\Tests\Fixtures\OptimisticLock\WithAllParameters;
use Cycle\ORM\Entity\Behavior\Tests\Functional\Driver\Common\BaseSchemaTest;
use Spiral\Tokenizer\Config\TokenizerConfig;
use Spiral\Tokenizer\Tokenizer;
Expand Down Expand Up @@ -65,6 +66,18 @@ public function testExistColumn(): void
$this->assertSame(2, $fields->count());
}

public function testExistColumnAndAllOptimisticLockParameters(): void
{
$fields = $this->registry->getEntity(WithAllParameters::class)->getFields();

$this->assertTrue($fields->has('revision'));
$this->assertTrue($fields->hasColumn('revision_field'));
$this->assertSame('integer', $fields->get('revision')->getType());

// not added new columns
$this->assertSame(2, $fields->count());
}

public function testAddDefaultColumAndRule(): void
{
$fields = $this->registry->getEntity(News::class)->getFields();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ abstract class RegistryModifierTest extends BaseTest
private const ROLE_TEST = 'test';

protected RegistryModifier $modifier;
protected Registry $registry;

public function setUp(): void
{
Expand Down
Loading