/** * Helper function to find the parents class recordType * @param \EBT\ExtensionBuilder\Domain\Model\DomainObject $domainObject * @return string */ public function render(\EBT\ExtensionBuilder\Domain\Model\DomainObject $domainObject) { $classSettings = $this->configurationManager->getExtbaseClassConfiguration($domainObject->getParentClass()); if (isset($classSettings['recordType'])) { $parentRecordType = \EBT\ExtensionBuilder\Utility\Tools::convertClassNameToRecordType($classSettings['recordType']); } else { $parentRecordType = \EBT\ExtensionBuilder\Utility\Tools::convertClassNameToRecordType($domainObject->getParentClass()); $existingTypes = $GLOBALS['TCA'][$domainObject->getDatabaseTableName()]['types']; \TYPO3\CMS\Core\Utility\GeneralUtility::devLog('Parent Record type: ' . $parentRecordType, 'extension_builder', 2, $existingTypes); if (is_array($existingTypes) && !isset($existingTypes[$parentRecordType])) { // no types field for parent record type configured, use the default type 1 if (isset($existingTypes['1'])) { $parentRecordType = 1; } else { //if it not exists get first existing key $parentRecordType = reset(array_keys($existingTypes)); } } } $this->templateVariableContainer->add('parentModelName', end(explode('\\', $domainObject->getParentClass()))); $this->templateVariableContainer->add('parentRecordType', $parentRecordType); $content = $this->renderChildren(); $this->templateVariableContainer->remove('parentRecordType'); $this->templateVariableContainer->remove('parentModelName'); return $content; }
/** * @param \EBT\ExtensionBuilder\Domain\Model\DomainObject $domainObject * @throws \InvalidArgumentException */ public function prepareLabelArrayForContextHelp($domainObject) { $labelArray = array(); foreach ($domainObject->getProperties() as $property) { $labelArray[$property->getFieldName() . '.description'] = htmlspecialchars($property->getDescription()); } return $labelArray; }
/** * This is the description * * @param \EBT\ExtensionBuilder\Domain\Model\DomainObject $domainObject * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TOOOL\Projects\Domain\Model\Calculation> $tests * @return \EBT\ExtensionBuilder\Domain\Model\DomainObject */ private static function testMethod(\EBT\ExtensionBuilder\Domain\Model\DomainObject $domainObject, \TYPO3\CMS\Extbase\Persistence\ObjectStorage $tests) { $number = 7; if ($number > $tests->count()) { return $domainObject; } else { $domainObject->setName('Foo'); } }
/** * Returns the relation table name. It is build by having 'tx_myextension_' followed by the * first domain object name followed by the second domain object name followed by '_mm'. * * @return string */ public function getRelationTableName() { if (!empty($this->relationTableName)) { return $this->relationTableName; } $relationTableName = 'tx_' . str_replace('_', '', $this->domainObject->getExtension()->getExtensionKey()) . '_'; $relationTableName .= strtolower($this->domainObject->getName()); if ($this->useExtendedRelationTableName) { $relationTableName .= '_' . strtolower($this->getName()); } $relationTableName .= '_' . strtolower($this->getForeignModelName()) . '_mm'; return $relationTableName; }
public function getForeignModelName() { if (is_object($this->foreignModel)) { return $this->foreignModel->getName(); } $parts = explode('\\Domain\\Model\\', $this->foreignClassName); return $parts[1]; }
/** * Do we have to create a typefield in database and configuration? * * A typefield is needed if either the domain objects extends another class * or if other domain objects of this extension extend it or if it is mapped * to an existing table * * @param string $tableName * @param string $parentClass * @param bool $isMappedToExternalTable * @return bool */ protected function needsTypeField(\EBT\ExtensionBuilder\Domain\Model\DomainObject $domainObject, $isMappedToExternalTable) { $needsTypeField = false; if ($domainObject->getChildObjects() || $isMappedToExternalTable) { $tableName = $domainObject->getDatabaseTableName(); if (!isset($GLOBALS['TCA'][$tableName]['ctrl']['type']) || $GLOBALS['TCA'][$tableName]['ctrl']['type'] == 'tx_extbase_type') { /** * if the type field is set but equals the default extbase record type field name it might * have been defined by the current extension and thus has to be defined again when rewriting TCA definitions * this might result in duplicate definition, but the type field definition is always wrapped in a condition * "if (!isset($GLOBALS['TCA'][table][ctrl][type]){ ..." * * If we don't check the TCA at runtime it would result in a repetition of type field definitions * in case an extension has multiple models extending other models of the same extension */ $needsTypeField = true; } } return $needsTypeField; }
/** * The locallang key for this property which contains the label. * * @return <type> */ public function getLabelNamespace() { return $this->domainObject->getLabelNamespace() . '.' . $this->getFieldName(); }
/** * Add a domain object to the extension. Creates the reverse link as well. * @param \EBT\ExtensionBuilder\Domain\Model\DomainObject $domainObject */ public function addDomainObject(\EBT\ExtensionBuilder\Domain\Model\DomainObject $domainObject) { $domainObject->setExtension($this); foreach (array_keys($this->domainObjects) as $existingDomainObjectName) { if (strtolower($domainObject->getName()) == strtolower($existingDomainObjectName)) { throw new \EBT\ExtensionBuilder\Domain\Exception\ExtensionException('Duplicate domain object name "' . $domainObject->getName() . '".', \EBT\ExtensionBuilder\Domain\Validator\ExtensionValidator::ERROR_DOMAINOBJECT_DUPLICATE); } } if ($domainObject->getNeedsUploadFolder()) { $this->needsUploadFolder = TRUE; } $this->domainObjects[$domainObject->getName()] = $domainObject; }
/** * * @param array $relationJsonConfiguration * @param \EBT\ExtensionBuilder\Domain\Model\DomainObject $domainObject * @throws \Exception * @return \EBT\ExtensionBuilder\Domain\Model\DomainObject\Relation\AbstractRelation */ public function buildRelation($relationJsonConfiguration, $domainObject) { $relationSchemaClassName = 'EBT\\ExtensionBuilder\\Domain\\Model\\DomainObject\\Relation\\'; $relationSchemaClassName .= ucfirst($relationJsonConfiguration['relationType']) . 'Relation'; if (!class_exists($relationSchemaClassName)) { throw new \Exception('Relation of type ' . $relationSchemaClassName . ' not found (configured in "' . $relationJsonConfiguration['relationName'] . '")'); } /** * @var $relation \EBT\ExtensionBuilder\Domain\Model\DomainObject\Relation\AbstractRelation */ $relation = new $relationSchemaClassName(); $relation->setName($relationJsonConfiguration['relationName']); $relation->setLazyLoading((bool) $relationJsonConfiguration['lazyLoading']); $relation->setExcludeField($relationJsonConfiguration['propertyIsExcludeField']); $relation->setDescription($relationJsonConfiguration['relationDescription']); $relation->setUniqueIdentifier($relationJsonConfiguration['uid']); $relation->setType($relationJsonConfiguration['type']); if (!empty($relationJsonConfiguration['foreignRelationClass'])) { // relations without wires if (strpos($relationJsonConfiguration['foreignRelationClass'], '\\') > 0) { // add trailing slash if not set $relationJsonConfiguration['foreignRelationClass'] = '\\' . $relationJsonConfiguration['foreignRelationClass']; } $relation->setForeignClassName($relationJsonConfiguration['foreignRelationClass']); $relation->setRelatedToExternalModel(TRUE); $extbaseClassConfiguration = $this->configurationManager->getExtbaseClassConfiguration($relationJsonConfiguration['foreignRelationClass']); if (isset($extbaseClassConfiguration['tableName'])) { $foreignDatabaseTableName = $extbaseClassConfiguration['tableName']; $this->relatedForeignTables[$foreignDatabaseTableName] = 1; } else { $foreignDatabaseTableName = Tools::parseTableNameFromClassName($relationJsonConfiguration['foreignRelationClass']); } $relation->setForeignDatabaseTableName($foreignDatabaseTableName); if ($relation instanceof \EBT\ExtensionBuilder\Domain\Model\DomainObject\Relation\ZeroToManyRelation) { $foreignKeyName = strtolower($domainObject->getName()); if (\EBT\ExtensionBuilder\Service\ValidationService::isReservedMYSQLWord($foreignKeyName)) { $foreignKeyName = 'tx_' . $foreignKeyName; } if (isset($this->relatedForeignTables[$foreignDatabaseTableName])) { $foreignKeyName .= $this->relatedForeignTables[$foreignDatabaseTableName]; $this->relatedForeignTables[$foreignDatabaseTableName] += 1; } else { $foreignDatabaseTableName = Tools::parseTableNameFromClassName($relationJsonConfiguration['foreignRelationClass']); } $relation->setForeignDatabaseTableName($foreignDatabaseTableName); } if ($relation->isFileReference() && !empty($relationJsonConfiguration['maxItems'])) { /** @var $relation \EBT\ExtensionBuilder\Domain\Model\DomainObject\FileProperty */ $relation->setMaxItems($relationJsonConfiguration['maxItems']); } } return $relation; }
/** * Not used right now * TODO: Needs better implementation * @param \EBT\ExtensionBuilder\Domain\Model\DomainObject $domainObject * @return void */ public function sortMethods($domainObject) { $objectProperties = $domainObject->getProperties(); $sortedProperties = array(); $propertyRelatedMethods = array(); $customMethods = array(); // sort all properties and methods according to domainObject sort order foreach ($objectProperties as $objectProperty) { if ($this->classObject->propertyExists($objectProperty->getName())) { $sortedProperties[$objectProperty->getName()] = $this->classObject->getProperty($objectProperty->getName()); $methodPrefixes = array('get', 'set', 'add', 'remove', 'is'); foreach ($methodPrefixes as $methodPrefix) { $methodName = self::getMethodName($objectProperty, $methodPrefix); if ($this->classObject->methodExists($methodName)) { $propertyRelatedMethods[$methodName] = $this->classObject->getMethod($methodName); } } } } // add the properties that were not in the domainObject $classProperties = $this->classObject->getProperties(); $sortedPropertyNames = array_keys($sortedProperties); foreach ($classProperties as $classProperty) { if (!in_array($classProperty->getName(), $sortedProperties)) { $sortedProperties[$classProperty->getName()] = $classProperty; } } // add custom methods that were manually added to the class $classMethods = $this->classObject->getMethods(); $propertyRelatedMethodNames = array_keys($propertyRelatedMethods); foreach ($classMethods as $classMethod) { if (!in_array($classMethod->getName(), $propertyRelatedMethodNames)) { $customMethods[$classMethod->getName()] = $classMethod; } } $sortedMethods = array_merge($customMethods, $propertyRelatedMethods); $this->classObject->setProperties($sortedProperties); $this->classObject->setMethods($sortedMethods); }
/** * @param \EBT\ExtensionBuilder\Domain\Model\DomainObject $domainObject * @return void * @throws ExtensionException */ private function validateProperties($domainObject) { $propertyNames = array(); foreach ($domainObject->getProperties() as $property) { // Check if property name is given if (!$property->getName()) { $this->validationResult['errors'][] = new ExtensionException('A property of ' . $domainObject->getName() . ' has no name', self::ERROR_PROPERTY_NO_NAME); } $propertyName = $property->getName(); /** * Character test * Allowed characters are: a-z (lowercase), A-Z (uppercase) and 0-9 */ if (!preg_match('/^[a-zA-Z0-9]*$/', $propertyName)) { $this->validationResult['errors'][] = new ExtensionException('Illegal property name "' . $propertyName . '" of ' . $domainObject->getName() . '.' . LF . 'Please use lowerCamelCase, no spaces or underscores.', self::ERROR_PROPERTY_ILLEGAL_CHARACTER); } $firstChar = $propertyName[0]; if (strtoupper($firstChar) == $firstChar) { $this->validationResult['errors'][] = new ExtensionException('Illegal first character of property name "' . $property->getName() . '" of domain object "' . $domainObject->getName() . '".' . LF . 'Please use lowerCamelCase.', self::ERROR_PROPERTY_UPPER_FIRST_CHARACTER); } if (\EBT\ExtensionBuilder\Service\ValidationService::isReservedTYPO3Word($propertyName)) { $this->validationResult['warnings'][] = new ExtensionException('The name of property "' . $propertyName . '" in Model "' . $domainObject->getName() . '" will result in a TYPO3 specific column name.' . LF . ' This might result in unexpected behaviour. If you didn\'t choose that name by purpose' . LF . ' it is recommended to use another name', self::ERROR_PROPERTY_RESERVED_WORD); } if (\EBT\ExtensionBuilder\Service\ValidationService::isReservedMYSQLWord($propertyName)) { $this->validationResult['warnings'][] = new ExtensionException('Property "' . $propertyName . '" in Model "' . $domainObject->getName() . '".', self::ERROR_PROPERTY_RESERVED_SQL_WORD); } // Check for duplicate property names if (in_array($propertyName, $propertyNames)) { $this->validationResult['errors'][] = new ExtensionException('Property "' . $property->getName() . '" of ' . $domainObject->getName() . ' exists twice.', self::ERROR_PROPERTY_DUPLICATE); } $propertyNames[] = $propertyName; if ($property instanceof \EBT\ExtensionBuilder\Domain\Model\DomainObject\Relation\AbstractRelation) { if (!$property->getForeignModel() && $property->getForeignClassName()) { if (!class_exists($property->getForeignClassName())) { $this->validationResult['errors'][] = new ExtensionException('Related class not loadable: "' . $property->getForeignClassName() . '" configured in relation "' . $property->getName() . '".', self::ERROR_MAPPING_NO_FOREIGNCLASS); } } if ($property->getForeignModel() && $property->getForeignModel()->getFullQualifiedClassName() != $property->getForeignClassName()) { $this->validationResult['errors'][] = new ExtensionException('Relation "' . $property->getName() . '" in model "' . $domainObject->getName() . '" has a external class relation and a wire to ' . $property->getForeignModel()->getName(), self::ERROR_MAPPING_WIRE_AND_FOREIGNCLASS); } } } }
/** * Move custom TCA in files generated by EB versions <= 6.2 * to the appropriate overrides files * * @param \EBT\ExtensionBuilder\Domain\Model\DomainObject $domainObject */ public static function moveAdditionalTcaToOverrideFile($domainObject) { $tcaDir = $domainObject->getExtension()->getExtensionDir() . 'Configuration/TCA/'; $existingTcaFile = $tcaDir . $domainObject->getName() . '.php'; if (file_exists($existingTcaFile)) { $existingFileContent = file_get_contents($existingTcaFile); $fileParts = explode(self::SPLIT_TOKEN, $existingFileContent); if (count($fileParts) === 2) { $customFileContent = str_replace('$TCA[', '$GLOBALS[\'TCA\'][', $fileParts[1]); $customFileContent = '<?php ' . LF . self::SPLIT_TOKEN . LF . str_replace('?>', '', $customFileContent); if (!empty($customFileContent)) { $overrideDir = $tcaDir . 'Overrides/'; if (!is_dir($overrideDir)) { \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir_deep($tcaDir, 'Overrides'); } $success = \TYPO3\CMS\Core\Utility\GeneralUtility::writeFile($overrideDir . $domainObject->getDatabaseTableName() . '.php', $customFileContent); if (!$success) { throw new \Exception('File ' . $overrideDir . $domainObject->getDatabaseTableName() . '.php could not be created!'); } else { unlink($existingTcaFile); } } } } }
/** * Returns the current TCA for a domain objects table if the * extension is installed * TODO: check for previous table name if an extension is renamed * * @param \EBT\ExtensionBuilder\Domain\Model\DomainObject $domainObject * * @return array */ protected static function getTcaForDomainObject($domainObject) { $tableName = $domainObject->getDatabaseTableName(); GeneralUtility::loadTca($tableName); if (isset($GLOBALS['TCA'][$tableName])) { return $GLOBALS['TCA'][$tableName]; } else { return NULL; } }