Speeding Up Laravel Tests with tmpfs (MySQL in RAM)
If your Laravel test suite is painfully slow and does a lot of database work (multi-tenancy, migrations, seeding), the bottleneck is almost…
If you're using Laravel Jetstream for team management, you might want to add custom fields to your teams. However, this isn't as straightforward as you might expect.
Jetstream doesn't publish its controllers to your application. Instead, it uses a pattern of "Actions" - classes that handle specific tasks like creating or updating teams. This approach gives you more flexibility but means you'll need to work with these Action classes to customize your teams.
I'll walk through how to add a website field to teams in Laravel Jetstream. You can use the same approach to add any custom fields you need, whether that's social media links, company information, or team preferences.
Let's break down what we need to do:
Add the new field to the teams database table
Update the Team model
Modify the team creation form
Update the team settings form
Extend the team update logic
Create a new migration to add the website field:
public function up(): void
{
Schema::table('teams', function (Blueprint $table) {
$table->string('website')->nullable();
});
}
Add the new field to the $fillable array in your Team model:
protected $fillable = [
'name',
'website',
'personal_team',
];
Modify your CreateTeamForm.vue component to include the new field:
<div class="col-span-6 sm:col-span-4">
<InputLabel for="website" value="Website" />
<TextInput
id="website"
v-model="form.website"
type="url"
class="block w-full mt-1"
/>
<InputError :message="form.errors.website" class="mt-2" />
</div>
Update the form data in the script section:
const form = useForm({
name: '',
website: '',
});
Similarly, update your UpdateTeamNameForm.vue to include the new field:
<div class="col-span-6 sm:col-span-4">
<InputLabel for="website" value="Website" />
<TextInput
id="website"
v-model="form.website"
type="url"
class="mt-1 block w-full"
:disabled="! permissions.canUpdateTeam"
/>
<InputError :message="form.errors.website" class="mt-2" />
</div>
Initialize the form with the team's existing data:
const form = useForm({
name: props.team.name,
website: props.team.website || '',
});
Modify the UpdateTeamName action class in app/Actions/Jetstream/UpdateTeamName.php:
public function update(User $user, Team $team, array $input): void
{
Gate::forUser($user)->authorize('update', $team);
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
'website' => ['nullable', 'string', 'url', 'max:255'],
])->validateWithBag('updateTeamName');
$team->forceFill([
'name' => $input['name'],
'website' => $input['website'],
])->save();
}
When you submit the team update form, here's exactly what happens behind the scenes:
teams.update routeTeamController handles this request and calls:
app(UpdatesTeamNames::class)->update($request->user(), $team, $request->all());
UpdatesTeamNames which is a contract (interface) defined in JetstreamUpdateTeamName class in app/Actions/Jetstream/UpdateTeamName.phpThis architecture is intentional - Jetstream uses contracts and actions to allow you to customize the implementation without modifying vendor files. The empty UpdatesTeamNames interface in the vendor directory only defines the contract that your application must fulfill, while your local UpdateTeamName class provides the actual implementation.
The pattern shown here can be used to add any custom fields you need to your teams. Common examples include:
Just follow the same steps for each new field:
Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.
Take a look at vroni.com