So-called callback functions, "callables" or "closures" are used in Laravel very often, so we need to understand all the details behind their syntax.
Take a look at these Laravel examples.
You can provide the route functionality in a callback function without any Controller:
Route::get('/greeting', function () { return 'Hello World';});
You can use Collections to map through data with your custom logic defined in a callback function:
$socialLinks = collect([ 'Twitter' => $user->link_twitter, 'Facebook' => $user->link_facebook, 'Instagram' => $user->link_instagram,])->filter()->map(fn ($link, $network) => '<a href="' . $link . '">' . $network . '</a>') ->implode(' | ');
In Eloquent, you may use the chunk()
method with a closure, too:
use App\Models\Flight;use Illuminate\Database\Eloquent\Collection; Flight::chunk(200, function (Collection $flights) { foreach ($flights as $flight) { // ... }});
So, what are the main things we need to know about them?
When the method is defined with the parameters types as callable
.
So, if you take a look at the Laravel framework core, you will see these examples:
src/Illuminate/Database/Concerns/BuildsQueries.php
public function chunk($count, callable $callback){ // ...
Another one:
src/Illuminate/Collections/Arr.php
public static function map(array $array, callable $callback){ // ...
Also, remember that callable
may be only one of the accepted parameter types. There can be others:
src/Illuminate/Collections/Arr.php
/** * Sort the array using the given callback or "dot" notation. * * @param array $array * @param callable|array|string|null $callback * @return array */public static function sort($array, $callback = null){ return Collection::make($array)->sortBy($callback)->all();}
Let me show you the most typical mistake developers make with closures.
When using some variable in a closure, many starting developers think that variable is accessible in a closure by default, but it isn't.
To access the variable in a closure, it must be added in a use.
Here is a typical example from open-source financial freedom project using database transactions.
app/Actions/Fortify/CreateNewUser.php:
public function create(array $input){ Validator::make($input, [ 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], 'password' => $this->passwordRules(), ])->validate(); return DB::transaction(function () use ($input) { return tap(User::create([ 'name' => $input['name'], 'email' => $input['email'], 'password' => Hash::make($input['password']), ]), function (User $user) { $this->createTeam($user); }); });}
If you forget to add a variable in a use, you will receive a typical error message:
Undefined variable $input
Also, your IDE should inform you about the undefined variable.
When using functions, you can use a shorter version called the arrow function.
use Illuminate\Database\Eloquent\Builder; public function table(Table $table): Table{ return $table ->modifyQueryUsing(fn (Builder $query) => $query->withoutGlobalScopes());}
But sometimes, using short closure can be hard to read, and it isn't that short.
Forms\Components\TextInput::make('name') ->afterStateUpdated(fn (string $operation, $state, Forms\Set $set) => $operation === 'create' ? $set('slug', Str::slug($state)) : null),
Writing it as a normal closure would be more readable in this case.
Forms\Components\TextInput::make('name') ->afterStateUpdated(function(string $operation, $state, Forms\Set $set) { if ($operation === 'create') { $set('slug', Str::slug($state)); } })
There might be places where you need to use some variable in more than one place, but that variable could have a value based on some condition.
In this case, assigning a variable to a closure is called an anonymous function.
In this example from Filament, a variable is assigned based on condition.
packages/support/resources/views/components/grid/column.blade.php:
$getSpanValue = function ($span): string { if ($span === 'full') { return '1 / -1'; } return "span {$span} / span {$span}";};
Then, this variable is used in more than one place.
packages/support/resources/views/components/grid/column.blade.php:
<div {{ $attributes // ... ->style([ "--col-span-default: {$getSpanValue($default)}" => $default, "--col-span-sm: {$getSpanValue($sm)}" => $sm, "--col-span-md: {$getSpanValue($md)}" => $md, "--col-span-lg: {$getSpanValue($lg)}" => $lg, "--col-span-xl: {$getSpanValue($xl)}" => $xl, "--col-span-2xl: {$getSpanValue($twoXl)}" => $twoXl, "--col-start-default: {$defaultStart}" => $defaultStart, "--col-start-sm: {$smStart}" => $smStart, "--col-start-md: {$mdStart}" => $mdStart, "--col-start-lg: {$lgStart}" => $lgStart, "--col-start-xl: {$xlStart}" => $xlStart, "--col-start-2xl: {$twoXlStart}" => $twoXlStart, ]) }}> {{ $slot }}</div>