芝麻web文件管理V1.00
编辑当前文件:/home/royashxg/www/wp-content/plugins/fluentformpro/src/Components/DynamicField/DynamicBase.php
source = $source; $this->joinTables = $joins; $this->config = $config; /** * Database instance * @var $db WPDBConnection */ $db = App::getInstance('db'); $this->wpdb = $db->getWPDB(); if ($tableName) { $this->model = $db->table($tableName); } $this->filterPrefix = "fluentform/dynamic_field_filter_{$this->source}"; } /** * Get the selectable columns for the query. * * @return array */ public abstract function selectableColumns(); /** * Get the supported columns for the query. * * @return array */ public abstract function getSupportedColumns(); /** * Retrieve the default configuration settings. * * @return array */ public abstract function getDefaultConfig(); /** * Retrieve the value options. * * @return array */ public abstract function getValueOptions(); /** * Set the configuration. * * @param mixed $config The configuration to set. * @return void */ public function setConfig($config) { $this->config = $config; } /** * Magic method to access object properties dynamically. * * @param string $name * @return mixed|null The value of the property if it exists, otherwise null. */ public function __get($name) { if (property_exists($this, $name)) { return $this->{$name}; } return null; } /** * Retrieves the result data including result counts and valid options. * * @return array The result data including total counts, valid counts, valid options, and all options. */ public function getResult() { $this->setResult(); $validOptions = $this->getAdvanceOptions(); return [ 'result_counts' => [ 'total' => count($this->result), 'valid' => count($validOptions), ], 'valid_options' => $validOptions, 'all_options' => $this->result, ]; } /** * Sets the result based on the configured filters. * * @return void */ public function setResult() { // Prepare filter groups $filters = $this->formatAndSanitizeFilters(); // Clone the model to ensure a fresh instance $query = clone $this->model; $select = $this->selectableColumns(); // Adjust select and join tables if join tables filter present foreach ($this->joinTables as $table) { if (Arr::isTrue($table, 'enable') && $joinInfo = Arr::get($table, 'join')) { $select = array_merge($select, Arr::get($table, 'columns')); list ($table, $one, $operator, $two) = $joinInfo; $query->join($table, $one, $operator, $two); } } // Apply filter groups $this->applyFiltersQuery($filters, $query); // Apply sorting, ordering, and limit $this->applySortingOrderAndLimit($query); // Execute the query $result = $query->get($select); $this->result = $result ?: []; } /** * Apply the filters to the query. * * This method applies the given filters to the query using dynamic WHERE clauses. * * @param array $filters The filters to apply. * @return void */ protected function applyFiltersQuery($filters, $query) { $query->where(function ($query) use ($filters) { foreach ($filters as $groups) { $query->orWhere(function ($query) use ($groups) { foreach ($groups as $group) { list ($column, $operator, $value) = $group; if (is_array($value)) { if ('IN' === $operator) { $query->whereIn($column, $value); } elseif ('NOT IN' === $operator) { $query->whereNotIn($column, $value); } elseif ('BETWEEN' === $operator) { $query->whereBetween($column, $value); } elseif ('NOT BETWEEN' === $operator) { $query->whereNotBetween($column, $value); } } else { $query->where($column, $operator, $value); } } }); } }); } /** * Apply sorting, ordering, and limit clauses to the query based on the configuration. * * @return void */ protected function applySortingOrderAndLimit($query) { $defaultConfig = $this->getDefaultConfig(); $defaultSortBy = Arr::get($defaultConfig, 'sort_by'); // Ensure unique results by ID or term_id if enable $uniqueResult = Arr::get($this->config, 'unique_result'); if ('yes' === $uniqueResult && $defaultSortBy) { $query->groupBy($defaultSortBy); } // Sort by column $sortBy = Arr::get($this->config, 'sort_by', $defaultSortBy); if (!$this->isSupportedColumn($sortBy)) { $sortBy = $defaultSortBy; } // If sorting by a join table column and there is no join table filter, use the default sort by column foreach ($this->joinTables as $table) { if (!Arr::isTrue($table, 'enable') && in_array($sortBy, Arr::get($table, 'columns'))) { $sortBy = $defaultSortBy; break; } } // Order by $defaultOrderBy = Arr::get($defaultConfig, 'order_by'); $orderBy = Arr::get($this->config, 'order_by', $defaultOrderBy); if (!in_array($orderBy, ['ASC', 'DESC'])) { $orderBy = $defaultOrderBy; } // Result Limit $limit = intval(Arr::get($this->config, 'result_limit', 0)); if (!$limit) { $limit = $this->getResultLimit(); } $query->orderBy($sortBy, $orderBy)->limit($limit); } /** * Format and sanitize the filters based on the configuration. * * This method prepares the filter groups, sanitizes them, and update tables join. * * @return array The formatted and sanitized filters. */ protected function formatAndSanitizeFilters() { if ('basic' === Arr::get($this->config, 'query_type')) { $filters = apply_filters("fluentform/dynamic_field_basic_filters_{$this->source}", [], Arr::get($this->config, 'basic_query', [])); } else { $filters = Arr::get($this->config, 'filters', []); } // Use default filters if not has a valid filter if (empty($filters)) { $filters = Arr::get($this->getDefaultConfig(), 'filters', []); } $formattedFilters = []; foreach ($filters as $groupsIndex => $groups) { if (!is_array($groups)) { continue; } foreach ($groups as $group) { if ($group = $this->sanitizeFilterGroup($group)) { list ($column) = $group; $this->maybeEnableJoinTablesByColumn($column); $formattedFilters[$groupsIndex][] = $group; } } } return $formattedFilters; } /** * Maybe Enable join tables base on filter group column. * * @param string $column The filter group column. */ protected function maybeEnableJoinTablesByColumn($column) { foreach ($this->joinTables as &$join) { if (!Arr::isTrue($join, 'enable') && in_array($column, Arr::get($join, 'columns'))) { $join['enable'] = true; } } } /** * Sanitizes the provided filter group. * * @param array $group The filter group to sanitize. * @return array|false The sanitized filter group, or false if the group is not valid or cannot be sanitized. */ protected function sanitizeFilterGroup($group) { if (!$this->isValidGroup($group)) { return false; } $column = Arr::get($group, 'column'); $operator = Arr::get($group, 'operator'); $value = Arr::get($group, 'value'); $value = $this->sanitizeValueByColumn($value, $column, $operator); if (null === $value || (is_array($value) && empty($value))) { return false; } return $this->sanitizeFilterGroupForEluquentModel($column, $operator, $value); } /** * Checks if the provided group is valid. * * @param array $group The group to check. * @return bool True if the group is valid, false otherwise. */ protected function isValidGroup($group) { if (!$this->isSupportedColumn(Arr::get($group, 'column'))) { return false; } if (!$this->isValidOperator(Arr::get($group, 'operator'))) { return false; } return true; } /** * Checks if the provided column is supported. * * @param string $column * @return bool */ protected function isSupportedColumn($column) { if (!$column) { return false; } return Arr::exists($this->getSupportedColumns(), $column); } /** * Checks if the provided operator is valid. * * @param string $operator The operator to check. * @return bool True if the operator is valid, false otherwise. */ protected function isValidOperator($operator) { if (!$operator) { return false; } return Arr::exists(DynamicFieldHelper::getOperators(), $operator); } /** * Sanitizes a value based on the column and operator. * * This method determines the appropriate sanitization callback function based on the column, * and then sanitizes the value using the provided callback function or the WordPress database prepare method. * * @param mixed $value The value to sanitize. * @param string $column The column name. * @param string $operator The operator used for sanitization. * @return mixed The sanitized value. */ protected function sanitizeValueByColumn($value, $column, $operator) { $callback = null; if (in_array($column, DynamicFieldHelper::numericColumns())) { $callback = 'absint'; } elseif (in_array($column, DynamicFieldHelper::dateColumns())) { $value = $this->sanitizeDate($value); } elseif ('is_favourite' == $column) { $value = (int)Arr::isTrue(['is_favorites' => $value],'is_favorites'); } return $this->sanitizeValue($value, $callback, $operator); } protected function sanitizeDate($date) { if (is_array($date)) { foreach ($date as $i => &$d) { $d = date('Y-m-d H:i:s', strtotime($d)); if (0 !== $i) { $d = str_replace('00:00:00', '23:59:59', $d); } } } else { $date = date('Y-m-d H:i:s', strtotime($date)); } return $date; } /** * Sanitizes a value based on a callback function or the WordPress database prepare method. * * @param mixed $value The value to sanitize. * @param callable|null $callback The callback function to apply to the value. * @param string $operator The operator used for sanitization. * @return mixed The sanitized value or null if the input value is null. */ protected function sanitizeValue($value, $callback, $operator) { if (null === $value) { return null; } $value = $this->resolveValueForSanitize($value, $operator); if (is_array($value)) { if (is_callable($callback)) { $value = array_filter(array_map($callback, $value)); } else { $value = array_filter(array_map(function ($v) { return $this->sanitizeWithWPDB($v); }, $value)); } } elseif (is_callable($callback)) { $value = $callback($value); } else { $value = $this->sanitizeWithWPDB($value); } return $value; } /** * Resolves the value for sanitization based on the provided operator. * * @param mixed $value The value to resolve for sanitization. * @param string $operator The operator to determine how the value should be resolved. * @return mixed The resolved value for sanitization. */ protected function resolveValueForSanitize($value, $operator) { switch ($operator) { case 'IN': case 'NOT IN': if (is_string($value)) { $value = explode(',', $value); } $value = array_map('trim', $value); break; default: break; } return $value; } /** * Sanitizes a value using the WordPress database prepare method. * It also trims leading and trailing quotes from the sanitized value. * * @param mixed $value The value to sanitize. * @return string|null The sanitized value or null if the input value is null. */ protected function sanitizeWithWPDB($value) { $value = $this->wpdb->prepare('%s', $value); if ($value) { // Trim leading and trailing quotes $value = trim($value, "'\""); } return $value; } /** * Sanitizes a filter group for an Eloquent model query. * * @param string $column The column to filter on. * @param string $operator The comparison operator. * @param mixed $value The value to compare against. * @return array An array containing the sanitized column, operator, and value. */ protected function sanitizeFilterGroupForEluquentModel($column, $operator, $value) { switch ($operator) { case 'contains': case 'doNotContains': case 'startsWith': case 'endsWith': if (is_array($value)) { $value = join('', $value); } $value = $this->wpdb->esc_like($value); if ('startsWith' === $operator) { $value = "%" . $value; } elseif ('endsWith' === $operator) { $value = $value . "%"; } else { $value = "%" . $value . "%"; } $operator = 'doNotContains' === $operator ? 'NOT LIKE' : 'LIKE'; break; default: break; } return [$column, $operator, $value]; } /** * Get advance options based on template mapping. * * @return array */ public function getAdvanceOptions() { $value = sanitize_text_field(Arr::get($this->config, 'template_value.value', '')); $label = sanitize_text_field(Arr::get($this->config, 'template_label.value', '')); if (!$value || !$this->result) { return []; } $validOptions = []; $uniqueValueSet = []; foreach ($this->result as $index => $result) { $response = []; if (isset($result->response) && Helper::isJson($result->response)) { $response = \json_decode($result->response, true); unset($result->response); } // Replace placeholders in value $valueResult = $this->replacePlaceholders($value, $result, $response); if (!is_string($value)) { continue; } $valueResult = esc_attr($valueResult); if (!$valueResult) { continue; } // Replace placeholders in label $labelResult = $this->replacePlaceholders($label, $result, $response); // Use value result as label if label result is not a string or empty if (!is_string($labelResult)) { $labelResult = $valueResult; } $labelResult = esc_html($labelResult); if (!$labelResult) { $labelResult = $valueResult; } // Check for duplicate values and skip if found if (in_array($valueResult, $uniqueValueSet)) { continue; } $validOptions[] = ['id' => $index, 'label' => $labelResult, 'value' => $valueResult]; $uniqueValueSet[] = $valueResult; } return $validOptions; } /** * Replaces placeholders in a string with corresponding values from a object. * * @param string $string The string containing placeholders. * @param object $obj The object containing values to replace placeholders. * @param array $response The submission response array. * @return string The string with placeholders replaced by corresponding values. */ protected function replacePlaceholders($string, $obj, $response = []) { static $advanceOptions = null; if (!$advanceOptions && false !== strpos($string, 'option_label')) { $formId = Arr::get($this->config, 'basic_query.form_id'); $fieldName = Arr::get($this->config, 'basic_query.form_field'); if ($form = Helper::getForm($formId)) { $inputs = FormFieldsParser::getField($form, ['input_radio', 'input_checkbox', 'multi_payment_component', 'select'], $fieldName, ['options']); if ($inputs) { $advanceOptions = Arr::get($inputs, $fieldName . '.options'); } } } return preg_replace_callback('/\{([\w.]+)\}/', function ($matches) use ($obj, $response, $advanceOptions) { $value = isset($obj->{$matches[1]}) ? trim($obj->{$matches[1]}) : ''; // resolve {option_label} value by response if ('option_label' == $matches[1] && isset($obj->field_value)) { if ($advanceOptions && $label = Arr::get($advanceOptions, $obj->field_value)) { $value = $label; } } // resolve {inputs.field_name} value by response if (false !== strpos($matches[1], 'inputs.')) { $name = substr($matches[1], strlen('inputs.')); $name = isset($obj->{$name}) ? trim($obj->{$name}) : $name; if ($name && isset($response[$name])) { if (is_array($response[$name])) { $value = fluentImplodeRecursive(' ', $response[$name]); } elseif (is_string($response[$name])) { $value = $response[$name]; } } } return $value; }, $string); } /** * Gets the maximum number of editor value options allowed. * * @return int The maximum number of editor value options allowed. */ protected function getEditorValueOptionsLimit() { return apply_filters("{$this->filterPrefix}_editor_value_options_limit", 200); } /** * Gets the maximum number of result allowed in the query. * * @return int The maximum number of result allowed in the query, default 500. */ protected function getResultLimit() { return apply_filters("{$this->filterPrefix}_record_limit", 500); } }