/** * 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]; }
/** * 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; }
/** * This method generates the repository class object, * which is passed to the template * it keeps all methods and properties including * user modified method bodies and comments * needed to create a repository class file * * @param \EBT\ExtensionBuilder\Domain\Model\DomainObject $domainObject * @param \EBT\ExtensionBuilder\Domain\Model\File $existingClassFileObject * * @return \EBT\ExtensionBuilder\Domain\Model\File */ public function generateRepositoryClassFileObject($domainObject, $repositoryTemplateClassPath, $existingClassFileObject = null) { $this->classObject = null; $className = $domainObject->getName() . 'Repository'; $this->templateFileObject = $this->parserService->parseFile($repositoryTemplateClassPath); $this->templateClassObject = $this->templateFileObject->getFirstClass(); if ($existingClassFileObject) { $this->classFileObject = $existingClassFileObject; $this->classObject = $existingClassFileObject->getFirstClass(); if ($this->classFileObject->getNamespace() === false) { $nameSpace = new \EBT\ExtensionBuilder\Domain\Model\NamespaceObject('dummy'); $this->classFileObject->addNamespace($nameSpace); } } if ($this->classObject == null) { $this->classFileObject = clone $this->templateFileObject; $this->classObject = clone $this->templateClassObject; $this->classObject->resetAll(); $this->classObject->setName($className); $this->classObject->setDescription('The repository for ' . Inflector::pluralize($domainObject->getName())); if (isset($this->settings['Repository']['parentClass'])) { $parentClass = $this->settings['Repository']['parentClass']; } else { $parentClass = '\\TYPO3\\CMS\\Extbase\\Persistence\\Repository'; } $this->classObject->setParentClassName($parentClass); } if ($domainObject->getSorting() && is_null($this->classObject->getProperty('defaultOrderings'))) { $defaultOrderings = $this->templateClassObject->getProperty('defaultOrderings'); $this->classObject->addProperty($defaultOrderings); } $this->classFileObject->getNamespace()->setName($this->extension->getNamespaceName() . '\\Domain\\Repository')->setClasses(array($this->classObject)); return $this->classFileObject; }
/** * This method generates the repository class object, * which is passed to the template * it keeps all methods and properties including * user modified method bodies and comments * needed to create a repository class file * * @param \EBT\ExtensionBuilder\Domain\Model\DomainObject $domainObject * @param boolean $mergeWithExistingClass * * @return \EBT\ExtensionBuilder\Domain\Model\File */ public function generateRepositoryClassFileObject($domainObject, $repositoryTemplateClassPath, $mergeWithExistingClass) { $this->classObject = NULL; $className = $domainObject->getName() . 'Repository'; $this->templateFileObject = $this->parserService->parseFile($repositoryTemplateClassPath); $this->templateClassObject = $this->templateFileObject->getFirstClass(); if ($mergeWithExistingClass) { try { $this->classFileObject = $this->roundTripService->getRepositoryClassFile($domainObject); if ($this->classFileObject instanceof Model\File) { $this->classObject = $this->classFileObject->getFirstClass(); } } catch (\Exception $e) { \TYPO3\CMS\Core\Utility\GeneralUtility::devLog('Class ' . $className . ' could not be imported: ' . $e->getMessage(), 'extension_builder'); } } if ($this->classObject == NULL) { $this->classFileObject = clone $this->templateFileObject; $this->classObject = clone $this->templateClassObject; $this->classObject->resetAll(); $this->classObject->setName($className); $this->classObject->setNamespaceName($this->extension->getNamespaceName() . '\\Domain\\Repository'); $this->classObject->setDescription('The repository for ' . Inflector::pluralize($domainObject->getName())); if (isset($this->settings['Repository']['parentClass'])) { $parentClass = $this->settings['Repository']['parentClass']; } else { $parentClass = '\\TYPO3\\CMS\\Extbase\\Persistence\\Repository'; } $this->classObject->setParentClassName($parentClass); } $this->classFileObject->getNamespace()->setName($this->extension->getNamespaceName() . '\\Domain\\Repository')->setClasses(array($this->classObject)); return $this->classFileObject; }
/** * @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); } } } } }
/** * remove domainObject related files if a domainObject was deleted * * @param \EBT\ExtensionBuilder\Domain\Model\DomainObject $domainObject * @return void */ protected function removeDomainObjectFiles(Model\DomainObject $domainObject) { $this->log('Remove domainObject ' . $domainObject->getName()); $this->cleanUp(FileGenerator::getFolderForClassFile($this->previousExtensionDirectory, 'Model', FALSE), $domainObject->getName() . '.php'); $this->cleanUp($this->previousExtensionDirectory . 'Configuration/TCA/', $domainObject->getName() . '.php'); if ($domainObject->isAggregateRoot()) { $this->cleanUp(FileGenerator::getFolderForClassFile($this->previousExtensionDirectory, 'Controller', FALSE), $domainObject->getName() . 'Controller.php'); $this->cleanUp(FileGenerator::getFolderForClassFile($this->previousExtensionDirectory, 'Repository', FALSE), $domainObject->getName() . 'Repository.php'); } if (count($domainObject->getActions()) > 0) { $this->cleanUp(FileGenerator::getFolderForClassFile($this->previousExtensionDirectory, 'Controller', FALSE), $domainObject->getName() . 'Controller.php'); } // other files $iconsDirectory = $this->extensionDirectory . 'Resources/Public/Icons/'; $languageDirectory = $this->extensionDirectory . 'Resources/Private/Language/'; $locallang_cshFile = $languageDirectory . 'locallang_csh_' . $domainObject->getDatabaseTableName() . '.xml'; $iconFile = $iconsDirectory . $domainObject->getDatabaseTableName() . '.gif'; if (file_exists($locallang_cshFile)) { // no overwrite settings check here... unlink($locallang_cshFile); GeneralUtility::devLog('locallang_csh file removed: ' . $locallang_cshFile, 'extension_builder', 1); } if (file_exists($iconFile)) { unlink($iconFile); GeneralUtility::devLog('icon file removed: ' . $iconFile, 'extension_builder', 1); } }