├── .gitignore ├── README.md ├── mycomponent ├── build │ ├── build.models.php │ ├── build.package.php │ ├── build.schema.php │ ├── config │ │ ├── config.inc.php │ │ └── version.php │ ├── data │ │ ├── README.md │ │ ├── setup.options.php │ │ ├── setup.requires.php │ │ ├── transport.chunks.php │ │ ├── transport.events.php │ │ ├── transport.menu.php │ │ ├── transport.plugins.php │ │ ├── transport.settings.php │ │ ├── transport.snippets.php │ │ ├── transport.templates.php │ │ └── transport.tvs.php │ ├── data_static │ │ ├── README.md │ │ ├── setup.options.php │ │ ├── transport.chunks.php │ │ ├── transport.menu.php │ │ ├── transport.plugins.php │ │ ├── transport.settings.php │ │ ├── transport.snippets.php │ │ ├── transport.templates.php │ │ └── transport.tvs.php │ ├── resolvers │ │ ├── resolver.tables.php │ │ └── resolvers.php │ └── templates │ │ ├── changelog.item.txt │ │ └── readme.txt └── copy-from-www.php └── tools ├── modxbuilder.class.php └── xpdogenerator.class.php /.gitignore: -------------------------------------------------------------------------------- 1 | /mycomponent/core 2 | /omtestimonials 3 | .idea 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Набор скриптов для автоматической генерации xPDO моделей и сборки компонента MODx. 2 | 3 | Работает только с mySQL, так как используется информация из базы данных information_schema. 4 | 5 | ## Установка 6 | 7 | По-умолчанию папка modxbuilder должна располагаться в каталоге на уровень выше вашей корневой папки сайта (www). 8 | 9 | ``` 10 | . 11 | |-modxbuilder 12 | | |-mycomponent 13 | | | |-build 14 | | | 15 | | |-tools 16 | | 17 | |-www 18 | |-assets 19 | |-connectors 20 | |-core 21 | |-manager 22 | |-config.core.php 23 | |-index.php 24 | ``` 25 | 26 | После того, как вы склонировали репозиторий в папку modxbuilder, можно приступать к работе 27 | 28 | ## Определяем имя компонента, префикс классов и префикс таблиц 29 | 30 | Перед тем как создавать свои таблицы в БД, необходимо определиться с названием вашего расширения, префиксом таблиц и префиксом генерируемых классов 31 | 32 | Например, вы хотите назвать ваш компонент `yourcomponent`, префикс классов `your`, префикс таблиц `your_`: 33 | 34 | 1. Переименуйте папку `modxbuilder/mycomponent` в `modxbuilder/yourcomponent` 35 | 2. Откройте файл `modxbuilder/yourcomponent/build/config/build.config.php` и укажите соответствующие значения в переменных `real_package_name` `package_name` `package_table_prefix` `package_class_prefix` 36 | 37 | ## Создание пользовательских таблиц 38 | 39 | Создайте в вашей базе данных таблицу(-ы) с именем в формате: 40 | 41 | ``` 42 | [глобальный_префикс][префикс_ваших_таблиц][имя_таблицы] 43 | ``` 44 | 45 | Например: 46 | 47 | ``` 48 | modx_your_books 49 | ``` 50 | 51 | При создании таблицы вы можете явно указать класс будущей модели через поле комментария к таблице. Если его не указать, то имя класса будет сформировано 52 | автоматически из названия таблицы. В нашем случае имя класса будет yourBooks. 53 | 54 | Поскольку books - это множественное число, то вы, вероятно, хотели бы, чтобы ваш класс назывался yourBook. Выхода из этой ситуации два: 55 | 56 | 1. Изменить название таблицы на modx_your_book 57 | 2. Указать в комментариях к таблице modx_your_book значение yourBook. В этом случае, независимо от названия таблицы (после modx_your_) имя вашей модели 58 | будет четко указано - yourBook 59 | 60 | ## Генерация первичной xml-схемы 61 | 62 | После того, как вы создали все необходимые таблицы с префиксом modx_your_ (или другим, выбранным вами), запускаем скрипт `modxbuilder/[имя_компонента]/build/build.schema.php` 63 | 64 | При успешном выполнении скрипта, вы получите файл `modxbuilder/[имя_компонента]/core/components/[имя_компонента]/model/schema/[имя_компонента].mysql.schema.new.xml` 65 | 66 | ## Создание основной xml-схемы 67 | 68 | После того, как вы получили файл первичной схемы, вам необходимо скопировать содержимое файла `modxbuilder/[имя_компонента]/core/components/[имя_компонента]/model/schema/[имя_компонента].mysql.schema.new.xml` 69 | в файл `modxbuilder/[имя_компонента]/core/components/[имя_компонента]/model/schema/[имя_компонента].mysql.schema.xml`. 70 | 71 | При необходимости, дополните содержимое файла `modxbuilder/[имя_компонента]/core/components/[имя_компонента]/model/schema/[имя_компонента].mysql.schema.xml` 72 | дополнительной информацией о связях между таблицами и полями валидации. 73 | 74 | [Подробнее об описании связей между моделями](https://docs.modx.com/xpdo/2.x/getting-started/creating-a-model-with-xpdo/defining-a-schema/defining-relationships) 75 | 76 | [Подробнее о правилах валидации моделей](https://docs.modx.com/xpdo/2.x/getting-started/creating-a-model-with-xpdo/defining-a-schema/validation-rules-in-your-schema) 77 | 78 | ## Генерация моделей 79 | 80 | После того, как вы добавили содержимое в файл `modxbuilder/[имя_компонента]/core/components/[имя_компонента]/model/schema/[имя_компонента].mysql.schema.xml` 81 | запустите скрипт `modxbuilder/[имя_компонента]/build/build.models.php`. 82 | 83 | После успешного запуска вашего скрипта в папке `modxbuilder/[имя_компонента]/core/components/[имя_компонента]/model/[имя_компонента]/` появятся все 84 | сгенерированные файлы моделей и маппинги mysql. 85 | 86 | ## Использование моделей в проекте 87 | 88 | Как вы заметили, вся описанная выше работа изолирована в каталоге `modxbuilder/[имя_компонента]`. Для дальнейшего использования 89 | ваших моделей в проекте, вам необходимо: 90 | 91 | 1. Если сборка производится впервые и в папке www/core/components еще нет папки с вашим компонентом, то просто скопируйте содержимое каталога 92 | `modxbuilder/[имя_компонента]/core/components/` в каталог `www/core/components/` 93 | 2. Если сборка производится не впервые и в процессе разработки файлы в каталоге `www/core/components/[имя_компонента]/` претерпели ряд изменений, таких, например, как 94 | описание дополнительных методов внутри моделей или создание дополнительных классов, не связанных с базой данных, то вам необходимо только лишь переносить изменения из 95 | каталога `modxbuilder/[имя_компонента]/core/components/[имя_компонента]/` в каталог `www/core/components/[имя_компонента]`. Удобнее всего это делать при помощи функции сравнения каталогов в phpStorm. 96 | 97 | ## Сборщик пакетов 98 | 99 | Скрипт сборки пакета `modxbuilder/[имя_компонента]/build.package.php` 100 | 101 | Подробная инструкция в разработке... -------------------------------------------------------------------------------- /mycomponent/build/build.models.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | initialize('mgr'); 19 | $modx->setLogLevel(modX::LOG_LEVEL_INFO); 20 | $modx->setLogTarget(XPDO_CLI_MODE ? 'ECHO' : 'HTML'); 21 | 22 | $modxBuilder = new modxBuilder($modx,$buildConfig); 23 | 24 | //Парсим схему и генерируем модели и map.inc-файлы 25 | $modxBuilder->parseSchema(); -------------------------------------------------------------------------------- /mycomponent/build/build.package.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | initialize('mgr'); 19 | $modx->setLogLevel(modX::LOG_LEVEL_INFO); 20 | $modx->setLogTarget(XPDO_CLI_MODE ? 'ECHO' : 'HTML'); 21 | 22 | $modxBuilder = new modxBuilder($modx,$buildConfig); 23 | 24 | //Сборка компонента 25 | $modxBuilder->buildComponent(); -------------------------------------------------------------------------------- /mycomponent/build/build.schema.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | initialize('mgr'); 19 | $modx->setLogLevel(modX::LOG_LEVEL_INFO); 20 | $modx->setLogTarget(XPDO_CLI_MODE ? 'ECHO' : 'HTML'); 21 | 22 | $modxBuilder = new modxBuilder($modx,$buildConfig); 23 | 24 | //Здесь мы генерируем xml схему 25 | $modxBuilder->writeSchema(true,true,false); -------------------------------------------------------------------------------- /mycomponent/build/config/config.inc.php: -------------------------------------------------------------------------------- 1 | "MyComponent", 13 | //name for folder 14 | "package_name" => "mycomponent", 15 | "package_version" => "0.1", 16 | "package_release" => "", 17 | "package_table_prefix" => "my_", 18 | "package_class_prefix" => "my", 19 | 20 | "regenerate_schema" => true, 21 | //switch to false if you don't need to rewrite your class-files 22 | "regenerate_classes" => true, 23 | //switch to false if you don't need to rewrite your map.inc-files 24 | "regenerate_maps" => true, 25 | 26 | "modx_root" => $modxRoot, 27 | "builder_root" => $builderRoot, 28 | "tools_root" => $builderRoot . "tools/", 29 | ); 30 | 31 | $builderComponentRoot = $buildConfig["builder_root"] . $buildConfig['package_name'] . '/'; 32 | 33 | $sources = array(); 34 | 35 | if (COMPONENT_BUILD) 36 | { 37 | $buildConfig = array_merge($buildConfig, array( 38 | "root" => $root, 39 | "build" => $builderComponentRoot . "build/", 40 | "resolvers" => $builderComponentRoot . "build/resolvers/", 41 | "data" => $builderComponentRoot . "build/data/", 42 | 43 | "source_core" => $modxRoot . "core/components/{$buildConfig['package_name']}/", 44 | "source_lexicon" => $modxRoot . "core/components/{$buildConfig['package_name']}/lexicon/", 45 | "source_assets" => $modxRoot . "assets/components/{$buildConfig['package_name']}/", 46 | "source_docs" => $modxRoot . "core/components/{$buildConfig['package_name']}/docs/", 47 | 48 | "package_dir" => $builderComponentRoot . "core/components/{$buildConfig['package_name']}", 49 | "model_dir" => $builderComponentRoot . "core/components/{$buildConfig['package_name']}/model", 50 | "class_dir" => $builderComponentRoot . "core/components/{$buildConfig['package_name']}/model/{$buildConfig['package_name']}", 51 | "schema_dir" => $builderComponentRoot . "core/components/{$buildConfig['package_name']}/model/schema", 52 | "mysql_class_dir" => $builderComponentRoot . "core/components/{$buildConfig['package_name']}/model/{$buildConfig['package_name']}/mysql", 53 | 54 | //It's a main file we edit 55 | "xml_schema_file" => $builderComponentRoot . "core/components/{$buildConfig['package_name']}/model/schema/{$buildConfig['package_name']}.mysql.schema.xml", 56 | 57 | //It's a new file generated automatically. We will transfer new code to file above 58 | "new_xml_schema_file" => $builderComponentRoot . "core/components/{$buildConfig['package_name']}/model/schema/{$buildConfig['package_name']}.mysql.schema.new.xml" 59 | )); 60 | } 61 | else 62 | { 63 | $buildConfig = array_merge($buildConfig, array( 64 | "root" => $root, 65 | "build" => $builderComponentRoot . "build/", 66 | "resolvers" => $builderComponentRoot . "build/resolvers/", 67 | "data" => $builderComponentRoot . "build/data/", 68 | 69 | "source_core" => $modxRoot . "core/components/{$buildConfig['package_name']}/", 70 | "source_lexicon" => $modxRoot . "core/components/{$buildConfig['package_name']}/lexicon/", 71 | "source_assets" => $modxRoot . "assets/components/{$buildConfig['package_name']}/", 72 | "source_docs" => $modxRoot . "core/components/{$buildConfig['package_name']}/docs/", 73 | 74 | "package_dir" => $root . "core/components/{$buildConfig['package_name']}", 75 | "model_dir" => $root . "core/components/{$buildConfig['package_name']}/model", 76 | "class_dir" => $root . "core/components/{$buildConfig['package_name']}/model/{$buildConfig['package_name']}", 77 | "schema_dir" => $root . "core/components/{$buildConfig['package_name']}/model/schema", 78 | "mysql_class_dir" => $root . "core/components/{$buildConfig['package_name']}/model/{$buildConfig['package_name']}/mysql", 79 | 80 | //It's a main file we edit 81 | "xml_schema_file" => $root . "core/components/{$buildConfig['package_name']}/model/schema/{$buildConfig['package_name']}.mysql.schema.xml", 82 | 83 | //It's a new file generated automatically. We will transfer new code to file above 84 | "new_xml_schema_file" => $root . "core/components/{$buildConfig['package_name']}/model/schema/{$buildConfig['package_name']}.mysql.schema.new.xml" 85 | )); 86 | } 87 | 88 | //Объявляем базовые константы 89 | define("MODX_CORE_PATH", $modxRoot . "core/"); 90 | define("MODX_BASE_PATH", $modxRoot); 91 | define('MODX_BASE_URL', '/'); 92 | 93 | unset($root,$modxRoot,$builderRoot,$builderComponentRoot); 94 | 95 | return $buildConfig; 96 | -------------------------------------------------------------------------------- /mycomponent/build/config/version.php: -------------------------------------------------------------------------------- 1 | 0, 5 | 'major' => 0, 6 | 'minor' => 0, 7 | ); 8 | -------------------------------------------------------------------------------- /mycomponent/build/data/README.md: -------------------------------------------------------------------------------- 1 | Данный каталог используется для хранения данных об объектах компонента в 2 | динамичном виде, то есть данные будут браться из базы данных проекта, на 3 | котором данный компонент уже развернут -------------------------------------------------------------------------------- /mycomponent/build/data/setup.options.php: -------------------------------------------------------------------------------- 1 | 'someValue', 12 | ); 13 | switch ($options[xPDOTransport::PACKAGE_ACTION]) { 14 | case xPDOTransport::ACTION_INSTALL: 15 | case xPDOTransport::ACTION_UPGRADE: 16 | /* 17 | $setting = $modx->getObject('modSystemSetting',array('key' => 'mypkg.someKey')); 18 | if ($setting != null) { $values['someKey'] = $setting->get('value'); } 19 | unset($setting); 20 | */ 21 | break; 22 | case xPDOTransport::ACTION_UNINSTALL: break; 23 | } 24 | 25 | $output = ''; 26 | /* 27 | $output = ' 28 | 29 | */ 30 | 31 | return $output; -------------------------------------------------------------------------------- /mycomponent/build/data/setup.requires.php: -------------------------------------------------------------------------------- 1 | '>=2.8.2' 6 | ); -------------------------------------------------------------------------------- /mycomponent/build/data/transport.chunks.php: -------------------------------------------------------------------------------- 1 | modx->getObject('modCategory',array( 12 | 'category' => $categoryName 13 | )); 14 | 15 | if(!$mainCategory) return $chunks; 16 | 17 | /** @var modChunk[] $realChunks */ 18 | $realChunks = $mainCategory->getMany('Chunks'); 19 | 20 | if(!$realChunks) return $chunks; 21 | 22 | foreach($realChunks as $realChunk){ 23 | /** @var modChunk $chunk */ 24 | $chunk = $this->modx->newObject('modChunk'); 25 | $chunkData = $realChunk->toArray(); 26 | $chunkData['id'] = 0; 27 | //TODO remove comment if you want to make your chunks static 28 | //$chunkData['static'] = 1; 29 | $chunk->fromArray($chunkData); 30 | $chunks[] = $chunk; 31 | } 32 | 33 | unset($realChunks,$chunkData); 34 | 35 | return $chunks; -------------------------------------------------------------------------------- /mycomponent/build/data/transport.events.php: -------------------------------------------------------------------------------- 1 | modx->getCollection('modEvent', array( 11 | 'groupname' => $categoryName 12 | )); 13 | 14 | if(!$realEvents) return $events; 15 | 16 | /** @var modEvent[] $realEvents */ 17 | foreach($realEvents as $realEvent){ 18 | /** @var modEvent $event */ 19 | $event = $this->modx->newObject('modEvent'); 20 | $eventData = $realEvent->toArray(); 21 | $event->fromArray($eventData,'',true); 22 | $events[] = $event; 23 | } 24 | 25 | unset($realEvents,$eventData); 26 | 27 | return $events; 28 | -------------------------------------------------------------------------------- /mycomponent/build/data/transport.menu.php: -------------------------------------------------------------------------------- 1 | modx->getCollection('modMenu',array( 11 | 'namespace' => $namespace 12 | )); 13 | 14 | if(!$realMenus) return $menus; 15 | 16 | /** @var modMenu[] $realMenus */ 17 | foreach($realMenus as $realMenu){ 18 | /** @var modMenu $menu */ 19 | $menu = $this->modx->newObject('modMenu'); 20 | $menuData = $realMenu->toArray(); 21 | $menu->fromArray($menuData,'',true); 22 | $menus[] = $menu; 23 | } 24 | 25 | unset($realMenus,$menuData); 26 | 27 | return $menus; -------------------------------------------------------------------------------- /mycomponent/build/data/transport.plugins.php: -------------------------------------------------------------------------------- 1 | modx->getObject('modCategory',array( 13 | 'category' => $categoryName 14 | )); 15 | 16 | if(!$mainCategory) return $plugins; 17 | 18 | /** @var modPlugin[] $realPlugins */ 19 | $realPlugins = $mainCategory->getMany('Plugins'); 20 | 21 | if(!$realPlugins) return $plugins; 22 | 23 | foreach($realPlugins as $realPlugin){ 24 | /** @var modPluginEvent[] $pluginEvents */ 25 | if($pluginEvents = $realPlugin->getMany('PluginEvents')){ 26 | foreach($pluginEvents as &$pluginEvent){ 27 | $pluginEvent->set('pluginid', 0); 28 | } 29 | } 30 | 31 | /** @var modPlugin $plugin */ 32 | $plugin = $this->modx->newObject('modPlugin'); 33 | $pluginData = $realPlugin->toArray(); 34 | $pluginData['id'] = 0; 35 | //TODO remove comment if you want make your plugin static 36 | //$pluginData['static'] = 1; 37 | $plugin->fromArray($pluginData); 38 | $plugin->addMany($pluginEvents); 39 | $plugins[] = $plugin; 40 | } 41 | 42 | unset($realPlugins,$pluginData); 43 | 44 | return $plugins; -------------------------------------------------------------------------------- /mycomponent/build/data/transport.settings.php: -------------------------------------------------------------------------------- 1 | modx->getCollection('modSystemSetting',array( 11 | 'namespace' => $namespace 12 | )); 13 | 14 | if(!$realSettings) return $settings; 15 | 16 | /** @var modSystemSetting[] $realSettings */ 17 | foreach($realSettings as $realSetting){ 18 | /** @var modSystemSetting $setting */ 19 | $setting = $this->modx->newObject('modSystemSetting'); 20 | $settingData = $realSetting->toArray(); 21 | $setting->fromArray($settingData,'',true); 22 | $settings[] = $setting; 23 | } 24 | 25 | unset($realSettings,$settingData); 26 | 27 | return $settings; -------------------------------------------------------------------------------- /mycomponent/build/data/transport.snippets.php: -------------------------------------------------------------------------------- 1 | modx->getObject('modCategory',array( 12 | 'category' => $categoryName 13 | )); 14 | 15 | if(!$mainCategory) return $snippets; 16 | 17 | /** @var modSnippet[] $realSnippets */ 18 | $realSnippets = $mainCategory->getMany('Snippets'); 19 | 20 | if(!$realSnippets) return $snippets; 21 | 22 | foreach($realSnippets as $realSnippet){ 23 | /** @var modSnippet $snippet */ 24 | $snippet = $this->modx->newObject('modSnippet'); 25 | $snippetData = $realSnippet->toArray(); 26 | $snippetData['id'] = 0; 27 | //TODO remove comment if you want to make your snippets static 28 | //$snippetData['static'] = 1; 29 | $snippet->fromArray($snippetData); 30 | $snippets[] = $snippet; 31 | } 32 | 33 | unset($realSnippets,$snippetData); 34 | 35 | return $snippets; -------------------------------------------------------------------------------- /mycomponent/build/data/transport.templates.php: -------------------------------------------------------------------------------- 1 | modx->getObject('modCategory',array( 12 | 'category' => $categoryName 13 | )); 14 | 15 | if(!$mainCategory) return $templates; 16 | 17 | /** @var modTemplate[] $realTemplates */ 18 | $realTemplates = $mainCategory->getMany('Templates'); 19 | if(!$realTemplates) return $templates; 20 | 21 | foreach($realTemplates as $realTemplate){ 22 | /** @var modTemplate $template */ 23 | $template = $this->modx->newObject('modTemplate'); 24 | $templateData = $realTemplate->toArray(); 25 | $templateData['id'] = 0; 26 | //TODO remove comment if you want to make templates static 27 | //$templateData['static'] = 1; 28 | $template->fromArray($templateData); 29 | $templates[] = $template; 30 | } 31 | 32 | unset($realTemplates,$templateData); 33 | 34 | return $templates; -------------------------------------------------------------------------------- /mycomponent/build/data/transport.tvs.php: -------------------------------------------------------------------------------- 1 | modx->getObject('modCategory',array( 12 | 'category' => $categoryName 13 | )); 14 | 15 | if(!$mainCategory) return $templateVars; 16 | 17 | /** @var modTemplateVar[] $realTemplateVars */ 18 | $realTemplateVars = $mainCategory->getMany('TemplateVars'); 19 | 20 | if(!$realTemplateVars) return $templateVars; 21 | 22 | foreach($realTemplateVars as $realTemplateVar){ 23 | /** @var modTemplateVarTemplate $templateVarTemplates */ 24 | $templateVarTemplates = $realTemplateVar->getMany('TemplateVarTemplates'); 25 | 26 | /** @var modTemplateVar $templateVar */ 27 | $templateVar = $this->modx->newObject('modTemplateVar'); 28 | $templateVarData = $realTemplateVar->toArray(); 29 | $templateVarData['id'] = 0; 30 | $templateVar->fromArray($templateVarData); 31 | $templateVar->addMany($templateVarTemplates); 32 | $templateVars[] = $templateVar; 33 | } 34 | 35 | unset($realTemplateVars,$templateVarData); 36 | 37 | return $templateVars; -------------------------------------------------------------------------------- /mycomponent/build/data_static/README.md: -------------------------------------------------------------------------------- 1 | Данный каталог используется для хранения данных об объектах компонента в 2 | статичном виде, то есть описанном в файлах -------------------------------------------------------------------------------- /mycomponent/build/data_static/setup.options.php: -------------------------------------------------------------------------------- 1 | 'someValue', 12 | ); 13 | switch ($options[xPDOTransport::PACKAGE_ACTION]) { 14 | case xPDOTransport::ACTION_INSTALL: 15 | case xPDOTransport::ACTION_UPGRADE: 16 | /* 17 | $setting = $modx->getObject('modSystemSetting',array('key' => 'mypkg.someKey')); 18 | if ($setting != null) { $values['someKey'] = $setting->get('value'); } 19 | unset($setting); 20 | */ 21 | break; 22 | case xPDOTransport::ACTION_UNINSTALL: break; 23 | } 24 | 25 | $output = ''; 26 | /* 27 | $output = ' 28 | 29 | */ 30 | 31 | return $output; -------------------------------------------------------------------------------- /mycomponent/build/data_static/transport.chunks.php: -------------------------------------------------------------------------------- 1 | modx->newObject('modChunk'); 8 | $chunk->fromArray(array( 9 | 'id' => 0, 10 | 'name' => 'exampleChunk', 11 | 'description' => 'exampleDescription', 12 | 'snippet' => 'exampleContent. You can get content from file by file_get_contents()', 13 | 'static' => 0, 14 | 'source' => 1, 15 | 'static_file' => "core/components/{$this->config['package_name']}/elements/chunks/exampleChunk.tpl", 16 | ), '', true, true); 17 | $chunks[] = $chunk; 18 | unset($chunk); 19 | return $chunks; -------------------------------------------------------------------------------- /mycomponent/build/data_static/transport.menu.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azernov/modxbuilder/130c83e1a630c0af6078b3cd2bcdc8ca5fcacd06/mycomponent/build/data_static/transport.menu.php -------------------------------------------------------------------------------- /mycomponent/build/data_static/transport.plugins.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azernov/modxbuilder/130c83e1a630c0af6078b3cd2bcdc8ca5fcacd06/mycomponent/build/data_static/transport.plugins.php -------------------------------------------------------------------------------- /mycomponent/build/data_static/transport.settings.php: -------------------------------------------------------------------------------- 1 | modx->newObject('modSystemSetting'); 8 | $setting->fromArray( 9 | array( 10 | 'key' => 'test_setting', 11 | 'namespace' => $this->config['package_name'], 12 | 'xtype' => 'textfield', 13 | 'value' => 'test_value', 14 | 'area' => 'mycmp_main', 15 | ),'',true,true); 16 | $settings[] = $setting; 17 | unset($setting); 18 | return $settings; -------------------------------------------------------------------------------- /mycomponent/build/data_static/transport.snippets.php: -------------------------------------------------------------------------------- 1 | modx->newObject('modSnippet'); 9 | 10 | $snippetContent = 'return date("Y-m-d");'; 11 | //Удаляем fromArray(array( 16 | 'id' => 0, 17 | 'name' => 'testSnippet', 18 | 'description' => 'test description', 19 | 'snippet' => $snippetContent, 20 | 'static' => 0, 21 | 'source' => 1, 22 | 'static_file' => "core/components/{$this->config['package_name']}/elements/snippets/testSnippet.php", 23 | ), '', true, true); 24 | 25 | $snippet->setProperties(array( 26 | array( 27 | 'name' => 'test_snippet_property1', 28 | 'desc' => 'mycmp_prop_test_snippet_property_1', 29 | 'lexicon' => 'mycmp:default', 30 | ) 31 | )); 32 | $snippets[] = $snippet; 33 | unset($snippet,$snippetContent); 34 | return $snippets; -------------------------------------------------------------------------------- /mycomponent/build/data_static/transport.templates.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azernov/modxbuilder/130c83e1a630c0af6078b3cd2bcdc8ca5fcacd06/mycomponent/build/data_static/transport.templates.php -------------------------------------------------------------------------------- /mycomponent/build/data_static/transport.tvs.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azernov/modxbuilder/130c83e1a630c0af6078b3cd2bcdc8ca5fcacd06/mycomponent/build/data_static/transport.tvs.php -------------------------------------------------------------------------------- /mycomponent/build/resolvers/resolver.tables.php: -------------------------------------------------------------------------------- 1 | xpdo) { 8 | $modx =& $object->xpdo; 9 | 10 | //TODO put your package name here 11 | $packageName = 'mycomponent'; 12 | $modelPath = $modx->getOption($packageName.'.core_path',null,$modx->getOption('core_path').'components/'.$packageName.'/').'model/'; 13 | $modx->addPackage($packageName,$modelPath); 14 | $manager = $modx->getManager(); 15 | 16 | switch ($options[xPDOTransport::PACKAGE_ACTION]) { 17 | case xPDOTransport::ACTION_INSTALL: 18 | //TODO add your database related custom objects 19 | //$manager->createObjectContainer('myObjectClass'); 20 | break; 21 | case xPDOTransport::ACTION_UPGRADE: 22 | break; 23 | case xPDOTransport::ACTION_UNINSTALL: 24 | //TODO add your database related custom objects 25 | //$manager->removeObjectContainer('myObjectClass'); 26 | break; 27 | } 28 | } 29 | return true; -------------------------------------------------------------------------------- /mycomponent/build/resolvers/resolvers.php: -------------------------------------------------------------------------------- 1 | [ 4 | [ 5 | 'source' => $this->config['source_core'], 6 | 'target' => "return MODX_CORE_PATH.'components/';", 7 | ], 8 | [ 9 | 'source' => $this->config['source_assets'], 10 | 'target' => "return MODX_ASSETS_PATH.'components/';", 11 | ] 12 | ], 13 | 'php' => [ 14 | [ 15 | 'source' => $this->config['resolvers'].'resolver.tables.php' 16 | ] 17 | ] 18 | ]; -------------------------------------------------------------------------------- /mycomponent/build/templates/changelog.item.txt: -------------------------------------------------------------------------------- 1 | [[+real_package_name]] [[+package_version]]-[[+package_release]] 2 | ========================================== 3 | [[+input]] 4 | 5 | -------------------------------------------------------------------------------- /mycomponent/build/templates/readme.txt: -------------------------------------------------------------------------------- 1 | -------------------- 2 | Extra: [[+real_package_name]] 3 | -------------------- 4 | Version: [[+package_version]]-[[+package_release]] 5 | Created: [[+package_created]] 6 | Updated: [[+package_updated]] 7 | Author: [[+package_author]] 8 | License: [[+package_license]] 9 | 10 | [[+package_description]] 11 | -------------------------------------------------------------------------------- /mycomponent/copy-from-www.php: -------------------------------------------------------------------------------- 1 | modx = &$modx; 24 | $version = include($config['build'].'config/version.php'); 25 | $this->config = array( 26 | //List default settings 27 | 'package_version' => implode('.', $version) 28 | ); 29 | $this->config = array_merge($this->config, $config); 30 | } 31 | 32 | protected function getManager() 33 | { 34 | $this->modx->getManager(); 35 | } 36 | 37 | protected function getGenerator() 38 | { 39 | if (!$this->generator) 40 | { 41 | //Подключаем наш класс генератора 42 | include_once $this->config['modx_root'] . 'core/xpdo/om/mysql/xpdogenerator.class.php'; 43 | include_once($this->config['tools_root'] . "xpdogenerator.class.php"); 44 | $manager = $this->modx->getManager(); 45 | $this->generator = new xPDOGenerator_my($manager); 46 | } 47 | return $this->generator; 48 | } 49 | 50 | /** 51 | * @param bool $restrict_prefix - If you specify a table prefix, you probably want this set to 'true'. E.g. if you 52 | * have custom tables alongside the modx_xxx tables, restricting the prefix ensures 53 | * that you only generate classes/maps for the tables identified by the $this->config['package_table_prefix']. 54 | * @param bool $verbose - if true, will print status info. 55 | * @param bool $debug - if true, will include verbose debugging info, including SQL errors. 56 | */ 57 | public function writeSchema($restrict_prefix = true, $verbose = true, $debug = true) 58 | { 59 | if (!defined('MODX_CORE_PATH')) 60 | { 61 | $this->modx->log(MODX_LOG_LEVEL_ERROR, 'Reverse Engineering Error! MODX_CORE_PATH not defined! Did you include the correct config file?'); 62 | exit; 63 | } 64 | 65 | // A few variables used to track execution times. 66 | $mtime = microtime(); 67 | $mtime = explode(' ', $mtime); 68 | $mtime = $mtime[1] + $mtime[0]; 69 | $tstart = $mtime; 70 | 71 | // Validations 72 | if (empty($this->config['package_name'])) 73 | { 74 | $this->modx->log(MODX_LOG_LEVEL_ERROR, "Reverse Engineering Error! The package_name cannot be empty! Please adjust the configuration and try again."); 75 | exit; 76 | } 77 | 78 | // Create directories if necessary 79 | $dirs = array($this->config["package_dir"], $this->config["schema_dir"], $this->config["mysql_class_dir"], $this->config["class_dir"]); 80 | 81 | foreach ($dirs as $d) 82 | { 83 | if (!file_exists($d)) 84 | { 85 | if (!mkdir($d, 0777, true)) 86 | { 87 | $this->modx->log(MODX_LOG_LEVEL_ERROR, sprintf('Reverse Engineering Error! Error creating %s. Create the directory (and its parents) and try again.', $d)); 88 | exit; 89 | } 90 | } 91 | if (!is_writable($d)) 92 | { 93 | $this->modx->log(MODX_LOG_LEVEL_ERROR, sprintf('Reverse Engineering Error! The %s directory is not writable by PHP. Adjust the permissions and try again.', $d)); 94 | exit; 95 | } 96 | } 97 | 98 | if ($verbose) 99 | { 100 | $this->modx->log(MODX_LOG_LEVEL_INFO, sprintf('Ok: The necessary directories exist and have the correct permissions inside of %s', $this->config["package_dir"])); 101 | } 102 | 103 | // Delete/regenerate map files? 104 | if (file_exists($this->config["new_xml_schema_file"]) && !$this->config['regenerate_schema'] && $verbose) 105 | { 106 | $this->modx->log(MODX_LOG_LEVEL_INFO, sprintf('Ok: Using existing XML schema file: %s', $this->config["new_xml_schema_file"])); 107 | } 108 | 109 | // Set the package name and root path of that package 110 | $this->modx->setPackage($this->config['package_name'], $this->config["model_dir"].'/'); 111 | $this->modx->setDebug($debug); 112 | 113 | //$generator = $manager->getGenerator(); // Станадртное получение mysql генератора 114 | $generator = $this->getGenerator(); 115 | $generator->setClassPrefix($this->config['package_class_prefix']); 116 | 117 | //Use this to create an XML schema from an existing database 118 | if ($this->config['regenerate_schema']) 119 | { 120 | if (!file_exists($this->config['new_xml_schema_file'])) 121 | { 122 | touch($this->config['new_xml_schema_file']); 123 | } 124 | $xml = $generator->writeSchema($this->config["new_xml_schema_file"], $this->config['package_name'], 'xPDOObject', '', $restrict_prefix, $this->config['package_table_prefix']); 125 | if ($verbose) 126 | { 127 | $this->modx->log(MODX_LOG_LEVEL_INFO, sprintf('Ok: XML schema file generated: %s', $this->config["new_xml_schema_file"])); 128 | } 129 | } 130 | 131 | $mtime = microtime(); 132 | $mtime = explode(" ", $mtime); 133 | $mtime = $mtime[1] + $mtime[0]; 134 | $tend = $mtime; 135 | $totalTime = ($tend - $tstart); 136 | $totalTime = sprintf("%2.4f s", $totalTime); 137 | 138 | if ($verbose) 139 | { 140 | $this->modx->log(MODX_LOG_LEVEL_INFO, "Finished! Execution time: {$totalTime}"); 141 | 142 | if ($this->config['regenerate_schema']) 143 | { 144 | $this->modx->log(MODX_LOG_LEVEL_INFO, "If you need to define aggregate/composite relationships in your XML schema file, be sure to regenerate your class files."); 145 | } 146 | } 147 | } 148 | 149 | public function parseSchema($verbose = true) 150 | { 151 | $this->modx->loadClass('transport.modPackageBuilder', '', false, true); 152 | $this->modx->setLogLevel(modX::LOG_LEVEL_INFO); 153 | $this->modx->setLogTarget(XPDO_CLI_MODE ? 'ECHO' : 'HTML'); 154 | 155 | if (!is_dir($this->config['model_dir'])) 156 | { 157 | $this->modx->log(modX::LOG_LEVEL_ERROR, 'Model directory not found!'); 158 | die(); 159 | } 160 | if (!file_exists($this->config['xml_schema_file'])) 161 | { 162 | $this->modx->log(modX::LOG_LEVEL_ERROR, 'Schema file not found!'); 163 | die(); 164 | } 165 | 166 | // Use this to generate classes from your schema 167 | if ($this->config['regenerate_classes']) 168 | { 169 | $this->modx->log(MODX_LOG_LEVEL_INFO, 'Attempting to remove/regenerate class files...'); 170 | modxBuilder::deleteClassFiles($this->config["class_dir"], $verbose); 171 | modxBuilder::deleteClassFiles($this->config["mysql_class_dir"], $verbose); 172 | } 173 | 174 | // Use this to generate maps from your schema 175 | if ($this->config['regenerate_maps']) 176 | { 177 | if ($verbose) 178 | { 179 | $this->modx->log(MODX_LOG_LEVEL_INFO, 'Attempting to remove/regenerate map files...'); 180 | } 181 | modxBuilder::deleteMapFiles($this->config["mysql_class_dir"], $verbose); 182 | } 183 | 184 | $this->getGenerator()->parseSchema($this->config["xml_schema_file"], $this->config["model_dir"] . "/"); 185 | 186 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Done!'); 187 | } 188 | 189 | public function getPackageBulder(){ 190 | if(!$this->builder){ 191 | //Подгружаем класс для сборки пакетов 192 | $this->modx->loadClass('transport.modPackageBuilder', '', false, true); 193 | $this->builder = new modPackageBuilder($this->modx); 194 | } 195 | return $this->builder; 196 | } 197 | 198 | /** 199 | * @param modCategory $category 200 | * @param array $snippets 201 | * @param array $attr 202 | * @param bool $updateObject 203 | * @return bool 204 | */ 205 | public function addSnippetsToCategory(&$category,$snippets,&$attr,$updateObject = true){ 206 | $attr[xPDOTransport::RELATED_OBJECT_ATTRIBUTES]['Snippets'] = array( 207 | xPDOTransport::PRESERVE_KEYS => true, 208 | xPDOTransport::UPDATE_OBJECT => $updateObject, 209 | xPDOTransport::UNIQUE_KEY => 'name', 210 | ); 211 | return $category->addMany($snippets); 212 | } 213 | 214 | /** 215 | * @param modCategory $category 216 | * @param array modChunk[] $chunks 217 | * @param array $attr 218 | * @param bool $updateObject 219 | * @return bool 220 | */ 221 | public function addChunksToCategory(&$category,$chunks,&$attr,$updateObject = true){ 222 | $attr[xPDOTransport::RELATED_OBJECT_ATTRIBUTES]['Chunks'] = array( 223 | xPDOTransport::PRESERVE_KEYS => true, 224 | xPDOTransport::UPDATE_OBJECT => $updateObject, 225 | xPDOTransport::UNIQUE_KEY => 'name', 226 | ); 227 | return $category->addMany($chunks); 228 | } 229 | 230 | /** 231 | * @param modCategory $category 232 | * @param array modTemplate[] $templates 233 | * @param array $attr 234 | * @param bool $updateObject 235 | * @return bool 236 | */ 237 | public function addTemplatesToCategory(&$category,$templates,&$attr,$updateObject = true){ 238 | $attr[xPDOTransport::RELATED_OBJECT_ATTRIBUTES]['Templates'] = array( 239 | xPDOTransport::PRESERVE_KEYS => true, 240 | xPDOTransport::UPDATE_OBJECT => $updateObject, 241 | xPDOTransport::UNIQUE_KEY => 'templatename', 242 | ); 243 | return $category->addMany($templates); 244 | } 245 | 246 | /** 247 | * @param modCategory $category 248 | * @param array modPlugin[] $templates 249 | * @param array $attr 250 | * @param bool $updateObject 251 | * @return bool 252 | */ 253 | public function addPluginsToCategory(&$category, $plugins, &$attr, $updateObject = true){ 254 | $attr[xPDOTransport::RELATED_OBJECT_ATTRIBUTES]['Plugins'] = array( 255 | xPDOTransport::PRESERVE_KEYS => true, 256 | xPDOTransport::UPDATE_OBJECT => $updateObject, 257 | xPDOTransport::UNIQUE_KEY => 'name', 258 | xPDOTransport::RELATED_OBJECTS => true, 259 | xPDOTransport::RELATED_OBJECT_ATTRIBUTES => array( 260 | 'PluginEvents' => array( 261 | xPDOTransport::PRESERVE_KEYS => true, 262 | xPDOTransport::UPDATE_OBJECT => $updateObject, 263 | xPDOTransport::UNIQUE_KEY => ['pluginid', 'event'], 264 | ) 265 | ) 266 | ); 267 | return $category->addMany($plugins); 268 | } 269 | 270 | /** 271 | * @param modCategory $category 272 | * @param array modTemplateVar[] $tvs 273 | * @param array $attr 274 | * @param bool $updateObject 275 | * @return bool 276 | */ 277 | public function addTVsToCategory(&$category, $tvs, &$attr, $updateObject = true){ 278 | $attr[xPDOTransport::RELATED_OBJECT_ATTRIBUTES]['TemplateVars'] = array( 279 | xPDOTransport::PRESERVE_KEYS => true, 280 | xPDOTransport::UPDATE_OBJECT => $updateObject, 281 | xPDOTransport::UNIQUE_KEY => 'name', 282 | xPDOTransport::RELATED_OBJECTS => true, 283 | xPDOTransport::RELATED_OBJECT_ATTRIBUTES => array( 284 | 'TemplateVarTemplates' => array( 285 | xPDOTransport::PRESERVE_KEYS => true, 286 | xPDOTransport::UPDATE_OBJECT => $updateObject, 287 | xPDOTransport::UNIQUE_KEY => ['tmplvarid','templateid'], 288 | ) 289 | ) 290 | ); 291 | return $category->addMany($tvs); 292 | } 293 | 294 | /** 295 | * @param modTransportVehicle $vehicle 296 | * @param array $resolvers 297 | * @return int 298 | */ 299 | public function addResolvers(&$vehicle,$resolvers){ 300 | $cnt = 0; 301 | foreach($resolvers as $type => $resolver){ 302 | if(isset($resolver['source'])) 303 | { 304 | $cnt++; 305 | $vehicle->resolve($type,$resolver); 306 | } 307 | else{ 308 | foreach($resolver as $resolverItem){ 309 | $cnt++; 310 | $vehicle->resolve($type,$resolverItem); 311 | } 312 | } 313 | } 314 | return $cnt; 315 | } 316 | 317 | /** 318 | * @param modSystemSetting[] $settings 319 | * @param array $attr 320 | * @param bool $updateObject 321 | * @return bool 322 | */ 323 | public function addSystemSettings($settings,$attr = array(), $updateObject = false){ 324 | $noError = true; 325 | 326 | $sysSettingsAttr = array_merge(array( 327 | xPDOTransport::UNIQUE_KEY => 'key', 328 | xPDOTransport::PRESERVE_KEYS => true, 329 | xPDOTransport::UPDATE_OBJECT => $updateObject, 330 | ),$attr); 331 | foreach ($settings as $setting) { 332 | $vehicle = $this->builder->createVehicle($setting,$sysSettingsAttr); 333 | $noError = $noError && $this->builder->putVehicle($vehicle); 334 | } 335 | return $noError; 336 | } 337 | 338 | /** 339 | * @param modEvent[] $events 340 | * @param array $attr 341 | * @param bool $updateObject 342 | * @return bool 343 | */ 344 | public function addSystemEvents($events, $attr = array(), $updateObject = false){ 345 | $noError = true; 346 | 347 | $sysSettingsAttr = array_merge(array( 348 | xPDOTransport::UNIQUE_KEY => 'name', 349 | xPDOTransport::PRESERVE_KEYS => true, 350 | xPDOTransport::UPDATE_OBJECT => $updateObject, 351 | ),$attr); 352 | foreach ($events as $event) { 353 | $vehicle = $this->builder->createVehicle($event,$sysSettingsAttr); 354 | $noError = $noError && $this->builder->putVehicle($vehicle); 355 | } 356 | return $noError; 357 | } 358 | 359 | /** 360 | * @param modMenu[] $menus 361 | * @param array $attr 362 | * @param bool $updateObject 363 | * @return bool 364 | */ 365 | public function addMenus($menus,$attr = array(), $updateObject = true){ 366 | $noError = true; 367 | 368 | $menuSettingsArray = array_merge(array( 369 | xPDOTransport::UNIQUE_KEY => 'text', 370 | xPDOTransport::PRESERVE_KEYS => true, 371 | xPDOTransport::UPDATE_OBJECT => $updateObject, 372 | ),$attr); 373 | foreach ($menus as $menu) { 374 | $vehicle = $this->builder->createVehicle($menu,$menuSettingsArray); 375 | $noError = $noError && $this->builder->putVehicle($vehicle); 376 | } 377 | return $noError; 378 | } 379 | 380 | public function addPackageAttributes(){ 381 | $attrs = array(); 382 | if(file_exists($this->config['source_docs'] . 'changelog.txt')){ 383 | $attrs['changelog'] = file_get_contents($this->config['source_docs'] . 'changelog.txt'); 384 | } 385 | if(file_exists($this->config['source_docs'] . 'license.txt')){ 386 | $attrs['license'] = file_get_contents($this->config['source_docs'] . 'license.txt'); 387 | } 388 | if(file_exists($this->config['source_docs'] . 'readme.txt')){ 389 | $attrs['readme'] = file_get_contents($this->config['source_docs'] . 'readme.txt'); 390 | } 391 | if(file_exists($this->config['data'] . 'setup.options.php')){ 392 | $attrs['setup-options'] = array('source' => $this->config['data'] . 'setup.options.php'); 393 | } 394 | if(file_exists($this->config['data'] . 'setup.requires.php')){ 395 | $requires = include $this->config['data'] . 'setup.requires.php'; 396 | if(!empty($requires)){ 397 | $attrs['requires'] = $requires; 398 | } 399 | } 400 | $this->builder->setPackageAttributes($attrs); 401 | } 402 | 403 | public function buildComponent() 404 | { 405 | $this->updateVersionAndDocs(); 406 | 407 | $this->getPackageBulder(); 408 | $this->builder->createPackage(str_replace(' ','',$this->config['real_package_name']), $this->config['package_version'], $this->config['package_release']); 409 | $namespace = $this->config['package_name']; 410 | $this->modx->log(xPDO::LOG_LEVEL_INFO,'Registering new namespace: '.$namespace); 411 | $this->builder->registerNamespace($this->config['package_name'], false, true, "{core_path}components/{$this->config['package_name']}/"); 412 | 413 | // Create new category for chunks and snippets 414 | $categoryName = $this->config['real_package_name']; 415 | $this->modx->log(xPDO::LOG_LEVEL_INFO,'Creating new category: '.$categoryName); 416 | 417 | /** @var modCategory $category */ 418 | $category= $this->modx->newObject('modCategory'); 419 | $category->set('category',$this->config['real_package_name']); 420 | 421 | // Define attributes for category transport 422 | $categoryAttr = array( 423 | xPDOTransport::UNIQUE_KEY => 'category', 424 | xPDOTransport::PRESERVE_KEYS => false, 425 | xPDOTransport::UPDATE_OBJECT => true, 426 | xPDOTransport::RELATED_OBJECTS => true, 427 | ); 428 | 429 | //Define snippets 430 | /** @var modSnippet $snippets */ 431 | $snippets = include $this->config['data'] . 'transport.snippets.php'; 432 | if (!is_array($snippets)){ 433 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Are snippets empty? Skip them'); 434 | } 435 | elseif($this->addSnippetsToCategory($category,$snippets,$categoryAttr)){ 436 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Added snippets: ' . count($snippets) . '.'); 437 | } 438 | 439 | 440 | //Define chunks 441 | /** @var modChunk[] $chunks */ 442 | $chunks = include $this->config['data'] . 'transport.chunks.php'; 443 | if (!is_array($chunks)){ 444 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Are chunks empty? Skip them'); 445 | } 446 | elseif($this->addChunksToCategory($category,$chunks,$categoryAttr)){ 447 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Added chunks: ' . count($chunks) . '.'); 448 | } 449 | 450 | //Define templates 451 | /** @var modTemplate[] $templates */ 452 | $templates = include $this->config['data'] . 'transport.templates.php'; 453 | if (!is_array($templates)){ 454 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Are templates empty? Skip them'); 455 | } 456 | elseif($this->addTemplatesToCategory($category,$templates,$categoryAttr)){ 457 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Added templates: ' . count($templates) . '.'); 458 | } 459 | 460 | //Define tvs 461 | /** @var modTemplateVar[] $tvs */ 462 | $tvs = include $this->config['data'] . 'transport.tvs.php'; 463 | if (!is_array($tvs)) 464 | { 465 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Are template variables empty? Skip them'); 466 | } 467 | elseif ($this->addTVsToCategory($category, $tvs, $categoryAttr, true)) 468 | { 469 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Added template variables: ' . count($tvs) . '.'); 470 | } 471 | 472 | /*if (isset($templates) && is_array($templates)) 473 | { 474 | foreach ($templates as $template) 475 | { 476 | //TODO add tvs to templates 477 | } 478 | }*/ 479 | 480 | //Define plugins 481 | $plugins = include $this->config['data'] . 'transport.plugins.php'; 482 | if (!is_array($plugins)){ 483 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Are plugins empty? Skip them'); 484 | } 485 | elseif($this->addPluginsToCategory($category,$plugins,$categoryAttr)){ 486 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Added plugins: ' . count($plugins) . '.'); 487 | } 488 | 489 | $vehicle = $this->builder->createVehicle($category,$categoryAttr); 490 | $this->builder->putVehicle($vehicle); 491 | 492 | //Define file resolvers 493 | $resolvers = include $this->config['resolvers'] . 'resolvers.php'; 494 | if(!is_array($resolvers)){ 495 | $this->modx->log(modX::LOG_LEVEL_INFO,'Are file resolvers empty? Skip them'); 496 | } 497 | else{ 498 | $count = $this->addResolvers($vehicle,$resolvers); 499 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Added resolvers: ' . $count . '.'); 500 | } 501 | 502 | $this->builder->putVehicle($vehicle); 503 | 504 | //Define system settings 505 | $settings = include $this->config['data'].'transport.settings.php'; 506 | if (!is_array($settings)) { 507 | $this->modx->log(modX::LOG_LEVEL_ERROR,'Are system settings empty? Skip them'); 508 | } else { 509 | $this->addSystemSettings($settings); 510 | $this->modx->log(modX::LOG_LEVEL_INFO,'Added system settings: '.count($settings).'.'); 511 | } 512 | 513 | //Define system events 514 | $events = include $this->config['data'].'transport.events.php'; 515 | if (!is_array($events)) { 516 | $this->modx->log(modX::LOG_LEVEL_ERROR,'Are events empty? Skip them'); 517 | } else { 518 | $this->addSystemEvents($events); 519 | $this->modx->log(modX::LOG_LEVEL_INFO,'Added system events: '.count($events).'.'); 520 | } 521 | 522 | //Define menu items 523 | $menus = include $this->config['data'].'transport.menu.php'; 524 | if (!is_array($menus)) { 525 | $this->modx->log(modX::LOG_LEVEL_ERROR,'Are menu items empty? Skip them'); 526 | } else { 527 | $this->addMenus($menus); 528 | $this->modx->log(modX::LOG_LEVEL_INFO,'Added menu items: '.count($menus).'.'); 529 | } 530 | 531 | $this->addPackageAttributes(); 532 | $this->modx->log(modX::LOG_LEVEL_INFO,'Added package attributes!'); 533 | 534 | //Start packing into zip 535 | $this->modx->log(modX::LOG_LEVEL_INFO,'Start packing into zip'); 536 | 537 | if($this->builder->pack()){ 538 | $this->modx->log(modX::LOG_LEVEL_INFO,'Packet was successfully packed!'); 539 | } 540 | else{ 541 | $this->modx->log(modX::LOG_LEVEL_ERROR,'Something went wrong... Error while packing into zip'); 542 | } 543 | } 544 | 545 | public function updateVersionAndDocs(){ 546 | //Апдейтим описание версии и changelog 547 | $versionFilePath = $this->config['build'].'config/version.php'; 548 | $version = include($versionFilePath); 549 | $version['minor']++; 550 | file_put_contents($versionFilePath, "config['package_version'] = implode('.', $version); 553 | 554 | $readmeTemplate = file_get_contents($this->config['build'].'templates/readme.txt'); 555 | $changeLogItemTemplate = file_get_contents($this->config['build'].'templates/changelog.item.txt'); 556 | 557 | $readmeTemplate = $this->parseString($readmeTemplate, $this->config); 558 | 559 | $prompt = "What's new?:"; 560 | $emptyLinesCount = 0; 561 | $inputContent = ''; 562 | while($emptyLinesCount < 1){ 563 | $line = readline($prompt); 564 | if(!empty($line)){ 565 | $inputContent .= $line."\n"; 566 | } 567 | else{ 568 | $emptyLinesCount++; 569 | } 570 | } 571 | 572 | $changeLogItemTemplate = $this->parseString($changeLogItemTemplate, array_merge($this->config, [ 573 | 'input' => $inputContent 574 | ])); 575 | 576 | file_put_contents($this->config['source_docs'].'readme.txt', $readmeTemplate); 577 | $this->filePrependContent($this->config['source_docs'].'changelog.txt', $changeLogItemTemplate); 578 | } 579 | 580 | protected function filePrependContent($filePath, $content){ 581 | //if(!file_exists($filePath)) 582 | $handle = fopen($filePath, "r+"); 583 | $len = strlen($content); 584 | $finalLen = filesize($filePath) + $len; 585 | $cacheOld = fread($handle, $len); 586 | rewind($handle); 587 | $i = 1; 588 | while (ftell($handle) < $finalLen) { 589 | fwrite($handle, $content); 590 | $content = $cacheOld; 591 | $cacheOld = fread($handle, $len); 592 | fseek($handle, $i * $len); 593 | $i++; 594 | } 595 | fclose($handle); 596 | } 597 | 598 | protected function makePlaceholders( 599 | array $array = array(), 600 | $plPrefix = '', 601 | $prefix = '[[+', 602 | $suffix = ']]', 603 | $uncacheable = true 604 | ) 605 | { 606 | $result = ['pl' => [], 'vl' => []]; 607 | 608 | $uncached_prefix = str_replace('[[', '[[!', $prefix); 609 | foreach ($array as $k => $v) { 610 | if (is_array($v)) { 611 | $result = array_merge_recursive($result, 612 | $this->makePlaceholders($v, $plPrefix . $k . '.', $prefix, $suffix, $uncacheable)); 613 | } else { 614 | $pl = $plPrefix . $k; 615 | $result['pl'][$pl] = $prefix . $pl . $suffix; 616 | $result['vl'][$pl] = $v; 617 | if ($uncacheable) { 618 | $result['pl']['!' . $pl] = $uncached_prefix . $pl . $suffix; 619 | $result['vl']['!' . $pl] = $v; 620 | } 621 | } 622 | } 623 | 624 | return $result; 625 | } 626 | 627 | protected function parseString($string, $settings){ 628 | $pl1 = $this->makePlaceholders($settings, '', '{', '}', false); 629 | $pl2 = $this->makePlaceholders($settings, '', '[[+', ']]', false); 630 | 631 | return str_replace($pl1['pl'], $pl1['vl'], 632 | str_replace($pl2['pl'], $pl2['vl'], $string) 633 | ); 634 | } 635 | 636 | /** 637 | * @param string $dir - a directory containing class files you wish to delete. 638 | * @param bool $verbose 639 | */ 640 | public function deleteClassFiles($dir, $verbose = false) 641 | { 642 | $all_files = scandir($dir); 643 | foreach ($all_files as $f) 644 | { 645 | if (preg_match('#\.class\.php$#i', $f)) 646 | { 647 | if (unlink("$dir/$f")) 648 | { 649 | if ($verbose) 650 | { 651 | $this->modx->log(MODX_LOG_LEVEL_INFO, sprintf('Deleted file: %s/%s', $dir, $f)); 652 | } 653 | } 654 | else 655 | { 656 | $this->modx->log(MODX_LOG_LEVEL_ERROR, sprintf('Failed to delete file: %s/%s', $dir, $f)); 657 | } 658 | } 659 | } 660 | } 661 | 662 | /** 663 | * @param string $dir - a directory containing map files you wish to delete. 664 | * @param bool $verbose 665 | */ 666 | public function deleteMapFiles($dir, $verbose = false) 667 | { 668 | $all_files = scandir($dir); 669 | foreach ($all_files as $f) 670 | { 671 | if (preg_match('#\.map\.inc\.php$#i', $f)) 672 | { 673 | if (unlink("$dir/$f")) 674 | { 675 | if ($verbose) 676 | { 677 | $this->modx->log(MODX_LOG_LEVEL_INFO, sprintf('Deleted file: %s/%s', $dir, $f)); 678 | } 679 | } 680 | else 681 | { 682 | $this->modx->log(MODX_LOG_LEVEL_ERROR, sprintf('Failed to delete file: %s/%s', $dir, $f)); 683 | } 684 | } 685 | } 686 | } 687 | } 688 | -------------------------------------------------------------------------------- /tools/xpdogenerator.class.php: -------------------------------------------------------------------------------- 1 | classPrefix = $string; 23 | } 24 | 25 | /** 26 | * Gets a class name from a table name by splitting the string by _ and 27 | * capitalizing each token. 28 | * 29 | * Переопределяет родительскую функцию и добавляет префикс к названию класса 30 | * 31 | * @access public 32 | * @param string $string The table name to format. 33 | * @return string The formatted string. 34 | */ 35 | public function getClassName($string) { 36 | if (is_string($string) && $strArray= explode('_', $string)) { 37 | $return= ''; 38 | while (list($k, $v)= each($strArray)) { 39 | //Если мы обрезаем табличный префикс, то первый кусочек пропускаем 40 | if($this->restrictPrefix && $k==0) continue; 41 | $return.= strtoupper(substr($v, 0, 1)) . substr($v, 1) . ''; 42 | } 43 | $string= $return; 44 | } 45 | return $this->classPrefix.trim($string); 46 | } 47 | 48 | /** 49 | * Write an xPDO XML Schema from your database. 50 | * Переписан механизм формирования имени класса. 51 | * 52 | * @param string $schemaFile The name (including path) of the schemaFile you 53 | * want to write. 54 | * @param string $package Name of the package to generate the classes in. 55 | * @param string $baseClass The class which all classes in the package will 56 | * extend; by default this is set to {@link xPDOObject} and any 57 | * auto_increment fields with the column name 'id' will extend {@link 58 | * xPDOSimpleObject} automatically. 59 | * @param string $tablePrefix The table prefix for the current connection, 60 | * which will be removed from all of the generated class and table names. 61 | * Specify a prefix when creating a new {@link xPDO} instance to recreate 62 | * the tables with the same prefix, but still use the generic class names. 63 | * @param boolean $restrictPrefix Only reverse-engineer tables that have the 64 | * specified tablePrefix; if tablePrefix is empty, this is ignored. 65 | * @return boolean True on success, false on failure. 66 | */ 67 | public function writeSchema($schemaFile, $package= '', $baseClass= '', $tablePrefix= '', $restrictPrefix= false, $tablePrefixForSchema = '') { 68 | if (empty ($package)) 69 | $package= $this->manager->xpdo->package; 70 | if (empty ($baseClass)) 71 | $baseClass= 'xPDOObject'; 72 | if (empty ($tablePrefix) || $tablePrefix == '') 73 | $tablePrefix= $this->manager->xpdo->config[xPDO::OPT_TABLE_PREFIX]; 74 | $this->tablePrefix = $tablePrefixForSchema; 75 | $this->restrictPrefix = $restrictPrefix; 76 | $tablePrefix .= $tablePrefixForSchema; 77 | $schemaVersion = xPDO::SCHEMA_VERSION; 78 | $xmlContent = array(); 79 | $xmlContent[] = ""; 80 | $xmlContent[] = ""; 81 | //read list of tables 82 | $dbname= $this->manager->xpdo->escape($this->manager->xpdo->config['dbname']); 83 | $tablePrefixEscaped = str_replace('_','\_', $tablePrefix); 84 | $tableLike= ($tablePrefix && $restrictPrefix) ? " LIKE '{$tablePrefixEscaped}%'" : ''; 85 | $tablesStmt= $this->manager->xpdo->prepare("SHOW TABLES FROM {$dbname}{$tableLike}"); 86 | $tstart = microtime(true); 87 | $tablesStmt->execute(); 88 | $this->manager->xpdo->queryTime += microtime(true) - $tstart; 89 | $this->manager->xpdo->executedQueries++; 90 | $tables= $tablesStmt->fetchAll(PDO::FETCH_NUM); 91 | if ($this->manager->xpdo->getDebug() === true) $this->manager->xpdo->log(xPDO::LOG_LEVEL_DEBUG, print_r($tables, true)); 92 | foreach ($tables as $table) { 93 | $xmlObject= array(); 94 | $xmlFields= array(); 95 | $xmlIndices= array(); 96 | $tmpPrefix = str_replace($tablePrefixForSchema,'',$tablePrefix); 97 | if (!$tableName= $this->getTableName($table[0], $tmpPrefix, false)) { 98 | continue; 99 | } 100 | 101 | //Здесь мы немного заменим принцип формирования имени класса 102 | 103 | //Сначала получим то, что лежит в комментариях к таблице 104 | $infoStmt = $this->manager->xpdo->query("SHOW TABLE STATUS FROM {$dbname} WHERE Name = '" . $table[0] . "'"); 105 | $comment = ''; 106 | if($infoStmt) 107 | { 108 | $info = $infoStmt->fetchAll(PDO::FETCH_ASSOC); 109 | if(!empty($info)) 110 | { 111 | $comment = $info[0]['Comment']; 112 | } 113 | } 114 | 115 | //Если названия класса нет в комментариях к таблице, то формируем его по общему прицнипу 116 | $class = trim($comment) == '' ? $this->getClassName($tableName) : $comment; 117 | 118 | $extends= $baseClass; 119 | $fieldsStmt= $this->manager->xpdo->query('SHOW COLUMNS FROM ' . $this->manager->xpdo->escape($table[0])); 120 | if ($fieldsStmt) { 121 | $fields= $fieldsStmt->fetchAll(PDO::FETCH_ASSOC); 122 | if ($this->manager->xpdo->getDebug() === true) $this->manager->xpdo->log(xPDO::LOG_LEVEL_DEBUG, print_r($fields, true)); 123 | if (!empty($fields)) { 124 | foreach ($fields as $field) { 125 | $Field= ''; 126 | $Type= ''; 127 | $Null= ''; 128 | $Key= ''; 129 | $Default= ''; 130 | $Extra= ''; 131 | extract($field, EXTR_OVERWRITE); 132 | $Type= xPDO :: escSplit(' ', $Type, "'", 2); 133 | $precisionPos= strpos($Type[0], '('); 134 | $dbType= $precisionPos? substr($Type[0], 0, $precisionPos): $Type[0]; 135 | $dbType= strtolower($dbType); 136 | $Precision= $precisionPos? substr($Type[0], $precisionPos + 1, strrpos($Type[0], ')') - ($precisionPos + 1)): ''; 137 | if (!empty ($Precision)) { 138 | $Precision= ' precision="' . trim($Precision) . '"'; 139 | } 140 | $attributes= ''; 141 | if (isset ($Type[1]) && !empty ($Type[1])) { 142 | $attributes= ' attributes="' . trim($Type[1]) . '"'; 143 | } 144 | $PhpType= $this->manager->xpdo->driver->getPhpType($dbType); 145 | $Null= ' null="' . (($Null === 'NO') ? 'false' : 'true') . '"'; 146 | $Key= $this->getIndex($Key); 147 | $Default= $this->getDefault($Default); 148 | if (!empty ($Extra)) { 149 | if ($Extra === 'auto_increment') { 150 | if ($baseClass === 'xPDOObject' && $Field === 'id') { 151 | $extends= 'xPDOSimpleObject'; 152 | continue; 153 | } else { 154 | $Extra= ' generated="native"'; 155 | } 156 | } else { 157 | $Extra= ' extra="' . strtolower($Extra) . '"'; 158 | } 159 | $Extra= ' ' . $Extra; 160 | } 161 | $xmlFields[] = "\t\t"; 162 | } 163 | } else { 164 | $this->manager->xpdo->log(xPDO::LOG_LEVEL_ERROR, 'No columns were found in table ' . $table[0]); 165 | } 166 | } else { 167 | $this->manager->xpdo->log(xPDO::LOG_LEVEL_ERROR, 'Error retrieving columns for table ' . $table[0]); 168 | } 169 | $whereClause= ($extends === 'xPDOSimpleObject' ? " WHERE `Key_name` != 'PRIMARY'" : ''); 170 | $indexesStmt= $this->manager->xpdo->query('SHOW INDEXES FROM ' . $this->manager->xpdo->escape($table[0]) . $whereClause); 171 | if ($indexesStmt) { 172 | $indexes= $indexesStmt->fetchAll(PDO::FETCH_ASSOC); 173 | if ($this->manager->xpdo->getDebug() === true) $this->manager->xpdo->log(xPDO::LOG_LEVEL_DEBUG, "Indices for table {$table[0]}: " . print_r($indexes, true)); 174 | if (!empty($indexes)) { 175 | $indices = array(); 176 | foreach ($indexes as $index) { 177 | if (!array_key_exists($index['Key_name'], $indices)) $indices[$index['Key_name']] = array(); 178 | $indices[$index['Key_name']][$index['Seq_in_index']] = $index; 179 | } 180 | foreach ($indices as $index) { 181 | $xmlIndexCols = array(); 182 | if ($this->manager->xpdo->getDebug() === true) $this->manager->xpdo->log(xPDO::LOG_LEVEL_DEBUG, "Details of index: " . print_r($index, true)); 183 | foreach ($index as $columnSeq => $column) { 184 | if ($columnSeq == 1) { 185 | $keyName = $column['Key_name']; 186 | $primary = $keyName == 'PRIMARY' ? 'true' : 'false'; 187 | $unique = empty($column['Non_unique']) ? 'true' : 'false'; 188 | $packed = empty($column['Packed']) ? 'false' : 'true'; 189 | $type = $column['Index_type']; 190 | } 191 | $null = $column['Null'] == 'YES' ? 'true' : 'false'; 192 | $xmlIndexCols[]= "\t\t\t"; 193 | } 194 | $xmlIndices[]= "\t\t"; 195 | $xmlIndices[]= implode("\n", $xmlIndexCols); 196 | $xmlIndices[]= "\t\t"; 197 | } 198 | } else { 199 | $this->manager->xpdo->log(xPDO::LOG_LEVEL_WARN, 'No indexes were found in table ' . $table[0]); 200 | } 201 | } else { 202 | $this->manager->xpdo->log(xPDO::LOG_LEVEL_ERROR, 'Error getting indexes for table ' . $table[0]); 203 | } 204 | $xmlObject[] = "\t"; 205 | $xmlObject[] = implode("\n", $xmlFields); 206 | if (!empty($xmlIndices)) { 207 | $xmlObject[] = ''; 208 | $xmlObject[] = implode("\n", $xmlIndices); 209 | } 210 | $xmlObject[] = "\t"; 211 | $xmlContent[] = implode("\n", $xmlObject); 212 | } 213 | $xmlContent[] = ""; 214 | if ($this->manager->xpdo->getDebug() === true) { 215 | $this->manager->xpdo->log(xPDO::LOG_LEVEL_DEBUG, implode("\n", $xmlContent)); 216 | } 217 | $file= fopen($schemaFile, 'wb'); 218 | $written= fwrite($file, implode("\n", $xmlContent)); 219 | fclose($file); 220 | return true; 221 | } 222 | 223 | /** 224 | * Parses an XPDO XML schema and generates classes and map files from it. 225 | * 226 | * Requires SimpleXML for parsing an XML schema. 227 | * 228 | * @param string $schemaFile The name of the XML file representing the 229 | * schema. 230 | * @param string $outputDir The directory in which to generate the class and 231 | * map files into. 232 | * @param boolean $compile Create compiled copies of the classes and maps from the schema. 233 | * @return boolean True on success, false on failure. 234 | */ 235 | public function parseSchema($schemaFile, $outputDir= '', $compile= false) { 236 | $this->schemaFile= $schemaFile; 237 | $this->classTemplate= $this->getClassTemplate(); 238 | if (!is_file($schemaFile)) { 239 | $this->manager->xpdo->log(xPDO::LOG_LEVEL_ERROR, "Could not find specified XML schema file {$schemaFile}"); 240 | return false; 241 | } 242 | 243 | $this->schema = new SimpleXMLElement($schemaFile, 0, true); 244 | if (isset($this->schema)) { 245 | foreach ($this->schema->attributes() as $attributeKey => $attribute) { 246 | /** @var SimpleXMLElement $attribute */ 247 | $this->model[$attributeKey] = (string) $attribute; 248 | } 249 | if (isset($this->schema->object)) { 250 | foreach ($this->schema->object as $object) { 251 | /** @var SimpleXMLElement $object */ 252 | $class = (string) $object['class']; 253 | $extends = isset($object['extends']) ? (string) $object['extends'] : $this->model['baseClass']; 254 | $this->classes[$class] = array('extends' => $extends); 255 | $this->map[$class] = array( 256 | 'package' => $this->model['package'], 257 | 'version' => $this->model['version'] 258 | ); 259 | foreach ($object->attributes() as $objAttrKey => $objAttr) { 260 | if ($objAttrKey == 'class') continue; 261 | $this->map[$class][$objAttrKey]= (string) $objAttr; 262 | } 263 | 264 | $engine = (string) $object['engine']; 265 | if (!empty($engine)) { 266 | $this->map[$class]['tableMeta'] = array('engine' => $engine); 267 | } 268 | 269 | $this->map[$class]['fields']= array(); 270 | $this->map[$class]['fieldMeta']= array(); 271 | if (isset($object->field)) { 272 | foreach ($object->field as $field) { 273 | $key = (string) $field['key']; 274 | $dbtype = (string) $field['dbtype']; 275 | $defaultType = $this->manager->xpdo->driver->getPhpType($dbtype); 276 | $this->map[$class]['fields'][$key]= null; 277 | $this->map[$class]['fieldMeta'][$key]= array(); 278 | foreach ($field->attributes() as $fldAttrKey => $fldAttr) { 279 | $fldAttrValue = (string) $fldAttr; 280 | switch ($fldAttrKey) { 281 | case 'key': 282 | continue 2; 283 | case 'default': 284 | if ($fldAttrValue === 'NULL') { 285 | $fldAttrValue = null; 286 | } 287 | switch ($defaultType) { 288 | case 'integer': 289 | case 'boolean': 290 | case 'bit': 291 | $fldAttrValue = (integer) $fldAttrValue; 292 | break; 293 | case 'float': 294 | case 'numeric': 295 | $fldAttrValue = (float) $fldAttrValue; 296 | break; 297 | default: 298 | break; 299 | } 300 | $this->map[$class]['fields'][$key]= $fldAttrValue; 301 | break; 302 | case 'null': 303 | $fldAttrValue = (!empty($fldAttrValue) && strtolower($fldAttrValue) !== 'false') ? true : false; 304 | break; 305 | default: 306 | break; 307 | } 308 | $this->map[$class]['fieldMeta'][$key][$fldAttrKey]= $fldAttrValue; 309 | } 310 | } 311 | } 312 | if (isset($object->alias)) { 313 | $this->map[$class]['fieldAliases'] = array(); 314 | foreach ($object->alias as $alias) { 315 | $aliasKey = (string) $alias['key']; 316 | $aliasNode = array(); 317 | foreach ($alias->attributes() as $attrName => $attr) { 318 | $attrValue = (string) $attr; 319 | switch ($attrName) { 320 | case 'key': 321 | continue 2; 322 | case 'field': 323 | $aliasNode = $attrValue; 324 | break; 325 | default: 326 | break; 327 | } 328 | } 329 | if (!empty($aliasKey) && !empty($aliasNode)) { 330 | $this->map[$class]['fieldAliases'][$aliasKey] = $aliasNode; 331 | } 332 | } 333 | } 334 | if (isset($object->index)) { 335 | $this->map[$class]['indexes'] = array(); 336 | foreach ($object->index as $index) { 337 | $indexNode = array(); 338 | $indexName = (string) $index['name']; 339 | foreach ($index->attributes() as $attrName => $attr) { 340 | $attrValue = (string) $attr; 341 | switch ($attrName) { 342 | case 'name': 343 | continue 2; 344 | case 'primary': 345 | case 'unique': 346 | case 'fulltext': 347 | $attrValue = (empty($attrValue) || $attrValue === 'false' ? false : true); 348 | default: 349 | $indexNode[$attrName] = $attrValue; 350 | break; 351 | } 352 | } 353 | if (!empty($indexNode) && isset($index->column)) { 354 | $indexNode['columns']= array(); 355 | foreach ($index->column as $column) { 356 | $columnKey = (string) $column['key']; 357 | $indexNode['columns'][$columnKey] = array(); 358 | foreach ($column->attributes() as $attrName => $attr) { 359 | $attrValue = (string) $attr; 360 | switch ($attrName) { 361 | case 'key': 362 | continue 2; 363 | case 'null': 364 | $attrValue = (empty($attrValue) || $attrValue === 'false' ? false : true); 365 | default: 366 | $indexNode['columns'][$columnKey][$attrName]= $attrValue; 367 | break; 368 | } 369 | } 370 | } 371 | if (!empty($indexNode['columns'])) { 372 | $this->map[$class]['indexes'][$indexName]= $indexNode; 373 | } 374 | } 375 | } 376 | } 377 | if (isset($object->composite)) { 378 | $this->map[$class]['composites'] = array(); 379 | foreach ($object->composite as $composite) { 380 | $compositeNode = array(); 381 | $compositeAlias = (string) $composite['alias']; 382 | foreach ($composite->attributes() as $attrName => $attr) { 383 | $attrValue = (string) $attr; 384 | switch ($attrName) { 385 | case 'alias' : 386 | continue 2; 387 | case 'criteria' : 388 | $attrValue = $this->manager->xpdo->fromJSON(urldecode($attrValue)); 389 | default : 390 | $compositeNode[$attrName]= $attrValue; 391 | break; 392 | } 393 | } 394 | if (!empty($compositeNode)) { 395 | if (isset($composite->criteria)) { 396 | /** @var SimpleXMLElement $criteria */ 397 | foreach ($composite->criteria as $criteria) { 398 | $criteriaTarget = (string) $criteria['target']; 399 | $expression = (string) $criteria; 400 | if (!empty($expression)) { 401 | $expression = $this->manager->xpdo->fromJSON($expression); 402 | if (!empty($expression)) { 403 | if (!isset($compositeNode['criteria'])) $compositeNode['criteria'] = array(); 404 | if (!isset($compositeNode['criteria'][$criteriaTarget])) $compositeNode['criteria'][$criteriaTarget] = array(); 405 | $compositeNode['criteria'][$criteriaTarget] = array_merge($compositeNode['criteria'][$criteriaTarget], (array) $expression); 406 | } 407 | } 408 | } 409 | } 410 | $this->map[$class]['composites'][$compositeAlias] = $compositeNode; 411 | } 412 | } 413 | } 414 | if (isset($object->aggregate)) { 415 | $this->map[$class]['aggregates'] = array(); 416 | foreach ($object->aggregate as $aggregate) { 417 | $aggregateNode = array(); 418 | $aggregateAlias = (string) $aggregate['alias']; 419 | foreach ($aggregate->attributes() as $attrName => $attr) { 420 | $attrValue = (string) $attr; 421 | switch ($attrName) { 422 | case 'alias' : 423 | continue 2; 424 | case 'criteria' : 425 | $attrValue = $this->manager->xpdo->fromJSON(urldecode($attrValue)); 426 | default : 427 | $aggregateNode[$attrName]= $attrValue; 428 | break; 429 | } 430 | } 431 | if (!empty($aggregateNode)) { 432 | if (isset($aggregate->criteria)) { 433 | /** @var SimpleXMLElement $criteria */ 434 | foreach ($aggregate->criteria as $criteria) { 435 | $criteriaTarget = (string) $criteria['target']; 436 | $expression = (string) $criteria; 437 | if (!empty($expression)) { 438 | $expression = $this->manager->xpdo->fromJSON($expression); 439 | if (!empty($expression)) { 440 | if (!isset($aggregateNode['criteria'])) $aggregateNode['criteria'] = array(); 441 | if (!isset($aggregateNode['criteria'][$criteriaTarget])) $aggregateNode['criteria'][$criteriaTarget] = array(); 442 | $aggregateNode['criteria'][$criteriaTarget] = array_merge($aggregateNode['criteria'][$criteriaTarget], (array) $expression); 443 | } 444 | } 445 | } 446 | } 447 | $this->map[$class]['aggregates'][$aggregateAlias] = $aggregateNode; 448 | } 449 | } 450 | } 451 | if (isset($object->validation)) { 452 | $this->map[$class]['validation'] = array(); 453 | $validation = $object->validation[0]; 454 | $validationNode = array(); 455 | foreach ($validation->attributes() as $attrName => $attr) { 456 | $validationNode[$attrName]= (string) $attr; 457 | } 458 | if (isset($validation->rule)) { 459 | $validationNode['rules'] = array(); 460 | foreach ($validation->rule as $rule) { 461 | $ruleNode = array(); 462 | $field= (string) $rule['field']; 463 | $name= (string) $rule['name']; 464 | foreach ($rule->attributes() as $attrName => $attr) { 465 | $attrValue = (string) $attr; 466 | switch ($attrName) { 467 | case 'field' : 468 | case 'name' : 469 | continue 2; 470 | default : 471 | $ruleNode[$attrName]= $attrValue; 472 | break; 473 | } 474 | } 475 | if (!empty($field) && !empty($name) && !empty($ruleNode)) { 476 | $validationNode['rules'][$field][$name]= $ruleNode; 477 | } 478 | } 479 | if (!empty($validationNode['rules'])) { 480 | $this->map[$class]['validation'] = $validationNode; 481 | } 482 | } 483 | } 484 | } 485 | } else { 486 | $this->manager->xpdo->log(xPDO::LOG_LEVEL_ERROR, "Schema {$schemaFile} contains no valid object elements."); 487 | } 488 | } else { 489 | $this->manager->xpdo->log(xPDO::LOG_LEVEL_ERROR, "Could not read schema from {$schemaFile}."); 490 | } 491 | 492 | $om_path= XPDO_CORE_PATH . 'om/'; 493 | $path= !empty ($outputDir) ? $outputDir : $om_path; 494 | if (isset ($this->model['package']) && strlen($this->model['package']) > 0) { 495 | $path .= strtr($this->model['package'], '.', '/'); 496 | $path .= '/'; 497 | } 498 | $this->outputMeta($path); 499 | $this->outputMaps($path); 500 | $this->outputClasses($path); 501 | 502 | if ($compile) $this->compile($path, $this->model, $this->classes, $this->maps); 503 | unset($this->model, $this->classes, $this->map); 504 | return true; 505 | } 506 | 507 | /** 508 | * Write the generated class files to the specified path. 509 | * 510 | * @access public 511 | * @param string $path An absolute path to write the generated class files 512 | * to. 513 | */ 514 | public function outputClasses($path) { 515 | $newClassGeneration= false; 516 | $newPlatformGeneration= false; 517 | $platform= $this->model['platform']; 518 | if (!is_dir($path)) { 519 | $newClassGeneration= true; 520 | if ($this->manager->xpdo->getCacheManager()) { 521 | if (!$this->manager->xpdo->cacheManager->writeTree($path)) { 522 | $this->manager->xpdo->log(xPDO::LOG_LEVEL_ERROR, "Could not create model directory at {$path}"); 523 | return false; 524 | } 525 | } 526 | } 527 | $ppath= $path; 528 | $ppath .= $platform; 529 | if (!is_dir($ppath)) { 530 | $newPlatformGeneration= true; 531 | if ($this->manager->xpdo->getCacheManager()) { 532 | if (!$this->manager->xpdo->cacheManager->writeTree($ppath)) { 533 | $this->manager->xpdo->log(xPDO::LOG_LEVEL_ERROR, "Could not create platform subdirectory {$ppath}"); 534 | return false; 535 | } 536 | } 537 | } 538 | $model= $this->model; 539 | if (isset($this->model['phpdoc-package'])) { 540 | $model['phpdoc-package']= '@package ' . $this->model['phpdoc-package']; 541 | if (isset($this->model['phpdoc-subpackage']) && !empty($this->model['phpdoc-subpackage'])) { 542 | $model['phpdoc-subpackage']= '@subpackage ' . $this->model['phpdoc-subpackage'] . '.' . $this->model['platform']; 543 | } else { 544 | $model['phpdoc-subpackage']= '@subpackage ' . $this->model['platform']; 545 | } 546 | } else { 547 | $basePos= strpos($this->model['package'], '.'); 548 | $package= $basePos 549 | ? substr($this->model['package'], 0, $basePos) 550 | : $this->model['package']; 551 | $subpackage= $basePos 552 | ? substr($this->model['package'], $basePos + 1) 553 | : ''; 554 | $model['phpdoc-package']= '@package ' . $package; 555 | if ($subpackage) $model['phpdoc-subpackage']= '@subpackage ' . $subpackage; 556 | } 557 | 558 | 559 | 560 | foreach ($this->classes as $className => $classDef) { 561 | /*echo '
';
562 |             var_dump($this->map[$className]);
563 |             echo '
';*/ 564 | $newClass= false; 565 | $classDef['class']= $className; 566 | $classDef['class-lowercase']= strtolower($className); 567 | 568 | $classDef['phpdoc-vars'] = $this->generateClassPhpDoc($this->map[$className]); 569 | 570 | $classDef= array_merge($model, $classDef); 571 | $replaceVars= array (); 572 | foreach ($classDef as $varKey => $varValue) { 573 | if (is_scalar($varValue)) $replaceVars["[+{$varKey}+]"]= $varValue; 574 | } 575 | 576 | 577 | 578 | 579 | $fileContent= str_replace(array_keys($replaceVars), array_values($replaceVars), $this->classTemplate); 580 | if (is_dir($path)) { 581 | $fileName= $path . strtolower($className) . '.class.php'; 582 | if (!file_exists($fileName)) { 583 | if ($file= @ fopen($fileName, 'wb')) { 584 | if (!fwrite($file, $fileContent)) { 585 | $this->manager->xpdo->log(xPDO::LOG_LEVEL_ERROR, "Could not write to file: {$fileName}"); 586 | } 587 | $newClass= true; 588 | @fclose($file); 589 | } else { 590 | $this->manager->xpdo->log(xPDO::LOG_LEVEL_ERROR, "Could not open or create file: {$fileName}"); 591 | } 592 | } else { 593 | $newClass= false; 594 | $this->manager->xpdo->log(xPDO::LOG_LEVEL_INFO, "Skipping {$fileName}; file already exists.\nMove existing class files to regenerate them."); 595 | } 596 | } else { 597 | $this->manager->xpdo->log(xPDO::LOG_LEVEL_ERROR, "Could not open or create dir: {$path}"); 598 | } 599 | $fileContent= str_replace(array_keys($replaceVars), array_values($replaceVars), $this->getClassPlatformTemplate($platform)); 600 | if (is_dir($ppath)) { 601 | $fileName= $ppath . '/' . strtolower($className) . '.class.php'; 602 | if (!file_exists($fileName)) { 603 | if ($file= @ fopen($fileName, 'wb')) { 604 | if (!fwrite($file, $fileContent)) { 605 | $this->manager->xpdo->log(xPDO::LOG_LEVEL_ERROR, "Could not write to file: {$fileName}"); 606 | } 607 | @fclose($file); 608 | } else { 609 | $this->manager->xpdo->log(xPDO::LOG_LEVEL_ERROR, "Could not open or create file: {$fileName}"); 610 | } 611 | } else { 612 | $this->manager->xpdo->log(xPDO::LOG_LEVEL_INFO, "Skipping {$fileName}; file already exists.\nMove existing class files to regenerate them."); 613 | if ($newClassGeneration || $newClass) $this->manager->xpdo->log(xPDO::LOG_LEVEL_WARN, "IMPORTANT: {$fileName} already exists but you appear to have generated classes with an older xPDO version. You need to edit your class definition in this file to extend {$className} rather than {$classDef['extends']}."); 614 | } 615 | } else { 616 | $this->manager->xpdo->log(xPDO::LOG_LEVEL_ERROR, "Could not open or create dir: {$path}"); 617 | } 618 | } 619 | } 620 | 621 | /** 622 | * Generate phpdoc for model class 623 | * @param $classMap 624 | * @return string 625 | */ 626 | public function generateClassPhpDoc($classMap) 627 | { 628 | $doc = ''; 629 | $doc .= " * @package ".$classMap['package']."\n"; 630 | foreach($classMap['fieldMeta'] as $fieldName => $fieldData) 631 | { 632 | $doc .= " * @property ".$fieldData['phptype']." $".$fieldName."\n"; 633 | } 634 | 635 | if(isset($classMap['composites'])) 636 | { 637 | foreach($classMap['composites'] as $fieldName => $fieldData) 638 | { 639 | $brackets = $fieldData['cardinality'] == 'many' ? '[]' : ''; 640 | $doc .= " * @property ".$fieldData['class'].$brackets." $".$fieldName."\n"; 641 | } 642 | } 643 | 644 | if(isset($classMap['aggregates'])) 645 | { 646 | foreach($classMap['aggregates'] as $fieldName => $fieldData) 647 | { 648 | $brackets = $fieldData['cardinality'] == 'many' ? '[]' : ''; 649 | $doc .= " * @property ".$fieldData['class'].$brackets." $".$fieldName."\n"; 650 | } 651 | } 652 | $doc = trim($doc,"\n"); 653 | return $doc; 654 | } 655 | } 656 | --------------------------------------------------------------------------------