Laravel Artisan Tinker Aliases: Create Custom Helpers for Faster Development
Speed Up Your Development Workflow with .tinker.php
Laravel Artisan Tinker is one of the most useful tools for debugging, testing queries, and exploring application data without writing temporary code. However, many developers find themselves repeating the same model imports, database queries, and configuration checks every time they open a Tinker session. These repetitive tasks may seem small, but they quickly add up and slow down daily development workflows.
Fortunately, Laravel provides a simple yet powerful solution through the .tinker.php file. By creating custom aliases and helper functions that load automatically when Tinker starts, you can eliminate repetitive typing, access frequently used data instantly, and make debugging significantly faster. In this guide, you’ll learn how it .tinker.php works, how to set it up, and seven practical examples that can help streamline your Laravel development process.
The Problem: Repetitive Typing in Tinker
If you have spent time debugging a Laravel application in Artisan Tinker, you know the pattern. You open a session, then immediately start typing the same queries you ran yesterday. And the day before that.
| 1 2 3 4 5 | # Every. Single. Session. >>> Order::where('status', 'pending')->with('customer')->latest()->get(); >>> config('services.stripe.key'); >>> Cache::get('featured_products'); |
This adds up. A developer running five Tinker sessions a day types these queries hundreds of times a month.
The Solution: .tinker.php
The fix is a single file: .tinker.php, that loads your shortcuts automatically every time Tinker starts.
What Is the .tinker.php File?
Laravel’s PsySH shell (the engine behind Artisan Tinker) supports an include file that runs automatically at the start of every session. Laravel maps this to a file named .tinker.php in your project root, the same level as .env and composer.json.
| Project structure |
| my-laravel-app/ ├── app/ ├── bootstrap/ ├── config/ ├── routes/ ├── .env ├── .tinker.php ← create this file └── composer.json |
When you run php artisan tinker, PsySH checks for a .tinker.php file in your project root.
If it exists, PsySH executes it before handing control to the interactive shell.
Every function, import, and variable you define in that file is immediately available.
Setting Up .tinker.php
Create the file in your project root and add your use statements and helper functions:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <?php // ───────────────────────────────────────────────────────────────────── // .tinker.php — Auto-loaded by php artisan tinker // Do not commit secrets. Keep this file in .gitignore. // ───────────────────────────────────────────────────────────────────── use App\Models\User; use App\Models\Order; use App\Models\Product; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\DB; // Quick access to the admin account function admin(): ?User { } // Latest pending order function pendingOrder(): ?Order { return Order::where('status', 'pending') ->with('customer', 'items') ->latest() ->first(); } |
Add .tinker.php to your .gitignore file.
It is a personal dev tool. Team members should maintain their own versions.
Never hard-code real credentials, API keys, or production emails in this file.
Before vs. After: The Difference in Practice
Here is how the same workflow looks without and with .tinker.php:
| Without .tinker.php | With .tinker.php |
| >>> use App\Models\User; >>> use App\Models\Order; >>> >>> $admin = User::where(’email’, … )->first(); >>> >>> $order = Order … ::where(‘status’,’pending’) … ->with(‘customer’,’items’) … ->latest()->first(); >>> >>> // …type this every single session | >>> // Everything ready on startup >>> >>> admin(); => App\Models\User {#1234 …} >>> >>> pendingOrder(); => App\Models\Order {#5678 …} >>> >>> // Done. Move on to real work. |
7 Practical Examples for Real Laravel Projects
The following examples are built around a typical SaaS or e-commerce application with Users, Orders, Products, and Subscriptions. Copy, adapt, and extend them to match your own domain.
Example 1: Fetch the Admin User
Purpose: Quick access to the primary admin account for testing permission-gated features.
| 1 2 3 4 | function admin(): ?User { return User::where('role', 'admin')->first(); } |
Usage in Tinker:
| 1 2 3 4 5 | >>> admin(); => App\Models\User {#1 id: 1, name: "Admin", role: "admin" ...} >>> admin()->assignRole('super-admin'); => true |
Example 2: Latest Pending Order with Relationships
Purpose: Inspect the most recent unprocessed order, eagerly loading customer and line items.
| 1 2 3 4 5 6 7 | function pendingOrder(): ?Order { return Order::where('status', 'pending') ->with('customer', 'items.product') ->latest() ->first(); } |
Usage in Tinker:
| 1 2 3 4 5 6 | >>> pendingOrder(); >>> pendingOrder()->customer->email; >>> pendingOrder()->items->count(); => 3 |
Example 3: Subscription Status for a Customer
Purpose: Check if a user has an active subscription — useful when debugging billing flows.
| 1 2 3 4 5 6 | function activeSubscriber(): ?User { return User::whereHas('subscriptions', function ($q) { $q->where('stripe_status', 'active'); })->with('subscriptions')->first(); } |
Usage in Tinker:
| 1 2 3 4 5 | >>> activeSubscriber()->subscriptions->first()->stripe_status; => "active" >>> activeSubscriber()->subscribed('default'); => true |
Example 4: Flush and Inspect the Cache
Purpose: Toggle cache state quickly during development without leaving the Tinker session.
| 1 2 3 4 5 6 7 8 9 10 | function cacheFlush(): void { Cache::flush(); echo 'Application cache cleared.' . PHP_EOL; } function cacheGet(string $key): mixed { return Cache::get($key); } |
Usage in Tinker:
| 1 2 3 4 5 | >>> cacheFlush(); Application cache cleared. >>> cacheGet('featured_products'); => null // confirmed flushed |
Example 5: Impersonate Any User by ID
Purpose: Bind a specific user as the authenticated user for testing auth-dependent code.
| 1 2 3 4 5 6 | function loginAs(int $id): User { $user = User::findOrFail($id); auth()->setUser($user); return $user; } |
Usage in Tinker:
| 1 2 3 4 5 6 | >>> $u = loginAs(42); >>> $u->name; => "Jane Doe" >>> auth()->user()->can('manage-orders'); => false |
Example 6: Run a Raw Query and Pretty-Print Results
Purpose: Execute and inspect raw SQL without switching to a database client.
| 1 2 3 4 | function rawQuery(string $sql, array $bindings = []): array { return DB::select($sql, $bindings); } |
Usage in Tinker:
| 1 2 3 4 5 6 | >>> rawQuery( ... 'SELECT id, email, created_at FROM users ... WHERE created_at > ? LIMIT 5', ... [now()->subDays(7)] ... ); |
Example 7: Read Any Config or Environment Value
Purpose: Inspect runtime config values without tailing logs or opening source files.
| 1 2 3 4 5 6 7 8 9 | function cfg(string $key): mixed { return config($key); } function env_(string $key): mixed { return env($key); } |
Usage in Tinker:
| 1 2 3 4 5 6 7 8 | >>> cfg('services.stripe.key'); => "sk_test_4eC39HqLyjWDarjtT1zdp7dc" >>> cfg('mail.default'); => "smtp" >>> env_('APP_ENV'); => "local" |
Complete .tinker.php Reference File
Here is a single, copy-paste-ready .tinker.php combining all seven examples above. Trim to what your project needs.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | <?php // ───────────────────────────────────────────────────────────────────── // .tinker.php // Auto-loaded by php artisan tinker. Personal dev tool — not for git. // ───────────────────────────────────────────────────────────────────── use App\Models\Order; use App\Models\Product; use App\Models\User; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\DB; // 1. Admin user function admin(): ?User { return User::where('role', 'admin')->first(); } // 2. Latest pending order (eager-loaded) function pendingOrder(): ?Order { return Order::where('status', 'pending') ->with('customer', 'items.product') ->latest() ->first(); } // 3. First active subscriber function activeSubscriber(): ?User { return User::whereHas('subscriptions', function ($q) { $q->where('stripe_status', 'active'); })->with('subscriptions')->first(); } // 4a. Flush entire application cache function cacheFlush(): void { Cache::flush(); echo 'Application cache cleared.' . PHP_EOL; } // 4b. Inspect a cache key function cacheGet(string $key): mixed { return Cache::get($key); } // 5. Set the authenticated user by ID function loginAs(int $id): User { $user = User::findOrFail($id); auth()->setUser($user); return $user; } // 6. Run a raw SQL query function rawQuery(string $sql, array $bindings = []): array { return DB::select($sql, $bindings); } // 7a. Read a config value function cfg(string $key): mixed { return config($key); } // 7b. Read an environment variable function env_(string $key): mixed { return env($key); } |
Keeping .tinker.php Out of Version Control
Because .tinker.php is a personal developer shortcut — not shared application code — it should live in your global git ignore rather than the project .gitignore. That way, you never accidentally commit it, and you never need to add it to every project separately.
Option A: Global gitignore (Recommended)
| 1 2 | # Add to your global gitignore (~/.gitignore_global or ~/.config/git/ignore) .tinker.php |
| 1 2 | # Tell git to use your global ignore file (one-time setup) git config --global core.excludesFile ~/.gitignore_global |
Option B: Project .gitignore
| 1 2 | # .gitignore .tinker.php |
and ensures every Laravel project automatically excludes .tinker.php.
You only configure it once, across your entire machine.
Best Practices and What to Avoid
Follow these rules to get the most out of your Tinker aliases without causing problems:
Do
- Function signatures: Use precise return types (
?User, Collection, mixed) So Tinker’s tab completion works better.
- Descriptive names: Name functions by intent:
pendingOrder() is clearer than order() or getOrder()
- Scope functions for dev tasks: queries, cache operations, auth shortcuts, and config inspection.
- Keep the file short. If it exceeds ~100 lines, split concerns into separate sections with clear comments.
Avoid
- Hard-coding production emails, user IDs, or API keys in the file.
- Writing functions that mutate production data without confirmation prompts.
- Importing so many models that the startup time degrades noticeably.
- Committing the file to git, your teammates have different local setups.
Conclusion
The .tinker.php file is one of the smallest changes you can make to your Laravel workflow with one of the biggest returns. It costs five minutes to set up and saves hours over the lifetime of a project.
Here is what you have now:
- A self-documenting shortcut library that loads automatically on every Tinker session.
- Seven real-world helpers covering users, orders, subscriptions, cache, auth, SQL, and config.
- A safe version-control strategy that keeps the file local without any manual steps.
- Guidelines to keep the file lean, secure, and team-safe.
Recent help desk articles
Greetings! I'm Aneesh Sreedharan, CEO of 2Hats Logic Solutions. At 2Hats Logic Solutions, we are dedicated to providing technical expertise and resolving your concerns in the world of technology. Our blog page serves as a resource where we share insights and experiences, offering valuable perspectives on your queries.

