Skip to content

[#460] Introduce db() helper to encapsulate Database DI lookup#465

Merged
armanist merged 2 commits into
quantum-php:masterfrom
armanist:issue/460-db-helper
Apr 21, 2026
Merged

[#460] Introduce db() helper to encapsulate Database DI lookup#465
armanist merged 2 commits into
quantum-php:masterfrom
armanist:issue/460-db-helper

Conversation

@armanist

@armanist armanist commented Apr 21, 2026

Copy link
Copy Markdown
Collaborator

Closes #460

  • Create src/Database/Helpers/db.php with db() service-locator helper
  • Replace DI boilerplate in RelationalTrait, TransactionTrait, ModelFactory, and MigrationManager with db() calls
  • Add DbHelperTest covering instance type and singleton behavior
  • Normalize line endings in ViewTest::testRenderWithLayout to fix cross-platform test failure

Summary by CodeRabbit

  • New Features

    • Added a global db() helper function for convenient access to the database instance throughout the application.
  • Tests

    • Added comprehensive unit test coverage for the database helper, verifying correct instantiation and consistency across multiple calls.
    • Fixed platform-specific line break handling in view rendering tests.
  • Chores

    • Refactored internal database resolution patterns across multiple components to use the new database helper function.

…okup

- Create src/Database/Helpers/db.php with db() service-locator helper
- Replace DI boilerplate in RelationalTrait, TransactionTrait,
  ModelFactory, and MigrationManager with db() calls
- Add DbHelperTest covering instance type and singleton behavior
- Normalize line endings in ViewTest::testRenderWithLayout to fix
  cross-platform test failure

Made-with: Cursor
@coderabbitai

coderabbitai Bot commented Apr 21, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@armanist has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 6 minutes and 13 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 6 minutes and 13 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: af890e10-5de0-48b6-9d7a-4a460d83e4dc

📥 Commits

Reviewing files that changed from the base of the PR and between 1bc7aae and eda2b8d.

📒 Files selected for processing (2)
  • src/Model/Factories/ModelFactory.php
  • tests/Unit/Database/Helpers/DbHelperTest.php
📝 Walkthrough

Walkthrough

A new db() helper function is introduced to encapsulate Database service resolution from the dependency injection container, eliminating repetitive DI boilerplate across multiple trait and manager classes. This follows established patterns in the framework and simplifies Database module access.

Changes

Cohort / File(s) Summary
New Database Helper
src/Database/Helpers/db.php
Introduces a global db() function that lazily registers and retrieves the Database service instance from the DI container, centralizing DI logic.
Refactored Traits & Managers
src/Database/Traits/RelationalTrait.php, src/Database/Traits/TransactionTrait.php, src/Migration/MigrationManager.php, src/Model/Factories/ModelFactory.php
Replaced manual DI registration/retrieval boilerplate with calls to the db() helper; simplified imports by removing Di and Database dependencies where applicable.
Helper Unit Tests
tests/Unit/Database/Helpers/DbHelperTest.php
Added test class verifying that db() returns a Database instance and that repeated calls return the same instance.
Test Maintenance
tests/Unit/View/ViewTest.php
Normalized line-break handling in testRenderWithLayout to use platform-specific PHP_EOL instead of literal \n in assertions.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes

Suggested labels

enhancement

Suggested reviewers

  • andrey-smaelov

Poem

🐰 A helper hopped into the code,
Simplifying the DI load,
No more registration chore,
Just db() forevermore!
The boilerplate melts away, making traits so bright and clear.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly references the linked issue (#460) and clearly summarizes the main change: introducing a db() helper to encapsulate Database DI lookup.
Linked Issues check ✅ Passed The PR fully addresses all coding requirements from issue #460: creates db() helper at src/Database/Helpers/db.php, refactors TransactionTrait, RelationalTrait, MigrationManager, and ModelFactory to use the helper, and adds comprehensive unit tests in DbHelperTest.php.
Out of Scope Changes check ✅ Passed The only minor out-of-scope change is the line-ending normalization in ViewTest.php::testRenderWithLayout, which is unrelated to the db() helper objective but appears to address a cross-platform test failure.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Apr 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.71429% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 82.83%. Comparing base (5abd353) to head (eda2b8d).
⚠️ Report is 3 commits behind head on master.

Files with missing lines Patch % Lines
src/Migration/MigrationManager.php 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master     #465      +/-   ##
============================================
+ Coverage     82.80%   82.83%   +0.02%     
+ Complexity     2861     2857       -4     
============================================
  Files           249      250       +1     
  Lines          7619     7614       -5     
============================================
- Hits           6309     6307       -2     
+ Misses         1310     1307       -3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1bc7aae74f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Database/Helpers/db.php

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Model/Factories/ModelFactory.php (1)

95-107: ⚠️ Potential issue | 🟡 Minor

@throws ReflectionException should be retained.

db() calls Di::get(Database::class) which can throw ReflectionException during container resolution (same reason the other refactored sites — RelationalTrait, TransactionTrait, MigrationManager — kept ReflectionException in their @throws). Removing it here creates inconsistency and under-documents the method's failure modes. createDynamicModel() on Line 71 still declares ReflectionException, so the call chain is also inconsistent internally.

📝 Proposed fix
     /**
      * `@param` array<string> $foreignKeys
      * `@param` array<string> $hidden
-     * `@throws` DiException|BaseException
+     * `@throws` DiException|BaseException|ReflectionException
      */
     protected static function createOrmInstance(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Model/Factories/ModelFactory.php` around lines 95 - 107, The docblock for
ModelFactory::createOrmInstance currently omits ReflectionException even though
db() -> Di::get(Database::class) can throw it; update the method PHPDoc to
include `@throws` ReflectionException (alongside DiException|BaseException) so it
matches createDynamicModel and other refactored sites (RelationalTrait,
TransactionTrait, MigrationManager) and accurately documents the container
resolution failure mode.
🧹 Nitpick comments (4)
src/Database/Helpers/db.php (1)

23-30: Guard against function redeclaration.

Global helper files are prone to being loaded more than once (e.g., Composer files autoload + a helper-loader stage, or during test bootstrapping). Without a function_exists() guard, a duplicate load will trigger a fatal Cannot redeclare db() error. Recommend wrapping the definition:

🛡️ Proposed fix
 use Quantum\Database\Database;
 use Quantum\Di\Di;

-/**
- * Gets the Database instance from DI
- */
-function db(): Database
-{
-    if (!Di::isRegistered(Database::class)) {
-        Di::register(Database::class);
-    }
-
-    return Di::get(Database::class);
-}
+if (!function_exists('db')) {
+    /**
+     * Gets the Database instance from DI
+     */
+    function db(): Database
+    {
+        if (!Di::isRegistered(Database::class)) {
+            Di::register(Database::class);
+        }
+
+        return Di::get(Database::class);
+    }
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Database/Helpers/db.php` around lines 23 - 30, Wrap the global helper
function db() with a function_exists check to prevent fatal redeclaration;
specifically, before defining function db() (which uses Di::isRegistered,
Di::register and Di::get with Database::class), add a guard like if
(!function_exists('db')) { /* define function db() here */ } so the helper can
be loaded multiple times safely without redefining db().
tests/Unit/Database/Helpers/DbHelperTest.php (2)

10-13: Redundant setUp() override.

The override only calls parent::setUp() and can be removed for brevity. Non-blocking.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/Unit/Database/Helpers/DbHelperTest.php` around lines 10 - 13, Remove
the redundant setUp() override in the DbHelperTest class: the current setUp()
method only calls parent::setUp() and can be deleted to simplify the test class;
locate the public function setUp(): void in DbHelperTest and remove that method
so the parent implementation is used directly.

15-26: Consider adding coverage for the lazy-registration branch.

Both existing tests exercise the already-registered path after AppTestCase bootstrapping. A test that resets the DI container (or unregisters Database::class) before calling db() would exercise the !Di::isRegistered(...) branch in src/Database/Helpers/db.php (Lines 25–27), which is currently not directly verified.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/Unit/Database/Helpers/DbHelperTest.php` around lines 15 - 26, Add a
unit test that resets the DI registration before calling db() to cover the
lazy-registration branch: in DbHelperTest add a new test (e.g.,
testDbHelperLazilyRegistersDatabase) that unregisters Database::class from the
container (or resets Di) after AppTestCase bootstrapping, then calls db() and
asserts it returns an instance of Database and that the container now has
Database::class registered; reference the db() helper, Di::isRegistered/Di
unregister behavior, and Database::class to locate the code paths in
src/Database/Helpers/db.php.
tests/Unit/View/ViewTest.php (1)

120-122: Line-ending normalization is fine, but consider normalizing the expected string instead.

Mutating $renderedView before assertion masks what the renderer actually produced. An equivalent and clearer approach is to normalize both sides (or just the expected side) using PHP_EOL. Non-blocking; matches the pattern already used in testRenderViewWithTwig on Line 187, so consistency-wise this is acceptable.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/Unit/View/ViewTest.php` around lines 120 - 122, Instead of mutating
$renderedView before the assertion, normalize the expected string to use PHP_EOL
(consistent with testRenderViewWithTwig) so the assertion compares the raw
renderer output to a normalized expected value; update the assertion in ViewTest
(the $renderedView variable and the assertEquals call) to remove the str_replace
on $renderedView and apply PHP_EOL normalization to the expected HTML string
used in assertEquals.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/Model/Factories/ModelFactory.php`:
- Around line 95-107: The docblock for ModelFactory::createOrmInstance currently
omits ReflectionException even though db() -> Di::get(Database::class) can throw
it; update the method PHPDoc to include `@throws` ReflectionException (alongside
DiException|BaseException) so it matches createDynamicModel and other refactored
sites (RelationalTrait, TransactionTrait, MigrationManager) and accurately
documents the container resolution failure mode.

---

Nitpick comments:
In `@src/Database/Helpers/db.php`:
- Around line 23-30: Wrap the global helper function db() with a function_exists
check to prevent fatal redeclaration; specifically, before defining function
db() (which uses Di::isRegistered, Di::register and Di::get with
Database::class), add a guard like if (!function_exists('db')) { /* define
function db() here */ } so the helper can be loaded multiple times safely
without redefining db().

In `@tests/Unit/Database/Helpers/DbHelperTest.php`:
- Around line 10-13: Remove the redundant setUp() override in the DbHelperTest
class: the current setUp() method only calls parent::setUp() and can be deleted
to simplify the test class; locate the public function setUp(): void in
DbHelperTest and remove that method so the parent implementation is used
directly.
- Around line 15-26: Add a unit test that resets the DI registration before
calling db() to cover the lazy-registration branch: in DbHelperTest add a new
test (e.g., testDbHelperLazilyRegistersDatabase) that unregisters
Database::class from the container (or resets Di) after AppTestCase
bootstrapping, then calls db() and asserts it returns an instance of Database
and that the container now has Database::class registered; reference the db()
helper, Di::isRegistered/Di unregister behavior, and Database::class to locate
the code paths in src/Database/Helpers/db.php.

In `@tests/Unit/View/ViewTest.php`:
- Around line 120-122: Instead of mutating $renderedView before the assertion,
normalize the expected string to use PHP_EOL (consistent with
testRenderViewWithTwig) so the assertion compares the raw renderer output to a
normalized expected value; update the assertion in ViewTest (the $renderedView
variable and the assertEquals call) to remove the str_replace on $renderedView
and apply PHP_EOL normalization to the expected HTML string used in
assertEquals.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9364f83c-835b-4eb7-9a40-6ece168483f7

📥 Commits

Reviewing files that changed from the base of the PR and between 5abd353 and 1bc7aae.

📒 Files selected for processing (7)
  • src/Database/Helpers/db.php
  • src/Database/Traits/RelationalTrait.php
  • src/Database/Traits/TransactionTrait.php
  • src/Migration/MigrationManager.php
  • src/Model/Factories/ModelFactory.php
  • tests/Unit/Database/Helpers/DbHelperTest.php
  • tests/Unit/View/ViewTest.php

@armanist armanist requested a review from andrey-smaelov April 21, 2026 11:22
Document ReflectionException in ModelFactory::createOrmInstance and simplify DbHelperTest by removing redundant setup while adding lazy DI registration coverage for db().

Made-with: Cursor
@armanist armanist merged commit 7d33a8c into quantum-php:master Apr 21, 2026
7 checks passed
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.

Introduce db() Helper to Encapsulate Database DI Lookup

2 participants