From 2127b1bdd73ecd2423e443992da4b442f5a3c1a3 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Wed, 04 Feb 2026 21:19:25 +0000
Subject: [PATCH] =Major overhaul of MetaManager.php -> Meta.php and RestRouteManager.php -> Rest.php. Seems to work for JakeVan
---
inc/meta/Meta.php | 295 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 278 insertions(+), 17 deletions(-)
diff --git a/inc/meta/Meta.php b/inc/meta/Meta.php
index 05810e6..1316684 100644
--- a/inc/meta/Meta.php
+++ b/inc/meta/Meta.php
@@ -1,8 +1,6 @@
<?php
namespace JVBase\meta;
-use Exception;
-
if (!defined('ABSPATH')) {
exit;
}
@@ -10,37 +8,62 @@
/**
* Main facade for meta operations
* Fluent API for getting/setting meta values with validation & sanitization
+ *
+ * Usage:
+ * $meta = Meta::forPost($id);
+ * $meta->price = 150;
+ * $meta->save();
+ *
+ * Meta::forPost($id)->set('price', 150)->set('style', 'traditional')->save();
*/
class Meta
{
protected Item $item;
protected Storage $storage;
- protected MetaValidator $validator;
- protected MetaSanitizer $sanitizer;
+ protected Validator $validator;
+ protected Sanitizer $sanitizer;
protected MetaTypeManager $typeManager;
protected bool $autoValidate = true;
protected bool $autoSanitize = true;
+ /** @var array<string, callable[]> */
+ protected array $onChangeCallbacks = [];
+
+ /** @var array<string, callable> */
+ protected array $computed = [];
+
// ─────────────────────────────────────────────────────────────
// Factory Methods
// ─────────────────────────────────────────────────────────────
+ /**
+ * Create Meta instance for a post
+ */
public static function forPost(int $id): self
{
return new self($id, 'post');
}
+ /**
+ * Create Meta instance for a term
+ */
public static function forTerm(int $id): self
{
return new self($id, 'term');
}
+ /**
+ * Create Meta instance for a user
+ */
public static function forUser(int $id): self
{
return new self($id, 'user');
}
+ /**
+ * Create Meta instance for options
+ */
public static function forOptions(?string $baseKey = null): self
{
$instance = new self($baseKey, 'options');
@@ -48,6 +71,129 @@
return $instance;
}
+ /**
+ * Bulk load multiple posts with optional field preloading
+ * @return array<int, Meta>
+ */
+ public static function bulkForPosts(array $ids, array $preloadFields = []): array
+ {
+ return self::bulkFor($ids, 'post', $preloadFields);
+ }
+
+ /**
+ * Bulk load multiple terms with optional field preloading
+ * @return array<int, Meta>
+ */
+ public static function bulkForTerms(array $ids, array $preloadFields = []): array
+ {
+ return self::bulkFor($ids, 'term', $preloadFields);
+ }
+
+ /**
+ * Bulk load multiple users with optional field preloading
+ * @return array<int, Meta>
+ */
+ public static function bulkForUsers(array $ids, array $preloadFields = []): array
+ {
+ return self::bulkFor($ids, 'user', $preloadFields);
+ }
+
+ /**
+ * Generic bulk loader
+ * @return array<int, Meta>
+ */
+ protected static function bulkFor(array $ids, string $type, array $preloadFields = []): array
+ {
+ if (empty($ids)) {
+ return [];
+ }
+
+ $metas = [];
+
+ // Create instances
+ foreach ($ids as $id) {
+ $metas[$id] = new self($id, $type);
+ }
+
+ // Preload fields if specified
+ if (!empty($preloadFields)) {
+ self::bulkPreload($metas, $type, $preloadFields);
+ }
+
+ return $metas;
+ }
+
+ /**
+ * Bulk preload fields for multiple Meta instances
+ * @param Meta[] $metas
+ */
+ protected static function bulkPreload(array $metas, string $objectType, array $fields): void
+ {
+ if (empty($metas) || empty($fields)) {
+ return;
+ }
+
+ $ids = array_keys($metas);
+ $values = Storage::getBulkValues($ids, $objectType, $fields);
+
+ // Distribute results to Meta instances
+ foreach ($values as $id => $fieldValues) {
+ if (!isset($metas[$id])) {
+ continue;
+ }
+
+ $meta = $metas[$id];
+ foreach ($fieldValues as $name => $value) {
+ $config = $meta->config($name) ?? ['type' => 'text'];
+ $field = new Field($name, $value, $config);
+ $meta->item()->setField($field);
+ }
+ }
+ }
+
+ /**
+ * Save multiple Meta instances efficiently
+ * @param Meta[] $metas
+ * @return array<int, bool>
+ */
+ public static function saveBulk(array $metas, bool $updateTimestamp = true): array
+ {
+ // Validate all first
+ $invalid = [];
+ foreach ($metas as $id => $meta) {
+ if (!$meta->isValid()) {
+ $invalid[$id] = $meta->getErrors();
+ }
+ }
+
+ if (!empty($invalid)) {
+ JVB()->error()->log('meta', 'Bulk save has validation errors', [
+ 'invalid_items' => $invalid
+ ], 'warning');
+ }
+
+ // Filter to only valid metas
+ $validMetas = array_filter($metas, fn($m) => $m->isValid());
+
+ // Check overrides before bulk save
+ foreach ($validMetas as $meta) {
+ foreach ($meta->item()->getDirtyFields() as $field) {
+ if ($meta->checkOverrides($field)) {
+ $field->markClean();
+ }
+ }
+ }
+
+ $results = Storage::saveBulk($validMetas, $updateTimestamp);
+
+ // Mark invalid ones as failed
+ foreach ($invalid as $id => $errors) {
+ $results[$id] = false;
+ }
+
+ return $results;
+ }
+
// ─────────────────────────────────────────────────────────────
// Constructor
// ─────────────────────────────────────────────────────────────
@@ -55,8 +201,8 @@
public function __construct(int|string|null $id, string $type)
{
$this->storage = new Storage();
- $this->validator = new MetaValidator();
- $this->sanitizer = new MetaSanitizer();
+ $this->validator = new Validator();
+ $this->sanitizer = new Sanitizer();
$this->typeManager = new MetaTypeManager();
$this->item = $this->buildItem($id, $type);
@@ -118,7 +264,7 @@
public function __isset(string $name): bool
{
- return $this->item->hasField($name);
+ return $this->item->hasField($name) || isset($this->computed[$name]);
}
// ─────────────────────────────────────────────────────────────
@@ -130,6 +276,11 @@
*/
public function get(string $name): mixed
{
+ // Check computed fields first
+ if (isset($this->computed[$name])) {
+ return ($this->computed[$name])($this);
+ }
+
// Return from loaded field if exists
if ($field = $this->item->getField($name)) {
return $field->get();
@@ -146,7 +297,7 @@
}
/**
- * Set a field value (validates & sanitizes)
+ * Set a field value (validates & sanitizes by default)
*/
public function set(string $name, mixed $value): self
{
@@ -157,8 +308,6 @@
$config = ['type' => 'text', 'name' => $name];
}
- $config['name'] = $name;
-
// Validate
if ($this->autoValidate && !$this->validator->validate($value, $config)) {
$field = $this->item->getField($name) ?? new Field($name, $value, $config);
@@ -174,17 +323,27 @@
// Get or create field
$field = $this->item->getField($name);
+ $oldValue = null;
if ($field) {
+ $oldValue = $field->value;
$field->set($value);
} else {
- // Need to load original to track dirty state
+ // Load original to track dirty state
$original = $this->storage->get($this->item, $name);
+ $oldValue = $original;
$field = new Field($name, $original, $config);
$field->set($value);
$this->item->setField($field);
}
+ // Fire change callbacks
+ if (isset($this->onChangeCallbacks[$name]) && $oldValue !== $value) {
+ foreach ($this->onChangeCallbacks[$name] as $callback) {
+ $callback($value, $oldValue, $this);
+ }
+ }
+
return $this;
}
@@ -259,12 +418,36 @@
return $result;
}
+ /**
+ * Delete multiple field values
+ */
+ public function deleteAll(array $names): array
+ {
+ $results = [];
+ foreach ($names as $name) {
+ $results[$name] = $this->delete($name);
+ }
+ return $results;
+ }
+
+ // ─────────────────────────────────────────────────────────────
+ // Repeater Access
+ // ─────────────────────────────────────────────────────────────
+
+ /**
+ * Get repeater accessor for fluent repeater operations
+ */
+ public function repeater(string $name): Repeater
+ {
+ return new Repeater($this, $name);
+ }
+
// ─────────────────────────────────────────────────────────────
// Utility Methods
// ─────────────────────────────────────────────────────────────
/**
- * Get all dirty (changed) fields
+ * Get all dirty (changed) field values
*/
public function getDirty(): array
{
@@ -287,9 +470,7 @@
*/
public function reset(): self
{
- foreach ($this->item->fields as $field) {
- $field->reset();
- }
+ $this->item->resetAll();
return $this;
}
@@ -332,7 +513,17 @@
}
/**
- * Get the underlying item (for rendering, etc)
+ * Re-enable validation and sanitization
+ */
+ public function withDefaults(): self
+ {
+ $this->autoValidate = true;
+ $this->autoSanitize = true;
+ return $this;
+ }
+
+ /**
+ * Get the underlying Item
*/
public function item(): Item
{
@@ -355,11 +546,77 @@
return $this->item->fieldConfigs;
}
+ /**
+ * Get item ID
+ */
+ public function id(): int|string|null
+ {
+ return $this->item->id;
+ }
+
+ /**
+ * Get object type (post, term, user, options)
+ */
+ public function objectType(): string
+ {
+ return $this->item->objectType;
+ }
+
+ /**
+ * Get content type (tattoo, artist, etc)
+ */
+ public function contentType(): ?string
+ {
+ return $this->item->contentType;
+ }
+
+ /**
+ * Eager load all fields
+ */
+ public function eager(): self
+ {
+ $this->getAll();
+ return $this;
+ }
+
+ /**
+ * Convert loaded fields to array
+ */
+ public function toArray(): array
+ {
+ return $this->item->toArray();
+ }
+
+ // ─────────────────────────────────────────────────────────────
+ // Event Callbacks
+ // ─────────────────────────────────────────────────────────────
+
+ /**
+ * Register callback for field changes
+ */
+ public function onChange(string $field, callable $callback): self
+ {
+ $this->onChangeCallbacks[$field][] = $callback;
+ return $this;
+ }
+
+ /**
+ * Register computed/virtual field
+ */
+ public function computed(string $name, callable $getter): self
+ {
+ $this->computed[$name] = $getter;
+ return $this;
+ }
+
// ─────────────────────────────────────────────────────────────
// Protected Helpers
// ─────────────────────────────────────────────────────────────
- protected function checkOverrides(Field $field): bool
+ /**
+ * Check for field update overrides
+ */
+ public function checkOverrides(Field $field): bool
{
$name = $field->name;
$type = $field->type();
@@ -384,6 +641,9 @@
return false;
}
+ /**
+ * Get default value for a field type
+ */
protected function getDefaultValue(string $name): mixed
{
$config = $this->item->getFieldConfig($name);
@@ -396,4 +656,5 @@
default => '',
};
}
+
}
--
Gitblit v1.10.0