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
27 changes: 19 additions & 8 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ jobs:
fail-fast: false
matrix:
php: ['8.2', '8.3', '8.4']
laravel: ['11.*', '12.*', '13.*']
laravel: ['10.*', '11.*', '12.*', '13.*']
include:
- laravel: '10.*'
testbench: '8.*'
pest: '^2.0'
- laravel: '11.*'
testbench: '9.*'
- laravel: '12.*'
Expand All @@ -25,6 +28,9 @@ jobs:
# Laravel 13 requires PHP 8.3.
- php: '8.2'
laravel: '13.*'
# Laravel 10 predates PHP 8.4 and was never certified against it.
- php: '8.4'
laravel: '10.*'

name: PHP ${{ matrix.php }} · Laravel ${{ matrix.laravel }}

Expand All @@ -37,15 +43,20 @@ jobs:
extensions: dom, curl, libxml, mbstring, zip, pdo, pdo_sqlite, sqlite3
coverage: none

# Laravel 11's security window closed in March 2026, so Composer will not
# load any 11.x release at all. The package still supports Laravel 11 and
# this leg still proves it, but the framework it tests against is
# unpatched — which is Composer's point, and why this is scoped to the one
# leg that needs it rather than written into composer.json.
- name: Allow Laravel 11's security advisories
if: matrix.laravel == '11.*'
# The security windows for Laravel 10 and 11 have closed, so Composer will
# not load any 10.x or 11.x release at all. The package still supports
# both and these legs still prove it, but the frameworks they test against
# are unpatched — which is Composer's point, and why this is scoped to the
# legs that need it rather than written into composer.json.
- name: Allow the unpatched framework lines' security advisories
if: matrix.laravel == '10.*' || matrix.laravel == '11.*'
run: composer config --global policy.advisories.block false

# Testbench 8 pins PHPUnit 10, which Pest 3 and 4 do not support.
- name: Pin Pest to the PHPUnit 10 line
if: matrix.pest != ''
run: composer require --dev "pestphp/pest:${{ matrix.pest }}" --no-interaction --no-update

- name: Install dependencies
run: |
composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ All notable changes to Laravel DynamicTable are documented here. The format
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- Laravel 10 support. `Schema::getColumns()` and `Schema::getIndexes()` are
Laravel 11 additions, so the new `Support\SchemaIntrospector` uses them when
they exist and otherwise runs the equivalent driver queries (SQLite, MySQL,
MariaDB, PostgreSQL, SQL Server) itself. `doctrine/dbal` is still never
required. The test matrix now covers Laravel 10 on PHP 8.2 and 8.3.

## [1.0.0] — 2026-08-30

First public release.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ Full checklist in [docs/security.md](docs/security.md).

## Requirements

PHP 8.2+ · Laravel 11, 12 or 13 · MySQL, MariaDB, PostgreSQL or SQLite.
PHP 8.2+ · Laravel 10, 11, 12 or 13 · MySQL, MariaDB, PostgreSQL or SQLite.

No Livewire. No Alpine. No jQuery. No npm. No CSS framework required.
(It works happily *inside* a Livewire app — it just doesn't need one. The
Expand Down
16 changes: 8 additions & 8 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,17 @@
"source": "https://github.com/shwaeki/DynamicTable"
},
"require": {
"illuminate/contracts": "^11.0|^12.0|^13.0",
"illuminate/database": "^11.0|^12.0|^13.0",
"illuminate/http": "^11.0|^12.0|^13.0",
"illuminate/support": "^11.0|^12.0|^13.0",
"illuminate/view": "^11.0|^12.0|^13.0",
"illuminate/contracts": "^10.0|^11.0|^12.0|^13.0",
"illuminate/database": "^10.0|^11.0|^12.0|^13.0",
"illuminate/http": "^10.0|^11.0|^12.0|^13.0",
"illuminate/support": "^10.0|^11.0|^12.0|^13.0",
"illuminate/view": "^10.0|^11.0|^12.0|^13.0",
"php": "^8.2",
"symfony/finder": "^7.0|^8.0"
"symfony/finder": "^6.2|^7.0|^8.0"
},
"require-dev": {
"orchestra/testbench": "^9.0|^10.0|^11.0",
"pestphp/pest": "^3.0|^4.0",
"orchestra/testbench": "^8.0|^9.0|^10.0|^11.0",
"pestphp/pest": "^2.0|^3.0|^4.0",
"larastan/larastan": "^2.9|^3.0",
"laravel/pint": "^1.18"
},
Expand Down
12 changes: 7 additions & 5 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,13 @@ dropped, a warning is surfaced, and the table still renders.

## 9. Supported versions

- **PHP 8.2+** — matches the floor of every currently supported Laravel release.
- **Laravel 11, 12 and 13.** Laravel 11 is the floor because the package relies
on `Schema::getColumns()`, which removed the `doctrine/dbal` requirement for
schema introspection. Supporting Laravel 10 would mean carrying dbal for one
release line; that cost is not worth paying.
- **PHP 8.2+.** Laravel 10 itself also runs on PHP 8.1, but 8.1 is end of life,
so the package does not carry it.
- **Laravel 10, 11, 12 and 13.** Laravel 11 introduced `Schema::getColumns()`
and `Schema::getIndexes()`, which read the schema natively instead of through
`doctrine/dbal`. Rather than require dbal on Laravel 10, `SchemaIntrospector`
uses those methods when they exist and otherwise runs the same driver queries
itself — so the package never pulls dbal in, on any version.
- Databases: MySQL, MariaDB, PostgreSQL, SQLite. No database-specific SQL is
emitted; `LIKE` is used for search because it is the portable option, and the
docs explain when to reach for a real search index instead.
Expand Down
2 changes: 1 addition & 1 deletion docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
| | |
|---|---|
| PHP | 8.2+ |
| Laravel | 11, 12 or 13 |
| Laravel | 10, 11, 12 or 13 |
| Database | MySQL, MariaDB, PostgreSQL or SQLite |
| JavaScript build step | none |
| CSS framework | none required |
Expand Down
2 changes: 2 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
<env name="APP_ENV" value="testing"/>
<env name="DB_CONNECTION" value="testing"/>
<env name="CACHE_STORE" value="array"/>
<!-- Laravel 10 reads CACHE_DRIVER; 11+ reads CACHE_STORE. -->
<env name="CACHE_DRIVER" value="array"/>
<env name="QUEUE_CONNECTION" value="sync"/>
<env name="SESSION_DRIVER" value="array"/>
</php>
Expand Down
13 changes: 9 additions & 4 deletions src/Metadata/MetadataEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
use ReflectionClass;
use ReflectionMethod;
use ReflectionNamedType;
use Shwaeki\DynamicTable\Support\SchemaIntrospector;
use Throwable;
use UnitEnum;

Expand All @@ -30,6 +30,8 @@ class MetadataEngine
/** @var array<string, FieldMetadata|false> */
protected array $pathMemo = [];

protected SchemaIntrospector $schema;

/** Method names on Eloquent/base classes that must never be probed as relations. */
protected const SKIP_METHODS = [
'newQuery', 'newModelQuery', 'newQueryWithoutRelationships', 'newCollection',
Expand All @@ -46,7 +48,10 @@ class MetadataEngine
'getQualifiedKeyName', 'query', 'on', 'onWriteConnection', 'all', 'with',
];

public function __construct(protected ?string $storeName = null) {}
public function __construct(protected ?string $storeName = null, ?SchemaIntrospector $schema = null)
{
$this->schema = $schema ?? new SchemaIntrospector;
}

/**
* @param class-string<Model>|Model $model
Expand Down Expand Up @@ -305,15 +310,15 @@ protected function introspect(Model $model): array
$columns = [];

try {
$columns = Schema::connection($connection)->getColumns($table);
$columns = $this->schema->columns($connection, $table);
} catch (Throwable) {
$columns = [];
}

$indexed = [];

try {
foreach (Schema::connection($connection)->getIndexes($table) as $index) {
foreach ($this->schema->indexes($connection, $table) as $index) {
foreach ($index['columns'] as $column) {
$indexed[$column] = true;
}
Expand Down
Loading
Loading