Liquid Filters
Custom Liquid filters are created in a similar fashion to routes: implement ILiquidFilter and return a LiquidFilter object that pairs a filter name with a delegate. This design keeps filter logic encapsulated and testable while allowing full dependency injection.
Filters implement the
ILiquidFilterinterface and define aGetFilter()method that returns aLiquidFilter.The
LiquidFiltercontains aName(the identifier used in templates) and aFilterDelegate—the actual function that performs the transformation.The
FilterDelegatecomes from the Fluid library and must return a value derived fromFluidValue(e.g.,ArrayValue,ObjectValue,StringValue).Because filters are registered in the DI container, they can consume any service, such as a search index or database context.
The example below shows a filter that retrieves featured articles and returns them as an array of view models:
public class GetFeaturedArticlesFilter : ILiquidFilter
{
private readonly IIndexProvider _indexProvider;
public GetFeaturedArticlesFilter(IIndexProvider indexProvider)
{
_indexProvider = indexProvider;
}
public async Task<LiquidFilter> GetFilter()
{
return new LiquidFilter()
{
Name = "get_featured_articles",
FilterDelegate = GetFeaturedArticles
};
}
private async ValueTask<FluidValue> GetFeaturedArticles(FluidValue input, FilterArguments arguments, TemplateContext context)
{
string filterInput = input.ToStringValue();
var blogPosts =
(await _indexProvider.Search()
.Must(() => new TermQuery(new Term(nameof(BlogPost.IsPublished), Boolean.TrueString)))
.Sort(() => new SortField(nameof(BlogPost.PublishedDate), SortFieldType.INT64, true))
.Paged(1, 4)
.ListResult<BlogPost>()).Hits
.Select(x => x.Hit)
.ToList();
var featuredBlogPosts = new List<FluidValue>();
foreach (var blogPost in blogPosts)
{
var blogPostViewModel = new LinkViewModel()
{
Title = blogPost.Name,
Url = $"{blogPost.Path}"
};
featuredBlogPosts.Add(new ObjectValue(blogPostViewModel));
}
return new ArrayValue(featuredBlogPosts);
}
}
