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.
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.
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.
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:
Pro Tip: Manually typing / remembering
eval(\Psy\sh());
will get old fast, so I recommend creating a snippet in your IDE. I called minetinker
.
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.
Pro Tip: Tinker shells can accidentally run within each other and become difficult to reason about. If you try to
exit
,q
, orquit
, and find yourself in another Tinker shell, simply usectrl+c
to exit the process.
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 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.
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.
Easter Egg:
rtfm
is an alias for thedoc
command. 😂
trace
- prints a full stack trace.wtf
- shows a few lines of the backtrace of the most recent exception.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).
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
anduse
statements from Tinker:
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
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.
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:
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.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.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.eval(\Psy\sh())
) next time you reach for dd()
.doc array_intersect
instead of googling PHP array_intersect
.Tinker is crazy amounts of awesome, and you deserve to know all its capabilities.
Have fun!
We appreciate your interest.
We will get right back to you.