Attachment Model

Data-access models live in includes/models/ and are the only supported way to read or write Jetonomy's custom tables - no raw $wpdb outside includes/db/. This page documents Jetonomy\Models\Attachment.

Attachment

Jetonomy\Models\Attachment - includes/models/class-attachment.php

Links WordPress media items to posts/replies, with ordering and a batch primer so rendering N reply cards issues zero per-row queries. Table: jt_attachments (free-owned since 1.8.0; it's the renamed jt_pro_attachments

  • same schema, no rows copied). Free carries the link so a site that drops Pro still shows its attachments; Pro adds the upload composer, size/type limits, and richer previews on top of the same table.

hydrate()

Attachment::hydrate( object $row ): ?array

Turns a jt_attachments link row into the canonical attachment shape. There is exactly ONE shape, used by both the frontend renderer and the REST payload, so the web and the app cannot drift apart.

Returns null when the underlying media item has been deleted, so a dangling link renders nothing rather than a broken card.

[
    'id'       => 0,     // the MEDIA id
    'link_id'  => 0,     // the jt_attachments row id
    'url'      => '',
    'thumb'    => '',    // 'medium' for images, 'thumbnail' otherwise
    'mime'     => '',
    'name'     => '',    // real filename WITH extension, e.g. "sample.pdf"
    'size'     => 0,     // bytes; 0 when the file is not on disk
    'type'     => '',    // 'image' | 'pdf' | 'file'
    'ext'      => '',    // uppercased, e.g. "PDF"
    'is_image' => false,
]

id being the media id while link_id is the row id reads backwards, but it is the established contract the app already consumes and is not worth breaking clients over. Pro enriches each item through jetonomy_rest_attachment_data (it swaps in its own download URL for non-images), so a Pro site's payload shape is unchanged.

link()

Attachment::link( string $object_type, int $object_id, int $attachment_id, int $sort = 0 ): int

Attaches a WP media item to a post or reply. Returns the jt_attachments row id. Idempotent - linking the same file to the same object twice is a no-op (backed by a UNIQUE KEY on object_type, object_id, attachment_id), so a resumed or re-run import can't double-attach a file.

get_for()

Attachment::get_for( string $object_type, int $object_id ): array

All link rows for one object, ordered by sort then id. Request-cached per [type][object_id].

get_for_many()

Attachment::get_for_many( string $object_type, int[] $object_ids ): array

Batch-loads attachments for many objects of one type in one query and seeds the per-object cache, keyed by object id (empty array for objects with none). Use when rendering a page of N posts/replies.

prime_for_post()

Attachment::prime_for_post( int $post_id ): void

The N+1 guard for a single-post view: loads attachments for every reply on a post in one query (JOIN jt_replies), so rendering N reply cards costs zero further attachment queries. Called by Attachments::render_post() before the reply loop. Seeds every reply on the post, including ones with no attachments, so a later get_for() call never re-queries.

count_for()

Attachment::count_for( string $object_type, int $object_id ): int

SELECT COUNT(*) for one object. Use for a badge/count display - never count( Attachment::get_for(...) ).

unlink()

Attachment::unlink( int $link_id ): bool

Removes one link row by its own id (not the media id).

unlink_all()

Attachment::unlink_all( string $object_type, int $object_id ): int

Removes every link row for an object (post/reply cascade delete). Returns the number of rows deleted.

hydrate()

Attachment::hydrate( object $row ): ?array

Turns a jt_attachments row into the canonical attachment shape consumed by both the frontend card renderer and the REST payload - one shape, so the web and the app can never drift. Returns null when the underlying media item was deleted (render nothing rather than a broken card). See the shape table in Hooks Reference - REST payload.

payload_for()

Attachment::payload_for( string $object_type, int $object_id ): array

Hydrates every attachment on an object and runs each through the jetonomy_rest_attachment_data filter. Backs both Attachments::render() and the REST attachments field.

Usage example

use Jetonomy\Models\Attachment;

// Attach an already-uploaded media item to a reply.
$link_id = Attachment::link( 'reply', $reply_id, $media_id, $sort = 0 );

// Render a page of replies without N+1 attachment queries.
Attachment::prime_for_post( $post_id );
foreach ( $replies as $reply ) {
    $attachments = Attachment::get_for( 'reply', $reply->id ); // no extra query
}