Testing uploads in Laravel
Testing @spatie_be 's media library with the new #laravel upload fakes ? Trust, but verify ? #testing pic.twitter.com/Ss98rGloRf
— Christoph Rumpel (@christophrumpel) July 3, 2017
Read more [twitter.com]
Testing @spatie_be 's media library with the new #laravel upload fakes ? Trust, but verify ? #testing pic.twitter.com/Ss98rGloRf
— Christoph Rumpel (@christophrumpel) July 3, 2017
Read more [twitter.com]
Browsershot is a package that can easily convert any webpage into a image. Under the hood the conversion is made possible new headless options in Chrome 59. In this post I'd like to show you how you can use Browsershot v2. Here's a quick example of how it can be used:…
Join 9,500+ smart developers
Get my monthly newsletter with what I learn from running Spatie, building Oh Dear, and maintaining 300+ open source packages. Practical takes on Laravel, PHP, and AI that you can actually use.
No spam. Unsubscribe anytime. You can also follow me on X.
"If you are one of the *millions* who have downloaded a Spatie package, then you understand the quality that Freek aims for. The newsletter is of the same quality of any Spatie package you're using."
Tinker is probably one of my most used Artisan commands. A minor annoyance is that it can be quite bothersome having to type the fully qualified classname to do something simple.
Today we release a new package called laravel-tinker-tools. When fully installed let's you use the short class names in a tinker session:
This magic does not only work with models but with every class in your Laravel app.
There are a few non standard steps you need to take in order to install the package.
First, pull in the package like you normally would:
composer require spatie/laravel-tinker-tools
Next, create a file named .psysh.php in the root of your Laravel app with this content:
<?php
\Spatie\TinkerTools\ShortClassNames::register();
Finally, dump the optimized version of the autoloader so autoload_classmap.php gets created.
composer dump-autoload -o
And with that all out of the way you can use short class names in your tinker session.
When you use a class that hasn't been loaded in yet, PHP will call the registered autoloader functions. Such autoloader functions are responsible for loading up the requested class. In a typical project Composer will register an autoloader function that can include the file where the class is stored in.
Composer has a few ways to locate the right files. In most cases it will convert the fully qualified class name to a path. For example, when using a class App\Models\NewsItem Composer will load the file in app/Models/NewsItem.php. It's a bit more complicated behind the scenes but that's the gist of it. To make the process of finding a class fast, Composer caches all the fully qualified classnames and their paths in the generated autoload_classmap.php, which can be found in vendor/composer.
Now, to make this package work, \Spatie\TinkerTools\ShortClassNames will read Composer's autoload_classmap.php and convert the fully qualified class names to short class names. The result is a collection that's being kept in the $classes property
Our class will also register an autoloader. When you use NewsItem in your code. PHP will first call Composer's autoloader. But of course that autoloader can't find the class. So the autoloader from this package comes next. Our autoloader will use the aforementioned $classes collection to find to fully qualified class name. It will then use class_alias to alias NewsItem to App\Models\NewsItem.
Now you might wonder what'll happen it there are more classes with the same name in different namespaces? E.g. App\Models\NewsItem, Vendor\PackageName\NewsItem. Well, autoload_classmap.php is sorted alphabetically on the fully qualified namespace. So App\Models\NewsItem will be used and not Vendor\PackageName\NewsItem.
Because App starts with an "A" there's a high chance that, in case of a collision, a class inside your application will get picked. Currently there are no ways to alter this. I'd accept PRs that make this behaviour customizable.
There are a lot of other options that can be set in tinker.config.php. Learn all the options by reading the official psysh configuration documentation. Caleb Porzio's excellent blogpost "Supercharge Your Laravel Tinker Workflow" is an excellent read as well. This package was inspired by that blogpost
If you want to run a single line of code tinker can be a bit of overkill. You must start up tinker, type the code, press enter, and quit tinker. Our laravel-artisan-dd package contains an Artisan command that can dump anything from the commandline. No need to start and quit tinker anymore.
We've updated the package so you can make use of short class names too. Here's how you can dump a model using a minimal amount of keystrokes:
laravel-tinker-tools and laravel-artisan-dd aren't the only packages we've made that make developing Laravel apps easier.
Be sure to take a look at these ones at well:
Need even more stuff? The open source section on our company website lists everything we've released previously: https://spatie.be/en/opensource
All our packages are MIT-licensed. But if you use our stuff and want to make us happy, we highly appreciate a postcard from your hometown. This suggestion is mentioned in all readme's of our packages We've been asking for postcards for quite some time now and have built up a nice collection. Today,…
If you want to know how a visitor got on your site you can check the referer request header. Yeah, it's misspelled. That header contains the url of the previously visited page. Unfortunately browsers will fill that header regardless of the previous url was an internal or external one. So after the first click on an internal link you won't know anymore on which site a visitor was on previously.
Our new laravel-referer package aims to fix that problem. Once the package is installed it will remember the original referer in session. So even after a users clicks around on your site, you are still able to detect which site he or she visited previously.
Because users are also often tracked using UTM Codes the package will also remember the utm_source query parameter.
The easiest way to retrieve the referer is by just resolving it out of the container:
use App\Spatie\Referer\Referer;
$referer = app(Referer::class)->get(); // 'google.com'
Or you could opt to use Laravel's 5.4 fancy new automatic facades:
use Facades\Spatie\Referer\Referer;
$referer = Referer::get(); // 'google.com'
To know more take a look at the readme of the package on GitHub.
Today we released a new package called image that makes manipulation images in PHP extremely easy. In this post I'd like to explain why we built it and how it can be used. Manipulating images in PHP To manipulate images in PHP there are already a lot of options. You can go hardcore and use the Gd or…
Jordi Boggiano and Nils Adermann, creators of Composer, have recently released a paid version of Packagist. The service aims to make managing private packages a breeze.
Private Packagist aims to remove all these hurdles for businesses to finally make working with Composer as convenient as it should be. Being a hosted service, setting up your own Composer package repository on Private Packagist is done with a few clicks. No matter if your private source code is hosted on GitHub, GitLab, Bitbucket, any of their on-premise solutions, or in any other Git, Mercurial, or Subversion repository, Private Packagist can immediately access your code after setting up your credentials to make it available for installation through Composer.
https://medium.com/packagist/introducing-private-packagist-492553d10660
If you're not afraid to get your hands dirty you could, instead of using Private Packagist, choose to use Satis. This tool is also written by Jordi & Nils. Laravelista has posted this great tutorial to get you started with the tool.
At Spatie we have set up a satis server to register packages that are intended to only be used in our own projects.
A few weeks ago we released our uptime and ssl certificate monitor. It's written in PHP and distributed as a Laravel package. If you're familiar with Laravel that's all fine, but if you have no experience with that (kick ass) framework, it's a bit difficult to get started with using our uptime…
Schema.org is a vocabulary of microdata markup that aims to make it easer for search crawlers to understand what's on a webpage. The vocabulary is very extensive: here's the official list of things that can be described with Schema.org. This article on Tutsplus explains schema.org and structured…
Today a new major version of our homegrown crawler was released. The crawler is used to power our http-status-check, laravel-sitemap and laravel-link-checker packages. A new major feature is the greatly improved crawling speed. This was accomplished by leveraging multiple concurrent requests. Let's…
Last friday Taylor Otwell tweeted an easy to use memoization function called once: Wanted a slick way to generalize class method memoization. Y'all don't even want to know how it works. ? ? pic.twitter.com/xRJAY1C14y— Taylor Otwell (@taylorotwell) November 4, 2016 Taylor was kind…
There are a lot of quality tagging packages out there. Most of them offer the same thing: creating tags, associating them with models and some functions to easily retrieve models with certain tags. But in our projects at Spatie we need more functionality. Last week we released our own - very…
For several projects and other packages we need to manipulate URL's. Instead of coding the same URL class over and over again, we extracted URL manipulation to it's own package. Here are some code examples on how you can use it. $url = Url::fromString('https://spatie.be/opensource'); echo…
In a vanilla Laravel installation you can use language files to localize your app. The Laravel documentation refers to any string in a language file as a language line. You can fetch the value of any language line easily with Laravel's handy trans-function. trans('messages.welcome'); //…
My colleague Seb needed to convert some color values in PHP. He looked around for some good packages, but there weren't any that fit the bill. So guess what he did? He just created a new package called spatie/color. Here's what it can do: $rgb = Rgb::fromString('rgb(55,155,255)'); echo…
I recently was interviewed by Povilas Korop about the business side of our packages.
Over at Laravel News Eric L. Barnes posted a new tutorial on how he used our Analytics package to create a list of most popular posts. Great stuff!
Here on Laravel News, I wanted to generate a list of the most popular posts for the past seven days and display the results from most popular to least popular.To solve this problem I thought of two solutions. The first is to build my own tracking system so I could keep a count and then use it for ordering. However, that could generate a huge amount of data and it seemed like a solution that an analytics tracking service could handle.
As I was fumbling through the Google Analytics API I found a Laravel Analytics package by Spatie.be that allows you to easily retrieve data from your Google Analytics account and it seemed like the best way to solve this problem. Let’s look at how I used it to generate a list of popular posts here on Laravel News.
https://laravel-news.com/2016/09/most-popular-list-laravel-google-analytics/
PHP offers some functions to work with regular expressions, most notably preg_match, preg_match_all and preg_replace. Unfortunately those functions are a bit hard to use. Take preg_match_all for example, it requires you to pass in an array by reference to get all the matches. When something goes…
My company has released a lot of PHP and Laravel packages. According to the packagist stats they have been downloaded for a little over 700 000 times. Up until now they've all been free. That is going to change. Our packages are now postcardware. This means that from now on you are required to send…
At Spatie we've recently introduced a bot to our Slack chat. We've named him Paolo (after the waiter in our favourite Italian restaurant in Antwerp: La Fontanella Da Enzo). Here's a demo of Paolo (the bot) in action.
Behind the scenes Paolo is powered by a Laravel application that responds to all requests Slack is sending to it. In this post I'd like to explain how you can set up your own Laravel powered Slack bot.
A message in slack that starts with a slash is called a slash command. Whenever you type in a slash command in Slack channel, an http request will be sent to your Laravel app. You have to respond to that command within 3 seconds. Failing to do some will result in an error being displayed in the channel.
After that initial response you're allowed to send multiple delayed responses. But there are some limitations for delayed responses. You may respond up to 5 times within 30 minutes after the user typed in the slash command on the slack channel. Want to know more about slash commands and how they work, then read this excellent article at Slack's API site.
To make responding to Slack a breeze we're going to used the package our team released a few days ago called spatie/laravel-slack-slash-command.
Before you can get started building our Laravel app, you'll need to set up slash command at Slack.com. Head over to the custom integrations page at Slack.com to get started. There click "Slash commands" and on the next page click "Add configuration". On that screen you can choose a name for your Slack command. In our examples we'll use paolo but you can choose anything that Slack allows.
You should now be on a screen that looks like this:
In the url field you should type the domain name of your Laravel app followed by one or more segments. In the screenshot we've added a slack segment. You can choose any segment you want. Normally the token field should already be filled. And that's all you need to do at Slack.com.
The next thing you'll need to do is to install the spatie/laravel-slack-slash-command package. Let's pull it in via Composer.
composer require spatie/laravel-slack-slash-command
Next, you must install the service provider:
// config/app.php
'providers' => [
...
Spatie\SlashCommand\SlashCommandServiceProvider::class,
];
The configuration file of the package can be published with:
php artisan vendor:publish --provider="Spatie\SlashCommand\SlashCommandServiceProvider" --tag="config"
This is the contents of the published config file:
return [
/**
* Over at Slack you can configure to which url the slack commands must be send.
* url here. You must specify that. Be sure to leave of the domain name.
*/
'url' => 'slack',
/**
* The token generated by Slack with which to verify if a incoming slash command request is valid.
*/
'token' => env('SLACK_SLASH_COMMAND_VERIFICATION_TOKEN'),
/**
* The handlers that will process the slash command. We'll call handlers from top to bottom
* until the first one whose `canHandle` method returns true.
*/
'handlers' => [
//add your own handlers here
//this handler will respond with a `Could not handle command` message.
Spatie\SlashCommand\Handlers\CatchAll::class,
],
];
And with that you're ready to respond to http requests coming from Slack.
Whenever a user types in a slash command Slack will send an http request to the Laravel app. Next, our package will go over all classes in the handlers key of the config file from top to bottom until the first one whose canHandle method returns true. A handler is a class that is responsible for receiving a request from slack and sending a response back.
Let's create our first handler. Handlers must extend Spatie\SlashCommand\Handlers\BaseHandler and implement the two abstract methods from that BaseHandler: canHandle and Handle.
Here's an example.
namespace App\SlashCommandHandlers;
use Spatie\SlashCommand\Request;
use Spatie\SlashCommand\Response;
use Spatie\SlashCommand\Handlers\BaseHandler;
class Hodor extends BaseHandler
{
/**
* If this function returns true, the handle method will get called.
*
* @param \Spatie\SlashCommand\Request $request
*
* @return bool
*/
public function canHandle(Request $request): bool
{
return true;
}
/**
* Handle the given request.
*
* @param \Spatie\SlashCommand\Request $request
*
* @return \Spatie\SlashCommand\Response
*/
public function handle(Request $request): Response
{
return $this->respondToSlack("Hodor, hodor...");
}
}
This Hodor class will just respond with Hodor, hodor, ... to every request that is sent to it.
You'll need to register this class in the config file.
// app/config/laravel-slack-slash-command
'handlers' => [
App\SlashCommandHandlers\Hodor::class,
...
],
Let's see that in action.
Let's create a slightly more interesting handler. This one that just repeats the command you've sent to it but only if the text after the command starts with repeat.
namespace App\SlashCommandHandlers;
use Spatie\SlashCommand\Request;
use Spatie\SlashCommand\Response;
use Spatie\SlashCommand\Handlers\BaseHandler;
class Repeat extends BaseHandler
{
public function canHandle(Request $request): bool
{
return starts_with($request->text, 'repeat');
}
public function handle(Request $request): Response
{
$textWithoutRepeat = substr($request->text, 7)
return $this->respondToSlack("You said {$textWithoutRepeat}");
}
}
Let's register this handler as well.
// app/config/laravel-slack-slash-command
'handlers' => [
App\SlashCommandHandlers\Repeat::class,
App\SlashCommandHandlers\Hodor::class,
...
],
If you type in /paolo repeat Hi, everybody in a slack channel now, you'll get a response Hi, everybody back. When you type in /poalo bla bla bla you'll get a response Hodor, hodor... because the Hodor handler is the first one which canHandle-method returns true.
Notice that Spatie\SlashCommand\Request being past in canHandle and handle? It contains all data that's being passed by Slack to our Laravel app. These are it's most important properties:
By default the response will be sent to the user who typed in the original message. If you want the response to be visible to all users in the channel you can do this:
public function handle(Request $request): Response
{
return $this
->respondToSlack("Hodor, hodor...")
->displayResponseToEveryoneOnChannel();
}
There are also many formatting options. Take a look at this response on Slack:

$this->respondToSlack()
->withAttachment(Attachment::create()
->setColor('good')
->setText('This is good!')
)
->withAttachment(Attachment::create()
->setColor('warning')
->setText('Warning!')
)
->withAttachment(Attachment::create()
->setColor('danger')
->setText('DANGER DANGER!')
)
->withAttachment(Attachment::create()
->setColor('#439FE0')
->setText('This was a hex value')
);
There are many more options to format a message. Take a look at Slacks documentation on attachments to learn what's possible.
A console command in Laravel can make use of a signature to set expectations on the input. A signature allows you to easily define arguments and options.
If you let your handler extend Spatie\SlashCommand\Handlers\SignatureHandler you can make use of a $signature and the getArgument and getOption methods to get the values of arguments and options.
Let's take a look at an example.
namespace App\SlashCommandHandlers;
use Spatie\SlashCommand\Request;
use Spatie\SlashCommand\Response;
use Spatie\SlashCommand\Handlers\SignatureHandler;
class SendEmail extends SignatureHandler
{
public $signature = "paolo email:send {to} {message} {--queue}"
public function handle(Request $request): Response
{
$to = $this->getArgument('to');
$message = $this->getArgument('message');
$queue = $this->getOption('queue') ?? 'default';
//send email message...
}
}
Notice that there is no canHandle method present in that class. The package will automatically determine that a command /paolo email:send test@email.com hello can be handled by this class.
Remember that restriction mentioned above about the initial response to a slash command. Your Laravel app only has three seconds to respond otherwise an error message will be shown at Slack. After that initial fast response you're allowed to send 5 more responses in the next 30 minutes for the command. These responses are called "delayed responses". We're going to leverage Laravel's queued jobs to send those delayed responses. Please make sure that you've set up a real queue driver in your app, it needs to be something other than sync.
Imagine you need to call a slow API to get a response for a slash command. Let's first create a handler that will send the initial fast response.
namespace App\SlashCommandHandlers;
use Spatie\SlashCommand\Request;
use Spatie\SlashCommand\Response;
class SlowApi extends BaseHandler
{
public function canHandle(Request $request): bool
{
return starts_with($request->text, 'give me the info');
}
public function handle(Request $request): Response
{
$this->dispatch(new SlowApiJob());
return $this->respondToSlack("Looking that up for you...");
}
}
Notice that we're dispatching a job right before sending a response. Behind the scenes Laravel will queue that job.
This is how that SlowApiJob would look like.
namespace App\SlashCommand\Jobs;
use Spatie\SlashCommand\Jobs\SlashCommandResponseJob;
class SlowApiJobJob extends SlashCommandResponseJob
{
// notice here that Laravel will automatically inject dependencies here
public function handle(MyApi $myApi)
{
$response = $myApi->fetchResponse();
$this
->respondToSlack("Here is your response: {$response}")
->send();
}
}
Notice that, unlike in the Handlers the response is not returned and that send() is called after the respondToSlack-method.
With this in place a quick response Looking that info for you... will be displayed right after the user typed /your-command get me the info. After a little while, when MyApi has done it's job Here is your response: ... will be sent to the channel.
The previous examples of this post were quite silly. You'll probably never going to use to handlers in your bot. Let's review a real life example. Our Poalo bot can lookup dns records for a given domain. This is how that looks like in a Slack channel.
This is the actual class that we use in our bot:
namespace App\SlashCommandHandlers;
use Spatie\SlashCommand\Attachment;
use Spatie\SlashCommand\AttachmentField;
use Spatie\SlashCommand\Handlers\SignatureHandler;
use Spatie\SlashCommand\Request;
use Spatie\SlashCommand\Response;
class Dns extends SignatureHandler
{
protected $signature = 'paolo dns {domain}';
/**
* Handle the given request. Remember that Slack expects a response
* within three seconds after the slash command was issued. If
* there is more time needed, dispatch a job.
*
* @param Request $request
*
* @return Response
*/
public function handle(Request $request): Response
{
$domain = $this->getArgument('domain');
if (empty($domain)) {
return $this->respondToSlack("You must provide a domain name.");
}
$sanitizedDomain = str_replace(['http://', 'https://'], '', strtolower($this->getArgument('domain')));
$dnsRecords = dns_get_record($sanitizedDomain, DNS_ALL);
if (!count($dnsRecords)) {
return $this->respondToSlack("Could not get any dns records for domain {$domain}");
}
$attachmentFields = collect($dnsRecords)->reduce(function (array $attachmentFields, array $dnsRecord) {
$value = $dnsRecord['ip'] ?? $dnsRecord['target'] ?? $dnsRecord['mname'] ?? $dnsRecord['txt'] ?? $dnsRecord['ipv6'] ?? '';
$attachmentFields[] = AttachmentField::create('Type', $dnsRecord['type'])->displaySideBySide();
$attachmentFields[] = AttachmentField::create('Value', $value)->displaySideBySide();
return $attachmentFields;
}, []);
return $this->respondToSlack("Here are the dns records for domain {$domain}")
->withAttachment(Attachment::create()
->setColor('good')
->setFields($attachmentFields)
);
}
}
In order to get home every member of our team needs to bike a bit. That's why we've also created a command to display a rain forecast. This is what happens when /paolo rain is typed in our slack channels.
This is the class responsible for creating that response.
namespace App\SlashCommandHandlers;
use Spatie\SlashCommand\Attachment;
use Spatie\SlashCommand\Handlers\SignatureHandler;
use Spatie\SlashCommand\Request;
use Spatie\SlashCommand\Response;
class Rain extends SignatureHandler
{
protected $signature = 'paolo rain';
/**
* Handle the given request. Remember that Slack expects a response
* within three seconds after the slash command was issued. If
* there is more time needed, dispatch a job.
*
* @param Request $request
*
* @return Response
*/
public function handle(Request $request): Response
{
return $this
->respondToSlack("Here you go!")
->withAttachment(
Attachment::create()->setImageUrl('http://api.buienradar.nl/image/1.0/radarmapbe?width=550')
);
}
}
The spatie/laravel-slack-slash-command package makes is it easy to let a Laravel app respond to a slash command from Slack. If you start using the package, let me know in the comments below what your bot can do. And if you like our package, take a look at this list of Laravel packages we've previously released to see if we've made something that can be of use to you.