Laravel Tinker: The Powerful Tool for Testing Your Laravel Code Quickly

Published on November 13, 2025
Laravel Tinker REPL Testing Debugging PHP

What is Laravel Tinker?

Laravel Tinker is a powerful REPL (Read-Eval-Print Loop) tool that allows you to interact with your entire Laravel application from the command line. Think of it as a interactive playground where you can test code, debug issues, and manipulate your database without creating temporary routes or controllers.

Why Tinker is a Game-Changer:

  • Instant feedback on code snippets
  • Database interaction without PHPMyAdmin
  • API testing without Postman
  • Debug complex logic in real-time
  • Learn Laravel features hands-on

Getting Started with Tinker

Basic Tinker Commands

# Start Tinker session
php artisan tinker

# Start Tinker with specific environment
php artisan tinker --env=local

# Start Tinker in production (be careful!)
php artisan tinker --env=production

# Exit Tinker
>>> exit
>>> quit
>>> Ctrl + D

Your First Tinker Session

$ php artisan tinker

Psy Shell v0.11.8 (PHP 8.1.0 — cli) by Justin Hileman
>>>

Basic Tinker Operations

Simple PHP Operations

>>> 2 + 2
= 4

>>> $name = "TeachyLeaf"
= "TeachyLeaf"

>>> echo "Hello, " . $name
Hello, null
= null

>>> "Hello, " . $name
= "Hello, TeachyLeaf"

>>> collect([1, 2, 3])->map(fn($n) => $n * 2)
= Illuminate\Support\Collection {#4682
    all: [
      2,
      4,
      6,
    ],
  }

Working with Laravel Helpers

>>> app()->environment()
= "local"

>>> config('app.name')
= "Laravel"

>>> now()
= Illuminate\Support\Carbon @1694567890 {#4685
    date: 2024-09-13 10:18:10.0 UTC (+00:00),
  }

>>> storage_path('app/public')
= "/path/to/your/project/storage/app/public"

>>> url('/posts')
= "http://localhost:8000/posts"

>>> route('posts.index')
= "http://localhost:8000/posts"

Database Interactions with Tinker

Basic Eloquent Operations

Creating Records

>>> use App\Models\User;

// Create a user
>>> $user = User::create([
...     'name' => 'John Doe',
...     'email' => 'john@teachyleaf.com',
...     'password' => bcrypt('password123')
... ]);
= App\Models\User {#4689
    name: "John Doe",
    email: "john@teachyleaf.com",
    updated_at: "2024-09-13 10:20:45",
    created_at: "2024-09-13 10:20:45",
    id: 1,
  }

// Alternative creation method
>>> $user = new User;
>>> $user->name = 'Jane Smith';
>>> $user->email = 'jane@teachyleaf.com';
>>> $user->password = bcrypt('password123');
>>> $user->save();
= true

Reading Records

// Get all users
>>> User::all()
= Illuminate\Database\Eloquent\Collection {#4691
    all: [
      App\Models\User {#4692
        id: 1,
        name: "John Doe",
        email: "john@teachyleaf.com",
        email_verified_at: null,
        #password: "$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi",
        #remember_token: "aBcDeFgHiJkL",
        created_at: "2024-09-13 10:20:45",
        updated_at: "2024-09-13 10:20:45",
      },
    ],
  }

// Find specific user
>>> User::find(1)
= App\Models\User {#4693
    id: 1,
    name: "John Doe",
    email: "john@teachyleaf.com",
    ...
  }

// Find or fail
>>> User::findOrFail(999)
Illuminate\Database\Eloquent\ModelNotFoundException with message 'No query results for model [App\Models\User] 999'

// Get first user
>>> User::first()
= App\Models\User {#4694
    id: 1,
    name: "John Doe",
    ...
  }

// Count records
>>> User::count()
= 1

Updating Records

// Update a user
>>> $user = User::find(1)
>>> $user->name = 'John Updated'
>>> $user->save()
= true

// Mass update
>>> User::where('id', 1)->update(['name' => 'John Mass Updated'])
= 1

// Increment values
>>> User::where('id', 1)->increment('login_count')
= 1

Deleting Records

// Delete a user
>>> $user = User::find(1)
>>> $user->delete()
= true

// Force delete (if using soft deletes)
>>> $user->forceDelete()
= true

// Mass delete
>>> User::where('email', 'like', '%test%')->delete()
= 0

Advanced Database Queries

Complex Where Clauses

>>> use App\Models\Post;

// Multiple conditions
>>> Post::where('is_published', true)
...     ->where('views', '>', 100)
...     ->get()

// OR conditions
>>> Post::where('is_published', true)
...     ->orWhere('user_id', 1)
...     ->get()

// Date queries
>>> Post::whereDate('created_at', '2024-09-13')->get()
>>> Post::whereBetween('created_at', ['2024-09-01', '2024-09-30'])->get()
>>> Post::whereYear('created_at', 2024)->get()

// Relationship queries
>>> Post::whereHas('comments', function($query) {
...     $query->where('is_approved', true);
... })->get()

Eager Loading Relationships

// Load relationships to avoid N+1 queries
>>> $posts = Post::with('user', 'tags', 'comments')->get()

// Check loaded relationships
>>> $posts->first()->user
>>> $posts->first()->tags
>>> $posts->first()->comments

Real-World Tinker Scenarios

Scenario 1: Testing Model Relationships

>>> $user = User::first()
>>> $user->posts
= Illuminate\Database\Eloquent\Collection {#4701
    all: [],
  }

>>> $user->posts()->create([
...     'title' => 'My First Post',
...     'content' => 'This is the content of my first post.',
...     'is_published' => true
... ])
= App\Models\Post {#4702
    title: "My First Post",
    content: "This is the content of my first post.",
    is_published: true,
    user_id: 1,
    updated_at: "2024-09-13 10:25:30",
    created_at: "2024-09-13 10:25:30",
    id: 1,
  }

>>> $post = Post::first()
>>> $post->user
= App\Models\User {#4703
    id: 1,
    name: "John Updated",
    email: "john@teachyleaf.com",
    ...
  }

Scenario 2: Testing Email Functionality

>>> use Illuminate\Support\Facades\Mail;
>>> use App\Mail\WelcomeEmail;

// Test email without actually sending
>>> Mail::fake()

// Check if email would be sent
>>> Mail::assertNothingSent()

// Test email sending
>>> $user = User::first()
>>> Mail::to($user)->send(new WelcomeEmail($user))
>>> Mail::assertSent(WelcomeEmail::class)

Scenario 3: Testing File Storage

>>> use Illuminate\Support\Facades\Storage;

// Fake storage for testing
>>> Storage::fake('local')

// Test file operations
>>> Storage::put('test.txt', 'Hello Tinker!')
= true

>>> Storage::get('test.txt')
= "Hello Tinker!"

>>> Storage::exists('test.txt')
= true

Scenario 4: Testing Queue Jobs

>>> use Illuminate\Support\Facades\Queue;
>>> use App\Jobs\ProcessPodcast;

// Fake queue for testing
>>> Queue::fake()

// Dispatch a job
>>> ProcessPodcast::dispatch($podcast)

// Assert job was pushed
>>> Queue::assertPushed(ProcessPodcast::class)

Working with Collections in Tinker

Collection Operations

>>> $users = User::all()
>>> $users->pluck('name')
= Illuminate\Support\Collection {#4710
    all: [
      "John Updated",
      "Jane Smith",
    ],
  }

>>> $users->where('id', 1)->first()
>>> $users->sortBy('created_at')
>>> $users->filter(fn($user) => strlen($user->name) > 5)

// Transform collection
>>> $users->map(fn($user) => [
...     'id' => $user->id,
...     'name' => strtoupper($user->name),
...     'email' => $user->email
... ])

Advanced Collection Methods

>>> $numbers = collect([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

>>> $numbers->sum()
= 55

>>> $numbers->avg()
= 5.5

>>> $numbers->chunk(3)
>>> $numbers->groupBy(fn($n) => $n % 2 == 0 ? 'even' : 'odd')

// Working with key-value pairs
>>> $data = collect(['name' => 'John', 'age' => 30, 'city' => 'New York'])
>>> $data->only(['name', 'age'])
>>> $data->except('city')

Testing Application Logic

Testing Service Classes

>>> use App\Services\UserService;

>>> $userService = app(UserService::class)
>>> $user = $userService->createUser([
...     'name' => 'Test User',
...     'email' => 'test@teachyleaf.com',
...     'password' => 'secret'
... ])

Testing Form Requests and Validation

>>> use Illuminate\Http\Request;
>>> use App\Http\Requests\StorePostRequest;

// Test validation rules
>>> $request = new Request([
...     'title' => 'Test Post',
...     'content' => 'This is content'
... ])

>>> $formRequest = StorePostRequest::createFrom($request)
>>> $formRequest->setContainer(app())
>>> $formRequest->validateResolved()

Testing Authentication

>>> use Illuminate\Support\Facades\Auth;

// Login a user
>>> $user = User::first()
>>> Auth::login($user)
>>> Auth::check()
= true

>>> Auth::user()
= App\Models\User {#4715
    id: 1,
    name: "John Updated",
    ...
  }

// Logout
>>> Auth::logout()
>>> Auth::check()
= false

Advanced Tinker Techniques

Multi-line Code Execution

>>> $result = function() {
...     $users = User::all();
...     return $users->map(function($user) {
...         return [
...             'id' => $user->id,
...             'name' => $user->name,
...             'email' => $user->email
...         ];
...     });
... }
>>> $result()

Using Tinker with Custom Code

// Create and test a custom function
>>> function calculateReadingTime($content) {
...     $wordCount = str_word_count(strip_tags($content));
...     return ceil($wordCount / 200);
... }
>>> calculateReadingTime("This is a sample blog post content.")
= 1

// Test a class method
>>> class Calculator {
...     public function add($a, $b) {
...         return $a + $b;
...     }
... }
>>> $calc = new Calculator
>>> $calc->add(5, 3)
= 8

Database Seeding with Tinker

// Create multiple test records quickly
>>> foreach(range(1, 10) as $i) {
...     User::create([
...         'name' => "Test User $i",
...         'email' => "user$i@teachyleaf.com",
...         'password' => bcrypt('password')
...     ]);
... }

Practical Tinker Workflows

Workflow 1: Debugging a Specific Issue

// Let's say you have an issue with a specific user's posts
>>> $user = User::where('email', 'problem@example.com')->first()
>>> $user->posts->load('comments', 'tags')
>>> $user->posts->each(function($post) {
...     echo "Post: {$post->title}\n";
...     echo "Comments: {$post->comments->count()}\n";
...     echo "Tags: {$post->tags->pluck('name')->implode(', ')}\n";
...     echo "---\n";
... })

Workflow 2: Testing API Responses

>>> use Illuminate\Http\Request;
>>> use App\Http\Controllers\Api\PostController;

>>> $controller = app(PostController::class)
>>> $request = Request::create('/api/posts', 'GET')
>>> $response = $controller->index($request)
>>> $response->getData()

Workflow 3: Database Maintenance

// Clean up test data
>>> User::where('email', 'like', '%test%')->delete()

// Update multiple records
>>> Post::where('is_published', false)
...     ->where('created_at', '<', now()->subMonth())
...     ->update(['status' => 'archived'])

// Check database size/statistics
>>> User::count()
>>> Post::count()
>>> Comment::count()
>>> Post::groupBy('is_published')->selectRaw('is_published, count(*) as count')->get()

Tinker Productivity Tips

1. Use Aliases for Frequently Used Classes

>>> alias User = App\Models\User
>>> alias Post = App\Models\Post
>>> User::first()

2. Save Common Snippets

Create a .psysh.php file in your project root:

<?php
// .psysh.php

return [
    'aliases' => [
        'User' => App\Models\User::class,
        'Post' => App\Models\Post::class,
        'Comment' => App\Models\Comment::class,
    ],
    'defaultIncludes' => [
        __DIR__ . '/vendor/autoload.php',
    ],
];

3. Use Tinker with Specific Context

// Set up application context
>>> config(['app.name' => 'TeachyLeaf Tinker Test'])
>>> app()->bind('current_user', fn() => User::first())

4. Debugging Complex Objects

// Instead of just echoing, use these techniques:
>>> $user = User::with('posts.tags')->first()
>>> dump($user->toArray())  // See all attributes
>>> $user->getRelations()   // See loaded relationships
>>> get_class_methods($user) // See available methods

Common Tinker Patterns for Development

Pattern 1: Rapid Prototyping

// Test a new feature idea quickly
>>> $idea = "What if we add a featured posts section?"
>>> $featuredPosts = Post::where('is_featured', true)
...     ->with('user')
...     ->orderBy('featured_at', 'desc')
...     ->limit(5)
...     ->get()
>>> $featuredPosts->count()

Pattern 2: Data Migration Testing

// Test a data migration before running it
>>> $usersToUpdate = User::whereNull('timezone')->get()
>>> $usersToUpdate->count()
>>> $usersToUpdate->each(function($user) {
...     $user->timezone = 'UTC';
...     // Don't save - just testing
... })

Pattern 3: Performance Testing

// Test query performance
>>> $start = microtime(true)
>>> $posts = Post::with(['user', 'comments', 'tags'])->get()
>>> $end = microtime(true)
>>> echo "Query took: " . ($end - $start) . " seconds"

Security Considerations

Production Tinker Safety

# Never run these in production without caution:
php artisan tinker --env=production

# Better approach for production:
# 1. Create a specific testing route
# 2. Use database transactions
# 3. Have backups ready

Safe Tinker Practices

// Always use transactions for destructive operations
>>> DB::beginTransaction();
>>> try {
...     // Your risky operations here
...     DB::commit();
... } catch (Exception $e) {
...     DB::rollback();
...     echo "Error: " . $e->getMessage();
... }

Common Interview Questions & Answers

1. What is Laravel Tinker and why is it useful?

Tinker is a REPL tool that allows interactive interaction with Laravel applications. It's useful for testing code snippets, debugging, database queries, and learning Laravel features without creating full application structures.

2. How do you test Eloquent relationships in Tinker?

You can load models with their relationships using with(), then access the related data directly. For example: $user = User::with('posts.comments')->first() then $user->posts->first()->comments.

3. What's the difference between User::all() and User::get() in Tinker?

They're identical - both return all records. all() is a static method that calls get() internally. The choice is mostly stylistic preference.

4. How can you test email functionality without actually sending emails?

Use Mail::fake() to mock the mail system, then use Mail::assertSent() to verify emails would be sent without actually delivering them.

5. What precautions should you take when using Tinker in production?

Always backup your database first, use database transactions for destructive operations, and avoid running Tinker on production unless absolutely necessary. Consider using specific testing routes instead.

6. How do you exit Tinker?

You can use exit, quit, or Ctrl + D to exit a Tinker session.

Tinker Command Cheat Sheet

# Basic Tinker
php artisan tinker
php artisan tinker --execute="echo 'Hello'"

# Common Tinker Operations
>>> User::all()                          # Get all users
>>> User::find(1)                        # Find user by ID
>>> User::where('active', true)->get()   # Conditional query
>>> $user->posts                         # Access relationship
>>> Post::with('user')->get()            # Eager loading
>>> DB::table('users')->count()          # Raw query
>>> exit                                 # Exit Tinker

Troubleshooting Common Tinker Issues

Issue: "Class not found"

// Solution: Import the class
>>> use App\Models\User
>>> User::first()

Issue: "MassAssignmentException"

// Solution: Use fillable attributes or forceFill
>>> $user = new User
>>> $user->forceFill([...])->save()

Issue: Tinker not reflecting code changes

# Solution: Clear compiled classes
php artisan clear-compiled
composer dump-autoload

Now you're equipped with the power of Laravel Tinker to test, debug, and explore your application with incredible speed and efficiency! In our next post, we'll dive into Laravel Eloquent Relationships: Demystifying One-to-Many and Many-to-One to master database relationships in Laravel.