Laravel: Boas praticas #2

Laravel: Boas Praticas #2

Recentemente falamos sobre as boas práticas de Laravel, onde abordamos algumas destas práticas que levaram certamente a um desenvolvimento mais claro e perceptível em grandes equipas e também ao longo do tempo.

Neste artigo, vamos abordar mais algumas dessas boas práticas.

Principio da responsabilidade única

Este principio é bastante simples de entender. Tal como o nome indica, o objectivo é atribuir uma única responsabilidade a algo. Segundo o conceito, a ideia é que cada Classe tenha apenas uma única responsabilidade quando se trata do fluxo de uma requisição do software.

No exemplo de uma Classe que faça um registo de utilizadores, dividir esse registo entre alguns tipos de utilizador não é uma boa politica, fugindo assim ao conceito.

class UserRegistrationController extends Controller
{
    public function register(Request $request)
    {
        $attributes = $request->all();
        if($attributes['type'] === 'player') {
            $this->saveEmployee($attributes);
        }

        if($attributes['type'] === 'agent') {
            $this->saveContractor($attributes);
        }

        return view('user.register');
    }

    public function savePlayer($request)
    {
        DB::table('users')->insert([
            /* Dados do funcionário aqui... */
        ]);
    }

    public function saveAgent($request)
    {
        DB::table('users')->insert([
            /* Dados do terceirizado aqui... */
        ]);
    }
}

Esta Classe não vai de encontro ao principio de responsabilidade única, pois está a misturar diversas responsabilidades. Neste caso sendo um Controller, este deve apenas gerir “requests” e “responses”.

Quando existem regras de negócio, estas podem ser colocas separadamente, podemos criar uma camada chamada “Service, que não é um padrão de Laravel, onde ficará então toda a responsabilidade dessas mesmas validações e comportamentos.

Podemos também observar, que o Controlador está com operações relacionadas a base de dados. Este também é um processo que deve estar numa outra camada, a dos “Models”, sendo este um padrão de Laravel. Podemos também fazer uma camada chamada “Repository”, que funciona de forma semelhante ao do Service, mas apenas com o objectivo de gerir tudo o que é referente a base de dados.

Dessa forma, devemos separar a nossa classe da seguinte forma:

Controller

class UserRegistrationController extends Controller
{
    protected $userService;

    public function __construct(UserService $userService)
    {
        $this->userService = $userService;
    }

    public function register(Request $request)
    {
        $userService->register($request);

        return view('user.register');
    }
}

Com esta alteração, isolamos o Controlador com a sua respectiva responsabilidade: gerir “Requests” e “Responses”.

Service

class UserService
{
    protected $playerRepository;
    protected $agentRepository;

    public function __construct(
        PlayerRepository $playerRepository,
        AgentRepository $agentRepository
    )
    {
        $this->playerRepository = $playerRepository;
        $this->agentRepository = $agentRepository;
    }

    public function register(Request $request)
    {
        $attributes = $request->all();
        if($attributes['type'] === 'player') {
            $this->playerRepository->save($attributes);
        }

        if($attributes['type'] === 'agent') {
            $this->agentRepository->save($attributes);
        }
    }
}

No “Service” fica toda a regra de negocio. Aqui usamos injecção de dependência, que serão os nossos “Repositórios” para adicionar os utilizadores na nossa base de dados.

Repository

class PlayerRespository
{
    protected $player;

    public function __construct(User $user)
    {
        $this->player = $user;
    }

    public function save(array $attributes)
    {
        $this->player->insert($attributes);
    }
}

class AgentRespository
{
    protected $agent;

    public function __construct(User $user)
    {
        $this->agent = $user;
    }

    public function save(array $attributes)
    {
        $this->agent->insert($attributes);
    }
}

No caso do “Repository” usamos também a injecção de dependência com os respectivos “Models” para adicionar a informação do User na base de dados.

Assim, todas as responsabilidades são separadas por diversas camadas, fazendo com que o código fique mais legível e fácil de entender ao longo do tempo.

Validações

As validações são um ponto que podem também ser mais bem geridos do que normalmente são.

É sempre aconselhado que as validações não sejam usadas no Controlador, o que normalmente não acontece. Sendo que a melhor politica para este caso, é colocar as validações em Classes a parte, chamando-lhes de “Classes de requisição”, como no exemplo abaixo:

public function store(PostRequest $request)
{    
    ....
}

class PostRequest extends Request
{
    public function rules()
    {
        return [
            'title' => 'required|unique:posts|max:255',
            'body' => 'required',
            'publish_at' => 'nullable|date',
        ];
    }
}

Usar Eloquent

Uma das maiores vantagens de usar o Eloquent, é a legibilidade que é dada ao código, conseguindo de forma fácil, entender o que aquele pedido está a executar e a retornar. É claro que devemos manter sempre o nome das relações o mais legíveis possível para que essa leitura nítida seja mantida.

Article::has('user’)->verified()->latest()->get();

Não usar Mass assignment

O Eloquent disponibiliza algumas operações como o Create e o Insert. Algo que pode ser muito utilizado nestes casos, é o Mass assignment.

$article = new Article;
$article->title = $request->title;
$article->content = $request->content;
$article->verified = $request->verified;
// Adicionar categoria em artigos
$article->category_id = $category->id;
$article->save();

Devemos privilegiar a utilização de um Create ou de um Insert quando pretendemos adicionar múltiplas entradas na base de dados.

Também a utilização do Update deve ser uma prioridade a utilização em Mass assignment.

Conversão de nomes

É importante manter a conversão de nomes que o próprio Laravel utiliza. Por exemplo, no caso das tabelas e dos Modelos, as tabelas devem sempre ter o nome no seu plural, por exemplo “articles”. Já no caso, o modelo pertencente a esta tabela, deve manter-se no singular como “Article.php” e os nomes da Classe Article”. O Controlador deve seguir também o mesmo caminho, mantendo o nome no singular, seguido do termo “controller”, por exemplo: “ArticleController.php” e o nome da sua classe igual.

Conclusão

Manter a legibilidade do código é extremamente importante no geral, mas especialmente em equipas grandes, com diversos programadores a trabalhar no mesmo projecto em simultâneo.

Estas boas práticas não são requisito obrigatório, mas facilitará em muito a vida de quem trabalha numa aplicação ao longo do tempo.

Mais sobre a serie de Laravel

Continua a acompanhar a serie sobre Laravel aqui no Blog. Podes ver mais artigos sobre Laravel aqui!