Adicionar a funcionalidade de links externos, frequentemente chamdaos de links de colaboração ou blogroll, é um recurso valioso para qualquer plataforma de blog. Ele não apenas permite que os administradores listem sites parceiros, mas também pode oferecer um mecenismo para que outros proprietários de sites solicitem uma parceria. Este artigo detalha a construção dessa funcionalidade em um projeto de blog utilizando .NET Core, abordando o design dos modelos de dados, a lógica de negócios e a apresentação na interface do usuário.
Definição dos Modelos de Dados
Para suportar os links de colaboração, precisamos de dois modelos principais: um para os links que serão exibidos publicamente e outro para registrar as solicitações de parceria. Os modelos serão criados no diretório StarBlog.Data/Models.
Primeiro, o modelo para os links ativos:
using FreeSql.DataAnnotations;
/// <summary>
/// Representa um link de parceria ativo no blog.
/// </summary>
public class ParceriaLink
{
[Column(IsIdentity = true, IsPrimary = true)]
public int Id { get; set; }
/// <summary>
/// Título do site ou projeto.
/// </summary>
public string Titulo { get; set; }
/// <summary>
/// Breve descrição do link.
/// </summary&n>
public string? Descricao { get; set; }
/// <summary>
/// URL de destino do link.
/// </summary>
public string UrlDestino { get; set; }
/// <summary>
/// Indica se o link está ativo e visível.
/// </summary>
public bool Ativo { get; set; }
}
Em seguida, o modelo para as solicitações de parceria recebidas:
using FreeSql.DataAnnotations;
/// <summary>
/// Representa uma solicitação de parceria enviada por terceiros.
/// </summary>
public class SolicitacaoParceria
{
[Column(IsIdentity = true, IsPrimary = true)]
public int Id { get; set; }
/// <summary>
/// Título do site do solicitante.
/// </summary&n>
public string TituloSite { get; set; }
/// <summary>
/// Descrição fornecida pelo solicitante.
/// </summary>
public string? DescricaoSite { get; set; }
/// <summary>
/// URL do site do solicitante.
/// </summary>
public string UrlSite { get; set; }
/// <summary>
/// Nome do responsável pela solicitação.
/// </summary>
public string NomeRemetente { get; set; }
/// <summary>
/// Email para contato com o solicitante.
/// </summary>
public string EmailContato { get; set; }
/// <summary>
/// Status de aprovação da solicitação.
/// </summary>
public bool Aprovado { get; set; } = false;
/// <summary>
/// Data e hora em que a solicitação foi enviada.
/// </summary>
public DateTime DataEnvio { get; set; } = DateTime.Now;
}
Camada de Serviço
Com os modelos definidos, a próxima etapa é implementar a lógica de negócios na camada de serviço. Criaremos dois serviços no diretório StarBlog.Web/Services para gerenciar links e solicitações.
O ServicoDeGerenciamentoDeLinks controlará os links ativos:
using FreeSql;
using StarBlog.Data.Models; // Assumindo namespace dos modelos
public class ServicoDeGerenciamentoDeLinks
{
private readonly IBaseRepository<ParceriaLink> _repoLinks;
public ServicoDeGerenciamentoDeLinks(IBaseRepository<ParceriaLink> repo)
{
_repoLinks = repo;
}
/// <summary>
/// Obtém uma lista de links de parceria, opcionalmente filtrando por status ativo.
/// </summary>
/// <param name="apenasAtivos">Se verdadeiro, retorna apenas links ativos.</param>
/// <returns>Lista de ParceriaLink.</returns>
public List<ParceriaLink> ObterLinksVisiveis(bool apenasAtivos = true)
{
return apenasAtivos
? _repoLinks.Where(l => l.Ativo).ToList()
: _repoLinks.Select.ToList();
}
/// <summary>
/// Busca um link de parceria pelo seu ID.
/// </summary>
public ParceriaLink? ObterPorId(int id) => _repoLinks.Where(l => l.Id == id).First();
/// <summary>
/// Busca um link de parceria pelo seu título.
/// </summary>
public ParceriaLink? ObterPorTitulo(string titulo) => _repoLinks.Where(l => l.Titulo == titulo).First();
/// <summary>
/// Adiciona um novo link ou atualiza um existente.
/// </summary>
public ParceriaLink SalvarOuAtualizar(ParceriaLink item) => _repoLinks.InsertOrUpdate(item);
/// <summary>
/// Altera o status de ativação de um link de parceria.
/// </summary>
public ParceriaLink? AtivarOuDesativar(int id, bool estadoAtivo)
{
var linkExistente = ObterPorId(id);
if (linkExistente == null) return null;
linkExistente.Ativo = estadoAtivo;
_repoLinks.Update(linkExistente);
return ObterPorId(id);
}
/// <summary>
/// Remove um link de parceria pelo seu ID.
/// </summary>
public int RemoverPorId(int id) => _repoLinks.Delete(l => l.Id == id);
}
O ServicoDeSolicitacoesDeParceria gerenciará as solicitações e sua integração com os links ativos:
using FreeSql;
using StarBlog.Data.Models; // Assumindo namespace dos modelos
public class ServicoDeSolicitacoesDeParceria
{
private readonly IBaseRepository<SolicitacaoParceria> _repoSolicitacoes;
private readonly ServicoDeGerenciamentoDeLinks _servicoLinks;
public ServicoDeSolicitacoesDeParceria(IBaseRepository<SolicitacaoParceria> repo, ServicoDeGerenciamentoDeLinks servicoLinks)
{
_repoSolicitacoes = repo;
_servicoLinks = servicoLinks;
}
// Métodos CRUD básicos para SolicitacaoParceria (ObterTodas, ObterPorId, Adicionar, Remover, etc. - omitidos para brevidade)
/// <summary>
/// Processa o status de aprovação de uma solicitação de parceria.
/// Se aprovada, o link é adicionado/ativado na lista de links ativos.
/// Se não aprovada, o link correspondente é removido/desativado.
/// </summary>
public SolicitacaoParceria? ProcessarStatusDeAprovacao(int solicitacaoId, bool statusAprovacao)
{
var solicitacao = _repoSolicitacoes.Where(s => s.Id == solicitacaoId).First();
if (solicitacao == null) return null;
solicitacao.Aprovado = statusAprovacao;
_repoSolicitacoes.Update(solicitacao);
var linkExistente = _servicoLinks.ObterPorTitulo(solicitacao.TituloSite);
if (statusAprovacao)
{
if (linkExistente == null)
{
_servicoLinks.SalvarOuAtualizar(new ParceriaLink
{
Titulo = solicitacao.TituloSite,
Descricao = solicitacao.DescricaoSite,
UrlDestino = solicitacao.UrlSite,
Ativo = true // Automaticamente ativo ao ser aprovado
});
}
else
{
_servicoLinks.AtivarOuDesativar(linkExistente.Id, true);
}
}
else // Solicitação negada ou desaprovada
{
if (linkExistente != null)
{
_servicoLinks.RemoverPorId(linkExistente.Id);
}
}
return _repoSolicitacoes.Where(s => s.Id == solicitacaoId).First();
}
}
Não se esqueça de registrar esses serviços no contêiner de injeção de dependência do seu projeto, geralmente no arquivo Program.cs:
builder.Services.AddScoped<ServicoDeGerenciamentoDeLinks>();
builder.Services.AddScoped<ServicoDeSolicitacoesDeParceria>();
Preparando a Interface do Usuário
Para exibir os links na página inicial do blog, primeiro precisamos atualizar o HomePageViewModel.cs (ou o ViewModel correspondente à sua página inicial) para incluir uma lista de links de parceria:
using StarBlog.Data.Models; // Assumindo namespace dos modelos
public class HomePageViewModel
{
/// <summary>
/// Lista de links de parceria a serem exibidos.
/// </summary>
public List<ParceriaLink> LinksDeParceria { get; set; } = new();
// Outras propriedades do ViewModel...
}
Componente Razor para Exibição de Links
Para um visual mais dinâmico, criaremos um componente Razor que exibe cada link como um botão com cores aleatórias. Primeiramente, um ViewModel para o componente:
/// <summary>
/// Define os alvos padrão para links HTML.
/// </summary>
public static class AlvosLink
{
public const string NovaJanela = "_blank";
public const string JanelaPai = "_parent";
public const string MesmaJanela = "_self";
public const string JanelaTopo = "_top";
}
/// <summary>
/// Modelo de dados para o componente de botão de link colorido.
/// </summary>
public class ModeloBotaoLink
{
public string TextoExibicao { get; set; }
public string UrlRedirecionamento { get; set; } = "#";
public string Alvo { get; set; } = AlvosLink.NovaJanela; // Abrir em nova janela por padrão para links externos
}
Em seguida, o componente Razor BotaoLinkColorido.cshtml no diretório StarBlog.Web/Views/Shared/Widgets:
@model ModeloBotaoLink
@{
var geradorAleatorio = new Random();
var paletaCores = new string[] {
"btn-outline-primary",
"btn-outline-secondary",
"btn-outline-success",
"btn-outline-danger",
"btn-outline-warning",
"btn-outline-info",
"btn-outline-dark"
};
var classeCorBtn = paletaCores[geradorAleatorio.Next(paletaCores.Length)];
}
<a href="@Model.UrlRedirecionamento" role="button" class="btn btn-sm @classeCorBtn mb-1" target="@Model.Alvo">
@Model.TextoExibicao
</a>
Integração na Página Inicial
Finalmente, integre os links na sua página inicial (por exemplo, StarBlog.Web/Views/Home/Index.cshtml). Uma boa prática é colocá-los na parte inferior da página, talvez após outras seções principais.
<div class="container px-4 py-3">
<h2 class="pb-2 border-bottom">Links de Colaboração</h2>
@if (Model.LinksDeParceria.Any()) {
<div class="d-grid gap-2 d-md-block">
@foreach (var linkItem in Model.LinksDeParceria) {
@await Html.PartialAsync("Widgets/BotaoLinkColorido",
new ModeloBotaoLink { TextoExibicao = linkItem.Titulo, UrlRedirecionamento = linkItem.UrlDestino, Alvo = AlvosLink.NovaJanela })
}
</div>
}
else {
@await Html.PartialAsync("Widgets/CartaoReservado", "Não há links de parceria para exibir.")
}
</div>
Assumindo que Widgets/CartaoReservado é um componente simples que exibe uma mensagem quando não há links disponíveis.