AthensFramework/Propel

View on GitHub
src/orm-wrapper/PropelQueryWrapper.php

Summary

Maintainability
A
3 hrs
Test Coverage
<?php

namespace Athens\Propel\ORMWrapper;

use Propel\Runtime\ActiveQuery\ModelCriteria;
use Propel\Runtime\ActiveQuery\Criteria;

use Athens\Core\ORMWrapper\AbstractQueryWrapper;
use Athens\Core\ORMWrapper\ObjectWrapperInterface;
use Athens\Core\ORMWrapper\QueryWrapperInterface;
use Athens\Core\Etc\StringUtils;

/**
 * Class PropelQueryWrapper
 *
 * @package Athens\Core\QueryWrapper
 */
class PropelQueryWrapper extends AbstractQueryWrapper implements QueryWrapperInterface
{
    use PropelORMWrapperTrait;

    /** @var ModelCriteria */
    protected $query;

    /**
     * @var string[]
     */
    protected $conditionMap = [
        QueryWrapperInterface::CONDITION_EQUAL => Criteria::EQUAL,
        QueryWrapperInterface::CONDITION_NOT_EQUAL => Criteria::NOT_EQUAL,
        QueryWrapperInterface::CONDITION_CONTAINS => Criteria::CONTAINS_SOME,
        QueryWrapperInterface::CONDITION_GREATER_THAN => Criteria::GREATER_THAN,
        QueryWrapperInterface::CONDITION_GREATER_THAN_OR_EQUAL => Criteria::GREATER_EQUAL,
        QueryWrapperInterface::CONDITION_LESS_THAN => Criteria::LESS_THAN,
        QueryWrapperInterface::CONDITION_LESS_THAN_OR_EQUAL => Criteria::LESS_EQUAL,
        QueryWrapperInterface::CONDITION_IN => Criteria::IN,
        QueryWrapperInterface::CONDITION_NOT_IN => Criteria::NOT_IN,
    ];

    /**
     * PropelQueryWrapper constructor.
     *
     * @param ModelCriteria $query
     */
    public function __construct(ModelCriteria $query)
    {
        $this->query = $query;

        $this->setTableMap($query->getTableMap());
    }

    /**
     * Does not handle multiple primary keys.
     *
     * @param mixed $primaryKeyValue
     * @return ObjectWrapperInterface|null
     */
    public function findOneByPrimaryKey($primaryKeyValue)
    {
        $primaryKeys = $this->getTableMap()->getPrimaryKeys();
        $primaryKeyPhpName = array_values($primaryKeys)[0]->getPhpName();

        $result = $this->query->{"findOneBy$primaryKeyPhpName"}($primaryKeyValue);

        $result = $result === null ? null : PropelObjectWrapper::fromObject($result);

        return $result;
    }

    /**
     * @return PropelCollectionWrapper
     */
    public function find()
    {
        $collection = $this->query->find();
        return new PropelCollectionWrapper($collection);
    }

    /**
     * @param string $columnName
     * @param mixed  $condition
     * @return $this
     */
    public function orderBy($columnName, $condition)
    {
        $this->query->orderBy($columnName, $condition);
        return $this;
    }

    /**
     * @param string $columnName
     * @param mixed  $value
     * @param string $comparison
     * @return $this
     */
    public function filterBy($columnName, $value, $comparison = QueryWrapperInterface::CONDITION_EQUAL)
    {
        $comparison = $this->conditionMap[$comparison];

        /** @var boolean $columnNameIsQualified */
        $columnNameIsQualified = strpos($columnName, '.') !== false;

        /** @var string $modelName */
        $modelName = $columnNameIsQualified === true ?
            strtok($columnName, '.') : $this->query->getTableMap()->getPhpName();

        /** @var string $columnName */
        $columnName = $columnNameIsQualified === true ? $this->getUnqualifiedFieldName($columnName) : $columnName;

        /** @var boolean $queryIsOnModel */
        $queryIsOnModel = $modelName === $this->query->getTableMap()->getPhpName();

        if ($queryIsOnModel === true) {
            $this->query->{"filterBy" . $columnName}($value, $comparison);
        } else {
            $this->query->{"use{$modelName}Query"}()
                ->{"filterBy" . $columnName}($value, $comparison)
                ->endUse();
        }

        return $this;
    }

    /**
     * @param string $columnName
     * @param mixed  $value
     * @param string $condition
     * @return boolean
     */
    public function canFilterBy($columnName, $value, $condition = QueryWrapperInterface::CONDITION_EQUAL)
    {
        return $this->canQueryColumnName($columnName);
    }

    /**
     * @param string $columnName
     * @param string $condition
     * @return boolean
     */
    public function canOrderBy($columnName, $condition)
    {
        return $this->canQueryColumnName($columnName);
    }

    /**
     * @param string $columnName
     * @return boolean
     */
    protected function canQueryColumnName($columnName)
    {

        /** @var boolean $columnNameIsQualified */
        $columnNameIsQualified = strpos($columnName, '.') !== false;

        /** @var string $objectName */
        $objectName = $columnNameIsQualified === true ? strtok($columnName, '.') : $this->getPascalCasedObjectName();

        /** @var string $columnName */
        $columnName = $columnNameIsQualified === true ? $this->getUnqualifiedFieldName($columnName) : $columnName;

        if ($objectName === $this->getPascalCasedObjectName()) {
            return in_array($columnName, $this->getUnqualifiedPascalCasedColumnNames()) === true;
        } else {
            /** @var \Propel\Runtime\Map\TableMap $foreignMap */
            $foreignMap = $this->query->getTableMap()->getRelation($objectName)->getForeignTable();

            foreach ($foreignMap->getColumns() as $column) {
                if ($columnName === $column->getPhpName()) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * @param integer $offset
     * @return $this
     */
    public function offset($offset)
    {
        $this->query->offset($offset);
        return $this;
    }

    /**
     * @param integer $limit
     * @return $this
     */
    public function limit($limit)
    {
        $this->query->limit($limit);
        return $this;
    }

    /**
     * @return ObjectWrapperInterface
     */
    public function createObject()
    {
        $className = $this->query->getModelName();
        return PropelObjectWrapper::fromObject(new $className());
    }

    /**
     * @return integer
     */
    public function count()
    {
        return $this->query->count();
    }

    /**
     * @return boolean
     */
    public function exists()
    {
        return $this->query->exists();
    }

    /**
     * Predicate that reports whether a given field name in a given class
     * table map name is under the athens/encryption Propel behavior.
     *
     * @param string $fieldName
     * @param string $classTableMapName
     * @return boolean
     */
    protected function isEncrypted($fieldName, $classTableMapName)
    {
        $qualifiedPropelFieldName = $this->getQualifiedPropelFieldName($fieldName, $classTableMapName);

        return method_exists($classTableMapName, 'isEncryptedColumnName')
        && $classTableMapName::isEncryptedColumnName($qualifiedPropelFieldName);
    }

    /**
     * @param string $columnName
     * @return boolean
     */
    protected function isColumn($columnName)
    {
        $columns = $this->getColumns();
        return array_key_exists($columnName, $columns);
    }


    /**
     * Retrieve an unqualified field name from a qualified one.
     *
     * @param string $qualifiedFieldName
     * @return string
     */
    protected function getUnqualifiedFieldName($qualifiedFieldName)
    {
        return explode('.', $qualifiedFieldName)[1];
    }

    /**
     * @param string $fieldName
     * @param string $classTableMapName
     * @return string
     */
    protected function getQualifiedPropelFieldName($fieldName, $classTableMapName)
    {
        $unqualifiedFieldName = explode('.', $fieldName)[1];

        $unqualifiedPropelFieldName = $classTableMapName::translateFieldName(
            $unqualifiedFieldName,
            $classTableMapName::TYPE_PHPNAME,
            $classTableMapName::TYPE_FIELDNAME
        );

        return $classTableMapName::TABLE_NAME . "." . $unqualifiedPropelFieldName;
    }

    /**
     * @param string           $fieldPhpName
     * @param mixed            $value
     * @param "find"|"findOne" $findType
     * @return \Propel\Runtime\ActiveRecord\ActiveRecordInterface[]|\Propel\Runtime\ActiveRecord\ActiveRecordInterface
     */
    protected function baseFindAmongInheritance($fieldPhpName, $value, $findType)
    {
        // If the field is native to this class...
        try {
            // Test that the table has the prescribed column. Will fail with a ColumnNotFoundException if it does not.
            $unused = $this->getClassTableMap($this->tableMapClass)->getColumnByPhpName($fieldPhpName);

            return $this->createQuery($this->tableMapClass)
                ->{"filterBy" . $fieldPhpName}($value)
                ->$findType();

            // Else, the field is native to a parent class (we hope!)...
        } catch (ColumnNotFoundException $e) {
            // Make recursive. Include error for when we cannot find the field.

            return $this->createQuery($this->tableMapClass)
                ->{"use" . $this->findParentTables($this->tableMapClass)[0]->getPhpName() . "Query"}()
                ->{"filterBy" . $fieldPhpName}($value)
                ->endUse()
                ->$findType();
        }
    }

    /**
     * @param string $fieldPhpName
     * @param mixed  $value
     * @return \Propel\Runtime\ActiveRecord\ActiveRecordInterface[]
     */
    protected function findByAmongInheritance($fieldPhpName, $value)
    {
        return $this->baseFindAmongInheritance($fieldPhpName, $value, "find");
    }

    /**
     * @param string $fieldPhpName
     * @param mixed  $value
     * @return \Propel\Runtime\ActiveRecord\ActiveRecordInterface[]
     */
    protected function findOneByAmongInheritance($fieldPhpName, $value)
    {
        return $this->baseFindAmongInheritance($fieldPhpName, $value, "findOne");
    }

    /**
     * @param integer $id
     * @return \Propel\Runtime\ActiveRecord\ActiveRecordInterface
     */
    protected function getObjectById($id)
    {
        return $this->createQuery()->findPk($id);
    }

    /**
     * @return string
     */
    protected function getTableName()
    {
        $columns = $this->getColumns();
        return array_values($columns)[0]->getTableName();
    }

    /**
     * @return string
     */
    protected function getObjectClass()
    {
        $search = ["\\Map", "TableMap"];
        $replace = ["", ""];

        return str_replace($search, $replace, $this->tableMapClass);
    }

    /**
     * Return a Propel ActiveQuery object corresponding to $this->_classTableMapName
     *
     * @return \Propel\Runtime\ActiveQuery\PropelQuery
     */
    protected function createQuery()
    {

        $queryName = str_replace(["TableMap", "\\Map\\"], ["Query", "\\"], $this->tableMapClass);
        return $queryName::create();
    }
}