راهنمای جامع کار با EnhancedWooCommerceProductManager
معرفی
کلاس EnhancedWooCommerceProductManager
یک ابزار قدرتمند برای مدیریت و دریافت محصولات ووکامرس است. این کلاس امکانات زیر را در اختیار شما قرار میدهد:
- دریافت محصولات با فرمت یکسان و استاندارد
- مرتبسازی محصولات براساس معیارهای مختلف
- فیلتر محصولات براساس دستهبندی، برچسب و برند
- مدیریت کش برای بهبود عملکرد
- مدیریت خطاها بصورت حرفهای
نصب و راهاندازی
- ابتدا فایل کلاس را در مسیر تم یا افزونه خود قرار دهید:
require_once 'class-enhanced-woocommerce-product-manager.php';
- یک نمونه از کلاس ایجاد کنید:
$productManager = new EnhancedWooCommerceProductManager(
posts_per_page: 12, // تعداد محصولات در هر صفحه
paged: 1, // شماره صفحه
enable_cache: true, // فعالسازی کش
cache_duration: HOUR_IN_SECONDS // مدت زمان کش
);
روشهای دریافت محصولات
1. دریافت براساس شناسه محصول
برای دریافت محصولات خاص براساس ID:
// دریافت محصولات با ID های مشخص
$products = $productManager->getFormattedProductsByIdsAndSort(
[1, 2, 3], // آرایهای از ID های محصولات
'price_low' // نوع مرتبسازی
);
2. دریافت براساس دستهبندی
برای دریافت محصولات یک دستهبندی:
// دریافت محصولات یک دستهبندی
$products = $productManager->getFormattedProductsByCategoryAndSort(
56, // شناسه دستهبندی
'price_low' // نوع مرتبسازی
);
// دریافت محصولات چند دستهبندی
$products = $productManager->getFormattedProductsByMultipleCategoriesAndSort(
[56, 57, 58], // آرایهای از شناسههای دستهبندی
'price_low'
);
3. دریافت براساس برچسب
// دریافت محصولات یک برچسب
$products = $productManager->getFormattedProductsByTagAndSort(
'sale', // نام برچسب
'price_low'
);
// دریافت محصولات چند برچسب
$products = $productManager->getFormattedProductsByMultipleTagsAndSort(
['sale', 'new'],
'price_low'
);
4. دریافت براساس برند
// دریافت محصولات یک برند
$products = $productManager->getFormattedProductsByBrandAndSort(
'samsung',
'price_low'
);
// دریافت محصولات چند برند
$products = $productManager->getFormattedProductsByMultipleBrandsAndSort(
['samsung', 'apple'],
'price_low'
);
5. دریافت همه محصولات
// دریافت همه محصولات با مرتبسازی
$products = $productManager->getAllFormattedProductsAndSort('price_low');
انواع مرتبسازی
میتوانید از انواع مرتبسازی زیر استفاده کنید:
price_low
: قیمت از کم به زیادprice_high
: قیمت از زیاد به کمnewest
: جدیدترین محصولاتoldest
: قدیمیترین محصولاتpopularity
: محبوبترین محصولاتdiscount_high
: بیشترین تخفیفrandom
: مرتبسازی تصادفی
تنظیمات اضافی
میتوانید تنظیمات اضافی را به همه متدها ارسال کنید:
$args = [
'posts_per_page' => 20, // تعداد محصولات در صفحه
'paged' => get_query_var('paged', 1), // شماره صفحه
'meta_query' => [
[
'key' => '_stock_status',
'value' => 'instock' // فقط محصولات موجود
]
]
];
$products = $productManager->getAllFormattedProductsAndSort('price_low', $args);
ساختار دادههای خروجی
هر محصول شامل اطلاعات زیر است:
[
'id' => 123, // شناسه محصول
'title' => 'نام محصول',
'link' => 'لینک محصول',
'imageUrl' => 'آدرس تصویر',
'imageSize' => [
'width' => 800,
'height' => 600
],
'original_price' => '100,000 تومان',
'discounted_price' => '80,000 تومان',
'discount_percentage' => 20,
'stock_status' => 'instock',
'categories' => [
[
'id' => 56,
'name' => 'الکترونیک',
'slug' => 'electronics'
]
],
'tags' => [
[
'id' => 12,
'name' => 'تخفیف',
'slug' => 'sale'
]
],
'rating' => 4.5,
'review_count' => 10,
'sku' => 'ABC123',
'type' => 'simple',
'status' => 'publish'
]
مدیریت خطاها
برای مدیریت خطاها از try-catch استفاده کنید:
try {
$products = $productManager->getFormattedProductsByCategoryAndSort(56, 'price_low');
} catch (InvalidArgumentException $e) {
error_log($e->getMessage());
echo 'خطا در دریافت محصولات: ' . $e->getMessage();
}
مثالهای کاربردی
مثال 1: نمایش محصولات پرتخفیف
// دریافت 10 محصول با بیشترین تخفیف
$args = ['posts_per_page' => 10];
$discounted_products = $productManager->getAllFormattedProductsAndSort('discount_high', $args);
foreach ($discounted_products as $product) {
echo sprintf(
'<div class="product">
<h3>%s</h3>
<img src="%s" alt="%s">
<p>قیمت اصلی: %s</p>
<p>قیمت با تخفیف: %s</p>
<p>درصد تخفیف: %s%%</p>
</div>',
$product['title'],
$product['imageUrl'],
$product['title'],
$product['original_price'],
$product['discounted_price'],
$product['discount_percentage']
);
}
مثال 2: فیلتر محصولات موجود یک دستهبندی
$args = [
'meta_query' => [
[
'key' => '_stock_status',
'value' => 'instock'
]
],
'posts_per_page' => 20
];
$category_products = $productManager->getFormattedProductsByCategoryAndSort(
56, // شناسه دستهبندی
'price_low',
$args
);
نکات مهم
- کش: برای بهبود عملکرد، نتایج بصورت خودکار کش میشوند. برای پاک کردن کش:
$productManager->clearCache();
- اعتبارسنجی: تمام ورودیها اعتبارسنجی میشوند و در صورت نامعتبر بودن خطا برمیگردانند.
- پردازش تصاویر: اندازه تصاویر بصورت خودکار محاسبه و برگردانده میشوند.
- قیمتها: قیمتها با فرمت پولی و واحد پول سایت برگردانده میشوند.
پشتیبانی و توسعه
این کلاس بصورت مداوم در حال بهبود است. اگر مشکلی پیدا کردید یا پیشنهادی برای بهبود دارید، لطفاً اطلاع دهید.
برای دریافت آخرین نسخه و گزارش مشکلات میتوانید به مخزن گیتهاب مراجعه کنید:
[لینک مخزن گیتهاب]
پرسشهای متداول
چرا از این کلاس استفاده کنیم؟
- استانداردسازی خروجی محصولات
- مدیریت بهتر کش
- کد تمیزتر و قابل نگهداری
- مدیریت خطاهای بهتر
آیا این کلاس با نسخههای مختلف ووکامرس سازگار است؟
بله، این کلاس با نسخههای مختلف ووکامرس (از نسخه 3.0 به بالا) سازگار است.
آیا میتوان این کلاس را گسترش داد؟
بله، میتوانید با ارثبری از این کلاس، متدهای جدید اضافه کنید یا متدهای موجود را تغییر دهید.
کد کامل کلاس:
<?php
namespace App;
use Exception;
use InvalidArgumentException;
use WP_Query;
class EnhancedWooCommerceProductManager
{
private bool $cache_enabled;
private int $posts_per_page;
private int $paged;
private int $cache_duration;
private array $valid_sort_types = [
'newest',
'oldest',
'price_low',
'price_high',
'popularity',
'discount_high',
'random'
];
public function __construct(
int $posts_per_page = 12,
int $paged = 1,
bool $enable_cache = true,
int $cache_duration = HOUR_IN_SECONDS
) {
$this->cache_enabled = $enable_cache;
$this->posts_per_page = max(1, $posts_per_page);
$this->paged = max(1, $paged);
$this->cache_duration = $cache_duration;
}
/**
* Get formatted products with all necessary data
*
* @param array $args Query arguments
* @return array
* @throws InvalidArgumentException
*/
public function getFormattedProducts(array $args = []): array
{
try {
$raw_products = $this->getProducts($args);
return array_map([$this, 'formatProductData'], $raw_products);
} catch (Exception $e) {
error_log("Product formatting error: " . $e->getMessage());
throw new InvalidArgumentException("Failed to format products: " . $e->getMessage());
}
}
/**
* Get formatted products by IDs
*
* @param array $product_ids Array of product IDs
* @return array
*/
public function getFormattedProductsByIds(array $product_ids): array
{
if (empty($product_ids)) {
return [];
}
$args = [
'post__in' => array_map('absint', $product_ids),
'orderby' => 'post__in'
];
return $this->getFormattedProducts($args);
}
/**
* Get formatted products sorted by specified type
*
* @param string $sort_type Sort type
* @param array $args Additional arguments
* @return array
*/
public function getFormattedProductsSortedBy(string $sort_type, array $args = []): array
{
return $this->getFormattedProducts(
$this->prepareSortArgs($sort_type, $args)
);
}
/**
* Format single product data
*
* @param WP_Post $product_post
* @return array
*/
private function formatProductData($product_post): array
{
try {
$product = wc_get_product($product_post->ID);
if (!$product) {
throw new Exception("Invalid product ID: {$product_post->ID}");
}
return [
'id' => $product->get_id(),
'title' => $product->get_name(),
'link' => get_permalink($product->get_id()),
'imageUrl' => $this->getProductImageUrl($product),
'imageSize' => $this->getProductImageSize($product),
'original_price' => $this->getFormattedPrice($product->get_regular_price()),
'discounted_price' => $this->getFormattedPrice($product->get_sale_price()),
'discount_percentage' => $this->calculateDiscount($product->get_id()),
'stock_status' => $product->get_stock_status(),
'categories' => $this->getProductCategories($product),
'tags' => $this->getProductTags($product),
'rating' => $product->get_average_rating(),
'review_count' => $product->get_review_count(),
'sku' => $product->get_sku(),
'type' => $product->get_type(),
'status' => $product->get_status()
];
} catch (Exception $e) {
error_log("Product formatting error: " . $e->getMessage());
return [
'id' => $product_post->ID,
'error' => 'Failed to format product data'
];
}
}
/**
* Get product image URL with error handling
*/
private function getProductImageUrl($product): ?string
{
try {
$image_id = $product->get_image_id();
if (!$image_id) {
return null;
}
return wp_get_attachment_image_url($image_id, 'full');
} catch (Exception $e) {
error_log("Error getting product image URL: " . $e->getMessage());
return null;
}
}
/**
* Get product image dimensions
*/
private function getProductImageSize($product): array
{
try {
$image_id = $product->get_image_id();
if (!$image_id) {
return ['width' => 0, 'height' => 0];
}
$metadata = wp_get_attachment_metadata($image_id);
return [
'width' => $metadata['width'] ?? 0,
'height' => $metadata['height'] ?? 0
];
} catch (Exception $e) {
error_log("Error getting image size: " . $e->getMessage());
return ['width' => 0, 'height' => 0];
}
}
/**
* Format price with currency
*/
private function getFormattedPrice(?string $price): ?string
{
if (empty($price)) {
return null;
}
return html_entity_decode(strip_tags(wc_price($price)));
}
/**
* Get product categories
*/
private function getProductCategories($product): array
{
try {
$categories = [];
$terms = get_the_terms($product->get_id(), 'product_cat');
if (!empty($terms) && !is_wp_error($terms)) {
foreach ($terms as $term) {
$categories[] = [
'id' => $term->term_id,
'name' => $term->name,
'slug' => $term->slug
];
}
}
return $categories;
} catch (Exception $e) {
error_log("Error getting product categories: " . $e->getMessage());
return [];
}
}
/**
* Get product tags
*/
private function getProductTags($product): array
{
try {
$tags = [];
$terms = get_the_terms($product->get_id(), 'product_tag');
if (!empty($terms) && !is_wp_error($terms)) {
foreach ($terms as $term) {
$tags[] = [
'id' => $term->term_id,
'name' => $term->name,
'slug' => $term->slug
];
}
}
return $tags;
} catch (Exception $e) {
error_log("Error getting product tags: " . $e->getMessage());
return [];
}
}
/**
* Prepare sorting arguments
*/
private function prepareSortArgs(string $sort_type, array $args = []): array
{
if (!in_array($sort_type, $this->valid_sort_types)) {
throw new InvalidArgumentException(
"Invalid sort type. Valid types are: " . implode(', ', $this->valid_sort_types)
);
}
$sort_options = $this->getSortOptions();
return array_merge($args, $sort_options[$sort_type]);
}
/**
* Get products based on provided arguments
*
* @param array $args Query arguments
* @return array
* @throws InvalidArgumentException
*/
public function getProducts(array $args = []): array
{
try {
$args = $this->prepareQueryArgs($args);
$cache_key = $this->generateCacheKey($args);
if ($this->shouldUseCache($cache_key)) {
return $this->getFromCache($cache_key);
}
$products = $this->executeQuery($args);
$this->cacheResults($cache_key, $products);
return $products;
} catch (Exception $e) {
error_log("EnhancedWooCommerceProductManager Error: " . $e->getMessage());
throw new InvalidArgumentException("Failed to fetch products: " . $e->getMessage());
}
}
/**
* Get products by category with validation
*/
public function getProductsByCategory(string $category, array $args = []): array
{
if (empty($category)) {
throw new InvalidArgumentException("Category slug cannot be empty");
}
if (!term_exists($category, 'product_cat')) {
throw new InvalidArgumentException("Invalid category slug: {$category}");
}
$args['tax_query'] = [[
'taxonomy' => 'product_cat',
'field' => 'slug',
'terms' => $category,
'operator' => 'IN'
]];
return $this->getProducts($args);
}
/**
* Get products by tag with validation
*/
public function getProductsByTag(string $tag, array $args = []): array
{
if (empty($tag)) {
throw new InvalidArgumentException("Tag slug cannot be empty");
}
$args['tax_query'] = [[
'taxonomy' => 'product_tag',
'field' => 'slug',
'terms' => $tag,
'operator' => 'IN'
]];
return $this->getProducts($args);
}
/**
* Sort products with validation
*/
public function sortBy(string $type, array $args = []): array
{
if (!in_array($type, $this->valid_sort_types)) {
throw new InvalidArgumentException(
"Invalid sort type. Valid types are: " . implode(', ', $this->valid_sort_types)
);
}
$sort_options = $this->getSortOptions();
return $this->getProducts(array_merge($args, $sort_options[$type]));
}
/**
* Get products with highest discount
*/
public function getProductsByHighestDiscount(array $args = []): array
{
$products = $this->getProducts($args);
return $this->sortProductsByDiscount($products);
}
/**
* Clear cache with specific patterns
*/
public function clearCache(string $pattern = 'wc_products_'): bool
{
try {
global $wpdb;
$pattern = $wpdb->esc_like('_transient_' . $pattern);
$result = $wpdb->query(
$wpdb->prepare(
"DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
$pattern . '%'
)
);
wp_cache_flush();
return $result !== false;
} catch (Exception $e) {
error_log("Cache clearing failed: " . $e->getMessage());
return false;
}
}
/**
* Private helper methods
*/
private function prepareQueryArgs(array $args): array
{
$default_args = [
'post_type' => 'product',
'post_status' => 'publish',
'posts_per_page' => $this->posts_per_page,
'paged' => $this->paged,
'no_found_rows' => !$this->shouldPaginate($args)
];
return wp_parse_args($args, $default_args);
}
private function generateCacheKey(array $args): string
{
return 'wc_products_' . md5(serialize($args) . $this->cache_duration);
}
private function shouldUseCache(string $cache_key): bool
{
return $this->cache_enabled && get_transient($cache_key) !== false;
}
private function getFromCache(string $cache_key)
{
return get_transient($cache_key);
}
private function executeQuery(array $args): array
{
$query = new WP_Query($args);
return $query->posts;
}
private function cacheResults(string $cache_key, array $products): void
{
if ($this->cache_enabled) {
set_transient($cache_key, $products, $this->cache_duration);
}
}
private function calculateDiscount(int $product_id): float
{
$product = wc_get_product($product_id);
if (!$product || !$product->is_on_sale()) {
return 0.0;
}
$regular_price = (float) $product->get_regular_price();
$sale_price = (float) $product->get_sale_price();
if ($regular_price <= 0) {
return 0.0;
}
return round((($regular_price - $sale_price) / $regular_price) * 100, 2);
}
private function sortProductsByDiscount(array $products): array
{
usort($products, function ($a, $b) {
$discount_a = $this->calculateDiscount($a->ID);
$discount_b = $this->calculateDiscount($b->ID);
return $discount_b <=> $discount_a;
});
return $products;
}
private function getSortOptions(): array
{
return [
'newest' => [
'orderby' => 'date',
'order' => 'DESC'
],
'oldest' => [
'orderby' => 'date',
'order' => 'ASC'
],
'price_low' => [
'orderby' => 'meta_value_num',
'meta_key' => '_price',
'order' => 'ASC'
],
'price_high' => [
'orderby' => 'meta_value_num',
'meta_key' => '_price',
'order' => 'DESC'
],
'popularity' => [
'orderby' => 'meta_value_num',
'meta_key' => 'total_sales',
'order' => 'DESC'
],
'discount_high' => [
'orderby' => 'meta_value_num',
'meta_key' => '_sale_price',
'order' => 'ASC'
],
'random' => [
'orderby' => 'rand'
]
];
}
private function shouldPaginate(array $args): bool
{
return !isset($args['no_found_rows']) || $args['no_found_rows'] === false;
}
public function sortFormattedProducts(array $products, string $sort_type): array
{
if (empty($products)) {
return [];
}
switch ($sort_type) {
case 'price_low':
usort($products, function ($a, $b) {
$price_a = $this->getPriceForSorting($a);
$price_b = $this->getPriceForSorting($b);
return $price_a <=> $price_b;
});
break;
case 'price_high':
usort($products, function ($a, $b) {
$price_a = $this->getPriceForSorting($a);
$price_b = $this->getPriceForSorting($b);
return $price_b <=> $price_a;
});
break;
case 'discount_high':
usort($products, function ($a, $b) {
return $b['discount_percentage'] <=> $a['discount_percentage'];
});
break;
case 'newest':
usort($products, function ($a, $b) {
return $b['id'] <=> $a['id'];
});
break;
case 'oldest':
usort($products, function ($a, $b) {
return $a['id'] <=> $b['id'];
});
break;
}
return $products;
}
public function getFormattedProductsByIdsAndSort(array $product_ids, string $sort_type = 'price_low'): array
{
$products = $this->getFormattedProductsByIds($product_ids);
return $this->sortFormattedProducts($products, $sort_type);
}
private function getPriceForSorting(array $product): float
{
$price = $product['discounted_price'] ?? $product['original_price'] ?? '0';
return (float) preg_replace('/[^0-9.]/', '', $price);
}
public function getFormattedProductsByTagAndSort(
string $tag,
string $sort_type = 'price_low',
array $args = []
): array {
if (empty($tag)) {
throw new InvalidArgumentException("Tag slug cannot be empty");
}
$args['tax_query'] = [[
'taxonomy' => 'product_tag',
'field' => 'slug',
'terms' => $tag,
'operator' => 'IN'
]];
$products = $this->getFormattedProducts($args);
return $this->sortFormattedProducts($products, $sort_type);
}
public function getFormattedProductsByMultipleTagsAndSort(
array $tags,
string $sort_type = 'price_low',
array $args = []
): array {
if (empty($tags)) {
throw new InvalidArgumentException("Tags array cannot be empty");
}
$args['tax_query'] = [[
'taxonomy' => 'product_tag',
'field' => 'slug',
'terms' => $tags,
'operator' => 'IN'
]];
$products = $this->getFormattedProducts($args);
return $this->sortFormattedProducts($products, $sort_type);
}
public function getAllFormattedProductsAndSort(string $sort_type = 'price_low', array $args = []): array
{
$default_args = [
'post_type' => 'product',
'post_status' => 'publish',
'posts_per_page' => $this->posts_per_page,
'paged' => $this->paged
];
$query_args = wp_parse_args($args, $default_args);
$products = $this->getFormattedProducts($query_args);
return $this->sortFormattedProducts($products, $sort_type);
}
public function getFormattedProductsByBrandAndSort(string $brand, string $sort_type = 'price_low', array $args = []): array
{
if (empty($brand)) {
throw new InvalidArgumentException("Brand slug cannot be empty");
}
$args['tax_query'] = [[
'taxonomy' => 'product_brand', // یا نام تکسونومی برند شما
'field' => 'slug',
'terms' => $brand,
'operator' => 'IN'
]];
$products = $this->getFormattedProducts($args);
return $this->sortFormattedProducts($products, $sort_type);
}
public function getFormattedProductsByMultipleBrandsAndSort(array $brands, string $sort_type = 'price_low', array $args = []): array
{
if (empty($brands)) {
throw new InvalidArgumentException("Brands array cannot be empty");
}
$args['tax_query'] = [[
'taxonomy' => 'product_brand', // یا نام تکسونومی برند شما
'field' => 'slug',
'terms' => $brands,
'operator' => 'IN'
]];
$products = $this->getFormattedProducts($args);
return $this->sortFormattedProducts($products, $sort_type);
}
public function getFormattedProductsByCategoryAndSort($category_id, string $sort_type = 'price_low', array $args = []): array
{
if (empty($category_id)) {
throw new InvalidArgumentException("Category ID cannot be empty");
}
// Convert to integer and validate
$category_id = (int) $category_id;
$term = get_term($category_id, 'product_cat');
if (!$term || is_wp_error($term)) {
throw new InvalidArgumentException("Invalid category ID: {$category_id}");
}
$args['tax_query'] = [[
'taxonomy' => 'product_cat',
'field' => 'term_id',
'terms' => $category_id,
'operator' => 'IN'
]];
$products = $this->getFormattedProducts($args);
return $this->sortFormattedProducts($products, $sort_type);
}
/**
* Get formatted products by multiple category IDs with sorting
*
* @param array $category_ids Array of category IDs
* @param string $sort_type Sort type
* @param array $args Additional arguments
* @return array
*/
public function getFormattedProductsByMultipleCategoriesAndSort(array $category_ids, string $sort_type = 'price_low', array $args = []): array
{
if (empty($category_ids)) {
throw new InvalidArgumentException("Category IDs array cannot be empty");
}
// Convert all IDs to integers
$category_ids = array_map('intval', $category_ids);
// Validate all category IDs
foreach ($category_ids as $id) {
$term = get_term($id, 'product_cat');
if (!$term || is_wp_error($term)) {
throw new InvalidArgumentException("Invalid category ID: {$id}");
}
}
$args['tax_query'] = [[
'taxonomy' => 'product_cat',
'field' => 'term_id',
'terms' => $category_ids,
'operator' => 'IN'
]];
$products = $this->getFormattedProducts($args);
return $this->sortFormattedProducts($products, $sort_type);
}
}