Skip to content

PHP 8 native attribute support for grid configuration#35

Open
mmucklo wants to merge 17 commits intomasterfrom
feature/php8-attribute-reader
Open

PHP 8 native attribute support for grid configuration#35
mmucklo wants to merge 17 commits intomasterfrom
feature/php8-attribute-reader

Conversation

@mmucklo
Copy link
Copy Markdown
Owner

@mmucklo mmucklo commented Apr 16, 2026

Summary

Entities can now be configured using PHP 8 native attributes alongside (or instead of) Doctrine annotations:

#[Grid]
class Product {
    #[Column(label: 'Product Name', sortable: true, searchable: true)]
    private $name;

    #[Column(label: 'Price')]
    private $price;
}
  • Annotation classes now have constructors following Symfony's dual-compatibility pattern: $data array (Doctrine annotation reader BC) + named params (PHP 8 attributes).
  • ColumnSource refactored: shared buildColumnInfoFromGrid() extracted; new readAndCacheGridAttributes() reads #[Grid]/#[Column] via ReflectionAttribute on PHP 8+.
  • Flow: attributes are tried first, then Doctrine annotations, then reflection columns.
  • PHP 7 is unaffected — #[\Attribute] markers are parsed as comments, and ReflectionAttribute calls are guarded by PHP_VERSION_ID check.

Test plan

  • 35 tests / 69 assertions pass on PHP 8.3
  • PHPStan clean
  • php-cs-fixer clean
  • AttributeReaderTest covers named-arg instantiation, Doctrine-array BC, and attribute marker detection
  • CI workflow runs green on GitHub Actions

🤖 Generated with Claude Code

mmucklo and others added 2 commits April 17, 2026 00:23
Annotation classes (Grid, Column, Sort, Action, ShowAction, DeleteAction)
can now be used as PHP 8 attributes alongside the existing Doctrine
annotation reader. On PHP 8+ entities can be configured with:

    #[Grid]
    class MyEntity {
        #[Column(label: 'Name', sortable: true)]
        private $name;
    }

Implementation:
- Add constructors to all annotation classes following Symfony's
  dual-compatibility pattern: $data array first param (for Doctrine
  annotation reader BC) + named params (for PHP 8 attributes).
- Refactor ColumnSource: extract shared buildColumnInfoFromGrid() from
  readAndCacheGridAnnotations(); add readAndCacheGridAttributes() that
  reads #[Grid]/#[Column] via ReflectionAttribute.
- getColumnSourceInfo() now tries PHP 8 attributes first, falls back
  to Doctrine annotations, then to reflection columns.
- Add AttributeReaderTest covering constructor instantiation via named
  args, Doctrine-style array compat, and attribute marker validation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PHP 7 cannot parse named-argument syntax (e.g. `new Grid(actions: null)`),
causing a fatal parse error even though @requires PHP 8.0 was set — PHPUnit
loads the file before evaluating the annotation.

- Move named-arg tests into Tests/Php8Only/NamedArgAttributeTest.php
- Rewrite Tests/Grid/Source/AttributeReaderTest.php to use only PHP 7.2+
  compatible syntax (Doctrine-style $data array + positional args)
- phpunit.xml: split into "default" (excludes Php8Only/) and "php8" suites
- CI workflow: run --testsuite=default on all PHP versions, then
  --testsuite=php8 only on PHP 8+

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mmucklo mmucklo force-pushed the feature/php8-attribute-reader branch from aed4ec4 to dd3c885 Compare April 17, 2026 07:26
mmucklo and others added 5 commits April 19, 2026 23:19
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Renders the bundle's grid templates (plain table + DataTables) to static
HTML using standalone Twig + stub Router/Translator, then screenshots them
with headless Chrome on GitHub Actions. Screenshots are uploaded as
artifacts on every push/PR for visual inspection.

No new composer dependencies — uses Twig (already required) and Chrome
(pre-installed on ubuntu-latest).

New files:
- Tests/Screenshots/generate-html.php: renders HTML from each renderer
- Tests/Screenshots/ScreenshotGridSource.php: mock grid data source
- Tests/Screenshots/StubRouter.php, StubTranslator.php: minimal stubs
- CI: new "screenshots" job on PHP 8.4, uploads PNG artifacts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Screenshots are committed to the branch and referenced via
raw.githubusercontent.com URLs in a PR comment, so they're
visible inline without downloading artifacts. The comment is
updated on each push (not duplicated).

Uses [skip ci] on the screenshot commit to avoid re-triggering.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 20, 2026

Grid Screenshots

Table renderer:
table

DataTables renderer:
datatables

Auto-generated from CI run

mmucklo and others added 3 commits April 20, 2026 01:14
The [skip ci] tag caused the screenshot commit to become the PR HEAD
with no checks, hiding all CI results. Without it, the re-triggered
run finds no screenshot diff and exits cleanly (no infinite loop).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Committing screenshots to the PR branch caused an infinite loop
(PNG diffs on every run). Now screenshots are pushed to a separate
orphan ref (refs/screenshots/{branch}) which doesn't trigger CI
and doesn't pollute the PR branch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mmucklo mmucklo force-pushed the feature/php8-attribute-reader branch from 90fc5c4 to bd39e06 Compare April 20, 2026 08:17
mmucklo and others added 7 commits April 20, 2026 01:19
git rm -rf deleted the working tree including screenshots.
Copy PNGs to /tmp before switching to the orphan branch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace static HTML generation with a real Symfony test app that boots
the full bundle stack: Doctrine ORM + SQLite, DtcGridBundle with a
Product entity using #[Grid] and #[Column] attributes, and the actual
GridController serving rendered pages and JSON data.

The screenshot CI job now:
1. Runs setup.php to create DB + seed 10 products
2. Starts PHP built-in server
3. Screenshots live URLs with --virtual-time-budget=5000 so DataTables
   AJAX loads complete before capture
4. Uploads PNGs as artifacts + posts PR comment

New files:
- Tests/App/Kernel.php: minimal Symfony kernel with FrameworkBundle,
  TwigBundle, DoctrineBundle, DtcGridBundle
- Tests/App/Entity/Product.php: test entity with PHP 8 attributes
- Tests/App/config/: YAML config for framework, Doctrine SQLite, routes
- Tests/App/public/index.php: front controller for php -S
- Tests/App/setup.php: creates schema + seeds fixtures

Also adds doctrine/doctrine-bundle + symfony/yaml to require-dev, and
regenerates the PHPStan baseline.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Symfony 8's Bundle::build() declares : void return type.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Chrome headless --screenshot exits before AJAX completes. Puppeteer
waits for networkidle0 + data rows to appear in the DOM, so the
DataTables screenshot captures the fully populated grid.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DataTables page loads local JS/CSS from /bundles/dtcgrid/ which were
missing. Symlink Resources/public/{css,js} into the test app's public
directory before starting the server.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The router script was routing all requests (including CSS/JS) through
Symfony. Return false for files that exist on disk so the built-in
server serves them directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The default config loaded dataTables.bootstrap.min.js (Bootstrap 3)
but the theme CSS uses Bootstrap 4.4.1. This caused the length
selector and search box to render beside the grid instead of above it.

Switch defaults to dataTables.bootstrap4.min.{css,js} which provides
proper Bootstrap 4 grid layout for DataTables controls.

Bug caught by the new E2E screenshot CI.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant