Laravel Introspect is one of those package ideas that feels obvious only after you see it. Instead of grepping through a Laravel project every time you need to answer a structural question, it gives you a fluent API for querying the codebase itself. Views, routes, classes, and Eloquent models become things you can filter and inspect in a way that looks much closer to Eloquent than to a pile of shell commands.
That is the core value here: faster answers to codebase questions that usually show up during refactors, internal tooling, migration work, audits, and AI-assisted developer workflows.
The repository was published on May 15, 2025, and the pitch is clear from the start: query your Laravel codebase like a database.
What Laravel Introspect does
At a high level, the package lets you inspect several parts of a Laravel codebase through a typed fluent API:
- Views and Blade usage relationships.
- Registered routes and their controllers, names, paths, methods, and middleware.
- Generic PHP classes, including inheritance, interfaces, and trait usage.
- Eloquent models, including properties, fillable fields, hidden fields, appended fields, readable and writable fields, and relationship method presence.
- Per-model structured inspection, including JSON schema generation.
That matters because many practical Laravel questions are not data questions. They are code structure questions:
- Which views still depend on an old layout?
- Which routes hit a controller or middleware I want to replace?
- Which models expose a property I need for an admin generator or API contract?
- Which classes use a trait that should be removed from the system?
Normally those answers come from a combination of grep, IDE search, route listing, and manual inspection. Laravel Introspect centralizes them.
Installation
The package is installed through Composer:
composer require mateffy/laravel-introspect --dev
Installing it as a dev dependency is usually the right default. This is primarily a development and tooling package, especially for audits, refactors, internal code generation, or local inspection workflows.
Basic usage starts with the facade:
use Mateffy\Introspect\Facades\Introspect;
Why this package is useful in real Laravel work
The interesting part is not that it can list things. Laravel already has ways to inspect routes, models, and views indirectly. The interesting part is that it gives you a programmable query layer over application structure.
That makes it useful in at least four common cases:
- Refactoring old applications.
- Building internal developer tools.
- Generating structured context for AI workflows.
- Auditing large projects where naming conventions drift over time.
If you have ever opened a Laravel project and asked “what actually uses this?”, this package is aimed at that exact moment.
Example 1: auditing Blade view usage
One of the most common problems in mature Laravel apps is understanding how views depend on one another. Blade components, package views, and layout inheritance create a structure that is easy to use and harder to inspect globally.
Laravel Introspect exposes view queries directly.
Find views by wildcard pattern
This is useful when cleaning up component libraries or checking naming conventions.
use Mateffy\Introspect\Facades\Introspect;
$views = Introspect::views()
->whereNameEquals('components.*.button')
->get();
foreach ($views as $view) {
dump($view);
}
A practical use case would be a design-system cleanup where button components have multiplied over time under different namespaces.
You can also query by prefix, suffix, or partial match:
$filamentViews = Introspect::views()
->whereNameStartsWith('filament::')
->get();
$buttonViews = Introspect::views()
->whereNameEndsWith('button')
->get();
$modalViews = Introspect::views()
->whereNameContains('modal')
->get();
These are the kinds of queries that help when a UI kit has grown organically and you want a map of what exists before deleting or consolidating anything.
Find all views used by a parent view
This is one of the most practical examples in the package. Suppose you want to understand what a page pulls in before redesigning it.
$dependencies = Introspect::views()
->whereUsedBy('pages.admin.dashboard')
->get();
In a real app, this lets you answer questions like:
- Which components does the dashboard depend on?
- Are legacy partials still being pulled in?
- Does a page depend on package-provided views that make replacement harder?
You can widen the query with wildcards:
$adminDependencies = Introspect::views()
->whereUsedBy('pages.admin.*')
->get();
That is useful for spotting shared dependencies across an area of the product.
Find all views that use a given component
This is the inverse query and arguably even more useful during refactors.
$consumers = Introspect::views()
->whereUses('components.button')
->get();
If you plan to change or remove a shared component, this gives you a list of affected views without relying on text search alone.
You can also use wildcards:
$buttonConsumers = Introspect::views()
->whereUses('*.button')
->get();
$viewsWithoutButtons = Introspect::views()
->whereDoesntUse('*.button')
->get();
That becomes useful if you are trying to standardize UI primitives across the app and want to see which screens are still bypassing them.
Find layout inheritance
Layout migrations are a classic Laravel maintenance task. This package can inspect which views extend a layout.
$legacyLayoutViews = Introspect::views()
->whereExtends('layouts.legacy')
->get();
$allAppLayoutViews = Introspect::views()
->whereExtends('layouts.*')
->get();
That is an immediate win when migrating old Blade layouts to a newer shell or when splitting admin and public layouts more aggressively.
Example 2: route audits during refactors
Laravel applications often accumulate route complexity faster than teams realize. Middleware stacks change, controller methods multiply, and API prefixes drift. Route inspection is where Laravel Introspect feels especially practical.
Find routes using a controller
If you want to replace or reorganize a controller, start here.
use App\Http\Controllers\Admin\UserController;
$routes = Introspect::routes()
->whereUsesController(UserController::class)
->get();
You can narrow it to a specific action:
$indexRoutes = Introspect::routes()
->whereUsesController(UserController::class, 'index')
->get();
This is useful when moving from large multi-method controllers to smaller controllers, actions, or route groups.
Audit middleware usage
Middleware audits are common in large apps, especially after auth, tenancy, or rate-limiting changes.
$protectedRoutes = Introspect::routes()
->whereUsesMiddleware('auth')
->get();
You can query routes that use multiple middleware entries:
$tenantAuthRoutes = Introspect::routes()
->whereUsesMiddlewares(['tenant', 'auth'])
->get();
Or match any of them instead of all:
$tenantOrAuthRoutes = Introspect::routes()
->whereUsesMiddlewares(['tenant', 'auth'], all: false)
->get();
That distinction matters. Sometimes you want routes that are fully protected by a combined middleware stack. Other times you are trying to find inconsistent coverage.
For example, this is a practical compliance or product-safety audit:
$publicApiRoutes = Introspect::routes()
->whereNameStartsWith('api.')
->whereDoesntUseMiddleware('auth:sanctum')
->get();
That kind of query is much closer to a real engineering review than to a toy example.
Query routes by name or path
This is helpful when route conventions matter, such as admin, API, partner, or public site boundaries.
$apiProductRoutes = Introspect::routes()
->whereNameEquals('api.products.*')
->get();
$adminRoutes = Introspect::routes()
->wherePathStartsWith('admin')
->get();
$checkoutRoutes = Introspect::routes()
->wherePathContains('checkout')
->get();
A realistic cleanup example would be finding routes that still sit under an old URI structure:
$legacyRoutes = Introspect::routes()
->wherePathStartsWith('backend')
->get();
That is often exactly what you need before introducing redirects, consolidating route groups, or renaming public endpoints.
Build more expressive route filters
The package supports grouped query logic with or(...), which is useful for targeting edge cases in large route tables.
use Mateffy\Introspect\Query\Contracts\RouteQueryInterface;
$routes = Introspect::routes()
->whereNameEquals('api.*')
->whereMethod('POST')
->or(fn (RouteQueryInterface $query) => $query
->whereHasParameter('product')
->whereHasParameter('category')
)
->get();
This is a good example of where the package goes beyond simple filtering. It starts to act more like a structural query language for Laravel applications.
Example 3: class discovery for architecture cleanup
Class inspection is useful when Laravel teams adopt patterns like actions, services, DTOs, blocks, pipelines, or traits, then need to answer questions about how consistently those patterns are being followed.
Find classes in a namespace
$services = Introspect::classes()
->whereName('App\\Services')
->get();
This is helpful when you want to inventory a subsystem quickly.
Find classes extending a base class
use App\Support\Blocks\BaseBlock;
$blocks = Introspect::classes()
->whereExtends(BaseBlock::class)
->get();
This kind of query is useful when migrating a framework abstraction or replacing a base class that accumulated too much responsibility.
Find classes implementing an interface
use App\Contracts\SynchronizesExternalState;
$syncers = Introspect::classes()
->whereImplements(SynchronizesExternalState::class)
->get();
That is a practical pattern in event-driven or integration-heavy Laravel apps where interface-based architecture matters.
Find classes using a trait
use App\Models\Concerns\HasLegacySlug;
$legacyTraitConsumers = Introspect::classes()
->whereUses(HasLegacySlug::class)
->get();
Trait cleanup is exactly the sort of work that usually turns into a fragile search exercise. A query API is a better fit.
Example 4: model inspection for tooling and audits
Model introspection is where the package becomes especially interesting for internal tooling, admin generation, schema export, and AI-assisted workflows.
Find models with a property
Suppose you need to identify all models that expose a timestamp or a field required by a reporting pipeline.
$timestampedModels = Introspect::models()
->whereHasProperty('created_at')
->get();
You can also search for any of several properties:
$personLikeModels = Introspect::models()
->whereHasProperties(['first_name', 'last_name'], all: false)
->get();
That is useful for consistency checks before introducing shared UI, exports, or search indexing logic.
Audit fillable fields
This is one of the most practical examples in the docs because fillable fields often matter in CRUD generation, admin tooling, and API safety reviews.
$titleModels = Introspect::models()
->whereHasFillable('title')
->get();
$contentModels = Introspect::models()
->whereHasFillableProperties(['name', 'description'])
->get();
If you are building a generic admin panel, this kind of query can help determine which models are good candidates for convention-based forms.
Audit hidden fields
Security and serialization reviews often need this.
$modelsHidingPasswords = Introspect::models()
->whereHasHidden('password')
->get();
Or the inverse:
$modelsExposingTooMuch = Introspect::models()
->whereDoesntHaveHiddenProperties(['email', 'remember_token'], all: false)
->get();
That is a very practical check if you are tightening API responses or reviewing older models that predate current conventions.
Audit appended attributes
Appended fields are common in APIs and frontend hydration layers.
$modelsWithComputedFlags = Introspect::models()
->whereHasAppendedProperties(['is_active', 'resource_type'])
->get();
This helps when trying to understand which models already expose convenience fields before building resources or frontend contracts around them.
Audit readable and writable properties
These queries are especially interesting for code generation or AI tooling because they speak more directly to capabilities than raw schema does.
$readableIdentityModels = Introspect::models()
->whereHasReadableProperties(['id', 'email'])
->get();
$writableStatusModels = Introspect::models()
->whereHasWritable('status')
->get();
$settingsModels = Introspect::models()
->whereHasWritableProperties(['name', 'settings'])
->get();
This is useful if you are generating forms, API contracts, or admin capabilities from model metadata.
Find models by relationship presence
The package can also inspect whether models define specific relationship methods.
$modelsWithUserRelation = Introspect::models()
->whereHasRelationship('user')
->get();
$modelsWithoutLogs = Introspect::models()
->whereDoesntHaveRelationship('logs')
->get();
This is not a full relational graph engine, but it is enough for many practical discovery tasks.
For example, if you are rolling out auditing or ownership features, you can quickly see which models already follow the right relationship conventions.
Example 5: per-model detail and JSON schema generation
This is where Laravel Introspect becomes more than a search helper. You can inspect one model directly and derive structured metadata from it.
use App\Models\User;
$detail = Introspect::model(User::class);
From there, you can access model metadata and generate a schema:
$schema = Introspect::model(User::class)->schema();
dump($schema);
That is a strong feature for internal tools, especially when you want to:
- Generate admin forms.
- Feed structured model definitions into an AI tool.
- Create validation or documentation scaffolding.
- Build code-aware assistants for a Laravel project.
The README explicitly calls out query serialization and JSON compatibility as useful for LLM tool calling, and that makes sense. AI workflows work much better when the application can provide structured facts about the codebase instead of raw source text alone.
A realistic internal-tool example
Imagine you are building an internal helper that lists likely admin-manageable models. You might combine several model filters:
$adminCandidates = Introspect::models()
->whereHasFillableProperties(['name', 'status'], all: false)
->whereHasReadable('id')
->whereDoesntHaveHidden('deleted_at')
->limit(20)
->get();
Then for each result, derive a schema:
$schemas = collect($adminCandidates)
->mapWithKeys(fn (string $modelClass) => [
$modelClass => Introspect::model($modelClass)->schema(),
]);
That is the kind of thing that would otherwise require a lot of one-off reflection and custom parsing.
Example 6: using limit and offset for large projects
Large Laravel monoliths can return a lot of matches. The package supports limit and offset, which is useful for chunked workflows.
$models = Introspect::models()
->limit(10)
->offset(20)
->get();
That is not full Laravel pagination, but it is enough for:
- stepping through large result sets in a CLI tool
- exposing chunks in an internal dashboard
- batching analysis jobs
A practical walkthrough: refactoring an admin area
The most convincing way to think about Laravel Introspect is not as a set of isolated methods, but as a helper for real refactor sequences.
Suppose you inherited a large admin panel and want to modernize it.
You might start by finding the old layout consumers:
$legacyPages = Introspect::views()
->whereExtends('layouts.admin-legacy')
->get();
Then identify the components those pages depend on:
$legacyDependencies = Introspect::views()
->whereUsedBy('admin.*')
->get();
Then inspect which routes still hit old controllers:
use App\Http\Controllers\Admin\LegacyReportController;
$legacyRoutes = Introspect::routes()
->whereUsesController(LegacyReportController::class)
->get();
Then inspect which models are likely feeding those screens:
$reportingModels = Introspect::models()
->whereHasProperties(['status', 'created_at'])
->whereHasReadableProperties(['id', 'status'])
->get();
That chain of queries gives you a map. And that is the real value of the package: it reduces the cost of building a structural map of a Laravel application before making risky changes.
Where this package fits best
Laravel Introspect looks especially useful in these contexts:
- Mature Laravel apps with lots of Blade and route surface area.
- Teams doing incremental refactors instead of rewrites.
- Agencies and consultants onboarding onto unfamiliar codebases.
- Internal platforms that want Laravel-aware tooling.
- AI-assisted developer tools that need structured codebase context.
If your app is tiny, plain IDE search may be enough. But as soon as project size, inconsistency, or tooling ambition grows, a queryable structural layer becomes much more attractive.
Limits and tradeoffs
There are still some practical limits to keep in mind.
- This is introspection, not runtime truth in every possible dynamic case.
- Relationship existence checks are useful, but they are not the same as deep semantic model understanding.
- It complements IDE search and static analysis tools rather than replacing them completely.
- Some convenience APIs, like true pagination, are not there yet.
Those are reasonable tradeoffs. The package is strongest when used as an application-aware discovery layer, not as a magical substitute for architectural judgment.
Conclusion
Laravel Introspect is a sharp idea executed in a very Laravel-friendly way. It takes structural questions that usually require scattered tools and manual reasoning, then turns them into fluent queries over the codebase itself.
That makes it useful for refactoring, audits, internal tooling, and any workflow where the hard part is not changing code, but first understanding the shape of the system.
The strongest signal here is not just the API style. It is the shift in mindset: treat the Laravel codebase as something queryable. Once you do that, a lot of higher-level tooling starts to feel much more feasible.