diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ef20b82 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,71 @@ +name: CI + +on: + push: + pull_request: + +jobs: + static-analysis: + name: Static Analysis + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP 8.1 + uses: shivammathur/setup-php@v2 + with: + php-version: '8.1' + tools: composer + + - name: Install dependencies + run: composer install --prefer-dist --no-interaction + + - name: Run PHPStan + run: composer phpstan + + - name: Run PHPCS + run: composer phpcs + + phpunit: + name: PHPUnit (PHP ${{ matrix.php }} + ORM ${{ matrix.orm }}) + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + include: + # ORM 4 requires PHP >= 7.2 + - { php: '7.4', orm: '4' } + - { php: '8.0', orm: '4' } + - { php: '8.1', orm: '4' } + - { php: '8.2', orm: '4' } + - { php: '8.3', orm: '4' } + - { php: '8.4', orm: '4' } + + # ORM 5 requires PHP >= 8.1 + - { php: '8.1', orm: '5' } + - { php: '8.2', orm: '5' } + - { php: '8.3', orm: '5' } + - { php: '8.4', orm: '5' } + + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + tools: composer + + - name: Install base dependencies + run: composer install --prefer-dist --no-interaction + + - name: Require CakePHP ORM version + run: composer require "cakephp/orm:^${{ matrix.orm }}" "cakephp/database:^${{ matrix.orm }}" --no-interaction --no-update + + - name: Update dependencies + run: composer update --prefer-dist --no-interaction + + - name: Run PHPUnit + run: composer phpunit-coverage diff --git a/composer.json b/composer.json index 16078d6..83c4cdb 100644 --- a/composer.json +++ b/composer.json @@ -4,19 +4,22 @@ "type": "library", "require": { "php": ">=7.4.0", - "cakephp/database": "^4.0", - "cakephp/orm": "^4.0" + "cakephp/database": "^4.0|^5.0", + "cakephp/orm": "^4.0|^5.0" }, "require-dev": { - "phpstan/phpstan": "^2.0.0", - "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan": "^1.0|^2.0", + "phpstan/phpstan-phpunit": "^1.0|^2.0", "phpunit/phpunit": "^8.5 || ^9.3", "squizlabs/php_codesniffer": "^4.0" }, "autoload": { "psr-4": { "Bancer\\NativeQueryMapper\\": "src/" - } + }, + "files": [ + "src/polyfill_str_contains.php" + ] }, "autoload-dev" : { "psr-4" : { diff --git a/src/ORM/AutoHydratorRecursive.php b/src/ORM/AutoHydratorRecursive.php index 31261e8..2c095b6 100644 --- a/src/ORM/AutoHydratorRecursive.php +++ b/src/ORM/AutoHydratorRecursive.php @@ -4,7 +4,6 @@ namespace Bancer\NativeQueryMapper\ORM; -use Cake\Database\StatementInterface; use Cake\Datasource\EntityInterface; use Cake\ORM\Table; use Cake\ORM\Entity; @@ -141,7 +140,7 @@ public function hydrateMany(array $rows): array $results = []; $rootAlias = $this->rootTable->getAlias(); foreach ($rows as $row) { - $tree = $this->buildEntityRecursive($this->rootTable, $row, $rootAlias); + $tree = $this->buildEntityRecursive($this->rootTable, $row); $root = $tree[$rootAlias]; $key = $this->entityKey($root, $this->rootTable); if (!isset($results[$key])) { @@ -156,14 +155,12 @@ public function hydrateMany(array $rows): array /** * @param Table $table * @param mixed[] $row - * @param string $expectedAlias * @param mixed[] $visited * @return \Cake\Datasource\EntityInterface[] */ protected function buildEntityRecursive( Table $table, array $row, - string $expectedAlias, array &$visited = [] ): array { $alias = $table->getAlias(); @@ -203,7 +200,7 @@ protected function buildEntityRecursive( continue; } if ($assoc instanceof HasMany) { - $tree = $this->buildEntityRecursive($target, $row, $childAlias, $visited); + $tree = $this->buildEntityRecursive($target, $row, $visited); if ($tree) { $list = $entity->get($assoc->getProperty()); if (!is_array($list)) { @@ -216,7 +213,7 @@ protected function buildEntityRecursive( continue; } if ($assoc instanceof BelongsTo || $assoc instanceof HasOne) { - $tree = $this->buildEntityRecursive($target, $row, $childAlias, $visited); + $tree = $this->buildEntityRecursive($target, $row, $visited); if ($tree) { $entity->set($assoc->getProperty(), $tree[$childAlias]); $out += $tree; @@ -224,17 +221,17 @@ protected function buildEntityRecursive( continue; } if ($assoc instanceof BelongsToMany) { - $junctionAlias = $assoc->getThrough(); - if (is_object($junctionAlias)) { - $junctionAlias = $junctionAlias->getAlias(); - } - $tree = $this->buildEntityRecursive($target, $row, $childAlias, $visited); + $tree = $this->buildEntityRecursive($target, $row, $visited); if ($tree) { $child = $tree[$childAlias]; + $junctionAlias = $assoc->getThrough(); + if (is_object($junctionAlias)) { + $junctionAlias = $junctionAlias->getAlias(); + } // hydrate join data only if the row contains it - if (isset($this->aliasMap[$junctionAlias])) { + if ($junctionAlias !== null && isset($this->aliasMap[$junctionAlias])) { $junctionTable = TableRegistry::getTableLocator()->get($junctionAlias); - $jTree = $this->buildEntityRecursive($junctionTable, $row, $junctionAlias, $visited); + $jTree = $this->buildEntityRecursive($junctionTable, $row, $visited); if ($jTree) { $child->set('_joinData', $jTree[$junctionAlias]); $out += $jTree; @@ -251,7 +248,7 @@ protected function buildEntityRecursive( continue; } // fallback - $tree = $this->buildEntityRecursive($target, $row, $childAlias, $visited); + $tree = $this->buildEntityRecursive($target, $row, $visited); if ($tree) { $entity->set($assoc->getProperty(), $tree[$childAlias]); $out += $tree; diff --git a/src/polyfill_str_contains.php b/src/polyfill_str_contains.php new file mode 100644 index 0000000..f07b778 --- /dev/null +++ b/src/polyfill_str_contains.php @@ -0,0 +1,11 @@ +execute("