Supercharge Your Laravel Tinker Workflow

Caleb Porzio
7 minute read

Laravel's command line tool is called "Artisan," and it comes with a few powerful features out of the box. Tinker, accessed via php artisan tinker, is arguably my favorite Artisan command. It speeds up my workflow and allows me to interact with my application in ways I would have never thought possible.

Tinker is a REPL (read-eval-print loop). A REPL gives you a prompt that allows you to interact with your application using your language's native syntax (in this case, PHP) in a command-line style. It’s commonly used for making simple database changes and testing out new ideas and workflows.

Although some of the value Tinker provides is clear at first glance, it also has loads of hidden and exciting features available out-of-the-box. Let’s walk through and take a look at some ways you can super-charge your Tinker workflow.

Behind the scenes

Before we get started, it’s important to know a little bit about how Tinker is implemented in Laravel. It's a wrapper for a PHP package called PsySH, and you can find more information about its features at its web site and the PsySH Github repo.

Since Laravel 5.4, Tinker has been pulled into Laravel as a separate Composer dependency, which you can explore here: GitHub - laravel/tinker.

Basic Tinker usage

Reading and manipulating the models in your app seems to be the most common use case for me. Commands like Post::all() are great for exploring the contents of your database without having to create a "test" route and dd(Post::all()). MySQL GUIs are great for direct database manipulation, but lack functionality defined inside the application, like accessors and mutators.

When writing code, I'm often unsure about the syntax of a particular library or PHP feature. Tinker makes it easy to try out bits of code much faster than executing a script and waiting for a an error, or worse, not getting one! I often find myself doing things like collect(['boots', 'cats', 'boots'])->unique() to verify the functionality of a Laravel collection method.

A better, more interactive, dd()

One common debugging workflow many Laravel developers use is the following: insert a dd() (dump and die) somewhere in your code; refresh the page; repeat. This process certainly gets the job done, but it can be limiting and repetitive. One alternative solution is to set up xDebug, but Tinker provides another option if you're trying to debug code you're running from the command line (e.g. in tests):

By replacing your calls to dd() with a special snippet of code: eval(\Psy\sh());, we'll get a Tinker prompt that drops us right into the application at that specific point in execution. It’s kind of like being able to pause time and play with the frozen environment. See it in action:

A Better dd()

Pro Tip: Manually typing / remembering eval(\Psy\sh()); will get old fast, so I recommend creating a snippet in your IDE. I called mine tinker.

Tinker Snippet

A quick look in the docs reveal what is actually happening when we type eval(\Psy\sh()):

eval(\Psy\sh());
// Is equivalent to:
extract(\Psy\Shell::debug(get_defined_vars(), $this));
// Notice `$this` is automatically bound.

You’ll notice subtle differences when accessing Tinker through eval(\Psy\sh()) instead of php artisan tinker. Two missing features worth noting are auto-completion, and object casting. Laravel’s object casters tell PsySh how to render Collection, Model, and Application objects nicely.

Tinker without Laravel conveniences

Pro Tip: Tinker shells can accidentally run within each other and become difficult to reason about. If you try to exit, q, or quit, and find yourself in another Tinker shell, simply use ctrl+c to exit the process.

Speeding up your Laravel Dusk workflow

Laravel Dusk is Laravel's package for testing the UI of your app in a browser. It allows you to test Javascript behavior you otherwise wouldn't be able to in other forms of testing. To learn more, check out the docs: Laravel Dusk

The nature of browser testing makes debugging issues in your tests time consuming given you have to run a lengthy browser interaction to then discover there was a problem. Fortunately, the eval(\Psy\sh()); snippet desribed earlier performs similarly for browser tests.

By placing eval(\Psy\sh()); in your Dusk test, you can interact with your browser test in real time. Dusk pauses the execution of your test and drops you into a Tinker shell where you can manipulate the currently open browser window. How cool is that? Take a look:

Tinker inside Dusk Test

Easy access to the PHP docs

Tinker offers another command, doc, that is a remedy for another common pattern:

google PHP function -> scroll past the W3 schools link -> click the PHP.net link.

Tinker doc command for PHP docs

Follow the guide here to install the PHP Docs sqlite file. Now you can use the doc command as a quick reference for common questions like function argument order.

The doc command also provides run-time documentation on objects, classes, and methods. This can saves many visits to the Laravel docs.

Tinker doc command for objects

Easter Egg: rtfm is an alias for the doc command. 😂

Other commands you might not know were available in Tinker:

  • trace - prints a full stack trace.
  • history - lists the command history. Also, it has handy options for searching history (—grep), and replaying (—replay) the history.
  • whereami - prints your current execution location (file and line number). Tinker whereami command
  • throw-up - re-throws the last thrown exception.
  • q, quit, exit - closes the Tinker shell.

Each command can be explained by typing: [command] -help into the Tinker prompt. Many commands have hidden features and aliases you can discover via the —help option. Feel free to explore!

Pro Tip: You can also execute namespace and use statements from Tinker:

Tinker namespace command

Tailoring Tinker with custom config

PsySH supports custom configuration through a config file stored in ~/.config/psysh/config.php. However, I personally prefer storing my configuration within my project for version control purposes. Fortunately PsySH looks in the environment for a variable PSYSH_CONFIG for a config path as well, allowing you to store one wherever you please.

I chose to store mine in the root directory of my Laravel app, next to composer.json, package.json, etc… and specify it in my .env file:

PSYSH_CONFIG=tinker.config.php

Take a look at the PsySH Configuration Docs for a list of available configuration options.

Here is a gist of my tinker.config.php: tinker.config.php · GitHub

Including PHP files at startup

The php artisan tinker command accepts an optional file path, which can be used to load helpful configurations or perform specific logic before getting inside the Tinker shell.

For example, if I create a bootstrap-tinker.php file in my root directory I can then run php artisan tinker bootstrap-tinker.php, and that bootstrap-tinker.php file will be run before I’m dropped in the shell. Any variables defined in the include will be made available in the running shell.

Pain Point: Typing App\Models\Post by hand.

Personally, writing App\Models\Post::create instead of being able to just write Post::create is a grave inconvenience. Here are a few options to ease the pain:

  1. namespace App\Models will allow you to write Post without the full namespace. This is not ideal because every time you restart tinker you have to re-type it.
  2. Including a file at startup (either in a config file or a custom include) that defines a set of class aliases. For example: class_alias(‘App\\Models\\Post’, ‘Post’);. The only issue here is that you will have to manually add aliases every time you add a new model.
  3. My preferred solution is to write a script in the config file that automatically registers Model aliases. It does so by looking through the app directory for classes that extend Illuminate\Database\Eloquent\Model.php and registering aliases for them. Feel free to take a look at my tinker.config.php file to see my implementation.

Takeaways

  • Take advantage of Tinker’s realtime / breakpoint debugging (eval(\Psy\sh())) next time you reach for dd().
  • Run doc array_intersect instead of googling PHP array_intersect.
  • Tailor your workflow with a custom, version controlled config file.

Tinker is crazy amounts of awesome, and you deserve to know all its capabilities.

Have fun!