Jake Vanderwerf
2026-05-31 d7e7d248cbe41cd7a9ef9c2fb022b6c4831f99a3
inc/meta/Storage.php
@@ -2,6 +2,7 @@
namespace JVBase\meta;
use Exception;
use JVBase\managers\Cache;
if (!defined('ABSPATH')) {
   exit;
@@ -34,6 +35,16 @@
         return $this->getWpDefault($item, $name);
      }
      // Taxonomy fields are stored in term_relationships, not meta
      $config = $item->getFieldConfig($name);
      if ($config
         && (
            ($config['type'] ?? '') === 'taxonomy'
            || (($config['type']??'') === 'selector' && ($config['subtype']??'') === 'taxonomy')
         ) && !isset($config['taxonomy_type'])) {
         return $this->getTaxonomyField($item, $config);
      }
      $metaKey = BASE . $name;
      return match ($item->objectType) {
@@ -56,23 +67,52 @@
      $defaults = Item::WP_DEFAULTS[$item->objectType] ?? [];
      $wpFields = array_intersect($defaults, $fieldNames);
      $metaFields = array_diff($fieldNames, $wpFields);
      // Separate taxonomy fields from regular meta fields
      $taxonomyFields = [];
      $metaFields = [];
      foreach (array_diff($fieldNames, $wpFields) as $name) {
         $config = $item->getFieldConfig($name);
         if ($config
            && (
               ($config['type'] ?? '') === 'taxonomy'
               || (($config['type']??'') === 'selector' && ($config['subtype']??'') === 'taxonomy')
            ) && (!isset($config['taxonomy_type']) || !isset($config['isReference']))) {
            $taxonomyFields[$name] = $config;
         } else {
            $metaFields[] = $name;
         }
      }
      $values = [];
      // Get meta fields in bulk query
      if (!empty($metaFields)) {
         $values = $this->bulkGetMeta($item, $metaFields);
      }
      // Get WP default fields
      foreach ($wpFields as $name) {
         $values[$name] = $this->getWpDefault($item, $name);
      }
      foreach ($taxonomyFields as $name => $config) {
         $values[$name] = $this->getTaxonomyField($item, $config);
      }
      return $values;
   }
   protected function getTaxonomyField(Item $item, array $config): string
   {
      $taxonomy = jvbCheckBase($config['taxonomy']);
      $terms = wp_get_object_terms($item->id, $taxonomy, ['fields' => 'ids']);
      if (is_wp_error($terms) || empty($terms)) {
         return '';
      }
      return implode(',', $terms);
   }
   /**
    * Save a single field
    */
@@ -83,18 +123,21 @@
      }
      if ($field->isTaxonomy()) {
         error_log('Saving Taxonomy field with set_object_terms');
         return $this->saveTaxonomyField($item, $field);
      }
      $metaKey = BASE . $field->name;
      return match ($item->objectType) {
         'post' => update_post_meta($item->id, $metaKey, $field->value) !== false,
         'term' => update_term_meta($item->id, $metaKey, $field->value) !== false,
         'user', 'integrations' => update_user_meta($item->id, $metaKey, $field->value) !== false,
      $result =  match ($item->objectType) {
         'post' => (bool)update_post_meta($item->id, $metaKey, $field->value),
         'term' => (bool)update_term_meta($item->id, $metaKey, $field->value),
         'user', 'integrations' => (bool)update_user_meta($item->id, $metaKey, $field->value),
         'options' => $this->saveOption($item, $field),
         default => false
      };
      error_log('Result: '.print_r($result, true));
      return $result;
   }
   /**
@@ -109,10 +152,10 @@
      }
      $this->wpdb->query('START TRANSACTION');
      try {
         foreach ($dirty as $field) {
            if (!$this->saveField($item, $field)) {
               error_log("Could not save field: {$field->name}");
               throw new Exception("Failed to save field: {$field->name}");
            }
            $field->markClean();
@@ -121,10 +164,7 @@
         $this->wpdb->query('COMMIT');
         // Update post modified timestamp
         if ($updateTimestamp && $item->objectType === 'post' && $item->id) {
            wp_update_post(['ID' => $item->id]);
         }
         Cache::invalidateItem($item->objectType, $item->id);
         $this->clearCache($item);
         return true;
@@ -394,7 +434,7 @@
   {
      return match ($name) {
         'post_title' => get_the_title($item->id),
         'post_excerpt' => get_the_excerpt($item->id),
         'post_excerpt' => has_excerpt($item->id) ? get_the_excerpt($item->id):'',
         'post_content' => get_post_field('post_content', $item->id),
         default => $item->wpObject->$name ?? ''
      };
@@ -403,7 +443,7 @@
   protected function getTermField(Item $item, string $name): mixed
   {
      return match ($name) {
         'term_name' => get_term_field('name', $item->id),
         'name' => get_term_field('name', $item->id),
         'description' => get_term_field('description', $item->id),
         default => ''
      };
@@ -426,9 +466,17 @@
      $value = $field->value;
      if (in_array($name, ['featured_image', 'post_thumbnail'])) {
         if (empty($value)) {
            return delete_post_thumbnail($item->id);
         }
         return set_post_thumbnail($item->id, $value) !== false;
      }
      // Special handling for post_status (trash/delete require specific functions)
      if ($item->objectType === 'post' && $name === 'post_status') {
         return $this->updatePostStatus($item->id, $value);
      }
      return match ($item->objectType) {
         'post' => wp_update_post(['ID' => $item->id, $name => $value]) !== 0,
         'term' => !is_wp_error(wp_update_term($item->id, $item->wpObject->taxonomy, [
@@ -440,11 +488,59 @@
      };
   }
   /**
    * Update post status with proper WordPress functions
    *
    * WordPress doesn't handle trash/delete via wp_update_post():
    * - wp_trash_post() required for trashing
    * - wp_delete_post() required for deletion
    * - 'delete' is not even a valid post_status value
    *
    * @param int $postId Post ID
    * @param string $status New status (trash, delete, publish, draft, etc.)
    * @return bool Success
    */
   protected function updatePostStatus(int $postId, string $status): bool
   {
      // Handle trash status
      if ($status === 'trash') {
         $result = wp_trash_post($postId);
         if ($result === false || $result === null) {
            error_log("[Storage] Failed to trash post {$postId}");
            return false;
         }
         return true;
      }
      // Handle permanent deletion
      if ($status === 'delete') {
         $result = wp_delete_post($postId, true); // true = force delete, bypass trash
         if ($result === false || $result === null) {
            error_log("[Storage] Failed to delete post {$postId}");
            return false;
         }
         return true;
      }
      // Handle all other statuses (publish, draft, pending, private, future)
      $result = wp_update_post([
         'ID' => $postId,
         'post_status' => $status
      ]);
      if ($result === 0 || is_wp_error($result)) {
         $error = is_wp_error($result) ? $result->get_error_message() : 'Unknown error';
         error_log("[Storage] Failed to update post {$postId} status to {$status}: {$error}");
         return false;
      }
      return true;
   }
   protected function saveTaxonomyField(Item $item, Field $field): bool
   {
      $taxonomy = jvbCheckBase($field->config['taxonomy']);
      $value = $field->value;
      if (empty(trim((string)$value))) {
         wp_set_object_terms($item->id, [], $taxonomy, false);
         return true;
@@ -644,8 +740,20 @@
            set_post_thumbnail($id, $fields['post_thumbnail']);
            unset($fields['post_thumbnail']);
         }
         if (isset($fields['post_thumbnail'])) {
            if (empty($fields['post_thumbnail'])) {
               delete_post_thumbnail($id);
            } else {
               set_post_thumbnail($id, $fields['post_thumbnail']);
            }
            unset($fields['post_thumbnail']);
         }
         if (isset($fields['featured_image'])) {
            set_post_thumbnail($id, $fields['featured_image']);
            if (empty($fields['featured_image'])) {
               delete_post_thumbnail($id);
            } else {
               set_post_thumbnail($id, $fields['featured_image']);
            }
            unset($fields['featured_image']);
         }