Autopreenchimento de campos no FilamentPHP usando API

Autopreenchimento de campos no FilamentPHP usando API

Neste artigo, vou mostrar como você pode consumir API e utilizá-la para autopreenchimento de campos no Filament, tornando assim seu formulário mais automatizado e dinâmico de forma simples e fácil. Vamos aprender, como exemplo, a autopreencher campos de endereço a partir do CEP digitado. Partiu?

 

Tópicos

Introdução

Passo a Passo

Passo 1: Criando projeto Laravel e adicionando o FilamentPHP
Passo 2: Criando as migrations e relacionamentos
Passo 3: Criando nossa resource
Passo 4: Adicionando funcionalidade de autopreenchimento com API

Conclusão
Referências

 

Introdução

E ai pessoal, este artigo não tem o intuito de ser clean code, mas sim de mostrar no geral como o autopreenchimento é feito para que a didática fique melhor, porém você pode organizar seu código da maneira que desejar. Neste exemplo, vou estar fazendo uso da API do ViaCep e vamos utilizá-la para autopreenchimento de campos de endereço, mas isso não se limita só a ela, sinta-se livre para usar a API que preferir. Vamos começar!

 

Passo a passo

Neste exemplo, estamos utilizando as seguintes versões de nossas ferramentas:

Laravel v11.x
PHP v8.2
FilamentPHP v3.2

Importante: O uso de outras versões pode mudar a forma como o procedimento abaixo é feito.

Passo 1: Criando projeto Laravel e adicionando o FilamentPHP

Para criar nosso projeto Laravel, podemos utilizar o seguinte comando:

composer create-project laravel/laravel example-app

Onde example-app é o nome do nosso projeto. Feito isso, dentro da pasta do projeto vamos usar os seguintes comandos, sendo o primeiro para adicionar o FilamentPHP e o outro para instalar os painéis do Filament:

composer require filament/filament:“^3.2” -W
 
php artisan filament:install –panels

Passo 2: Criando nossos models, migrations e relacionamentos

Agora vamos rodar o seguinte comando para criar o nosso Model, onde Address é o nome do nosso model e a flag -m fará com que a Migration seja criada junto com o Model:

php artisan make:model Address -m

Dentro da nossa Migration vamos adicionar os campos que vamos utilizar na nossa tabela. Levando em conta que vamos utilizar a API do ViaCep, os campos deverão ficar mais ou menos dessa forma:

<?php

use IlluminateDatabaseMigrationsMigration;
use IlluminateDatabaseSchemaBlueprint;
use IlluminateSupportFacadesSchema;

return new class extends Migration
{
/**
* Run the migrations.
*/

public function up(): void
{
Schema::create(‘addresses’, function (Blueprint $table) {
$table->id();
$table->foreignId(‘user_id’)->constrained()->cascadeOnDelete();
$table->string(‘postal_code’)->nullable();
$table->string(‘address’)->nullable();
$table->string(‘number’)->nullable();
$table->string(‘complement’)->nullable();
$table->string(‘neighborhood’)->nullable();
$table->string(‘city’)->nullable();
$table->string(‘uf’)->nullable();
$table->timestamps();
});
}

/**
* Reverse the migrations.
*/

public function down(): void
{
Schema::dropIfExists(‘addresses’);
}
};

Adicionado os campos na Migration, podemos rodar um php artisan migrate para que nossas tabelas sejam criadas no banco de dados.
E também, para criar um usuário para acessar nosso painel, usamos php artisan make:filament-user, digite um nome, um email e uma senha e use-os para fazer seu login no painel.

Agora vamos trabalhar nossos Models. Primeiro, precisamos adicionar os campos da nossa tabela na variável $fillable, como mostrado abaixo:

<?php

namespace AppModels;

use IlluminateDatabaseEloquentFactoriesHasFactory;
use IlluminateDatabaseEloquentModel;

class Address extends Model
{
use HasFactory;

protected $fillable = [
‘user_id’,
‘postal_code’,
‘address’,
‘number’,
‘complement’,
‘neighborhood’,
‘city’,
‘uf’,
];
}

Após isso, ainda dentro do nosso Model precisamos criar nossos relacionamentos. Considerando que um usuário terá somente um endereço e que um endereço está relacionado somente a um usuário, podemos adicionar uma relação do tipo hasOne:

Address.php

public function users()
{
return $this->hasOne(User::class);
}

User.php

public function addresses()
{
return $this->hasOne(Address::class);
}

Passo 3: Criando nossa resource

Vamos agora criar nossa resource de User, para isso utilizamos o seguinte comando:

php artisan make:filament-resource User –view –generate

// onde –view gera uma página de visualização e
// generate gerará nosso formulário e tabela automaticamente

Com isso, será criada sua resource em àpp/Filament/Resources já com seus campos e sua tabela gerada. Você perceberá também que nossos campos de endereço não estão nessa Resource, pois ele faz parte de outro Model e sendo assim “como vamos adicionar os campos de endereço na resource de User, se eles pertencem a outro Model?” Vamos lá, lembram da nossa relação? Então, vamos fazer o seguinte:

Podemos chamar nossa relação para dentro de um Fieldset através da flag ->relationship() como mostrado no exemplo de layout abaixo:

<?php

namespace AppFilamentResources;

use AppFilamentResourcesUserResourcePages;
use AppModelsUser;
use Exception;
use FilamentForms;
use FilamentFormsComponentsActionsAction;
use FilamentFormsComponentsFieldset;
use FilamentFormsForm;
use FilamentFormsSet;
use FilamentNotificationsNotification;
use FilamentResourcesResource;
use FilamentTables;
use FilamentTablesTable;
use IlluminateSupportFacadesHttp;

class UserResource extends Resource
{
protected static ?string $model = User::class;
protected static ?string $navigationIcon = ‘heroicon-o-users’;

public static function form(Form $form): Form
{
return $form
->schema([
FormsComponentsTextInput::make(‘name’)
->required()
->maxLength(255),
FormsComponentsTextInput::make(’email’)
->email()
->required()
->maxLength(255),
FormsComponentsDateTimePicker::make(’email_verified_at’),
FormsComponentsTextInput::make(‘password’)
->password()
->required()
->maxLength(255),
Fieldset::make(‘address’)->label(‘Address’)
->relationship(‘addresses’) // chamando nosso relacionamento
->schema([
FormsComponentsTextInput::make(‘postal_code’)
->maxLength(255),
FormsComponentsTextInput::make(‘address’)
->maxLength(255),
FormsComponentsTextInput::make(‘number’)
->maxLength(255),
FormsComponentsTextInput::make(‘complement’)
->maxLength(255),
FormsComponentsTextInput::make(‘neighborhood’)
->maxLength(255),
FormsComponentsTextInput::make(‘city’)
->maxLength(255),
FormsComponentsTextInput::make(‘uf’)
->maxLength(255),
]),
]);
}

public static function table(Table $table): Table
{
return $table
->columns([
TablesColumnsTextColumn::make(‘name’)
->searchable(),
TablesColumnsTextColumn::make(’email’)
->searchable(),
TablesColumnsTextColumn::make(’email_verified_at’)
->dateTime()
->sortable(),
TablesColumnsTextColumn::make(‘created_at’)
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
TablesColumnsTextColumn::make(‘updated_at’)
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
//
])
->actions([
TablesActionsViewAction::make(),
TablesActionsEditAction::make(),
])
->bulkActions([
TablesActionsBulkActionGroup::make([
TablesActionsDeleteBulkAction::make(),
]),
]);
}

// restante o código da resource…

}

O Fieldset não é o único componente de layout que aceita relações, em Managing relationships você pode ver os campos/componentes e os tipos de relações que são compatíveis com eles.

Você pode organizar esses campos e a sua tabela da maneira como preferir, dentro da documentação na aba de Layout (Layout de Formulário, Layout de Tabela), você encontra componentes e exemplos que você pode usar para organizar seus dados.

Passo 4: Adicionando funcionalidade de autopreenchimento com API

Enfim, vamos adicionar nossa funcionalidade, para isso vamos utilizar a flag ->suffixAction no nosso campo de CEP (postal_code), ela adicionará um botão no final do nosso campo que ao clicar disparará a ação que definirmos dentro dela:

FormsComponentsTextInput::make(‘postal_code’)->mask(‘99999-999’)
->suffixAction(
Action::make(‘search’)
->icon(‘heroicon-o-magnifying-glass’)
->action()
)
->maxLength(255),

Você pode escolher outro ícone pra sua Action no site Heroicons, lá você encontra todos os ícones suportados pelo Filament
E também usamos o método ->mask() para fazer a máscara do nosso CEP, a máscara já respeitará o uso de somente números no campo.

Ela ficará visualmente da seguinte forma:

Agora, dentro da nossa Action, dentro da flag ->action() vamos criar nossa função. Então, como parâmetros da nossa função vamos usar uma variável Set $set (para setar valores nos outros campos) e uma variável $state (usada no Filament para pegar o valor atual do campo).

Assim, a primeira coisa que vamos fazer é verificar se o valor atual do nosso campo está em branco e se estiver retornar uma mensagem para o usuário. No exemplo abaixo, estamos retornando uma notificação usando o Notification do Filament:

FormsComponentsTextInput::make(‘postal_code’)->mask(‘99999-999’)
->suffixAction(
Action::make(‘search’)
->icon(‘heroicon-o-magnifying-glass’)
->action(function(Set $set, $state){
if (blank($state)) {
Notification::make()
->title(‘Postal Code is required’)
->danger()->send();
return;
}
})
)
->maxLength(255),

Agora, dentro de um bloco try catch, podemos fazer nossa requisição. Dentro do try vamos adicionar uma variável $cepData que vai receber os dados que vierem da nossa requisição e dentro da URL da nossa requisição adicionamos nossa variável $state no endpoint para fazer a busca na API a partir do valor digitado no campo.

E dentro do catch fazemos nossa notificação ao usuário caso nossa requisição retorne algum erro.

FormsComponentsTextInput::make(‘postal_code’)->mask(‘99999-999’)
->suffixAction(
Action::make(‘search’)
->icon(‘heroicon-o-magnifying-glass’)
->action(function(Set $set, $state){
if (blank($state)) {
Notification::make()
->title(‘Postal Code is required’)
->danger()->send();
return;
}
try {
$cepData =
Http::get(“https://viacep.com.br/ws/{$state}/json”)
->throw()->json();

} catch (Exception $e) {
Notification::make()
->title(‘Postal Code not found’)
->danger()->send();
}
})
)
->maxLength(255),

Mas temos um detalhe, na API do ViaCep, ao enviarmos um CEP e ele não for encontrado ela irá retornar um array com uma chave de erro e não uma Exception (mostrado na imagem abaixo) e então não cairá no nosso bloco catch e o usuário não será notificado.

Para resolver isso, temos que forçar uma Exception para que nosso bloco catch “agarre” este erro e a notificação ao usuário seja enviada, fazemos isso da seguinte forma:

FormsComponentsTextInput::make(‘postal_code’)->mask(‘99999-999’)
->suffixAction(
Action::make(‘search’)
->icon(‘heroicon-o-magnifying-glass’)
->action(function(Set $set, $state){
if (blank($state)) {
Notification::make()
->title(‘Postal Code is required’)
->danger()->send();
return;
}
try {
$cepData =
Http::get(“https://viacep.com.br/ws/{$state}/json”)
->throw()->json();

// forçando a Exception
if (in_array(‘erro’, $cepData)) {
throw new Exception(‘Postal Code not found’);
}

} catch (Exception $e) {
Notification::make()
->title(‘Postal Code not found’)
->danger()->send();
}
})
)
->maxLength(255),

Agora, considerando que nossa variável $cepData, recebeu nossos dados da requisição, utilizamos a nossa variável $set para setar os valores que vieram da nossa requisição nos outros campos ou null caso não venha nada:

FormsComponentsTextInput::make(‘postal_code’)->mask(‘99999-999’)
->suffixAction(
Action::make(‘search’)
->icon(‘heroicon-o-magnifying-glass’)
->action(function(Set $set, $state){
if (blank($state)) {
Notification::make()
->title(‘Postal Code is required’)
->danger()->send();
return;
}
try {
$cepData =
Http::get(“https://viacep.com.br/ws/{$state}/json”)
->throw()->json();

// forçando a Exception
if (in_array(‘erro’, $cepData)) {
throw new Exception(‘Postal Code not found’);
}

} catch (Exception $e) {
Notification::make()
->title(‘Postal Code not found’)
->danger()->send();
}

$set(‘address’, $cepData[‘logradouro’] ?? null);
$set(‘neighborhood’, $cepData[‘bairro’] ?? null);
$set(‘city’, $cepData[‘localidade’] ?? null);
$set(‘uf’, $cepData[‘uf’] ?? null);
})
)
->maxLength(255),

Pronto! Agora nosso autopreenchimento já está funcionando. Abaixo mostro uma demonstração da nossa funcionalidade em ação.

Conclusão

Agora que você já aprendeu como fazer o autopreenchimento, pode usar isso nos seus projetos e tornar seus formulários menos cansativos de preencher e mais dinâmicos. Esta funcionalidade é até mesmo uma forma de facilitar a vida do usuário e reduzir erros de escrita na hora de preencher o formulário.
Abaixo vou estar disponibilizando o link do repositório do nosso exemplo, sinta-se livre para brincar como quiser. Espero que você tenha gostado e qualquer dúvida estou a disposição ^^
Até a próxima !!

 

Referências

Laravel Relationships
Managing Relationships – FIlament
Filament Form Layout
Repositório Filament ViaCep
API ViaCep
Heroicons

 

Obrigada @clintonrocha98 mais uma vez pelos feedbacks! 💜

Leave a Reply

Your email address will not be published. Required fields are marked *