Vamos conhecer o Filament, um conjunto de modulos com muitos componentes que irão acelerar o desenvolvimento de nossas aplicações web. O Filament se baseia na
Tall
Stack (TailWindCSS
,AlpineJS
,Laravel
,Livewire
) e neste projeto vamos desenvolver um projeto de clinica de exemplo da documentação e adicionar novas funcionalidades.
⭐ Este sistema de exemplo irá abordar o primeiro exemplo que a próprio documentação do
Filament
demosntra e também irei adicionar novas funcionalidades para melhor aprendizado e melhorar o sistema. Obs.: Toda codificação e exeplicações do exemplo que a documentação apresenta, não irei descrever.
Exemplo | Filament
: 🌟 Sera a construção de um sistema simples de clínica veterinária e com novas funcionalidades descritas abaixo, terá gerenciamento de pacientes para uma clínica veterinária usando o Filament. Apoiará a adição de novospacientes
(gatos, cães ou coelhos), atribuindo-os a umproprietário
e registrando quaistratamentos
eles receberam. O sistema terá um painel com estatísticas sobre os tipos de pacientes e um gráfico com a quantidade de tratamentos administrados no último ano.
🌟 Continuando a construção do sistema da clínica, vamos adicionar novas funcionalidades, com um blog, que terá uma lista de
posts
(notícias) com seus devidos autores, onde teremos associações decategorias
,comentários
e replicas de comentários dosusuários
. E por fim também terá a adição lista deprodutos
que a clínica terá em seu estoque com associação a categorias.
🔔 No sistema da clínica, vamos poder verificar em cada view, suas associações conforme o Filament disponibiliza com o gerenciamento das categorias, comentários, posts, pacientes, etc.
php artisan make:model Inventory -m
php artisan make:model Poost -m
php artisan make:model Category -m
💬 Propriedades das Migrations
documentação laravel migrations table
Vou demonstrar duas formas de relacionamento na migration, com os
exemplos
em Inventories e Post e suas associações com Categoria.
Schema::create('inventories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('description');
` $table->string('image');
$table->integer('quantity');
$table->foreignIdFor(\App\Models\Category::class);
$table->timestamps();
});
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->timestamps();
});
- 🔔 Se não tem certeza com a chave,
category_id
ou qualquer outra chave, podemos usar a funçãoforeignIdFor
e passar a classe Eloquent, que automaticamente irá criar a coluna com onome da classe
e_id
.
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('thumbnail')->nullable();
$table->string('title');
$table->string('color');
$table->string('slug')->unique();
$table->foreignId('category_id')->constrained()->cascadeOnDelete();
$table->text('content')->nullable();
$table->json('tags')->nullable();
$table->boolean('published')->default(false);
$table->timestamps();
});
php artisan make:migration alter_inventory_table_add_active_column --table=inventories
Schema::table('inventories', function (Blueprint $table) {
$table->boolean('active')->default(true);
});
Estes são os metodos de relacionamento que iremos utilizar na relação HasMany (1-1 & 1-M)
//Inventory and Post
public function category()
{
return $this->belongsTo(Category::class);
}
//Category
public function products()
{
return $this->hasMany(Inventory::class);
}
Para que a imagem do produto apareça de forma correta, temos que ativa o
storage link
e modificar logo apos no arquivo.ENV
a linha deAPP_URL
para receber a base do app=http://127.0.0.1:8000
.
php artisan storage:link
FileUpload::make('thumbnail')
->disk('public')
->directory('thumbnails')->columnSpanFull(),
Vamos utilizar dois dos vários comandos que o Filament disponibiliza filament-resource
e filament-relation-manager
.
- 💥 make:filament-relation-manager ✔️ O Filament permite que possamos gerenciar relacionamentos em nosso app. | documentation
- Os relacionamentos que podem ser gerenciados são
HasMany
,HasManyThrough
,BelongsToMany
,MorphMany
eMorphToMany
.
✔️ Os gerenciadores de relacionamento são tabelas interativas que permitem aos administradores listar, criar, anexar, associar, editar, desanexar, dissociar e excluir registros relacionados sem sair da página Editar ou Visualizar do recurso.
- Os relacionamentos que podem ser gerenciados são
- 💥 make:filament-resource ✔️ Cria o arquivo de
resources
do seu modelo em App/Filament e cria toda estrutura das classes padrão.- Qualquer
model
que você criar em seu projeto laravel, podemos criar os Filaments em nosso projeto e ter páginas ou modais.
- Qualquer
Criando as classes views completas
| O generate
irá add todas propriedades da sua migrate, criando páginas para seu projeto.
php artisan make:filament-resource Inventory --generate
php artisan make:filament-resource Post --generate
php artisan make:filament-resource User --generate
Opção: Você pode criar de forma simples, views
simplificadas com MODAIs
no lugar de um página, como editar ou criar.
php artisan make:filament-resource Inventory --simple --generate
Com os metodos de relacionamento criados nos models BelongsTo
e HasMany
, vamos add na view de InventoryResource
,
o relacionamento relationship e ele tem dois argumentos.
O primeiro argumento é o nome do metodo no modelo e segundo a proriedade que mostra.
Select::make('category_id')->relationship('category', 'name')
Para adiconar este gerenciador, utilizamos o comando abaixo e mais agluns argumentos como qual resource
você quer gerenciar,
(Ex.: CategoryResource
), segundo é o nome do relacionamento em sua model (Ex.: posts) e por último qual propriedade da model quer usar (title).
php artisan make:filament-relation-manager CategoryResource posts title
O filament irá criar um outro diretorio em App/Filament/Resources/CategoryResource/RelaionManagers. chamado de PostsRelationManager.php. Esta mesma é o complemento da categoria, mostrando os relacionamentos que a categoria tem com seus posts, mas antes disso, como a propria documentação do Filament informa, precisamos dizer qual seu relacionament no metodo getRelations da CategoryResource.
public static function getRelations(): array
{
return [
RelationManagers\PostsRelationManager::class
];
}
🔔 Validation | documentation
Abaixo um exemplo dos diversos metodos de validação dedicados
que o Filament inclui, mas você também pode usar
quaisquer outras regras de validação do Laravel, incluindo regras de validação personalizadas.
TextInput::make('title')->required()
->alpha()
->doesntStartWith(['admin'])
->rules(['min:3|max:30', 'alpha'])
->in(['test', 'hello'])
Algo interessante que o Filament nos proporciona, é poder adiconar outras regras de validação proprias ou usar as validações que o proprio laravel disponibiliza | documentation.
Nesta relação vamos ter uma tabela pivo
que irá guardar os IDs de relação entre User e Post
, assim vamos poder
visualizar e gerenciar quais autores
temos em cada postagem
. E aqui vamos criar a relação, que terá como ser definida no
formulário de criação do post, mas também vamos criar o gerenciamento que o Filament permite criar.
Schema::create('post__users', function (Blueprint $table) {
$table->id();
$table->foreignIdFor(\App\Models\Post::class);
$table->foreignIdFor(\App\Models\User::class);
$table->timestamps();
});
//USER
public function posts()
{
return $this->belongsToMany(Post::class, 'post__users')->withTimestamps();
}
//POST
public function authors()
{
return $this->belongsToMany(User::class, 'post__users')->withTimestamps();
}
💬 Em PostResource
teremos duas formas
de mostrar, multiple com multiplos autores
(Array) e CheckboxList autores
.
Select::make('authors casa')
->label('Autores')
->multiple()
->preload()
->relationship('authors', 'name'),
Forms\Components\CheckboxList::make('authors casa')
->label('Autores')
->searchable()
->relationship('authors', 'name'),
Agora vamos ao filament-relation-manager
onde vamos criar o gerenciamento dos autores dos post, onde
vamos poder adicionar novos autores ou vincular autores já cadastrados.
php artisan make:filament-relation-manager PostResource authors name
public static function getRelations(): array
{
return [
RelationManagers\AuthorsRelationManager::class
];
}
Coluna adicionada na migrate pivo post
$table->integer('nota')->default(0);
- php artisan migrate:refresh --step=1
//Post e User add.
public function authors()
{
return $this->belongsToMany(User::class, 'post__users')->withPivot('nota')->withTimestamps();
}
->headerActions([
Tables\Actions\AttachAction::make()
->form(fn (AttachAction $action): array => [
$action->getRecordSelect(),
Forms\Components\TextInput::make('nota')->required(),
]),
])
💬 No relacionamentos polimórficos
vamos lidar com o relacionamento de 1 para 1
e 1 para muitos
e o gerenciador
de relacionamento.
Alguns links de estudo que utilizei para aprendizado e desenvolvimento.
//Add migrate
php artisan make:model Comment -m
//Property in migrate comments
Schema::create('comments', function (Blueprint $table) {
$table->id();
$table->foreignIdFor(\App\Models\User::class);
$table->morphs('comentable');
$table->string('comment');
$table->timestamps();
});
php artisan make:filament-resource Comment
🔔 Aqui, criaremos um modelo de tabela de Comment, User e Post
. também usaremos "morphMany()" e "morphTo()" para relacionamento de ambos os modelos.
//Comment
public $guarded = ['id'];
/**
* Comment
* Obtenha todos os modelos comentáveis.
*/
public function commentable()
{
return $this->morphTo();
}
public function user()
{
return $this->belongsTo(User::class);
}
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
/**
* User e Post
* Obtenha todos os comentários.
*/
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
💬 No form passamos o relationship, comment e o MorphToSelect.
💬 MorphToSelect
: Passamos em make o nome definido na migrate comment $table->morphs('commentable')
.
:speech_balloon: types
: Definimos um array, com quais recursos ou modelos podemos usar no relacionamento.
No types teremos comentários em Postagens, usuários e em outros comentários.
//FORM
Select::make('user_id')->relationship('user', 'name')->searchable()->preload(),
TextInput::make('comment'),
MorphToSelect::make('commentable')
->label('Tipo de comentários')
->types([
Type::make(Post::class)->titleAttribute('title'),
Type::make(User::class)->titleAttribute('email'),
Type::make(Comment::class)->titleAttribute('id'),
])
->searchable()->preload()
//TABLE
->columns([
Tables\Columns\TextColumn::make('commentable_id'),
Tables\Columns\TextColumn::make('commentable_type'),
Tables\Columns\TextColumn::make('comment'),
])
💬 Depois de filament-relation-manager
, primeiro argumento a classe que terá o gerenciador Post
e
segundo o nome do metodo
que faz o relacionamento mophMany
e último é o identificador dos comentários commentable
.
php artisan make:filament-relation-manager Post comments commentable
💬 Depois, basta definir o gerenciamento a relação em getRelations
do PostResource
e CommentResource
.
💬 Aqui alguns recursos
do Filament que achei interessante
demonstrar, mas você pode verificar melhor na
documentação do Filament.
Alguns detalhes/Dicas de GRIDs
Groups
, Sections
com columns e columnSpans.
return $form->schema([
RichEditor::make('content')->columnSpan(3) //ou 'full' ou ->columnSpanFull()
])->columns(3),
Forms\Components\Grid::make()->schema([
//...
])->columns(2),
//Forms
return $form
->schema([
Section::make('Dados básicos da postagem')
->description('Criação de postagem')
->collapsible()
->schema([
//..
])->columnSpan(1)->columns(2),
Section::make('description')
->schema([
//...
])->columnSpan(1)->columns(2),
])->columns([
'default' => 1,
'md' => 2,
'lg' => 2,
'xl' => 2,
]);
Com o
collapsible()
podemos fazer com que uma seção seja recolhida, usando o collapsed atributo. Omake("...")
edescription("...")
são titulo e subtitulo easide()
se adicionado, podemos alinha a div a esquerda.
As Guias ou "Tabs"
, ajuda muito no front, por oferecer uma exibição de diversas telas em uma única guia.
Forms\Components\Tabs::make('Criar novo post')->tabs([
Forms\Components\Tabs\Tab::make('Image data')->icon('heroicon-m-inbox')->schema([
//...
]),
Forms\Components\Tabs\Tab::make('Conteudo')->icon('heroicon-m-inbox')->schema([
//...
])
])->columnSpanFull()->activeTab(1)->persistTabInQueryString(),
💬 Link docs. Guias em tabelas de listas personalizadas. Ex. Em Filament\Resources\PostResource\Pages\ListPosts
public function getTabs(): array
{
return [
'Todos' => Tab::make(),
'Ativos' => Tab::make()->modifyQueryUsing(function (Builder $query){
$query->where('published', true);
}),
'inativos' => Tab::make()->modifyQueryUsing(function (Builder $query){
$query->where('published', false);
})
];
}
Na tabela podemos adicionar filtros para todos tipos de propriedades que temos em nosso projeto e aqui vai dois exemplos.
Filter
aborda a propriedade booleana para ativos e não e aTernaryFilter
aborda da mesma forma, mas simplificada. E aSelectFilter
temos o filtro por categoria utilizando o relacionamento.
->filters([
Filter::make('Posts ativos')->query(
function (Builder $query): Builder {
return $query->where('published', true);
}
),
TernaryFilter::make('published')->label('Filtro por publicados ou não')->default(true),
SelectFilter::make('category_id')->label('Categorias')
->relationship('category', 'name')->preload()
->multiple()
])
php artisan make:filament-widget PatientTypeOverview --stats-overview
php artisan make:filament-widget TreatmentsChart --chart
composer require flowframe/laravel-trend
💬 Authorization | Policy Filament.
💬 De acordo com a documentação Laravel (10.x), Políticas são classes que organizam a lógica de autorização
em torno de um modelo ou recurso específico. E o Filament se utiliza de todas Polices criada juntamente com os metodos padrões criados no comando abaixo e com outros metodos
.
💬 Cria a policy de Post e os metodos (--model=Category) exemplo de CRUD da policy
.
php artisan make:policy CategoryPolicy --model=Category
💬 Se quiser ignorar a autorização
de um recurso, você pode definir a $shouldSkipAuthorizationpropriedade
como true
:
protected static bool $shouldSkipAuthorization = true;
Contatos 👇🏼 [[email protected]]
Adoro me conectar com pessoas diferentes, então se você quiser dizer oi, ficarei feliz em conhecê-lo mais! :)