entityClass = $entity; $this->alias = $alias; $this->adapter = $adapter ?: Ulmus::$defaultAdapter; $this->queryBuilder = new QueryBuilder( $this->adapter->adapter() ); $this->entityResolver = Ulmus::resolveEntity($entity); } public function loadOne() : ? object { return $this->limit(1)->collectionFromQuery()[0] ?? null; } public function loadFromPk($value, $primaryKey = "id") : ? object { return $this->where($primaryKey, $value)->loadOne(); } public function loadAll() : EntityCollection { return $this->collectionFromQuery(); } public function loadFromField($field, $value) : EntityCollection { return $this->where($field, $value)->collectionFromQuery(); } public function count() : int { $this->select("count(*) as totalItem")->selectSqlQuery(); $this->finalizeQuery(); foreach(Ulmus::iterateQueryBuilder($this->queryBuilder) as $entityData) { return $entityData['totalItem']; } return 0; } public function deleteOne() { return $this->limit(1)->deleteSqlQuery()->runQuery(); } public function deleteAll() { return $this->deleteSqlQuery()->runQuery(); } public function deleteFromPk($value) : bool { if ( $value !== 0 && empty($value) ) { throw new Exception\EntityPrimaryKeyUnknown("A primary key value has to be defined to delete an item."); } return (bool) $this->wherePrimaryKey($value)->deleteOne()->rowCount(); } public function save(object $entity) : bool { $dataset = $entity->toArray(); $primaryKeyDefinition = Ulmus::resolveEntity($this->entityClass)->getPrimaryKeyField(); if ( ! $entity->isLoaded() ) { $statement = $this->insertSqlQuery($dataset)->runQuery(); if ( ( 0 !== $statement->lastInsertId ) && ( null !== $primaryKeyDefinition )) { $pkField = key($primaryKeyDefinition); $entity->$pkField = $statement->lastInsertId; } return true; } else { if ( $primaryKeyDefinition === null ) { throw new \Exception(sprintf("No primary key found for entity %s", $this->entityClass)); } $pkField = key($primaryKeyDefinition); $pkFieldName = $primaryKeyDefinition[$pkField]->name ?? $pkField; $this->where($pkFieldName, $dataset[$pkFieldName]); $diff = array_diff_assoc($dataset, $entity->entityGetDataset(true)); var_dump( "
", $diff, $dataset, $entity->entityGetDataset(true) );
            
            if ( [] !== $diff ) {
                $update = $this->updateSqlQuery($diff)->runQuery();
                
                return (bool) $update->rowCount();
            }   
        }
        
        return false;
    }
    
    public function saveAll(EntityCollection $collection) : bool 
    {
        foreach($collection as $entity) {
            $this->save($entity);
        }
    }
    
    public function yieldAll() : \Generator
    {

    }

    public function select($fields) : self
    {
        $this->queryBuilder->select($fields);
        
        return $this;
    }

    public function insert(array $fieldlist, string $table, string $alias, ? string $schema) : self
    {
        $this->queryBuilder->insert($fieldlist, $table, $alias, $schema);
        
        return $this;
    }

    public function values(array $dataset) : self
    {
        $this->queryBuilder->values($dataset);
        
        return $this;
    }
    
    public function update(array $fieldlist, string $table, string $alias, ? string $schema) : self
    {
        $this->queryBuilder->update($table, $alias, $schema);
        
        return $this;
    }
    
    public function set(array $dataset) : self
    {
        $this->queryBuilder->set($dataset);
        
        return $this;
    }
    
    public function delete() : self
    {
        $this->queryBuilder->delete($this->alias);
        
        return $this;
    }

    public function from(string $table, ? string $alias, ? string $schema) : self
    {
        $this->queryBuilder->from($table, $alias, null, $schema);
        
        return $this;
    }

    public function join(string $type, $table, $field, $value) : self
    {
        return $this;
    }

    public function open(string $condition = Query\Where::CONDITION_AND) : self
    {
        $this->queryBuilder->open($condition);
        
        return $this;
    }

    public function orOpen() : self
    {
        return $this->open(Query\Where::CONDITION_OR);
    }

    public function close() : self
    {
        $this->queryBuilder->close();
        
        return $this;
    }

    public function where($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
    {
        $this->queryBuilder->where($field, $value, $operator, Query\Where::CONDITION_AND);
        
        return $this;
    }

    public function and($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
    {
        return $this->where($field, $value, $operator);
    }

    public function or($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
    {
        $this->queryBuilder->where($field, $value, $operator, Query\Where::CONDITION_OR);
        
        return $this;
    }

    public function notWhere($field, $value, string $operator = Query\Where::OPERATOR_NOT_EQUAL) : self
    {
        $this->queryBuilder->where($field, $value, $operator, Query\Where::CONDITION_AND, true);
        
        return $this;
    }

    public function orNot($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
    {
        $this->queryBuilder->notWhere($condition, Query\Where::CONDITION_OR, true);
        
        return $this;
    }

    public function having() : self
    {
        return $this;
    }

    public function notHaving() : self
    {
        return $this;
    }

    public function in($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
    {
        $this->queryBuilder->where($field, $value, $operator);
        
        return $this;
    }

    public function orIn($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
    {
        $this->queryBuilder->where($field, $value, $operator, Query\Where::CONDITION_OR);
        
        return $this;
    }

    public function notIn($field, $value) : self
    {
        $this->queryBuilder->where($field, $value, Query\Where::OPERATOR_NOT_EQUAL);
        
        return $this;
    }

    public function orNotIn($field, $value, string $operator = Query\Where::OPERATOR_EQUAL) : self
    {
        return $this->orNot($field, $value, Query\Where::OPERATOR_NOT_EQUAL, Query\Where::CONDITION_OR);
    }

    public function like($field, $value) : self
    {
        $this->queryBuilder->where($field, $value, Query\Where::OPERATOR_LIKE, Query\Where::CONDITION_AND);
        
        return $this;
    }

    public function notLike($field, $value) : self
    {
        $this->queryBuilder->where($field, $value, Query\Where::OPERATOR_LIKE, Query\Where::CONDITION_AND, true);
        
        return $this;
    }

    public function match() : self
    {

    }

    public function notMatch() : self
    {

    }

    public function between() : self
    {

    }

    public function notBetween() : self
    {

    }

    public function groupBy() : self
    {
        #$this->queryBuilder->groupBy();
        return $this;
    }

    public function orderBy($field, ? string $direction = null) : self
    {
        $this->queryBuilder->orderBy($field, $direction);
        
        return $this;
    }

    public function limit(int $value) : self
    {
        $this->queryBuilder->limit($value);
        
        return $this;
    }

    public function offset(int $value) : self
    {
        $this->queryBuilder->offset($value);
        
        return $this;
    }

    public function commit() : self
    {
        return $this;
    }

    public function rollback() : self
    {
        return $this;
    }

    public function wherePrimaryKey($value) : self
    {
        if ( null === $primaryKeyField = Ulmus::resolveEntity($this->entityClass)->getPrimaryKeyField() ) {
            throw new Exception\EntityPrimaryKeyUnknown("Entity has no field containing attributes 'primary_key'");
        }
        
        $pkField = key($primaryKeyField);
        
        return $this->where($primaryKeyField[$pkField]->name ?? $pkField, $value);
    }
    
    protected function collectionFromQuery() : EntityCollection
    {
        
        $class = $this->entityClass;

        $entityCollection = new EntityCollection();
        
        $this->selectSqlQuery();
        
        $this->finalizeQuery();

        foreach(Ulmus::iterateQueryBuilder($this->queryBuilder) as $entityData) {
            $entityCollection->append( ( new $class() )->entityFillFromDataset($entityData) );
        }

        return $entityCollection;
    }

    public function runQuery() : \PDOStatement
    {
        $this->finalizeQuery();
        
        return Ulmus::runQuery($this->queryBuilder);
    }
    
    protected function insertSqlQuery(array $dataset) : self
    {
        if ( null === $this->queryBuilder->getFragment(Query\Insert::class) ) {
            $this->insert(array_keys($dataset), $this->entityResolver->tableName(), $this->alias, $this->entityResolver->schemaName());
        }

        $this->values($dataset);

        return $this;
    }
    
    protected function updateSqlQuery(array $dataset) : self
    {
        if ( null === $this->queryBuilder->getFragment(Query\Update::class) ) {
            $this->update($dataset, $this->entityResolver->tableName(), $this->alias, $this->entityResolver->schemaName());
        }
        
        $this->set($dataset);

        return $this;
    }
    
    protected function selectSqlQuery() : self
    {
        if ( null === $this->queryBuilder->getFragment(Query\Select::class) ) {
            $this->select("{$this->alias}.*");
        }

        if ( null === $this->queryBuilder->getFragment(Query\From::class) ) {
            $this->from($this->entityResolver->tableName(), $this->alias, $this->entityResolver->schemaName());
        }

        return $this;
    }

    protected function deleteSqlQuery() : self
    {
        if ( null === $this->queryBuilder->getFragment(Query\Delete::class) ) {
            $this->delete();
        }

        if ( null === $this->queryBuilder->getFragment(Query\From::class) ) {
            $this->from($this->entityResolver->tableName(), null, $this->entityResolver->schemaName());
        }

        return $this;
    }
    
    protected function fromRow($row) : self
    {

    }

    protected function fromCollection($rows) : self
    {

    }
    
    public function table()
    {
        return "REFLECT TABLE";
    }
    
    protected function finalizeQuery() : void {}
}