Marking text
A Pushery\AiContentMarking\MarkedText holds a plain text and the Marking that says how it
came about. Create one where the text comes back from the model:
use Pushery\AiContentMarking\MarkedText;
$description = MarkedText::generated($modelOutput);
$returns = MarkedText::edited($rewrittenPolicy, generator: 'Amazon Bedrock', at: now());
generated() uses the term trainedAlgorithmicMedia, edited() the term
compositeWithTrainedAlgorithmicMedia. The generator and the moment are optional, and both end up
in the JSON form. For any other term, build the marking yourself:
use Pushery\AiContentMarking\DigitalSourceType;
use Pushery\AiContentMarking\MarkedText;
use Pushery\AiContentMarking\Marking;
$text = new MarkedText($proofread, new Marking(DigitalSourceType::AlgorithmicallyEnhanced));
In a page
html() returns the escaped text inside a span that carries the marking as schema.org
microdata. Wrap it in whatever element the page needs:
<h2>{{ $product->name }}</h2>
<p>{{ $description->html() }}</p>
The text becomes the text of a CreativeWork item, and a link element inside the item names
the term:
<span itemscope itemtype="https://schema.org/CreativeWork">
<link itemprop="digitalSourceType" href="https://schema.org/TrainedAlgorithmicMediaDigitalSource">
<span itemprop="text">A linen shirt with a relaxed fit.</span>
</span>
The term travels in a link because microdata reads a URL value only from href. For the two
terms schema.org has no member for, humanEdits and digitalCreation, the href is the IPTC URI
of the term.
As a plain string
A MarkedText is the plain text wherever a string is expected, and Blade's {{ }} escapes it like
any other string. Nothing marks itself, so a text used where no markup may go stays intact:
<title>{{ $description }}</title>
<meta name="description" content="{{ $description }}">
In JSON
toArray() and json_encode() give the text beside the values of its marking:
{
"text": "Returns take fourteen days.",
"marking": {
"digital_source_type": "compositeWithTrainedAlgorithmicMedia",
"digital_source_type_uri": "http://cv.iptc.org/newscodes/digitalsourcetype/compositeWithTrainedAlgorithmicMedia",
"generator": "Amazon Bedrock",
"generated_at": "2026-10-14T21:00:00+00:00"
}
}
Return the MarkedText from an API resource where the marking should reach the client, and the
plain string where the client expects one. A Marking serializes on its own to the inner object.
Rich HTML
A text that already is HTML, such as a description your application stores as sanitized rich
text, goes through Marking::wrap(). It puts the HTML into a div that carries the marking, and
it does not escape it:
{{ $marking->wrap(new \Illuminate\Support\HtmlString($sanitizedDescription)) }}
Pass only HTML that is safe to render. A plain text belongs in MarkedText::html(), which escapes
it.
Storing a marking with a model
Marking is castable. Name it in the casts of a model with a JSON column, and the column stores
exactly what toArray() writes:
use Illuminate\Database\Eloquent\Model;
use Pushery\AiContentMarking\MarkedText;
use Pushery\AiContentMarking\Marking;
final class Product extends Model
{
protected function casts(): array
{
return ['description_marking' => Marking::class];
}
public function markedDescription(): MarkedText|string
{
return $this->description_marking === null
? $this->description
: new MarkedText($this->description, $this->description_marking);
}
}
A column can stay null for a text nobody generated. Storing anything but a Marking or null
throws an InvalidArgumentException, and so does reading a column that holds no stored marking.
The cast reads the JSON as your database hands it back, so a PostgreSQL jsonb column, which
reorders the keys, works the same as a MySQL json or a SQLite text column.