/**
  * @param TypeInterface $type
  */
 public function buildType(TypeInterface $type)
 {
     $class_name = Inflector::classify(Inflector::singularize($type->getName()));
     $base_class_name = 'Base\\' . $class_name;
     $class_build_path = $this->getBuildPath() ? "{$this->getBuildPath()}/{$class_name}.php" : null;
     if ($class_build_path && is_file($class_build_path)) {
         $this->triggerEvent('on_class_build_skipped', [$class_name, $class_build_path]);
         return;
     }
     $result = [];
     $result[] = '<?php';
     $result[] = '';
     if ($this->getStructure()->getConfig('header_comment')) {
         $result = array_merge($result, explode("\n", $this->getStructure()->getConfig('header_comment')));
         $result[] = '';
     }
     if ($this->getStructure()->getNamespace()) {
         $result[] = 'namespace ' . $this->getStructure()->getNamespace() . ';';
         $result[] = '';
         $result[] = '/**';
         $result[] = ' * @package ' . $this->getStructure()->getNamespace();
         $result[] = ' */';
     }
     $result[] = 'class ' . $class_name . ' extends ' . $base_class_name;
     $result[] = '{';
     $result[] = '}';
     $result[] = '';
     $result = implode("\n", $result);
     if ($this->getBuildPath()) {
         file_put_contents($class_build_path, $result);
     } else {
         eval(ltrim($result, '<?php'));
     }
     $this->triggerEvent('on_class_built', [$class_name, $class_build_path]);
 }
 /**
  * @param TypeInterface $type
  */
 public function buildType(TypeInterface $type)
 {
     $base_manager_class_name = Inflector::classify($type->getName());
     $type_class_name = Inflector::classify(Inflector::singularize($type->getName()));
     $base_class_build_path = $this->getBuildPath() ? "{$this->getBuildPath()}/Manager/Base/{$base_manager_class_name}.php" : null;
     $result = [];
     $result[] = '<?php';
     $result[] = '';
     if ($this->getStructure()->getConfig('header_comment')) {
         $result = array_merge($result, explode("\n", $this->getStructure()->getConfig('header_comment')));
         $result[] = '';
     }
     if ($this->getStructure()->getNamespace()) {
         $base_class_namespace = $this->getStructure()->getNamespace() . '\\Manager\\Base';
         $type_class_name = '\\' . ltrim($this->getStructure()->getNamespace(), '\\') . '\\' . $type_class_name;
     } else {
         $base_class_namespace = 'Manager\\Base';
     }
     $result[] = 'namespace ' . $base_class_namespace . ';';
     $result[] = '';
     $result[] = '/**';
     $result[] = ' * @package ' . $base_class_namespace;
     $result[] = ' */';
     $interfaces = $traits = [];
     foreach ($type->getTraits() as $interface => $implementations) {
         if ($interface != '--just-paste-trait--') {
             $interfaces[] = '\\' . ltrim($interface, '\\');
         }
         if (count($implementations)) {
             foreach ($implementations as $implementation) {
                 $traits[] = '\\' . ltrim($implementation, '\\');
             }
         }
     }
     $result[] = 'abstract class ' . $base_manager_class_name . ' extends \\ActiveCollab\\DatabaseObject\\Entity\\Manager';
     $result[] = '{';
     $result[] = '    /**';
     $result[] = '     * Return type that this manager works with.';
     $result[] = '     *';
     $result[] = '     * @return string';
     $result[] = '     */';
     $result[] = '    public function getType()';
     $result[] = '    {';
     $result[] = '        return ' . var_export($type_class_name, true) . ';';
     $result[] = '    }';
     $result[] = '}';
     $result[] = '';
     $result = implode("\n", $result);
     if ($this->getBuildPath()) {
         file_put_contents($base_class_build_path, $result);
     } else {
         eval(ltrim($result, '<?php'));
     }
     $this->triggerEvent('on_class_built', [$base_manager_class_name, $base_class_build_path]);
 }
 /**
  * Prepare belongs to constraint statement.
  *
  * @param  TypeInterface    $type
  * @param  TriggerInterface $trigger
  * @return string
  */
 public function prepareCreateTriggerStatement(TypeInterface $type, TriggerInterface $trigger)
 {
     if (strpos($trigger->getBody(), "\n") === false) {
         return rtrim('CREATE TRIGGER ' . $this->getConnection()->escapeFieldName($trigger->getName()) . ' ' . strtoupper($trigger->getTime()) . ' ' . strtoupper($trigger->getEvent()) . ' ON ' . $this->getConnection()->escapeTableName($type->getName()) . ' FOR EACH ROW ' . $trigger->getBody(), ';') . ';';
     } else {
         $result = [];
         $result[] = 'CREATE TRIGGER ' . $this->getConnection()->escapeFieldName($trigger->getName()) . ' ' . strtoupper($trigger->getTime()) . ' ' . strtoupper($trigger->getEvent()) . ' ON ' . $this->getConnection()->escapeTableName($type->getName());
         $result[] = 'FOR EACH ROW BEGIN';
         $result[] = $trigger->getBody();
         $result[] = 'END;';
         return $this->wrapDelimiter(implode("\n", $result));
     }
 }
 /**
  * @param TypeInterface $type
  */
 public function buildType(TypeInterface $type)
 {
     $base_class_name = Inflector::classify(Inflector::singularize($type->getName()));
     $base_class_extends = '\\' . ltrim($type->getBaseClassExtends(), '\\');
     $base_class_build_path = $this->getBuildPath() ? "{$this->getBuildPath()}/Base/{$base_class_name}.php" : null;
     $result = [];
     $result[] = '<?php';
     $result[] = '';
     if ($this->getStructure()->getConfig('header_comment')) {
         $result = array_merge($result, explode("\n", $this->getStructure()->getConfig('header_comment')));
         $result[] = '';
     }
     $base_class_namespace = $this->getStructure()->getNamespace() ? $this->getStructure()->getNamespace() . '\\Base' : 'Base';
     $result[] = 'namespace ' . $base_class_namespace . ';';
     $result[] = '';
     $result[] = '/**';
     $this->buildBaseClassDocBlockProperties('', $result);
     $result[] = ' * @package ' . $base_class_namespace;
     $result[] = ' */';
     $interfaces = $traits = [];
     foreach ($type->getTraits() as $interface => $implementations) {
         if ($interface != '--just-paste-trait--') {
             $interfaces[] = '\\' . ltrim($interface, '\\');
         }
         if (count($implementations)) {
             foreach ($implementations as $implementation) {
                 $traits[] = '\\' . ltrim($implementation, '\\');
             }
         }
     }
     $result[] = 'abstract class ' . $base_class_name . ' extends ' . $base_class_extends . (empty($interfaces) ? '' : ' implements ' . implode(', ', $interfaces));
     $result[] = '{';
     if (count($traits)) {
         $trait_tweaks_count = count($type->getTraitTweaks());
         $result[] = '    use ' . implode(', ', $traits) . ($trait_tweaks_count ? '{' : ';');
         if ($trait_tweaks_count) {
             for ($i = 0; $i < $trait_tweaks_count - 1; ++$i) {
                 $result[] = '        ' . $type->getTraitTweaks()[$i] . ($i < $trait_tweaks_count - 2 ? ',' : '');
             }
             $result[] = '    }';
         }
         $result[] = '';
     }
     $result[] = '    /**';
     $result[] = '     * Name of the table where records are stored.';
     $result[] = '     *';
     $result[] = '     * @var string';
     $result[] = '     */';
     $result[] = '    protected $table_name = ' . var_export($type->getTableName(), true) . ';';
     $fields = $type->getAllFields();
     $this->buildFields($fields, '    ', $result);
     $this->buildGeneratedFields(array_keys($type->getGeneratedFields()), '    ', $result);
     if (count($type->getProtectedFields())) {
         $result[] = '';
         $result[] = '    /**';
         $result[] = '     * List of protected fields.';
         $result[] = '     *';
         $result[] = '     * @var array';
         $result[] = '     */';
         $result[] = '    protected $protected_fields = [' . implode(', ', array_map(function ($field) {
             return var_export($field, true);
         }, $type->getProtectedFields())) . '];';
     }
     if ($type->getOrderBy() != ['id']) {
         $result[] = '';
         $result[] = '    /**';
         $result[] = '     * @var string[]';
         $result[] = '     */';
         $result[] = '    protected $order_by = [' . implode(', ', array_map(function ($value) {
             return var_export($value, true);
         }, $type->getOrderBy())) . '];';
     }
     $this->buildConfigureMethod($type->getGeneratedFields(), '    ', $result);
     foreach ($type->getAssociations() as $association) {
         $association->buildClassMethods($this->getStructure(), $type, $this->getStructure()->getType($association->getTargetTypeName()), $result);
     }
     foreach ($fields as $field) {
         if ($field instanceof ScalarField && $field->getShouldBeAddedToModel() && $field->getName() != 'id') {
             $this->buildFieldGetterAndSetter($field, '    ', $result);
         }
     }
     foreach ($type->getGeneratedFields() as $field_name => $caster) {
         $this->buildGeneratedFieldGetter($field_name, $caster, '    ', $result);
     }
     $build_custom_get_field_value = false;
     foreach ($fields as $field) {
         if ($field instanceof ScalarField && $field->getShouldBeAddedToModel() && !empty($field->getDeserializingCode('raw_value'))) {
             $build_custom_get_field_value = true;
             break;
         }
     }
     if ($build_custom_get_field_value) {
         $result[] = '';
         $result[] = '    /**';
         $result[] = '     * {@inheritdoc}';
         $result[] = '     */';
         $result[] = '    public function getFieldValue($field, $default = null)';
         $result[] = '    {';
         $result[] = '        $value = parent::getFieldValue($field, $default);';
         $result[] = '';
         $result[] = '        if ($value === null) {';
         $result[] = '            return null;';
         $result[] = '        } else {';
         $result[] = '            switch ($field) {';
         foreach ($fields as $field) {
             if ($field instanceof ScalarField && $field->getShouldBeAddedToModel() && !empty($field->getDeserializingCode('value'))) {
                 $result[] = '                case ' . var_export($field->getName(), true) . ':';
                 $result[] = '                    return ' . $field->getDeserializingCode('value') . ';';
             }
         }
         $result[] = '            }';
         $result[] = '';
         $result[] = '            return $value;';
         $result[] = '        }';
         $result[] = '    }';
     }
     $result[] = '';
     $result[] = '    /**';
     $result[] = '     * {@inheritdoc}';
     $result[] = '     */';
     $result[] = '    public function &setFieldValue($name, $value)';
     $result[] = '    {';
     $result[] = '        if ($value === null) {';
     $result[] = '            parent::setFieldValue($name, null);';
     $result[] = '        } else {';
     $result[] = '            switch ($name) {';
     $casters = [];
     foreach ($fields as $field) {
         if ($field instanceof ScalarField && $field->getShouldBeAddedToModel()) {
             $casting_code = $field->getCastingCode('value');
             if (empty($casters[$casting_code])) {
                 $casters[$casting_code] = [];
             }
             $casters[$casting_code][] = $field->getName();
         }
     }
     foreach ($casters as $caster_code => $casted_field_names) {
         foreach ($casted_field_names as $casted_field_name) {
             $result[] = '                case ' . var_export($casted_field_name, true) . ':';
         }
         $result[] = '                    return parent::setFieldValue($name, ' . $caster_code . ');';
     }
     $result[] = '                default:';
     $result[] = '                    if ($this->isLoading()) {';
     $result[] = '                        return parent::setFieldValue($name, $value);';
     $result[] = '                    } else {';
     $result[] = '                        if ($this->isGeneratedField($name)) {';
     $result[] = '                            throw new \\LogicException("Generated field $name cannot be set by directly assigning a value");';
     $result[] = '                        } else {';
     $result[] = '                            throw new \\InvalidArgumentException("Field $name does not exist in this table");';
     $result[] = '                        }';
     $result[] = '                    }';
     $result[] = '            }';
     $result[] = '        }';
     $result[] = '';
     $result[] = '        return $this;';
     $result[] = '    }';
     $this->buildJsonSerialize($type->getSerialize(), '    ', $result);
     $this->buildCompositeFieldMethods($type->getFields(), '    ', $result);
     $this->buildValidate($type->getFields(), $type->getAssociations(), '    ', $result);
     $result[] = '}';
     $result[] = '';
     $result = implode("\n", $result);
     if ($this->getBuildPath()) {
         file_put_contents($base_class_build_path, $result);
     } else {
         eval(ltrim($result, '<?php'));
     }
     $this->triggerEvent('on_class_built', [$base_class_name, $base_class_build_path]);
 }
 /**
  * Build class methods.
  *
  * @param StructureInterface $structure
  * @param TypeInterface      $source_type
  * @param TypeInterface      $target_type
  * @param array              $result
  */
 public function buildClassMethods(StructureInterface $structure, TypeInterface $source_type, TypeInterface $target_type, array &$result)
 {
     $namespace = $structure->getNamespace();
     if ($namespace) {
         $namespace = '\\' . ltrim($namespace, '\\');
     }
     $target_instance_class = $namespace . '\\' . Inflector::classify(Inflector::singularize($target_type->getName()));
     $classified_association_name = Inflector::classify($this->getName());
     $getter_name = "get{$classified_association_name}";
     $setter_name = "set{$classified_association_name}";
     $fk_getter_name = "get{$classified_association_name}Id";
     $fk_setter_name = "set{$classified_association_name}Id";
     $result[] = '';
     $result[] = '    /**';
     $result[] = '     * Return ' . Inflector::singularize($source_type->getName()) . ' ' . $this->getName() . '.';
     $result[] = '     *';
     $result[] = '     * @return ' . $target_instance_class;
     $result[] = '     */';
     $result[] = '    public function ' . $getter_name . '()';
     $result[] = '    {';
     $result[] = '        return $this->pool->getById(' . var_export($target_instance_class, true) . ', $this->' . $fk_getter_name . '());';
     $result[] = '    }';
     $setter_access_level = $this->getProtectSetter() ? 'protected' : 'public';
     $result[] = '';
     $result[] = '    /**';
     $result[] = '     * Set ' . Inflector::singularize($source_type->getName()) . ' ' . $this->getName() . '.';
     $result[] = '     *';
     $result[] = '     * @param  ' . $target_instance_class . ' $value';
     $result[] = '     * @return $this';
     $result[] = '     */';
     $result[] = '    ' . $setter_access_level . ' function &' . $setter_name . '(' . $target_instance_class . ' $value' . ($this->isRequired() ? '' : ' = null') . ')';
     $result[] = '    {';
     if ($this->isRequired()) {
         $result[] = '        if (empty($value) || !$value->isLoaded()) {';
         $result[] = '            throw new \\InvalidArgumentException(\'Valid related instance is required\');';
         $result[] = '        }';
         $result[] = '';
         $result[] = '        $this->' . $fk_setter_name . '($value->getId());';
         $result[] = '';
         $result[] = '        return $this;';
     } else {
         $result[] = '        if (empty($value)) {';
         $result[] = '            $this->' . $fk_setter_name . '(0);';
         $result[] = '        } else {';
         $result[] = '            $this->' . $fk_setter_name . '($value->getId());';
         $result[] = '        }';
         $result[] = '';
         $result[] = '        return $this;';
     }
     $result[] = '    }';
 }
 /**
  * Return full instance class from namespace and type.
  *
  * @param  string        $namespace
  * @param  TypeInterface $type
  * @return string
  */
 protected function getInstanceClassFrom($namespace, TypeInterface $type)
 {
     return $namespace . '\\' . Inflector::classify(Inflector::singularize($type->getName()));
 }
 /**
  * Prepare has one constraint statement.
  *
  * @param  TypeInterface     $type
  * @param  HasOneAssociation $association
  * @return string
  */
 public function prepareHasOneConstraintStatement(TypeInterface $type, HasOneAssociation $association)
 {
     $result = [];
     $result[] = 'ALTER TABLE ' . $this->getConnection()->escapeTableName($type->getName());
     $result[] = '    ADD CONSTRAINT ' . $this->getConnection()->escapeFieldName($association->getConstraintName());
     $result[] = '    FOREIGN KEY (' . $this->getConnection()->escapeFieldName($association->getFieldName()) . ') REFERENCES ' . $this->getConnection()->escapeTableName($association->getTargetTypeName()) . '(`id`)';
     if ($association->isRequired()) {
         $result[] = '    ON UPDATE CASCADE ON DELETE CASCADE;';
     } else {
         $result[] = '    ON UPDATE SET NULL ON DELETE SET NULL;';
     }
     return implode("\n", $result);
 }
 /**
  * {@inheritdoc}
  */
 public function buildClearRelatedObjectsMethod(StructureInterface $structure, TypeInterface $source_type, TypeInterface $target_type, $namespace, array &$result)
 {
     $intermediary_type = $structure->getType($this->intermediary_type_name);
     $intermediary_instance_class = $this->getInstanceClassFrom($namespace, $intermediary_type);
     $result[] = '';
     $result[] = '    /**';
     $result[] = '     * Drop all connections between ' . str_replace('_', ' ', $target_type->getName()) . ' and this ' . Inflector::singularize($source_type->getName()) . '.';
     $result[] = '     *';
     $result[] = '     * @return $this';
     $result[] = '     */';
     $result[] = "    public function &clear{$this->getClassifiedAssociationName()}()";
     $result[] = '    {';
     $result[] = '        if ($objects = $this->get' . $this->getClassifiedAssociationName() . '()) {';
     $result[] = '            $object_ids = [];';
     $result[] = '';
     $result[] = '            $this->connection->transact(function () use ($objects, &$object_ids) {';
     $result[] = '                foreach ($objects as $object) {';
     $result[] = '                    $object_ids[] = $object->getId();';
     $result[] = '                    $object->delete(true);';
     $result[] = '                }';
     $result[] = '            });';
     $result[] = '';
     $result[] = '            $this->pool->forget(' . var_export($intermediary_instance_class, true) . ', $object_ids);';
     $result[] = '        }';
     $result[] = '';
     $result[] = '        return $this;';
     $result[] = '    }';
 }
 /**
  * Prepare create connection table statement.
  *
  * @param  TypeInterface                  $source
  * @param  TypeInterface                  $target
  * @param  HasAndBelongsToManyAssociation $association
  * @return string
  */
 public function prepareConnectionCreateTableStatement(TypeInterface $source, TypeInterface $target, HasAndBelongsToManyAssociation $association)
 {
     $result = [];
     $result[] = 'CREATE TABLE IF NOT EXISTS ' . $this->getConnection()->escapeTableName($association->getConnectionTableName()) . ' (';
     $left_field_name = $association->getLeftFieldName();
     $right_field_name = $association->getRightFieldName();
     $left_field = (new IntegerField($left_field_name, 0))->unsigned(true)->size($source->getIdField()->getSize());
     $right_field = (new IntegerField($right_field_name, 0))->unsigned(true)->size($target->getIdField()->getSize());
     $result[] = '    ' . $this->prepareFieldStatement($left_field) . ',';
     $result[] = '    ' . $this->prepareFieldStatement($right_field) . ',';
     $result[] = '    ' . $this->prepareIndexStatement(new Index('PRIMARY', [$left_field->getName(), $right_field->getName()], IndexInterface::PRIMARY)) . ',';
     $result[] = '    ' . $this->prepareIndexStatement(new Index($right_field->getName()));
     $result[] = ') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;';
     return implode("\n", $result);
 }