using-git-worktrees
OBobra/superpowers298k2,631 new stars in the last 7 days
Spatie's PHP, Laravel, JavaScript and Vue coding guidelines and conventions. Use when writing or reviewing PHP, Laravel, JavaScript, or Vue code for Spatie projects or packages. Covers code style, type declarations, docblocks, control flow, naming, routing, controllers, Blade, validation, comments, testing (Pest), package structure, service providers, and Git workflow. Triggers include "follow Spatie guidelines", "Spatie style", "Spatie package", or any code review for Spatie packages and projects.
Apply these guidelines when writing code for Spatie projects or contributing to Spatie packages.
Core principle: write things the way Laravel intended. If Laravel has a documented way to do something, use it. Deviate only with a clear justification.
?Type (short nullable), not Type|null.void return type when a method returns nothing.class MyClass {
public function __construct(
protected string $firstArgument,
protected string $secondArgument,
) {}
}
use Spatie\Url\Url;
/** @return Url */
/** @var string *//**
* @param array<int, MyObject> $myArray
* @param int $typedArgument
*/
function someFunction(array $myArray, int $typedArgument) {}
/** @var Collection|SomeWeirdVendor\Collection */
/** @return array{
first: SomeClass,
second: SomeClass
} */
final by default."Hi, I am {$name}."case Diamonds;use:class MyClass
{
use TraitA;
use TraitB;
}
use statements. Never use inline fully qualified class names such as \Exception or \Illuminate\Support\Facades\Http.$exception instead of $e, $request instead of $r.private constDon't introduce private const. Replace each one:
private static when the methods using it are static.else. Refactor to early returns or ternaries.if statements over && chains.{} brackets.// Happy path last
if (! $user) {
return null;
}
if (! $user->isActive()) {
return null;
}
// Process active user...
// Short ternary
$name = $isFoo ? 'foo' : 'bar';
// Multi-line ternary
$result = $object instanceof Model ?
$object->name :
'A default value';
// Ternary instead of else
$condition
? $this->doSomething()
: $this->doSomethingElse();
// Bad: compound condition with &&
if ($user->isActive() && $user->hasPermission('edit')) {
$user->edit();
}
// Good: nested ifs
if ($user->isActive()) {
if ($user->hasPermission('edit')) {
$user->edit();
}
}
Be very critical about adding comments. They often become outdated and mislead over time. Code should be self-documenting through descriptive variable and function names. Adding a comment should never be the first tactic for making code readable.
Instead of this:
// Get the failed checks for this site
$checks = $site->checks()->where('status', 'failed')->get();
Do this:
$failedChecks = $site->checks()->where('status', 'failed')->get();
media-library.php, pdf-generator.php)'chrome_path' => env('CHROME_PATH'))env() outside config files. Use the config() helper.config/services.php, not a new file./open-source, /front-end-developer)->name('openSource')){newsItem}, {userId})Route::get('open-source', [OpenSourceController::class, 'index'])[Controller::class, 'method'], not the string 'Controller@method'/, except the root //errors, /error-occurrences/error-occurrences/1 over /projects/1/errors/1/error-occurrences/1/errors/1/occurrencesController suffix: PostsControllerindex, create, store, show, edit, update, destroyFavoritePostsController with store and destroy)PerformCleanupControlleropenSource.blade.php)@if($condition)__() for translations, not @lang['required', 'email'], never the pipe form 'required|email'. Array notation is easier to combine with custom rule classes.public function rules() {
return [
'email' => ['required', 'email'],
];
}
Validator::extend('organisation_type', function ($attribute, $value) {
return OrganisationType::isValid($value);
});
Gate::define('editPost', ...)show with viewup methods in migrations. Don't write down methods.delete-old-records)$this->comment('All ok!') at the end.$items->each(function(Item $item) {
$this->info("Processing item id `{$item->id}`...");
$this->processItem($item);
});
$this->comment("Processed {$items->count()} items.");
| Type | Convention | Example |
|---|---|---|
| Controller | Plural + Controller | PostsController |
| Invokable Controller | Action + Controller | PerformCleanupController |
| Model | Singular | Post |
| Job | Action description | CreateUser, SendEmailNotification |
| Event | Tense indicates timing | UserRegistering / UserRegistered |
| Listener | Action + Listener | SendInvitationMailListener |
| Command | Action + Command | PublishScheduledPostsCommand |
| Mailable | Event/action + Mail | AccountActivatedMail |
| Resource | Plural + Resource | UsersResource |
| Enum | Descriptive, no prefix | OrderStatus, BookingType |
env() outside config files'required|email')$fillable instead of $guarded = [] in package models@if ($condition)){} bracketsfinal on classes (Spatie doesn't by default)use statement$e'Controller@method') instead of tuple notationelse where early returns workservices.php)void return type on methods that return nothingdown methods in migrationsprivate const instead of inlining it or using a private propertyLoad these as needed:
references/package-architecture.md. Structure, service providers, contracts, model and config patterns.references/testing-pest.md. Testbench setup, test style, what to test, helpers, composer stack.references/version-control.md. Branch naming, PR workflow, commit conventions.references/laravel-php.md.references/javascript.md.references/new-project-setup.md.OBobra/superpowers298k2,631 new stars in the last 7 days
OBobra/superpowers298k2,631 new stars in the last 7 days
OBobra/superpowers298k2,631 new stars in the last 7 days
MAmattpocock/skills288k10,178 new stars in the last 7 days
MAmattpocock/skills288k10,178 new stars in the last 7 days
MAmattpocock/skills288k10,178 new stars in the last 7 days