Compare commits

...

5 commits

Author SHA1 Message Date
1b0a9f385f fix: use AUTO_INCREMENT of table as id of new posts (#9)
Reviewed-on: https://codeberg.org/vlw/wp/pulls/9
2026-02-16 13:37:52 +01:00
d52154192d refactor: move properties above methods and substr() varchar table columns (#8)
Reviewed-on: https://codeberg.org/vlw/wp/pulls/8
2026-02-16 13:37:33 +01:00
4ca9f8be5e feat: add method for querying the current AUTO_INCREMENT for a table (#5)
This will be used to retrieve the primary key for newly inserted rows.

Reviewed-on: https://codeberg.org/vlw/wp/pulls/5
2026-02-16 11:27:30 +01:00
28f31250fa fix: proper table reference for PostMeta (#7)
Reviewed-on: https://codeberg.org/vlw/wp/pulls/7
2026-02-16 11:27:16 +01:00
a70015e314 feat: improved creation and assignment of post taxonomies and terms (#6)
With this PR we can (among actually creating taxonomies and terms) assign and remove terms from a post with the following
```php
$post = new Post(123);
$term = new Term(321);
```
Add a term to a post:
```php
$post->add_term($term);
```
Remove a term from a post:
```php
$post->remove_term($term);
```

Reviewed-on: https://codeberg.org/vlw/wp/pulls/6
2026-02-16 11:26:58 +01:00
5 changed files with 180 additions and 104 deletions

View file

@ -76,6 +76,19 @@
parent::__construct(); parent::__construct();
} }
/**
* Return the current AUTO_INCREMENT value for a given table
*
* @param string $table
* @return int
*/
public function latest(string $table): int {
return $this->execute_query("SELECT AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?", [
self::$name,
self::get_table($table)
])->fetch_assoc()["AUTO_INCREMENT"];
}
/** /**
* Fetch and set the WordPress siteurl from the options table * Fetch and set the WordPress siteurl from the options table
* *

View file

@ -47,6 +47,9 @@
return self::from_name($title); return self::from_name($title);
} }
// Current auto increment value will be the id for this entity
$id = Database::current()->latest(Posts::TABLE_NAME);
$values = [ $values = [
Posts::ID->value => null, Posts::ID->value => null,
Posts::POST_AUTHOR->value => 0, Posts::POST_AUTHOR->value => 0,
@ -59,7 +62,7 @@
Posts::COMMENT_STATUS->value => "closed", Posts::COMMENT_STATUS->value => "closed",
Posts::PING_STATUS->value => "closed", Posts::PING_STATUS->value => "closed",
Posts::POST_PASSWORD->value => "", Posts::POST_PASSWORD->value => "",
Posts::POST_NAME->value => slugify($title), Posts::POST_NAME->value => substr(slugify($title), 0, 200),
Posts::TO_PING->value => "", Posts::TO_PING->value => "",
Posts::PINGED->value => "", Posts::PINGED->value => "",
Posts::POST_MODIFIED->value => date(static::DATETIME_FORMAT), Posts::POST_MODIFIED->value => date(static::DATETIME_FORMAT),
@ -77,7 +80,7 @@
throw new Exception("Failed to create database entity"); throw new Exception("Failed to create database entity");
} }
return self::from_name($title); return new static($id);
} }
public function __construct(public readonly int $id) { public function __construct(public readonly int $id) {
@ -88,48 +91,6 @@
); );
} }
/**
* Get post meta fields for this Post. An array of all post meta is returned if no $key is provided
*
* @param string|null $key Return the value of this post meta key. Null for all post meta fields
* @return string|array|null Returns a string or null if a single key is selected. Array if all post meta fields are returned
*/
public function meta(?string $key = null): string|array|null {
return $key ? PostMeta::get_post_meta($this, $key) : PostMeta::get_all_post_meta($this);
}
/**
* Returns an array of all Terms assigned to this Post
*
* @return array
*/
public function terms(): array {
return Term::from_post($this);
}
/**
* Get, set, or unset featured media for this Post
*
* @param Attachment|false|null $attachment Pass Attachment to set featured media, false to unset, and null (or nothing) to get featured media
* @return Attachment|null Returns an Attachment if featured media is set. Null if no featured media is set
*/
public function featured_media(Attachment|false|null $featured_media = null): ?Attachment {
// Remove featured media from this Post
if ($featured_media === false) {
Attachment::remove_post_featured_media($this);
return null;
}
// Set Attachment as the featured media for this Post
if ($featured_media instanceof Attachment) {
$featured_media->set_post_featured($this);
return $featured_media;
}
return Attachment::from_post_featured($this);
}
public int $post_author { public int $post_author {
get => $this->get(Posts::POST_AUTHOR->value); get => $this->get(Posts::POST_AUTHOR->value);
set (int $post_author) => $this->set(Posts::POST_AUTHOR->value, $post_author); set (int $post_author) => $this->set(Posts::POST_AUTHOR->value, $post_author);
@ -162,27 +123,27 @@
public string $post_status { public string $post_status {
get => $this->get(Posts::POST_STATUS->value); get => $this->get(Posts::POST_STATUS->value);
set (string $post_status) => $this->set(Posts::POST_STATUS->value, $post_status); set (string $post_status) => $this->set(Posts::POST_STATUS->value, substr($post_status, 0, 20));
} }
public string $comment_status { public string $comment_status {
get => $this->get(Posts::COMMENT_STATUS->value); get => $this->get(Posts::COMMENT_STATUS->value);
set (string $comment_status) => $this->set(Posts::COMMENT_STATUS->value, $comment_status); set (string $comment_status) => $this->set(Posts::COMMENT_STATUS->value, substr($comment_status, 0, 20));
} }
public string $ping_status { public string $ping_status {
get => $this->get(Posts::PING_STATUS->value); get => $this->get(Posts::PING_STATUS->value);
set (string $ping_status) => $this->set(Posts::PING_STATUS->value, $ping_status); set (string $ping_status) => $this->set(Posts::PING_STATUS->value, substr($ping_status, 0, 20));
} }
public string $post_password { public string $post_password {
get => $this->get(Posts::POST_PASSWORD->value); get => $this->get(Posts::POST_PASSWORD->value);
set (string $post_password) => $this->set(Posts::POST_PASSWORD->value, $post_password); set (string $post_password) => $this->set(Posts::POST_PASSWORD->value, substr($post_password, 0, 255));
} }
public string $post_name { public string $post_name {
get => $this->get(Posts::POST_NAME->value); get => $this->get(Posts::POST_NAME->value);
set (string $post_name) => $this->set(Posts::POST_NAME->value, $post_name); set (string $post_name) => $this->set(Posts::POST_NAME->value, substr($post_name, 0, 200));
} }
public string $to_ping { public string $to_ping {
@ -222,16 +183,78 @@
public string $post_type { public string $post_type {
get => $this->get(Posts::POST_TYPE->value); get => $this->get(Posts::POST_TYPE->value);
set (string $post_type) => $this->set(Posts::POST_TYPE->value, $post_type); set (string $post_type) => $this->set(Posts::POST_TYPE->value, substr($post_type, 0, 20));
} }
public string $post_mime_type { public string $post_mime_type {
get => $this->get(Posts::POST_MIME_TYPE->value); get => $this->get(Posts::POST_MIME_TYPE->value);
set (string $post_mime_type) => $this->set(Posts::POST_MIME_TYPE->value, $post_mime_type); set (string $post_mime_type) => $this->set(Posts::POST_MIME_TYPE->value, substr($post_mime_type, 0, 100));
} }
public int $comment_count { public int $comment_count {
get => $this->get(Posts::COMMENT_COUNT->value); get => $this->get(Posts::COMMENT_COUNT->value);
set (int $comment_count) => $this->set(Posts::COMMENT_COUNT->value, $comment_count); set (int $comment_count) => $this->set(Posts::COMMENT_COUNT->value, $comment_count);
} }
/**
* Get post meta fields for this Post. An array of all post meta is returned if no $key is provided
*
* @param string|null $key Return the value of this post meta key. Null for all post meta fields
* @return string|array|null Returns a string or null if a single key is selected. Array if all post meta fields are returned
*/
public function meta(?string $key = null): string|array|null {
return $key ? PostMeta::get_post_meta($this, $key) : PostMeta::get_all_post_meta($this);
}
/**
* Returns an array of all Terms assigned to this Post
*
* @return array
*/
public function terms(): array {
return Term::from_post($this);
}
/**
* Add a Term to this Post
*
* @param Term $term
* @return void
*/
public function add_term(Term $term): void {
$term->add_to_post($this);
}
/**
* Remove a Term from this Post
*
* @param Term $term
* @return void
*/
public function remove_term(Term $term): void {
$term->remove_from_post($this);
}
/**
* Get, set, or unset featured media for this Post
*
* @param Attachment|false|null $attachment Pass Attachment to set featured media, false to unset, and null (or nothing) to get featured media
* @return Attachment|null Returns an Attachment if featured media is set. Null if no featured media is set
*/
public function featured_media(Attachment|false|null $featured_media = null): ?Attachment {
// Remove featured media from this Post
if ($featured_media === false) {
Attachment::remove_post_featured_media($this);
return null;
}
// Set Attachment as the featured media for this Post
if ($featured_media instanceof Attachment) {
$featured_media->set_post_featured($this);
return $featured_media;
}
return Attachment::from_post_featured($this);
}
} }

View file

@ -64,6 +64,9 @@
return $model; return $model;
} }
// Current auto increment value will be the id for this entity
$id = Database::current()->latest(PostMetaTable::TABLE_NAME);
$values = [ $values = [
PostMetaTable::META_ID->value => null, PostMetaTable::META_ID->value => null,
PostMetaTable::POST_ID->value => $post->id, PostMetaTable::POST_ID->value => $post->id,
@ -75,7 +78,7 @@
throw new Exception("Failed to create database entity"); throw new Exception("Failed to create database entity");
} }
return new static(self::get_post_meta($post, $meta_key)->id); return new static($id);
} }
public function __construct(public readonly int $id) { public function __construct(public readonly int $id) {
@ -86,17 +89,6 @@
); );
} }
/**
* Delete this post meta field from the database
*
* @return void
*/
public function delete(): void {
$this->db
->from(PostMetaTable::TABLE_NAME)
->delete([PostMetaTable::META_ID->value => $this->id]);
}
public Post $post { public Post $post {
get => new Post($this->get(PostMetaTable::POST_ID->value)); get => new Post($this->get(PostMetaTable::POST_ID->value));
set (Post $post) => $this->set(PostMetaTable::POST_ID->value, $post->id); set (Post $post) => $this->set(PostMetaTable::POST_ID->value, $post->id);
@ -104,11 +96,22 @@
public string $meta_key { public string $meta_key {
get => $this->get(PostMetaTable::META_KEY->value); get => $this->get(PostMetaTable::META_KEY->value);
set (string $meta_key) => $this->set(PostMetaTable::META_KEY->value, $meta_key); set (string $meta_key) => $this->set(PostMetaTable::META_KEY->value, substr($meta_key, 0, 255));
} }
public ?string $meta_value { public ?string $meta_value {
get => $this->get(PostMetaTable::META_VALUE->value); get => $this->get(PostMetaTable::META_VALUE->value);
set (?string $meta_value) => $this->set(PostMetaTable::META_VALUE->value, $meta_value); set (?string $meta_value) => $this->set(PostMetaTable::META_VALUE->value, $meta_value);
} }
/**
* Delete this post meta field from the database
*
* @return void
*/
public function delete(): void {
$this->db
->from(Database::get_table(PostMetaTable::TABLE_NAME))
->delete([PostMetaTable::META_ID->value => $this->id]);
}
} }

View file

@ -7,7 +7,9 @@
use vlw\WP\Database; use vlw\WP\Database;
use vlw\WP\Tables\Taxonomies; use vlw\WP\Tables\Taxonomies;
use vlw\WP\Posts\Taxonomy\Term;
require_once "Term.php";
require_once dirname(__DIR__, 2) . "/Tables/Taxonomies.php"; require_once dirname(__DIR__, 2) . "/Tables/Taxonomies.php";
class Taxonomy extends Model { class Taxonomy extends Model {
@ -15,16 +17,24 @@
* Returns a Taxonomy by taxonomy name * Returns a Taxonomy by taxonomy name
* *
* @param string $name * @param string $name
* @return static|null * @return array
*/ */
public function from(string $taxonomy): ?static { public static function from(string $taxonomy): array {
$query = Database::current() $query = Database::current()
->from(Database::get_table(Taxonomies::TABLE_NAME)) ->from(Database::get_table(Taxonomies::TABLE_NAME))
->where([Taxonomies::TAXONOMY->value => $taxonomy]) ->where([Taxonomies::TAXONOMY->value => $taxonomy])
->limit(1)
->select(Taxonomies::TERM_TAXONOMY_ID->value); ->select(Taxonomies::TERM_TAXONOMY_ID->value);
return $query->num_rows === 1 ? new static($query->fetch_assoc()[Taxonomies::TERM_ID->value]) : null; return array_map(fn(array $taxonomy): static => new static($taxonomy[Taxonomies::TERM_TAXONOMY_ID->value]), $query->fetch_all(MYSQLI_ASSOC));
}
public static function from_term(Term $term): array {
$query = Database::current()
->from(Database::get_table(Taxonomies::TABLE_NAME))
->where([Taxonomies::TERM_ID->value => $term->id])
->select(Taxonomies::TERM_TAXONOMY_ID->value);
return array_map(fn(array $taxonomy): static => new static($taxonomy[Taxonomies::TERM_TAXONOMY_ID->value]), $query->fetch_all(MYSQLI_ASSOC));
} }
/** /**
@ -34,15 +44,14 @@
* @param string $type Post type * @param string $type Post type
* @return static * @return static
*/ */
public static function new(string $taxonomy): static { public static function new(string $taxonomy, Term $term): static {
// Return existing instance of Taxonomy if it exists // Current auto increment value will be the id for this entity
if (self::from($taxonomy)) { $id = Database::current()->latest(Taxonomies::TABLE_NAME);
return self::from($taxonomy);
}
$values = [ $values = [
Taxonomies::TERM_TAXONOMY_ID->value => null, Taxonomies::TERM_TAXONOMY_ID->value => null,
Taxonomies::TERM_ID->value => 0, Taxonomies::TERM_ID->value => $term->id,
Taxonomies::TAXONOMY->value => substr($taxonomy, 0, 32),
Taxonomies::DESCRIPTION->value => "", Taxonomies::DESCRIPTION->value => "",
Taxonomies::PARENT->value => 0, Taxonomies::PARENT->value => 0,
Taxonomies::COUNT->value => 0 Taxonomies::COUNT->value => 0
@ -52,7 +61,8 @@
throw new Exception("Failed to create database entity"); throw new Exception("Failed to create database entity");
} }
return self::from($taxonomy); // Instance this Taxonomy from the last added taxonomy in the database
return new static($id);
} }
public function __construct(public readonly int $id) { public function __construct(public readonly int $id) {
@ -70,7 +80,7 @@
public string $taxonomy { public string $taxonomy {
get => $this->get(Taxonomies::TAXONOMY->value); get => $this->get(Taxonomies::TAXONOMY->value);
set (string $taxonomy) => $this->set(Taxonomies::TAXONOMY->value, $taxonomy); set (string $taxonomy) => $this->set(Taxonomies::TAXONOMY->value, substr($taxonomy, 0, 32));
} }
public string $description { public string $description {
@ -82,4 +92,9 @@
get => $this->get(Taxonomies::PARENT->value) ? new self($this->get(Taxonomies::PARENT->value)) : null; get => $this->get(Taxonomies::PARENT->value) ? new self($this->get(Taxonomies::PARENT->value)) : null;
set (?self $parent) => $this->set(Taxonomies::PARENT->value, $parent->id); set (?self $parent) => $this->set(Taxonomies::PARENT->value, $parent->id);
} }
public int $count {
get => (int) $this->get(Taxonomies::COUNT->value);
set (int $count) => $this->set(Taxonomies::COUNT->value, $count);
}
} }

View file

@ -10,9 +10,11 @@
use vlw\WP\Tables\Terms; use vlw\WP\Tables\Terms;
use vlw\WP\Posts\Taxonomy\Taxonomy; use vlw\WP\Posts\Taxonomy\Taxonomy;
use vlw\WP\Tables\TermRelationships; use vlw\WP\Tables\TermRelationships;
use function vlw\WP\Support\slugify;
require_once dirname(__DIR__, 1) . "/Post.php"; require_once dirname(__DIR__, 1) . "/Post.php";
require_once dirname(__DIR__, 2) . "/Tables/Terms.php"; require_once dirname(__DIR__, 2) . "/Tables/Terms.php";
require_once dirname(__DIR__, 2) . "/Support/Slugify.php";
require_once dirname(__DIR__, 2) . "/Tables/TermRelationships.php"; require_once dirname(__DIR__, 2) . "/Tables/TermRelationships.php";
class Term extends Model { class Term extends Model {
@ -55,18 +57,13 @@
* @return static * @return static
*/ */
public static function new(string $name, ?string $slug = null): static { public static function new(string $name, ?string $slug = null): static {
// Update and return meta key for existing id // Current auto increment value will be the id for this entity
if (self::from_name($name)) { $id = Database::current()->latest(Terms::TABLE_NAME);
$model = self::from_name($name);
$model->slug = $slug;
return $model;
}
$values = [ $values = [
Terms::TERM_ID->value => null, Terms::TERM_ID->value => null,
Terms::NAME->value => $name, Terms::NAME->value => substr($name, 0, 200),
Terms::SLUG->value => $slug, Terms::SLUG->value => substr($slug ? $slug : slugify($name), 0, 200),
Terms::TERM_GROUP->value => 0 Terms::TERM_GROUP->value => 0
]; ];
@ -74,7 +71,7 @@
throw new Exception("Failed to create database entity"); throw new Exception("Failed to create database entity");
} }
return self::from_name($name); return new static($id);
} }
public function __construct(public readonly int $id) { public function __construct(public readonly int $id) {
@ -85,13 +82,28 @@
); );
} }
public string $name {
get => $this->get(Terms::NAME->value);
set (string $name) => $this->set(Terms::NAME->value, substr($name, 0, 200));
}
public string $slug {
get => $this->get(Terms::SLUG->value);
set (string $slug) => $this->set(Terms::SLUG->value, substr($slug, 0, 200));
}
public int $term_group {
get => $this->get(Terms::TERM_GROUP->value);
set (int $term_group) => $this->set(Terms::TERM_GROUP->value, $term_group);
}
/** /**
* Returns the corresponding Taxonomy for this Term * Returns an array of Taxonomies that implement this Term
* *
* @return Taxonomy * @return array
*/ */
public function taxonomy(): Taxonomy { public function taxonomies(): array {
return new Taxonomy($this->id); return Taxonomy::from_term($this);
} }
/** /**
@ -102,7 +114,7 @@
*/ */
public function add_to_post(Post $post): void { public function add_to_post(Post $post): void {
// Bail out if this term has already been added to the target Post // Bail out if this term has already been added to the target Post
if (self::from_post($post)) { if ($this->post_has_term($post)) {
return; return;
} }
@ -117,6 +129,11 @@
if ($query === false) { if ($query === false) {
throw new Exception("Failed to create database entity"); throw new Exception("Failed to create database entity");
} }
// Increase the post count for each Taxonomy implementing this Term
foreach ($this->taxonomies() as $taxonomy) {
$taxonomy->count++;
}
} }
/** /**
@ -126,6 +143,11 @@
* @return void * @return void
*/ */
public function remove_from_post(Post $post): void { public function remove_from_post(Post $post): void {
// Bail out if this Term is not set on the target Post
if (!$this->post_has_term($post)) {
return;
}
$query = Database::current() $query = Database::current()
->from(Database::get_table(TermRelationships::TABLE_NAME)) ->from(Database::get_table(TermRelationships::TABLE_NAME))
->delete([ ->delete([
@ -136,20 +158,20 @@
if ($query === false) { if ($query === false) {
throw new Exception("Failed to create database entity"); throw new Exception("Failed to create database entity");
} }
// Decrease the post count for each Taxonomy implementing this Term
foreach ($this->taxonomies() as $taxonomy) {
$taxonomy->count--;
}
} }
public string $name { /**
get => $this->get(Terms::NAME->value); * Returns true if a given Post has this Term
set (string $name) => $this->set(Terms::NAME->value, $name); *
} * @param Post $post
* @return bool
public string $slug { */
get => $this->get(Terms::SLUG->value); private function post_has_term(Post $post): bool {
set (string $slug) => $this->set(Terms::SLUG->value, $slug); return in_array($this->id, array_column(self::from_post($post), "id"));
}
public int $term_group {
get => $this->get(Terms::TERM_GROUP->value);
set (int $term_group) => $this->set(Terms::TERM_GROUP->value, $term_group);
} }
} }