wp-net / WordPressPCL

This is a portable library for consuimg the WordPress REST-API in (almost) any C# application
MIT License
340 stars 130 forks source link

Categories - GetAllAsync method not get all sub categories #357

Closed elango-kamatchi closed 9 months ago

elango-kamatchi commented 10 months ago

I used GetAllAsync method for get categories with wordpress site. But its only returned back to parent categories and sub categories were missed. How can I get all? Thanks

richardaubin commented 10 months ago

Refer to https://developer.wordpress.org/rest-api/reference/categories/#list-categories

You will first need to get the id of the parent categories, then make an additional request for the subcategories of each parent.

elango-kamatchi commented 9 months ago

Finally i got to get all categories. We need to get every page results from wordpress, one page result is maximum of 100. So my code like;

Dim queryBuilder = New CategoriesQueryBuilder() queryBuilder.Search = "" queryBuilder.PerPage = 100 queryBuilder.Page = 1 Dim category = Await client.Categories.QueryAsync(queryBuilder)

Next time trying page 2 with next 100 category.

Thanks

richardaubin commented 9 months ago

I created a custom WPClient : WordPressClient class that uses HttpClient and a custom HttpMessageHandler that provides the HttpResponseMessage to the WPClient via an event.

I have this all configured in donet core dependency injection, but these are the classes and example usage:

public sealed class WPClient : WordPressClient, IWordPressClient, IDisposable
{
    readonly HttpMessageHandlerWithResponseEvent _handler;

    public WPClient(HttpClient httpClient,
                    HttpMessageHandlerWithResponseEvent handler) : base(httpClient)
    {
        _handler = handler;
        _handler.ResponseReceived += Handler_ResponseReceived;
    }
    public void Dispose()
    {
        _handler.ResponseReceived -= Handler_ResponseReceived;
    }

    private void Handler_ResponseReceived(HttpResponseMessage responseMessage)
    {
        if (responseMessage.Headers.TryGetValues("X-WP-Total", out IEnumerable<string>? total))
        {
            ResponseTotal = int.Parse(total.Single());
        }

        if (responseMessage.Headers.TryGetValues("X-WP-TotalPages", out IEnumerable<string>? totalPages))
        {
            ResponseTotalPages = int.Parse(totalPages.Single());
        }
    }

    public int ResponseTotal { get; private set; }
    public int ResponseTotalPages { get; private set; }
}

The DelegatingHandler allows the overriding of the SendAsync metthod to intercept the response before returning it.

public sealed class HttpMessageHandlerWithResponseEvent() 
    : DelegatingHandler(new HttpClientHandler())
{
    public event Action<HttpResponseMessage>? ResponseReceived;

    protected override async Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var response = await base.SendAsync(request, cancellationToken);

        if (response.IsSuccessStatusCode)
        {
            ResponseReceived?.Invoke(response);
        }

        return response;
    }
}

Create the HttpClient like this:

var httpClient = new HttpClient(new HttpMessageHandlerWithResponseEvent());

Now whenever I make any type of Get requests with any of the clients (products, categories, media) I can get the totals required for paging like so:

var maxPerPage = 100;
var categoryQuery = new Dictionary<string, string>
{
    {"page", "1"},
    { "per_page", maxPerPage.ToString() }
};
var categories = await _wordPressClient.Category.GetAllAsync(categoryQuery);

if ( _wordPressClient.ResponseTotalPages > 1)
{
     for (var page = 2; page <= _wordPressClient.ResponseTotalPages; page++)
     {
         categoryQuery["page"] = page.ToString();
         var tmpCategories = await _wordPressClient.Category.GetAllAsync(categoryQuery);

          categories.AddRange(tmpCategories);
     }
}