prefix plus a fixed suffix from a private helper (never user input); all runtime values go through $wpdb->prepare() placeholders. /** * Strands — topic clusters defined by the user. * * A strand groups the content of a single topic (silo). The metrics tell whether * the cluster is really braided: how many links run inside the strand and which * content are its loose ends. Braid Map colours the connections: inside a strand * (mint), between strands (violet), outside strands (neutral). */ class Linkbraid_Strands { private static function strands() { global $wpdb; return $wpdb->prefix . 'lbraid_strands'; } private static function members() { global $wpdb; return $wpdb->prefix . 'lbraid_strand_posts'; } private static function links() { global $wpdb; return $wpdb->prefix . 'lbraid_existing_links'; } /** Strand list with braid metrics. */ public static function all() { global $wpdb; $rows = $wpdb->get_results('SELECT * FROM ' . self::strands() . ' ORDER BY name ASC', ARRAY_A); foreach ($rows as &$row) { $id = (int) $row['id']; $row['posts_count'] = (int) $wpdb->get_var($wpdb->prepare( 'SELECT COUNT(*) FROM ' . self::members() . ' WHERE strand_id = %d', $id)); $row['internal_links'] = (int) $wpdb->get_var($wpdb->prepare( 'SELECT COUNT(*) FROM ' . self::links() . ' l JOIN ' . self::members() . ' a ON a.post_id = l.source_post_id AND a.strand_id = %d JOIN ' . self::members() . ' b ON b.post_id = l.target_post_id AND b.strand_id = %d', $id, $id)); $row['covered'] = (int) $wpdb->get_var($wpdb->prepare( 'SELECT COUNT(DISTINCT m.post_id) FROM ' . self::members() . ' m JOIN ' . self::links() . ' l ON l.target_post_id = m.post_id JOIN ' . self::members() . ' src ON src.post_id = l.source_post_id AND src.strand_id = %d WHERE m.strand_id = %d', $id, $id)); $row['loose'] = max(0, $row['posts_count'] - $row['covered']); } return $rows; } public static function create($name) { global $wpdb; $name = trim(preg_replace('/\s+/u', ' ', (string) $name)); if (mb_strlen($name) < 2 || mb_strlen($name) > 120) return ['error' => __( 'The strand name must be between 2 and 120 characters.', 'linkbraid' )]; if ($wpdb->get_var($wpdb->prepare('SELECT id FROM ' . self::strands() . ' WHERE name = %s', $name))) return ['error' => __( 'A strand with this name already exists.', 'linkbraid' )]; if (false === $wpdb->insert(self::strands(), ['name' => $name, 'created_at' => current_time('mysql')])) return ['error' => __( 'The strand could not be saved.', 'linkbraid' )]; return ['success' => true, 'id' => (int) $wpdb->insert_id]; } public static function delete($id) { global $wpdb; $id = absint($id); $wpdb->delete(self::members(), ['strand_id' => $id]); return false !== $wpdb->delete(self::strands(), ['id' => $id]); } public static function add_post($strand_id, $post_id) { global $wpdb; $strand_id = absint($strand_id); $post_id = absint($post_id); if (!$wpdb->get_var($wpdb->prepare('SELECT id FROM ' . self::strands() . ' WHERE id = %d', $strand_id))) return ['error' => __( 'The strand does not exist.', 'linkbraid' )]; $post = get_post($post_id); if (!$post || $post->post_status !== 'publish' || !in_array($post->post_type, lbraid_post_types(), true)) return ['error' => __( 'Pick published content from the enabled types.', 'linkbraid' )]; $wpdb->query($wpdb->prepare( 'INSERT IGNORE INTO ' . self::members() . ' (strand_id, post_id) VALUES (%d, %d)', $strand_id, $post_id)); return ['success' => true]; } public static function remove_post($strand_id, $post_id) { global $wpdb; return false !== $wpdb->delete(self::members(), ['strand_id' => absint($strand_id), 'post_id' => absint($post_id)]); } /** Strand content with the number of incoming links from inside the strand. */ public static function posts($strand_id) { global $wpdb; $strand_id = absint($strand_id); $rows = $wpdb->get_results($wpdb->prepare( 'SELECT m.post_id, p.post_title, (SELECT COUNT(*) FROM ' . self::links() . ' l JOIN ' . self::members() . ' src ON src.post_id = l.source_post_id AND src.strand_id = %d WHERE l.target_post_id = m.post_id) AS in_incoming FROM ' . self::members() . ' m LEFT JOIN ' . $wpdb->posts . ' p ON p.ID = m.post_id WHERE m.strand_id = %d ORDER BY in_incoming ASC, p.post_title ASC', $strand_id, $strand_id), ARRAY_A); foreach ($rows as &$row) { $row['url'] = get_permalink($row['post_id']); $row['edit'] = get_edit_post_link($row['post_id'], 'raw'); $row['post_title'] = $row['post_title'] ?: ('#' . $row['post_id']); } return $rows; } /** Map post_id -> strand_id (with several strands: the lowest id) for Braid Map. */ public static function node_map() { global $wpdb; $rows = $wpdb->get_results('SELECT post_id, MIN(strand_id) AS strand_id FROM ' . self::members() . ' GROUP BY post_id'); $map = []; foreach ($rows as $row) $map[(int) $row->post_id] = (int) $row->strand_id; return $map; } }