diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 62d0391..0c358ad 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,41 +16,27 @@ jobs: strategy: fail-fast: false matrix: - php: ['8.4', '8.3', '8.2', '8.1', '8.0'] - laravel: ['8.*', '9.*', '10.*', '11.*', '12.*'] + php: ['8.4', '8.3', '8.2', '8.1'] + laravel: ['10.*', '11.*', '12.*', '13.*'] dependency-version: [prefer-stable] exclude: - - php: 8.0 - laravel: 10.* - - php: 8.0 - laravel: 11.* - - php: 8.0 - laravel: 12.* - php: 8.1 laravel: 11.* - php: 8.1 laravel: 12.* + - php: 8.1 + laravel: 13.* - php: 8.2 - laravel: 8.* - - php: 8.3 - laravel: 8.* - - php: 8.3 - laravel: 9.* - - php: 8.4 - laravel: 8.* - - php: 8.4 - laravel: 9.* + laravel: 13.* include: - - laravel: 8.* - testbench: 6.23 - - laravel: 9.* - testbench: 7.* - laravel: 10.* testbench: 8.* - laravel: 11.* testbench: 9.* - laravel: 12.* testbench: 10.* + - laravel: 13.* + testbench: 11.* name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} - ubuntu-latest @@ -71,4 +57,4 @@ jobs: composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction - name: Execute tests - run: vendor/bin/phpunit \ No newline at end of file + run: vendor/bin/phpunit diff --git a/composer.json b/composer.json index e6da0e9..ef0eac9 100644 --- a/composer.json +++ b/composer.json @@ -17,12 +17,12 @@ ], "require": { "php": "^7.1 || ^8.0", - "illuminate/support": "^5.5 || ^6.0 || ^7.0 || ^8.0 || ^9.0|^10.0 || ^11.0 || ^12.0" + "illuminate/support": "^5.5 || ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0 || ^13.0" }, "require-dev": { "laravel/legacy-factories": "^1.0", - "orchestra/testbench": "^3.0 || ^4.0 || ^5.0 || ^6.0|^8.0 || ^9.0 || ^10.0", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0 || ^10.5 || ^11.5.3" + "orchestra/testbench": "^3.0 || ^4.0 || ^5.0 || ^6.0 || ^8.0 || ^9.0 || ^10.0 || ^11.0", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0 || ^10.5 || ^11.5.3 || ^12.5.12" }, "autoload": { "psr-4": { diff --git a/src/Outputs/Debugbar.php b/src/Outputs/Debugbar.php index 8d46898..c5b2760 100644 --- a/src/Outputs/Debugbar.php +++ b/src/Outputs/Debugbar.php @@ -2,13 +2,10 @@ namespace BeyondCode\QueryDetector\Outputs; +use DebugBar\DataCollector\MessagesCollector; use Illuminate\Support\Collection; use Symfony\Component\HttpFoundation\Response; -use Barryvdh\Debugbar\Facade as LaravelDebugbarV3; -use DebugBar\DataCollector\MessagesCollector; -use Fruitcake\LaravelDebugbar\Facades\Debugbar as LaravelDebugbar; - class Debugbar implements Output { protected $collector; @@ -17,26 +14,52 @@ public function boot() { $this->collector = new MessagesCollector('N+1 Queries'); - if (class_exists(\Fruitcake\LaravelDebugbar\Facades\Debugbar::class)) { - if (!LaravelDebugbar::hasCollector($this->collector->getName())) { - LaravelDebugbar::addCollector($this->collector); - } + $facade = $this->resolveDebugbarFacade(); + + if ($facade === null) { return; } - if (!LaravelDebugbarV3::hasCollector($this->collector->getName())) { - LaravelDebugbarV3::addCollector($this->collector); + if (! $facade::hasCollector($this->collector->getName())) { + $facade::addCollector($this->collector); } } public function output(Collection $detectedQueries, Response $response) { foreach ($detectedQueries as $detectedQuery) { - $this->collector->addMessage(sprintf('Model: %s => Relation: %s - You should add `with(%s)` to eager-load this relation.', + $this->collector->addMessage(sprintf( + 'Model: %s => Relation: %s - You should add `with(%s)` to eager-load this relation.', $detectedQuery['model'], $detectedQuery['relation'], $detectedQuery['relation'] )); } } + + /** + * Resolve the installed Debugbar facade at runtime. + * + * Supports barryvdh/laravel-debugbar (v3 and v4+) and + * fruitcake/laravel-debugbar. Returns the first class + * that the autoloader can locate, or null if none exist. + * + * @return string|null + */ + protected function resolveDebugbarFacade() + { + $candidates = [ + 'Barryvdh\Debugbar\Facades\Debugbar', + 'Barryvdh\Debugbar\Facade', + 'Fruitcake\LaravelDebugbar\Facades\Debugbar', + ]; + + foreach ($candidates as $candidate) { + if (class_exists($candidate)) { + return $candidate; + } + } + + return null; + } } diff --git a/tests/QueryDetectorTest.php b/tests/QueryDetectorTest.php index f2dbe0d..37419a5 100644 --- a/tests/QueryDetectorTest.php +++ b/tests/QueryDetectorTest.php @@ -12,8 +12,7 @@ class QueryDetectorTest extends TestCase { - /** @test */ - public function it_detects_n1_query_on_properties() + public function test_it_detects_n1_query_on_properties() { Route::get('/', function (){ $authors = Author::all(); @@ -34,8 +33,7 @@ public function it_detects_n1_query_on_properties() $this->assertSame('profile', $queries[0]['relation']); } - /** @test */ - public function it_detects_n1_query_on_multiple_requests() + public function test_it_detects_n1_query_on_multiple_requests() { Route::get('/', function (){ $authors = Author::get(); @@ -62,8 +60,7 @@ public function it_detects_n1_query_on_multiple_requests() $this->assertSame('profile', $queries[0]['relation']); } - /** @test */ - public function it_does_not_detect_a_false_n1_query_on_multiple_requests() + public function test_it_does_not_detect_a_false_n1_query_on_multiple_requests() { Route::get('/', function (){ $authors = Author::with("profile")->get(); @@ -82,8 +79,7 @@ public function it_does_not_detect_a_false_n1_query_on_multiple_requests() $this->assertCount(0, app(QueryDetector::class)->getDetectedQueries()); } - /** @test */ - public function it_ignores_eager_loaded_relationships() + public function test_it_ignores_eager_loaded_relationships() { Route::get('/', function (){ $authors = Author::with('profile')->get(); @@ -100,8 +96,7 @@ public function it_ignores_eager_loaded_relationships() $this->assertCount(0, $queries); } - /** @test */ - public function it_detects_n1_queries_from_builder() + public function test_it_detects_n1_queries_from_builder() { Route::get('/', function (){ $authors = Author::with('profile')->get(); @@ -123,8 +118,7 @@ public function it_detects_n1_queries_from_builder() $this->assertSame(Post::class, $queries[0]['relation']); } - /** @test */ - public function it_detects_all_n1_queries() + public function test_it_detects_all_n1_queries() { Route::get('/', function (){ $authors = Author::with('profile')->get(); @@ -154,8 +148,7 @@ public function it_detects_all_n1_queries() $this->assertSame('author', $queries[1]['relation']); } - /** @test */ - public function it_detects_n1_queries_on_morph_relations() + public function test_it_detects_n1_queries_on_morph_relations() { Route::get('/', function (){ foreach (Post::all() as $post) { @@ -174,8 +167,7 @@ public function it_detects_n1_queries_on_morph_relations() $this->assertSame('comments', $queries[0]['relation']); } - /** @test */ - public function it_detects_n1_queries_on_morph_relations_with_builder() + public function test_it_detects_n1_queries_on_morph_relations_with_builder() { Route::get('/', function (){ foreach (Post::all() as $post) { @@ -194,8 +186,7 @@ public function it_detects_n1_queries_on_morph_relations_with_builder() $this->assertSame(Comment::class, $queries[0]['relation']); } - /** @test */ - public function it_can_be_disabled() + public function test_it_can_be_disabled() { $this->app['config']->set('querydetector.enabled', false); @@ -212,8 +203,7 @@ public function it_can_be_disabled() $this->assertCount(0, $queries); } - /** @test */ - public function it_ignores_whitelisted_relations() + public function test_it_ignores_whitelisted_relations() { $this->app['config']->set('querydetector.enabled', true); $this->app['config']->set('querydetector.except', [ @@ -235,8 +225,7 @@ public function it_ignores_whitelisted_relations() $this->assertCount(0, $queries); } - /** @test */ - public function it_ignores_whitelisted_relations_with_attributes() + public function test_it_ignores_whitelisted_relations_with_attributes() { $this->app['config']->set('querydetector.enabled', true); $this->app['config']->set('querydetector.except', [ @@ -258,8 +247,7 @@ public function it_ignores_whitelisted_relations_with_attributes() $this->assertCount(0, $queries); } - /** @test */ - public function it_ignores_redirects() + public function test_it_ignores_redirects() { Route::get('/', function (){ foreach (Post::all() as $post) { @@ -275,8 +263,7 @@ public function it_ignores_redirects() $this->assertCount(1, $queries); } - /** @test */ - public function it_fires_an_event_if_detects_n1_query() + public function test_it_fires_an_event_if_detects_n1_query() { Event::fake(); @@ -293,8 +280,7 @@ public function it_fires_an_event_if_detects_n1_query() Event::assertDispatched(QueryDetected::class); } - /** @test */ - public function it_does_not_fire_an_event_if_there_is_no_n1_query() + public function test_it_does_not_fire_an_event_if_there_is_no_n1_query() { Event::fake(); @@ -310,8 +296,7 @@ public function it_does_not_fire_an_event_if_there_is_no_n1_query() Event::assertNotDispatched(QueryDetected::class); } - /** @test */ - public function it_uses_the_trace_line_to_detect_queries() + public function test_it_uses_the_trace_line_to_detect_queries() { Route::get('/', function (){ $authors = Author::all(); @@ -337,8 +322,7 @@ public function it_uses_the_trace_line_to_detect_queries() $this->assertSame('profile', $queries[0]['relation']); } - /** @test */ - public function it_empty_queries() + public function test_it_empty_queries() { Route::get('/', function (){ $authors = Author::all();