RebelCode/wp-cqrs-resource-models

View on GitHub
src/Posts/ExtractPostIdsFromExpressionCapableTrait.php

Summary

Maintainability
B
4 hrs
Test Coverage
<?php

namespace RebelCode\Storage\Resource\WordPress\Posts;

use Dhii\Data\KeyAwareInterface;
use Dhii\Data\ValueAwareInterface;
use Dhii\Expression\LogicalExpressionInterface;
use Dhii\Expression\Type\BooleanTypeInterface;
use Dhii\Storage\Resource\Sql\EntityFieldInterface;
use Dhii\Storage\Resource\Sql\Expression\SqlRelationalTypeInterface;
use Dhii\Util\String\StringableInterface as Stringable;
use Exception as RootException;
use InvalidArgumentException;
use Traversable;

/**
 * ExtractPostIdsFromExpressionCapableTrait.
 *
 * The expression is expected to be of type OR or EQUAL_TO. OR expressions can have terms of either of these types
 * for nesting, while EQUAL_TO expressions are scanned for a KeyAwareInterface term and a ValueAwareInterface term.
 * If a found key-aware term has a key that is considered to be a post ID field name, the values of all value-aware
 * terms are yielded as the post IDs.
 *
 * @since [*next-version*]
 */
trait ExtractPostIdsFromExpressionCapableTrait
{
    /**
     * Extracts post IDs from a logical expression.
     *
     * @since [*next-version*]
     *
     * @param LogicalExpressionInterface|null $expression The expression to extract from.
     *
     * @return string[]|Stringable A list of post IDs.
     */
    protected function _extractPostIdsFromExpression(LogicalExpressionInterface $expression)
    {
        if ($expression->isNegated()) {
            throw $this->_createInvalidArgumentException($this->__('Negated terms are not supported'), null, null);
        }

        switch ($expression->getType()) {
            case BooleanTypeInterface::T_OR:
                return $this->_extractPostIdsFromOrExpression($expression);

            case SqlRelationalTypeInterface::T_EQUAL_TO:
                return $this->_extractPostIdsFromEqualsExpression($expression);

            case SqlRelationalTypeInterface::T_BETWEEN:
                return $this->_extractPostIdsFromBetweenExpression($expression);

            case SqlRelationalTypeInterface::T_IN:
                return $this->_extractPostIdsFromInExpression($expression);
        }

        throw $this->_createInvalidArgumentException($this->__('Expression type is not supported'), null, null);
    }

    /**
     * Extracts post IDs from an OR expression.
     *
     * @since [*next-version*]
     *
     * @param LogicalExpressionInterface $expression The expression to extract from.
     *
     * @return string[] The extracted post IDs.
     */
    protected function _extractPostIdsFromOrExpression(LogicalExpressionInterface $expression)
    {
        $postIds = [];

        foreach ($expression->getTerms() as $_term) {
            if (!($_term instanceof LogicalExpressionInterface)) {
                throw $this->_createInvalidArgumentException(
                    $this->__('Expression term is not a logical expression'),
                    null,
                    null,
                    $_term
                );
            }

            $postIds = array_merge($postIds, $this->_extractPostIdsFromExpression($_term));
        }

        return array_unique($postIds);
    }

    /**
     * Extracts post IDs from an EQUAL_TO expression.
     *
     * @since [*next-version*]
     *
     * @param LogicalExpressionInterface|null $expression The expression to extract from.
     *
     * @return string[] The extracted post IDs.
     */
    protected function _extractPostIdsFromEqualsExpression(LogicalExpressionInterface $expression = null)
    {
        $postIds       = [];
        $isPostIdField = false;
        $pEntity       = $this->_getPostEntityName();
        $pIdField      = $this->_getPostIdFieldName();

        foreach ($expression->getTerms() as $_child) {
            if ($_child instanceof EntityFieldInterface &&
                $_child->getEntity() === $pEntity &&
                $_child->getField() === $pIdField
            ) {
                $isPostIdField = true;

                continue;
            }

            if ($_child instanceof ValueAwareInterface) {
                $_value    = $_child->getValue();
                $postIds[] = $this->_normalizeInt($_value);

                continue;
            }
        }

        return ($isPostIdField)
            ? $postIds
            : [];
    }

    /**
     * Extracts post IDs from a BETWEEN expression.
     *
     * @since [*next-version*]
     *
     * @param LogicalExpressionInterface $expression The expression to extract from.
     *
     * @return string[] The extracted post IDs.
     */
    protected function _extractPostIdsFromBetweenExpression(LogicalExpressionInterface $expression)
    {
        $isPostIdField = false;
        $pEntity       = $this->_getPostEntityName();
        $pIdField      = $this->_getPostIdFieldName();
        $bounds        = [];

        foreach ($expression->getTerms() as $_child) {
            if ($_child instanceof EntityFieldInterface &&
                $_child->getEntity() === $pEntity &&
                $_child->getField() === $pIdField
            ) {
                $isPostIdField = true;

                continue;
            }

            if ($_child instanceof ValueAwareInterface) {
                $_value   = $_child->getValue();
                $bounds[] = $this->_normalizeInt($_value);

                continue;
            }
        }

        if ($isPostIdField && count($bounds) === 2) {
            return range($bounds[0], $bounds[1]);
        }

        throw $this->_createInvalidArgumentException(
            $this->__('Given sql_between expression is not a valid expression for post IDs'),
            null,
            null,
            $expression
        );
    }

    /**
     * Extracts post IDs from an IN expression.
     *
     * @since [*next-version*]
     *
     * @param LogicalExpressionInterface $expression The expression to extract from.
     *
     * @return string[] The extracted post IDs.
     */
    protected function _extractPostIdsFromInExpression(LogicalExpressionInterface $expression)
    {
        $postIds       = [];
        $isPostIdField = false;
        $pEntity       = $this->_getPostEntityName();
        $pIdField      = $this->_getPostIdFieldName();

        foreach ($expression->getTerms() as $_child) {
            if ($_child instanceof EntityFieldInterface &&
                $_child->getEntity() === $pEntity &&
                $_child->getField() === $pIdField
            ) {
                $isPostIdField = true;

                continue;
            }

            if ($_child instanceof ValueAwareInterface) {
                $_value  = $_child->getValue();
                $postIds = $this->_normalizeArray($_value);

                continue;
            }
        }

        if ($isPostIdField) {
            return $postIds;
        }

        throw $this->_createInvalidArgumentException(
            $this->__('Given sql_in expression is not a valid expression for post IDs'),
            null,
            null,
            $expression
        );
    }

    /**
     * Retrieves the entity name for posts used in expressions.
     *
     * @since [*next-version*]
     *
     * @return string|Stringable The post entity name.
     */
    abstract protected function _getPostEntityName();

    /**
     * Retrieves the field name used in expressions for post ID terms.
     *
     * @since [*next-version*]
     *
     * @return string|Stringable The post ID field name.
     */
    abstract protected function _getPostIdFieldName();

    /**
     * Normalizes a value into an integer.
     *
     * The value must be a whole number, or a string representing such a number,
     * or an object representing such a string.
     *
     * @since [*next-version*]
     *
     * @param string|Stringable|float|int $value The value to normalize.
     *
     * @throws InvalidArgumentException If value cannot be normalized.
     *
     * @return int The normalized value.
     */
    abstract protected function _normalizeInt($value);

    /**
     * Normalizes a value into an array.
     *
     * @since [*next-version*]
     *
     * @param array|Traversable $value The value to normalize.
     *
     * @throws InvalidArgumentException If value cannot be normalized.
     *
     * @return array The normalized value.
     */
    abstract protected function _normalizeArray($value);

    /**
     * Creates a new Dhii invalid argument exception.
     *
     * @since [*next-version*]
     *
     * @param string|Stringable|null $message  The error message, if any.
     * @param int|null               $code     The error code, if any.
     * @param RootException|null     $previous The inner exception for chaining, if any.
     * @param mixed|null             $argument The invalid argument, if any.
     *
     * @return InvalidArgumentException The new exception.
     */
    abstract protected function _createInvalidArgumentException(
        $message = null,
        $code = null,
        RootException $previous = null,
        $argument = null
    );

    /**
     * Translates a string, and replaces placeholders.
     *
     * @since [*next-version*]
     * @see   sprintf()
     *
     * @param string $string  The format string to translate.
     * @param array  $args    Placeholder values to replace in the string.
     * @param mixed  $context The context for translation.
     *
     * @return string The translated string.
     */
    abstract protected function __($string, $args = [], $context = null);
}