Guidelines
Conteúdo
- Por quê?
- Regras Gerais
- Nomes
- Rotas
- Rota de API
- Nullable e União de Tipos
- Tipagem
- Use constructor property promotion
- Traits
- Strings
- Happy path
- Evite o uso do else
- Compound ifs
- Comentários
- Espaço em branco
- Configurações
- Artisan
- Controllers
- Views
- Validação
- Blade Templates
- Authorization
- Traduções
- Nomeando as classes
Por quê?
Como o Laravel fornece muitas features implícitas e/ou convencionadas, temos como objetivo, dar continuidade nesse fluxo.
Se existir algum jeito documentado de se chegar em algum lugar, faça deste jeito.
Caso monte algo diferente do que temos documentado, seja claro na sua justificativa do motivo de não seguir o padrão.
Regras Gerais
O estilo deve seguir o PSR-1, PSR-2 e PSR-12.
Nomes
NÃO utilize abreviações em nomes de variáveis, métodos, classes, etc.
Utilize:
- Inglês para variáveis, métodos, classes, tabelas, colunas, etc.
- camelCase para variáveis, métodos e rotas.
- snake_case para tabelas e colunas.
- PascalCase para classes.
- kebab-case para arquivos, diretórios e urls.
- UPPERCASE para constantes.
- Singular para controllers.
Rotas
Seja consistente nos nomes faça o vínculo com a url e a Controller
Utilize:
- Inglês para urls privadas.
- Português para as urls publicas.
- Plural para as urls e nome das rotas.
- camelCase para o nome das rotas e parâmetros.
https://fmd.ag/sobre-nos
https://fmd.ag/dashboard/profile
Não pluralize palavras que não devem ser pluralizadas. (Ex: blog -> blogs, faq -> faqs, etc.)
Faça
use Agenciafmd\Frontend\Http\Controllers\ArticleController;
Route::get('/artigos', [ArticleController::class, 'index'])
->name('frontend.articles.index');
Route::get('/artigos/{frontArticle}', [ArticleController::class, 'show'])
->name('frontend.articles.show');
Faça
use Agenciafmd\Frontend\Http\Controllers\Dashboard\IndexController;
use Agenciafmd\Frontend\Http\Controllers\Dashboard\ProfileController;
use Agenciafmd\Frontend\Http\Controllers\Dashboard\ArticleController;
Route::get('/dashboard', [IndexController::class, 'index'])
->name('frontend.dashboard.index');
Route::get('/dashboard/profile', [ProfileController::class, 'index'])
->name('frontend.dashboard.profile.index');
Route::put('/dashboard/profile', [ProfileController::class, 'update'])
->name('frontend.dashboard.profile.update');
Route::get('/dashboard/articles', [DashboardArticleController::class, 'index'])
->name('frontend.dashboard.articles.index');
Route::post('/dashboard/articles', [DashboardArticleController::class, 'store'])
->name('frontend.dashboard.articles.store');
Rotas do Livewire.
Note que o my-account foi omitido para não deixar a url /profile órfã
Faça
use Agenciafmd\Frontend\Livewire\Pages;
Route::get('/dashboard', Pages\Dashboard::class)
->name('frontend.pages.dashboard');
Route::get('/dashboard/profile', Pages\Dashboard\Profile\MyAccount::class)
->name('frontend.pages.dashboard.profile.myAccount');
Route::get('/dashboard/profile/change-password', Pages\Dashboard\Profile\ChangePassword::class)
->name('frontend.pages.dashboard.profile.changePassword');
Linke o caminho da rota com a controller e o método.
Faça
Route::get('customers/me', [CustomerController::class, 'me'])
->name('customer.me');
Não faça
Route::get('customers/me', 'CustomerController@me')
->name('customer.me');
Utilize o agrupamento apenas para middlewares.
Faça
Route::middleware('auth:api')
->group(function () {
Route::get('customers/me', [CustomerController::class, 'me'])
->name('customer.me');
Route::put('customers/me', [CustomerController::class, 'update'])
->name('customer.update');
Route::delete('customers/me', [CustomerController::class, 'destroy'])
->name('customers.destroy');
});
Não faça
Route::prefix('customers')
->name('customers.')
->group(function () {
Route::get('me', [CustomerController::class, 'me'])
->middleware('auth:api')
->name('me');
Route::put('me', [CustomerController::class, 'update'])
->middleware('auth:api')
->name('update');
Route::delete('me', [CustomerController::class, 'destroy'])
->middleware('auth:api')
->name('destroy');
});
Deixe os verbos no inicio e o nome da rota por último.
Faça
Route::get('/', [HomeController::class, 'index'])->name('home');
Route::get('open-source', [OpenSourceController::class, 'index'])->name('openSource');
Não faça
Route::name('home')->get('/', [HomeController::class, 'index']);
Route::name('openSource')->get([OpenSourceController::class, 'index']);
Os parâmetros devem estar em camelCase e no singular.
Faça
Route::get('articles/{frontArticle}', [ArticleController::class, 'show']);
Não faça
Route::get('articles/{articles}', [ArticleController::class, 'show']);
A route url should not start with / unless the url would be an empty string. As rotas não devem começar com / a menos que a url seja uma string vazia.
Faça
Route::get('/', [HomeController::class, 'index']);
Route::get('open-source', [OpenSourceController::class, 'index']);
Não faça
Route::get('', [HomeController::class, 'index']);
Route::get('/open-source', [OpenSourceController::class, 'index']);
Rota de API
Utilize:
- Inglês para todas as urls.
- Plural para os resources
- kebab-case para os resources.
Evite muitos níveis de nesting. Rotas muito profundas são mais difíceis de gerenciar. Limitando o nesting, você mantém a simplicidade e melhora a legibilidade.
Faça
/customers/1
/customers/1/orders
Não faça
/projects/1/errors/1/error-occurrences/1
Nullable e União de Tipos
Sempre que possível, utilizar a short notation para o nullable ao invés de união de tipos.
Faça
public ?string $variable;
Não faça
public string|null $variable;
Tipagem
Seja consistente com a tipagem dos métodos, variáveis e parâmetros.
Faça
public string $string;
public ?string $nullableString;
public array $array;
Faça
use Illuminate\Database\Eloquent\Builder
public function scopeIsActive(Builder $builder): void
{
$builder->where('is_active', 1);
}
Não faça
public $string;
public array|Collection $arrayOrCollection;
Não faça
public function scopeIsActive($builder)
{
$builder->where('is_active', 1);
}
Use constructor property promotion
Use o constructor property promotion
se todas as propriedades puderem ser promovidas.
Para torná-lo legível, coloque cada um em uma linha própria.
Use uma vírgula após a última propriedade.
Faça
class MyClass {
public function __construct(
protected string $firstArgument,
protected string $secondArgument,
) {}
}
Não faça
class MyClass {
protected string $secondArgument
public function __construct(protected string $firstArgument, string $secondArgument)
{
$this->secondArgument = $secondArgument;
}
}
Traits
Use as traits inline, em vez de em várias linhas e ordene-as alfabeticamente.
Faça
class MyClass
{
use TraitA, TraitB;
}
Não faça
class MyClass
{
use TraitA;
use TraitB;
}
Strings
Prefira a interpolação de strings ao invés do operador ´.´.
Faça
$greeting = "Hi, I am {$name}.";
Não faça
$greeting = 'Hi, I am ' . $name . '.';
Happy path
O ideal é que todas as condições que possam causar um erro sejam verificadas primeiro, e o restante do código seja executado se tudo estiver correto. Isso permite que o happy path fique em uma parte não recuada da função, o que o torna mais legível.
Faça
if (! $goodCondition) {
throw new Exception;
}
Não faça
if ($goodCondition) {
// do work
}
throw new Exception;
Evite o uso do else
Em geral, else deve ser evitado porque torna o código menos legível. Geralmente, pode ser refatorado usando early returns. Isso também fará com que o happy path seja executado por último, o que é o esperado.
Faça
if (! $conditionA) {
// condition A failed
return;
}
if (! $conditionB) {
// condition A passed, B failed
return;
}
// condition A and B passed
Não faça
if ($conditionA) {
if ($conditionB) {
// condition A and B passed
}
else {
// condition A passed, B failed
}
}
else {
// condition A failed
}
Compound ifs
Em geral, ifs separados devem ser prioridade em uma condição composta. Isso torna o debug do código mais fácil.
Faça
if (! $conditionA) {
return;
}
if (! $conditionB) {
return;
}
if (! $conditionC) {
return;
}
// do stuff
Não faça
if ($conditionA && $conditionB && $conditionC) {
// do stuff
}
Comentários
Comentários devem ser evitados o máximo possível escrevendo um código explicativo. Se precisar usar um comentário, formate-o assim:
// There should be a space before a single line comment.
/*
* If you need to explain a lot you can use a comment block. Notice the
* single * on the first line. Comment blocks don't need to be three
* lines long or three characters shorter than the previous line.
*/
Uma possível estratégia para refatorar um comentário é criar uma função com um nome que descreva o comentário.
Faça
$this->calculateLoans();
Não faça
// Start calculating loans
$this->calculate();
Espaço em branco
Declarações devem ser separadas por uma linha em branco. Em geral, sempre adicione linhas em branco entre as declarações, a menos que sejam uma sequência de operações equivalentes de uma linha.
Faça
public function getPage($url)
{
$page = $this->pages()->where('slug', $url)->first();
if (! $page) {
return null;
}
if ($page['private'] && ! Auth::check()) {
return null;
}
return $page;
}
Não faça
public function getPage($url)
{
$page = $this->pages()->where('slug', $url)->first();
if (! $page) {
return null;
}
if ($page['private'] && ! Auth::check()) {
return null;
}
return $page;
}
Não adicione espaços em branco entre os {}
colchetes.
Faça
if ($foo) {
$this->foo = $foo;
}
Não faça
if ($foo) {
$this->foo = $foo;
}
Configurações
Os arquivos de config devem estar em kebab-case.
config/pdf-generator.php
As variaveis de config devem estar em snake_case.
// config/pdf-generator.php
return [
'chrome_path' => env('CHROME_PATH'),
];
Atenção
Não utilize o env() fora dos arquivos de config.
Artisan
Os comandos devem ser todos em kebab-case. Utilize o formato nome-do-pacote:acao-do-comando.
Faça
php artisan admix:create-user
Não faça
php artisan createUserToAdmix
O comando deve sempre dar algum feedback sobre qual é o resultado.
// no comando
public function handle()
{
// faça uma mágica
$this->info('Mágica feita com sucesso!');
}
Quando estamos processando vários itens, importando um csv, por exemplo, é interessante adicionar uma saída dentro do loop, para que o progresso possa ser rastreado.
No final do comando, forneça um resumo de quanto processamento foi feito.
// no comando
public function handle()
{
$this->comment("Lendo o csv...");
// fazendo a mágica
$items->each(function(Item $item) {
$this->info("Importando o item de id: {$item-id}...");
$this->processItem($item);
});
$this->comment("{$item->count()} itens importados.");
}
Controllers
As controllers que controlam uma model, devemos usar o nome do recurso no singular.
class PostController
{
// ...
}
Tente manter os controllers simples e use as palavras-chave CRUD padrão (index
, create
, store
, show
, edit
, update
, destroy
). Extraia para uma nova controller se precisar de outras ações. No exemplo a seguir, poderíamos ter PostsController@favorite
e PostsController@unfavorite
, ou poderíamos extrair para uma nova FavoritePostController
.
class PostController
{
public function create()
{
// ...
}
// ...
public function favorite(Post $post)
{
request()->user()->favorites()->attach($post);
return response(null, 200);
}
public function unfavorite(Post $post)
{
request()->user()->favorites()->detach($post);
return response(null, 200);
}
}
Aqui temos um fallback
para o padrão CRUD, usando o store
e destroy
class FavoritePostController
{
public function store(Post $post)
{
request()->user()->favorites()->attach($post);
return response(null, 200);
}
public function destroy(Post $post)
{
request()->user()->favorites()->detach($post);
return response(null, 200);
}
}
Está é uma sugestão, não precisa seguir a risca, mas é recomendado.
Views
As views devem ser escritas em kebab-case
.
//resources/views/open-source.blade.php
class OpenSourceController
{
public function index() {
return view('open-source');
}
}
Validação
Quando usar várias regras para um campo em uma solicitação de formulário, evite usar |, sempre use array notation
. Isso facilitará a aplicação de classes de regras personalizadas a um campo.
Faça
public function rules()
{
return [
'email' => [
'required',
'email:rfc,dns',
],
];
}
Não faça
public function rules()
{
return [
'email' => 'required|email',
];
}
Todas as validações personalizadas devem estar em snake_case
:
Validator::extend('organisation_type', function ($attribute, $value) {
return OrganisationType::isValid($value);
});
Blade Templates
Faça a indentação em 4 espaços.
<h1>
Open Source
</h1>
Use as rotas nomeadas.
<a href="{{ route('frontend.open-source') }}">
Open Source
</a>
Não adicione espaços após as diretivas.
Faça
<div>
@if($condition)
Something
@endif
</div>
Não faça
<div>
@if($condition)
Something
@endif
</div>
Authorization
Policies devem ser camelCase
.
Gate::define('editPost', function ($user, $post) {
return $user->id == $post->user_id;
});
@can('editPost', $post)
<a href="{{ route('posts.edit', $post) }}">
Edit
</a>
@endcan
Tente nomear as habilidades usando palavras CRUD padrão. Uma exceção: substitua show
por view
.
Traduções
As traduções devem ser renderizadas com a função __
.
<h2>{{ __('newsletter.form.title') }}</h2>
{!! __('newsletter.form.description') !!}
Nomeando as classes
Nomear as coisas é visto como uma das coisas mais difíceis na programação. É por isso que estabelecemos algumas diretrizes de alto nível para nomear classes.
Controllers
Geralmente, os controllers são nomeados pela forma singular de seu recurso correspondente e um sufixo Controller.
ex. UserController
or EventDayController
Quando houver controller não relacionadas a model, você pode se deparar com controllers invocáveis que executam uma única ação. Estes podem ser nomeados pela ação que executam novamente sufixados por Controller.
ex. PerformCleanupController
Resources e Transformers
Tanto o Eloquent Resources quanto o Fractal Transformers devem estar no singular e sufixados com Resource / Collection ou Transformer respectivamente. Pense sempre como sendo a Model o nosso guia.
src/Http/Resources/CompanyCollection.php
src/Http/Resources/CompanyResource.php
src/Transformers/AppointmentTransformer.php
src/Transformers/AssociateTransformer.php
Jobs
O nome do Job deve descrever sua ação.
src/Jobs/UpdateAssociateSubscriptions.php
Events
Eventos são frequentemente disparados antes ou depois do evento real. Isso deve ser muito claro pela tensão usada em seu nome.
ex. ApprovingLoan
antes da ação ApproveLoan
ser feita e LoanApproved
depois que a ação ApproveLoan
seja completada.
Listeners
Listeners devem executar uma ação com base em um evento de entrada. Seu nome deve refletir essa ação com um sufixo Listener. Isso pode parecer estranho no início, mas evitará colisões de nomes com jobs.
src/Listeners/UpdateAssociateSubscriptionsListener.php
Commands
Para evitar colisões de nomes, vamos usar o sufixo Command.
src/Commands/UpdateAssociateSubscriptionsCommand.php
Mailables
Novamente, para evitar colisões de nomes, vamos usar o sufixo Mail.
src/Mails/SendMail.php
Enums
Enums não precisam de prefixos, pois geralmente, é claro lendo o nome que é um Enum.
ex. OrderStatus or BookingType or Suit