The Item API

Every provider — LinkedIn, RSS, YouTube, Bluesky, future ones — normalizes into the same TheWPFeeds\Item\Item value object. One model, one template contract: that's what makes a mixed LinkedIn + YouTube page render through the same pipeline.

Items are immutable. Getters return raw values — escape in your templates.

Getters

Method Returns Notes
title( $fallback = null ) ?string Post/article/video title. LinkedIn posts and Bluesky posts usually have none — hence the fallback.
content() string Full plain-text body. HTML is already stripped, entities decoded.
excerpt( $words = 30 ) string Body trimmed via wp_trim_words().
date( $format = '' ) string Localized via wp_date(). Empty format = the site's date format setting.
datetime() DateTimeImmutable For datetime="" attributes and custom formatting.
url() string Permalink on the source platform (LinkedIn post, YouTube watch page, Bluesky post…).
hasImage() bool
image() ?string Image URL — the locally cached copy when available (see Caching), remote fallback otherwise.
imageTag( $attrs = [] ) string Ready-made escaped <img> with loading="lazy", dimensions when known, alt text. Pass extra attributes: $item->imageTag(['class' => 'card__img']).
author() ?ItemAuthor Struct with ->name, ->url, ->imageUrl (avatar). The company page, channel, or profile.
raw() array The untouched provider payload — the escape hatch.

Public readonly properties are also available: $item->id, $item->provider, $item->title, $item->content, $item->image (an ItemImage with ->remoteUrl, ->localUrl, ->alt, ->width, ->height).

provider and branching

$item->provider is the provider id (linkedin, rss, youtube, bluesky, mock). Prefer the item template hierarchy over if-chains — but for small differences inline branching is fine:

<span class="badge badge--<?php echo esc_attr( $item->provider ); ?>">
    <?php echo esc_html( $item->provider ); ?>
</span>

raw() — the escape hatch

Anything provider-specific that didn't fit the normalized model is preserved verbatim. Examples:

// LinkedIn: the full /rest/posts element
$urn = $item->raw()['id'] ?? '';

// Bluesky: engagement counts
$likes = $item->raw()['likeCount'] ?? 0;

// YouTube: derive the video id from the watch URL
parse_str( (string) parse_url( $item->url(), PHP_URL_QUERY ), $q );
$video_id = $q['v'] ?? '';

Rule of thumb: if you find yourself using raw() for the same thing in every template, that's feedback — tell us and it may become a first-class getter.