<commit_before><?php
namespace Bolt\Storage\Mapping;

use Bolt\Exception\StorageException;
use Bolt\Storage\Database\Schema\Manager;
use Bolt\Storage\Mapping\ClassMetadata as BoltClassMetadata;
use Bolt\Storage\NamingStrategy;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver;
use Doctrine\DBAL\Schema\Table;

/**
 * This is a Bolt specific metadata driver that provides mapping information
 * for the internal and user-defined schemas. To do this it takes in the
 * constructor, an instance of Bolt\Storage\Database\Schema\Manager and uses
 * this to read in the schema.
 *
 * @author <NAME> <<EMAIL>>
 */
class MetadataDriver implements MappingDriver
{
    /** @var \Bolt\Storage\Database\Schema\Manager */
    protected $schemaManager;
    /** @var array */
    protected $contenttypes;
    /** @var array taxonomy configuration */
    protected $taxonomies;
    /** @var array metadata mappings */
    protected $metadata;

    /** @var array */
    protected $defaultAliases = [
        'bolt_authtoken'   => 'Bolt\Storage\Entity\Authtoken',
        'bolt_cron'        => 'Bolt\Storage\Entity\Cron',
        'bolt_field_value' => 'Bolt\Storage\Entity\FieldValue',
        'bolt_log'         => 'Bolt\Storage\Entity\Log',
        'bolt_log_change'  => 'Bolt\Storage\Entity\LogChange',
        'bolt_log_system'  => 'Bolt\Storage\Entity\LogSystem',
        'bolt_relations'   => 'Bolt\Storage\Entity\Relations',
        'bolt_taxonomy'    => 'Bolt\Storage\Entity\Taxonomy',
        'bolt_users'       => 'Bolt\Storage\Entity\Users',
    ];

    /** @var array */
    protected $typemap;
    /** @var NamingStrategy */
    protected $namingStrategy;
    /** @var array */
    protected $aliases = [];

    /**
     * Keeps a reference of which metadata is not mapped to
     * a specific entity.
     *
     * @var array $unmapped
     */
    protected $unmapped;

    /** @var string A default entity for any table not matched */
    protected $fallbackEntity = 'Bolt\Storage\Entity\Content';
    /** @var boolean */
    protected $initialized = false;

    /**
     * Constructor.
     *
     * @param Manager $schemaManager
     * @param array   $contenttypes
     * @param array   $taxonomies
     * @param array   $typemap
     */
    public function __construct(Manager $schemaManager, array $contenttypes, array $taxonomies, array $typemap, NamingStrategy $namingStrategy = null)
    {
        $this->schemaManager = $schemaManager;
        $this->contenttypes = $contenttypes;
        $this->taxonomies = $taxonomies;
        $this->typemap = $typemap;
        $this->namingStrategy = $namingStrategy;
    }

    /**
     * Reads the schema from Bolt\Storage\Database\Schema\Manager and creates mapping data
     */
    public function initialize()
    {
        $this->initializeShortAliases();
        foreach ($this->schemaManager->getSchemaTables() as $table) {
            $this->loadMetadataForTable($table);
        }
        $this->initialized = true;
    }

    /**
     * Setup some short aliases so non prefixed keys can be used to get metadata
     */
    public function initializeShortAliases()
    {
        foreach ($this->schemaManager->getSchemaTables() as $table) {
            if ($tableName = $table->getName()) {
                $this->aliases[$table->getOption('alias')] = $tableName;
            }
        }
    }

    /**
     * Getter for aliases
     *
     * @return array
     */
    public function getAliases()
    {
        return $this->aliases;
    }

    /**
     * Method will try to find an entity class name to handle data,
     * alternatively falling back to $this->fallbackEntity
     *
     * @param string $alias
     *
     * @return string Fully Qualified Class Name
     */
    public function resolveClassName($alias)
    {
        if (class_exists($alias)) {
            return $alias;
        }

        if (array_key_exists($alias, $this->aliases)) {
            $class = $this->aliases[$alias];
            if (class_exists($class)) {
                return $class;
            }
        }

        return $this->fallbackEntity;
    }

    /**
     * Load the metadata for a table.
     *
     * @param Table $table
     */
    protected function loadMetadataForTable(Table $table)
    {
        $tblName = $table->getName();

        if (isset($this->defaultAliases[$tblName])) {
            $className = $this->defaultAliases[$tblName];
        } else {
            $className = $tblName;
            $this->unmapped[] = $tblName;
        }

        $contentKey = $table->getOption('alias');
        $this->metadata[$className] = [];
        $this->metadata[$className]['identifier'] = $table->getPrimaryKey();
        $this->metadata[$className]['table'] = $table->getName();
        $this->metadata[$className]['boltname'] = $contentKey;
        foreach ($table->getColumns() as $colName => $column) {
            $mapping = [
                'fieldname'        => $colName,
                'type'             => $column->getType()->getName(),
                'fieldtype'        => $this->getFieldTypeFor($table->getOption('alias'), $column),
                'length'           => $column->getLength(),
                'nullable'         => $column->getNotnull(),
                'platformOptions'  => $column->getPlatformOptions(),
                'precision'        => $column->getPrecision(),
                'scale'            => $column->getScale(),
                'default'          => $column->getDefault(),
                'columnDefinition' => $column->getColumnDefinition(),
                'autoincrement'    => $column->getAutoincrement(),
            ];

            $this->metadata[$className]['fields'][$colName] = $mapping;

            if (isset($this->contenttypes[$contentKey]['fields'][$colName])) {
                $this->metadata[$className]['fields'][$colName]['data'] = $this->contenttypes[$contentKey]['fields'][$colName];
            }
        }

        // This loop checks the contenttypes definition for any non-db fields and adds them.
        if ($contentKey) {
            $this->setRelations($contentKey, $className, $table);
            $this->setIncomingRelations($contentKey, $className);
            $this->setTaxonomies($contentKey, $className, $table);
            $this->setTemplatefields($contentKey, $className, $table);
            $this->setRepeaters($contentKey, $className, $table);
        }

        foreach ($this->getAliases() as $alias => $table) {
            if (array_key_exists($table, $this->metadata)) {
                $this->metadata[$alias] = $this->metadata[$table];
            }
        }
    }

    public function setRepeaters($contentKey, $className, $table)
    {
        if (!isset($this->contenttypes[$contentKey])) {
            return;
        }

        foreach ($this->contenttypes[$contentKey]['fields'] as $key => $data) {
            $mapping = [
                'fieldname'        => $key,
                'type'             => 'null',
                'fieldtype'        => $this->typemap['repeater'],
                'tables'           => [
                    'field'        => $this->schemaManager->getTableName('field'),
                    'field_value'  => $this->schemaManager->getTableName('field_value'),
                ],
            ];

            if ($data['type'] === 'repeater') {
                foreach ($data['fields'] as $key => &$value) {
                    if (isset($this->typemap[$value['type']])) {
                        $value['fieldtype'] = $this->typemap[$value['type']];
                    } else {
                        $value['fieldtype'] = $this->typemap['text'];
                    }
                }

                $this->metadata[$className]['fields'][$key] = $mapping;
                $this->metadata[$className]['fields'][$key]['data'] = $data;
            }
        }
    }

    /**
     * Set the relationship.
     *
     * @param string $contentKey
     * @param string $className
     * @param Table  $table
     */
    public function setRelations($contentKey, $className, $table)
    {
        if (!isset($this->contenttypes[$contentKey]['relations'])) {
            return;
        }
        foreach ($this->contenttypes[$contentKey]['relations'] as $key => $data) {
            if (isset($data['alias'])) {
                $relationKey = $data['alias'];
            } else {<commit_msg>add fieldname setting to the metadata for fields in repeaters<commit_after><?php
namespace Bolt\Storage\Mapping;

use Bolt\Exception\StorageException;
use Bolt\Storage\Database\Schema\Manager;
use Bolt\Storage\Mapping\ClassMetadata as BoltClassMetadata;
use Bolt\Storage\NamingStrategy;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver;
use Doctrine\DBAL\Schema\Table;

/**
 * This is a Bolt specific metadata driver that provides mapping information
 * for the internal and user-defined schemas. To do this it takes in the
 * constructor, an instance of Bolt\Storage\Database\Schema\Manager and uses
 * this to read in the schema.
 *
 * @author <NAME> <<EMAIL>>
 */
class MetadataDriver implements MappingDriver
{
    /** @var \Bolt\Storage\Database\Schema\Manager */
    protected $schemaManager;
    /** @var array */
    protected $contenttypes;
    /** @var array taxonomy configuration */
    protected $taxonomies;
    /** @var array metadata mappings */
    protected $metadata;

    /** @var array */
    protected $defaultAliases = [
        'bolt_authtoken'   => 'Bolt\Storage\Entity\Authtoken',
        'bolt_cron'        => 'Bolt\Storage\Entity\Cron',
        'bolt_field_value' => 'Bolt\Storage\Entity\FieldValue',
        'bolt_log'         => 'Bolt\Storage\Entity\Log',
        'bolt_log_change'  => 'Bolt\Storage\Entity\LogChange',
        'bolt_log_system'  => 'Bolt\Storage\Entity\LogSystem',
        'bolt_relations'   => 'Bolt\Storage\Entity\Relations',
        'bolt_taxonomy'    => 'Bolt\Storage\Entity\Taxonomy',
        'bolt_users'       => 'Bolt\Storage\Entity\Users',
    ];

    /** @var array */
    protected $typemap;
    /** @var NamingStrategy */
    protected $namingStrategy;
    /** @var array */
    protected $aliases = [];

    /**
     * Keeps a reference of which metadata is not mapped to
     * a specific entity.
     *
     * @var array $unmapped
     */
    protected $unmapped;

    /** @var string A default entity for any table not matched */
    protected $fallbackEntity = 'Bolt\Storage\Entity\Content';
    /** @var boolean */
    protected $initialized = false;

    /**
     * Constructor.
     *
     * @param Manager $schemaManager
     * @param array   $contenttypes
     * @param array   $taxonomies
     * @param array   $typemap
     */
    public function __construct(Manager $schemaManager, array $contenttypes, array $taxonomies, array $typemap, NamingStrategy $namingStrategy = null)
    {
        $this->schemaManager = $schemaManager;
        $this->contenttypes = $contenttypes;
        $this->taxonomies = $taxonomies;
        $this->typemap = $typemap;
        $this->namingStrategy = $namingStrategy;
    }

    /**
     * Reads the schema from Bolt\Storage\Database\Schema\Manager and creates mapping data
     */
    public function initialize()
    {
        $this->initializeShortAliases();
        foreach ($this->schemaManager->getSchemaTables() as $table) {
            $this->loadMetadataForTable($table);
        }
        $this->initialized = true;
    }

    /**
     * Setup some short aliases so non prefixed keys can be used to get metadata
     */
    public function initializeShortAliases()
    {
        foreach ($this->schemaManager->getSchemaTables() as $table) {
            if ($tableName = $table->getName()) {
                $this->aliases[$table->getOption('alias')] = $tableName;
            }
        }
    }

    /**
     * Getter for aliases
     *
     * @return array
     */
    public function getAliases()
    {
        return $this->aliases;
    }

    /**
     * Method will try to find an entity class name to handle data,
     * alternatively falling back to $this->fallbackEntity
     *
     * @param string $alias
     *
     * @return string Fully Qualified Class Name
     */
    public function resolveClassName($alias)
    {
        if (class_exists($alias)) {
            return $alias;
        }

        if (array_key_exists($alias, $this->aliases)) {
            $class = $this->aliases[$alias];
            if (class_exists($class)) {
                return $class;
            }
        }

        return $this->fallbackEntity;
    }

    /**
     * Load the metadata for a table.
     *
     * @param Table $table
     */
    protected function loadMetadataForTable(Table $table)
    {
        $tblName = $table->getName();

        if (isset($this->defaultAliases[$tblName])) {
            $className = $this->defaultAliases[$tblName];
        } else {
            $className = $tblName;
            $this->unmapped[] = $tblName;
        }

        $contentKey = $table->getOption('alias');
        $this->metadata[$className] = [];
        $this->metadata[$className]['identifier'] = $table->getPrimaryKey();
        $this->metadata[$className]['table'] = $table->getName();
        $this->metadata[$className]['boltname'] = $contentKey;
        foreach ($table->getColumns() as $colName => $column) {
            $mapping = [
                'fieldname'        => $colName,
                'type'             => $column->getType()->getName(),
                'fieldtype'        => $this->getFieldTypeFor($table->getOption('alias'), $column),
                'length'           => $column->getLength(),
                'nullable'         => $column->getNotnull(),
                'platformOptions'  => $column->getPlatformOptions(),
                'precision'        => $column->getPrecision(),
                'scale'            => $column->getScale(),
                'default'          => $column->getDefault(),
                'columnDefinition' => $column->getColumnDefinition(),
                'autoincrement'    => $column->getAutoincrement(),
            ];

            $this->metadata[$className]['fields'][$colName] = $mapping;

            if (isset($this->contenttypes[$contentKey]['fields'][$colName])) {
                $this->metadata[$className]['fields'][$colName]['data'] = $this->contenttypes[$contentKey]['fields'][$colName];
            }
        }

        // This loop checks the contenttypes definition for any non-db fields and adds them.
        if ($contentKey) {
            $this->setRelations($contentKey, $className, $table);
            $this->setIncomingRelations($contentKey, $className);
            $this->setTaxonomies($contentKey, $className, $table);
            $this->setTemplatefields($contentKey, $className, $table);
            $this->setRepeaters($contentKey, $className, $table);
        }

        foreach ($this->getAliases() as $alias => $table) {
            if (array_key_exists($table, $this->metadata)) {
                $this->metadata[$alias] = $this->metadata[$table];
            }
        }
    }

    public function setRepeaters($contentKey, $className, $table)
    {
        if (!isset($this->contenttypes[$contentKey])) {
            return;
        }

        foreach ($this->contenttypes[$contentKey]['fields'] as $key => $data) {
            $mapping = [
                'fieldname'        => $key,
                'type'             => 'null',
                'fieldtype'        => $this->typemap['repeater'],
                'tables'           => [
                    'field'        => $this->schemaManager->getTableName('field'),
                    'field_value'  => $this->schemaManager->getTableName('field_value'),
                ],
            ];

            if ($data['type'] === 'repeater') {
                foreach ($data['fields'] as $key => &$value) {
                    $value['fieldname'] = $key;
                    if (isset($this->typemap[$value['type']])) {
                        $value['fieldtype'] = $this->typemap[$value['type']];
                    } else {
                        $value['fieldtype'] = $this->typemap['text'];
                    }
                }

                $this->metadata[$className]['fields'][$key] = $mapping;
                $this->metadata[$className]['fields'][$key]['data'] = $data;
            }
        }
    }

    /**
     * Set the relationship.
     *
     * @param string $contentKey
     * @param string $className
     * @param Table  $table
     */
    public function setRelations($contentKey, $className, $table)
    {
        if (!isset($this->contenttypes[$contentKey]['relations'])) {
            return;
        }
        foreach ($this->contenttypes[$contentKey]['relations'] as $key => $data) {
            if (isset($data['alias'])) {
                $relationKey = $data['alias'];
            } else {
{'fraction_non_alphanumeric': 0.1456470588235294, 'fraction_numerical': 0.0, 'mean_word_length': 2.0715447154471547, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 213, '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': '7305637', 'n_tokens_mistral': 4741, 'n_tokens_neox': 4599, 'n_words': 951}