<commit_before><?php
/*
 * Copyright (c) Ouzo contributors, http://ouzoframework.org
 * This file is made available under the MIT License (view the LICENSE file for more information).
 */
namespace Ouzo\Db;

use Iterator;
use Ouzo\Db;
use Ouzo\DbException;
use Ouzo\Model;
use Ouzo\Utilities\Arrays;
use Ouzo\Utilities\Iterator\BatchingIterator;
use Ouzo\Utilities\Iterator\TransformingIterator;
use Ouzo\Utilities\Iterator\UnbatchingIterator;
use PDO;

class ModelQueryBuilder
{
    /** @var Db */
    private $_db;

    /** @var Model */
    private $_model;

    /** @var ModelJoin[] */
    private $_joinedModels = array();

    /** @var RelationToFetch[] */
    private $_relationsToFetch = array();

    /** @var Query */
    private $_query;

    /** @var bool */
    private $_selectModel = true;

    public function __construct(Model $model, Db $db = null, $alias = null)
    {
        $this->_db = $db ? $db : Db::getInstance();
        $this->_model = $model;

        $this->_query = new Query();
        $this->_query->table = $model->getTableName();
        $this->_query->aliasTable = $alias;
        $this->_query->selectType = PDO::FETCH_NUM;
        $this->_query->selectColumns = array();
        $this->selectModelColumns($model, $this->getModelAliasOrTable());
    }

    private function getModelAliasOrTable()
    {
        return $this->_query->aliasTable ?: $this->_model->getTableName();
    }

    private function selectModelColumns(Model $metaInstance, $alias)
    {
        if ($this->_selectModel) {
            $prefix = $this->aliasPrefixForSelect($alias);
            $this->_query->selectColumns = $this->_query->selectColumns + ColumnAliasHandler::createSelectColumnsWithAliases($prefix, $metaInstance->_getFields(), $alias);
        }
    }

    private function aliasPrefixForSelect($alias)
    {
        return "_{$alias}_";
    }

    /**
     * @param string $where
     * @param array $values
     * @return ModelQueryBuilder
     */
    public function where($where = '', $values = array())
    {
        $this->_query->where($where, $values);
        return $this;
    }

    /**
     * @param $columns
     * @return ModelQueryBuilder
     */
    public function order($columns)
    {
        $this->_query->order = $columns;
        return $this;
    }

    /**
     * @param $offset
     * @return ModelQueryBuilder
     */
    public function offset($offset)
    {
        $this->_query->offset = $offset;
        return $this;
    }

    /**
     * @param $limit
     * @return ModelQueryBuilder
     */
    public function limit($limit)
    {
        $this->_query->limit = $limit;
        return $this;
    }

    /**
     * @return ModelQueryBuilder
     */
    public function lockForUpdate()
    {
        $this->_query->lockForUpdate = true;
        return $this;
    }

    public function count()
    {
        $this->_query->type = QueryType::$COUNT;
        $value = QueryExecutor::prepare($this->_db, $this->_query)->fetch();
        return intval(Arrays::firstOrNull(Arrays::toArray($value)));
    }

    /**
     * @return Model
     */
    public function fetch()
    {
        $result = QueryExecutor::prepare($this->_db, $this->_query)->fetch();
        if (!$result) {
            return null;
        }
        return !$this->_selectModel ? $result : Arrays::firstOrNull($this->_processResults(array($result)));
    }

    /**
     * @return Model[]
     */
    public function fetchAll()
    {
        $result = QueryExecutor::prepare($this->_db, $this->_query)->fetchAll();
        return !$this->_selectModel ? $result : $this->_processResults($result);
    }

    /**
     * @param int $batchSize
     * @return Iterator
     */
    public function fetchIterator($batchSize = 500)
    {
        $iterator = QueryExecutor::prepare($this->_db, $this->_query)->fetchIterator();
        $iterator->rewind();
        return !$this->_selectModel ? $iterator : new UnbatchingIterator(new TransformingIterator(new BatchingIterator($iterator, $batchSize), array($this, '_processResults')));
    }

    function _processResults($results)
    {
        $resultSetConverter = new ModelResultSetConverter($this->_model, $this->getModelAliasOrTable(), $this->_joinedModels, $this->_relationsToFetch);
        return $resultSetConverter->convert($results);
    }

    /**
     * Issues "delete from ... where ..." sql command.
     * Note that overridden Model::delete is not called.
     *
     */
    public function deleteAll()
    {
        $this->_query->type = QueryType::$DELETE;
        return QueryExecutor::prepare($this->_db, $this->_query)->execute();
    }

    /**
     * Calls Model::delete method for each matching object
     */
    public function deleteEach()
    {
        $objects = $this->fetchAll();
        return Arrays::map($objects, function (Model $object) {
            return !$object->delete();
        });
    }

    /**
     * Runs an update query against a set of models
     * @param array $attributes
     * @return int
     */
    public function update(array $attributes)
    {
        $this->_query->type = QueryType::$UPDATE;<commit_msg>Added phpDoc for ModelQueryBuilder.fetch()<commit_after><?php
/*
 * Copyright (c) Ouzo contributors, http://ouzoframework.org
 * This file is made available under the MIT License (view the LICENSE file for more information).
 */
namespace Ouzo\Db;

use Iterator;
use Ouzo\Db;
use Ouzo\DbException;
use Ouzo\Model;
use Ouzo\Utilities\Arrays;
use Ouzo\Utilities\Iterator\BatchingIterator;
use Ouzo\Utilities\Iterator\TransformingIterator;
use Ouzo\Utilities\Iterator\UnbatchingIterator;
use PDO;

class ModelQueryBuilder
{
    /** @var Db */
    private $_db;

    /** @var Model */
    private $_model;

    /** @var ModelJoin[] */
    private $_joinedModels = array();

    /** @var RelationToFetch[] */
    private $_relationsToFetch = array();

    /** @var Query */
    private $_query;

    /** @var bool */
    private $_selectModel = true;

    public function __construct(Model $model, Db $db = null, $alias = null)
    {
        $this->_db = $db ? $db : Db::getInstance();
        $this->_model = $model;

        $this->_query = new Query();
        $this->_query->table = $model->getTableName();
        $this->_query->aliasTable = $alias;
        $this->_query->selectType = PDO::FETCH_NUM;
        $this->_query->selectColumns = array();
        $this->selectModelColumns($model, $this->getModelAliasOrTable());
    }

    private function getModelAliasOrTable()
    {
        return $this->_query->aliasTable ?: $this->_model->getTableName();
    }

    private function selectModelColumns(Model $metaInstance, $alias)
    {
        if ($this->_selectModel) {
            $prefix = $this->aliasPrefixForSelect($alias);
            $this->_query->selectColumns = $this->_query->selectColumns + ColumnAliasHandler::createSelectColumnsWithAliases($prefix, $metaInstance->_getFields(), $alias);
        }
    }

    private function aliasPrefixForSelect($alias)
    {
        return "_{$alias}_";
    }

    /**
     * @param string $where
     * @param array $values
     * @return ModelQueryBuilder
     */
    public function where($where = '', $values = array())
    {
        $this->_query->where($where, $values);
        return $this;
    }

    /**
     * @param $columns
     * @return ModelQueryBuilder
     */
    public function order($columns)
    {
        $this->_query->order = $columns;
        return $this;
    }

    /**
     * @param $offset
     * @return ModelQueryBuilder
     */
    public function offset($offset)
    {
        $this->_query->offset = $offset;
        return $this;
    }

    /**
     * @param $limit
     * @return ModelQueryBuilder
     */
    public function limit($limit)
    {
        $this->_query->limit = $limit;
        return $this;
    }

    /**
     * @return ModelQueryBuilder
     */
    public function lockForUpdate()
    {
        $this->_query->lockForUpdate = true;
        return $this;
    }

    public function count()
    {
        $this->_query->type = QueryType::$COUNT;
        $value = QueryExecutor::prepare($this->_db, $this->_query)->fetch();
        return intval(Arrays::firstOrNull(Arrays::toArray($value)));
    }

    /**
     * @return Model|array
     */
    public function fetch()
    {
        $result = QueryExecutor::prepare($this->_db, $this->_query)->fetch();
        if (!$result) {
            return null;
        }
        return !$this->_selectModel ? $result : Arrays::firstOrNull($this->_processResults(array($result)));
    }

    /**
     * @return Model[]|array[]
     */
    public function fetchAll()
    {
        $result = QueryExecutor::prepare($this->_db, $this->_query)->fetchAll();
        return !$this->_selectModel ? $result : $this->_processResults($result);
    }

    /**
     * @param int $batchSize
     * @return Iterator
     */
    public function fetchIterator($batchSize = 500)
    {
        $iterator = QueryExecutor::prepare($this->_db, $this->_query)->fetchIterator();
        $iterator->rewind();
        return !$this->_selectModel ? $iterator : new UnbatchingIterator(new TransformingIterator(new BatchingIterator($iterator, $batchSize), array($this, '_processResults')));
    }

    function _processResults($results)
    {
        $resultSetConverter = new ModelResultSetConverter($this->_model, $this->getModelAliasOrTable(), $this->_joinedModels, $this->_relationsToFetch);
        return $resultSetConverter->convert($results);
    }

    /**
     * Issues "delete from ... where ..." sql command.
     * Note that overridden Model::delete is not called.
     */
    public function deleteAll()
    {
        $this->_query->type = QueryType::$DELETE;
        return QueryExecutor::prepare($this->_db, $this->_query)->execute();
    }

    /**
     * Calls Model::delete method for each matching object
     */
    public function deleteEach()
    {
        $objects = $this->fetchAll();
        return Arrays::map($objects, function (Model $object) {
            return !$object->delete();
        });
    }

    /**
     * Runs an update query against a set of models
     * @param array $attributes
     * @return int
     */
    public function update(array $attributes)
    {
        $this->_query->type = QueryType::$UPDATE;
{'fraction_non_alphanumeric': 0.14887422360248448, 'fraction_numerical': 0.0005822981366459627, 'mean_word_length': 2.5522233712512925, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 141, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5201997', 'n_tokens_mistral': 2973, 'n_tokens_neox': 2922, 'n_words': 630}