refactor: implement phase 3 ORM hardening#25
Merged
davebarnwell merged 2 commits intomainfrom Mar 8, 2026
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
Implements Phase 3 ORM “hardening” by making table-metadata cache invalidation explicit, normalizing update success semantics across PDO drivers, and expanding cross-driver integration coverage.
Changes:
- Add
Model::refreshTableMetadata()to invalidate cached column metadata per model class. - Centralize automatic timestamp generation via
currentTimestamp()and document UTC behavior. - Treat no-op updates as successful when the target row still exists; expand integration tests for schema refresh, custom PKs, schema-qualified PG tables, timestamp opt-out, and no-op updates.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
src/Model/Model.php |
Adds metadata refresh API, centralizes timestamp formatting, and changes update success logic to be driver-consistent. |
tests/Model/CategoryTest.php |
Extends cross-driver integration coverage for new Phase 3 behaviors and edge cases. |
test-src/Model/CodedCategory.php |
Test model for custom primary key support across drivers. |
test-src/Model/MetadataRefreshCategory.php |
Test model for validating metadata refresh behavior after schema changes. |
test-src/Model/SchemaQualifiedCategory.php |
Test model for PostgreSQL schema-qualified table name support. |
test-src/Model/UntimedCategory.php |
Test model for tables without timestamp columns. |
README.md |
Documents UTC timestamp formatting, metadata refresh usage, and PostgreSQL schema-qualified table support. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements Phase 3 of the roadmap around quality, portability, and maintenance for the ORM core.
This change tightens behavior that was already mostly present in the library but not consistently expressed across drivers. In practice, it makes metadata refresh explicit, normalizes no-op update handling, and broadens integration coverage for cases that were under-tested.
User impact
Consumers can now refresh cached table metadata without reconnecting by calling
YourModel::refreshTableMetadata(). That matters when schemas change during long-lived processes or test runs.Update behavior is also less sensitive to driver-specific
rowCount()semantics. A no-op update on an existing row is now treated as a successful update instead of looking like a failure simply because the database reported zero changed rows.The documentation now makes UTC timestamp behavior explicit and calls out support for PostgreSQL schema-qualified tables.
Root cause
A few driver-facing behaviors depended on incidental implementation details rather than explicit library rules.
Table metadata was cached per model class with no direct way to invalidate it short of reconnecting. Update success also relied too directly on raw PDO affected-row behavior, which varies across drivers for no-op writes. Several edge cases were effectively supported but not protected by integration coverage.
Fix
The model now exposes
refreshTableMetadata()to clear cached column metadata for the current model class. Timestamp generation was centralized throughcurrentTimestamp()so the UTC formatting behavior is explicit.The update path now uses an
updateSucceeded()check that treats an update as successful when the target row still exists, even if the driver reports zero changed rows. That keeps behavior consistent for no-op updates without loosening failure handling for missing rows.The test suite was expanded with cross-driver coverage for inherited connections, custom primary keys, schema refresh behavior, timestamp opt-out behavior, timestamp-less tables, PostgreSQL schema-qualified tables, and no-op update handling.
Validation
vendor/bin/phpunit -c phpunit.xml.distvendor/bin/phpstan analyse -c phpstan.neonvendor/bin/php-cs-fixer fix --dry-run --diff