1084

Does Trongate posses an inbuilt function that can list all modules?

Comments for “Does Trongate posses an inbuilt function that can list all modules?”
 

Posted by Dom on Tuesday 6th February 2024 at 08:50 GMT

I'm going to assume that the answer to this is no given that I can't find any obvious such function in the engine.

With that in mind can anyone better acquainted with the framework that I currently am suggest if it might be possible to:
a) return a list of all modules (that would need to include sub
modules.
b) determine whether those modules were linked to a database table (and crucially what that table is called).
c) determine whether they had an api.json file in their assets folder.

a and c can almost certainly be achieved via some recursive directory searching but what really interests me is whether it would be possible to determine b?

Getting lists of tables in the database is easy but being able to say definitively that said tables are linked to a specific modules is not so easy.
Level One Member

Dom

User Level: Level One Member

Date Joined: 12/01/2024

Posted by djnordeen on Tuesday 6th February 2024 at 13:18 GMT

Hello,
So If all modules have a database table.
Then you can get names of the tables.

Otherwise, you would have to look at using the segment method.

This is all I can think of at the moment.

Dan
Early Adopter

djnordeen

User Level: Early Adopter

Date Joined: 20/08/2021

Posted by Dom on Tuesday 6th February 2024 at 13:48 GMT

Hi Dan

Not every module would necessarily have a corresponding database table, I am for example currently creating one that has no need of one, and there will be modules that do have tables but not always named identically to the modules they are associated to.

I'm creating a module that will generate fake data for tables in an app's underlying database. Most of it is now working. There is an argument to be made for using trongate's inbuilt api's to insert that data (where they exist) but the potential disparity between the underlying table and module names and indeed whether or not a module even has an underlying data table makes it hard to do dynamically.

As with most things a little lateral thinking will get one there eventually. I'm in lazy mode at the moment and trying to establish if there's something under the hood that I could hook into.
Level One Member

Dom

User Level: Level One Member

Date Joined: 12/01/2024

Posted by djnordeen on Tuesday 6th February 2024 at 15:18 GMT

Hello Dom,
Sorry I could not be of much help.
Dan
Early Adopter

djnordeen

User Level: Early Adopter

Date Joined: 20/08/2021

Posted by Dom on Tuesday 6th February 2024 at 15:45 GMT

Not to worry Dan

With a little bit of lateral thinking and Dr Googles help on how php does recursive searching I was able to return the following.
<pre>Array
(
    [0] => Array
        (
            [name] => calendar
            [hasApiJson] => 
        )

    [1] => Array
        (
            [name] => enquiries
            [hasApiJson] => 1
        )

    [2] => Array
        (
            [name] => hi
            [hasApiJson] => 
        )

    [3] => Array
        (
            [name] => members
            [hasApiJson] => 1
        )

    [4] => Array
        (
            [name] => account
            [hasApiJson] => 
        )

    [5] => Array
        (
            [name] => join
            [hasApiJson] => 
        )

    [6] => Array
        (
            [name] => test
            [hasApiJson] => 
        )

    [7] => Array
        (
            [name] => trongate_administrators
            [hasApiJson] => 1
        )

    [8] => Array
        (
            [name] => trongate_comments
            [hasApiJson] => 1
        )

    [9] => Array
        (
            [name] => trongate_filezone
            [hasApiJson] => 1
        )

    [10] => Array
        (
            [name] => trongate_pages
            [hasApiJson] => 1
        )

    [11] => Array
        (
            [name] => trongate_security
            [hasApiJson] => 
        )

    [12] => Array
        (
            [name] => trongate_tokens
            [hasApiJson] => 1
        )

    [13] => Array
        (
            [name] => trongate_users
            [hasApiJson] => 
        )

    [14] => Array
        (
            [name] => trongate_user_levels
            [hasApiJson] => 
        )

    [15] => Array
        (
            [name] => vtl_gen
            [hasApiJson] => 
        )

    [16] => Array
        (
            [name] => vtl_faker
            [hasApiJson] => 
        )

    [17] => Array
        (
            [name] => welcome
            [hasApiJson] => 
        )

)
</pre>


That should allow me to determine whether I can use Trongate's built in api functions or whether I need to create actual sql insert statements.
Level One Member

Dom

User Level: Level One Member

Date Joined: 12/01/2024

Posted by DaFa on Wednesday 7th February 2024 at 13:18 GMT

Hi Dom,

I've created this function to create an array of all modules and submodules and indicate if it has an associated table and api.json file
function list_all_modules() {
	$modules_dir = APPPATH . 'modules';
	$tables = $this->model->query("SHOW TABLES", 'array');
	$table_names = [];
	foreach ($tables as $table) {
		$table_names[] = $table[array_key_first($table)];
	}
	$module_info = [];
	foreach (new DirectoryIterator($modules_dir) as $module_dir) {
		if ($module_dir->isDir() && !$module_dir->isDot() && $module_dir->getFilename() !== 'modules') {
			$module_name = $module_dir->getFilename();
			if (in_array($module_name, $table_names)) {
				$has_table = true;
				unset($table_names[array_search($module_name, $table_names)]);
			} else {
				$has_table = false;
			}
			$controllers_dir = $module_dir->getPathname() . '/controllers';
			if (is_dir($controllers_dir)) {
				$assets_dir = $module_dir->getPathname() . '/assets';
				if (is_dir($assets_dir)) {
					$api_json_exists = file_exists($assets_dir . '/api.json');
				} else {
					$api_json_exists = false;
				}
				$submodules = [];
				foreach (new DirectoryIterator($module_dir->getPathname()) as $submodule_dir) {
					if ($submodule_dir->isDir() && !$submodule_dir->isDot() && $submodule_dir->getFilename() !== 'controllers') {
						$submodule_name = $submodule_dir->getFilename();
						if (in_array($submodule_name, $table_names)) {
							$has_table = true;
							unset($table_names[array_search($submodule_name, $table_names)]);
						} else {
							$has_table = false;
						}
						$submodule_controllers_dir = $submodule_dir->getPathname() . '/controllers';
						$controllers_exist = is_dir($submodule_controllers_dir);
						$submodule_assets_dir = $submodule_dir->getPathname() . '/assets';
						$submodule_api_json_exists = is_dir($submodule_assets_dir) && file_exists($submodule_assets_dir . '/api.json');
						if ($controllers_exist) {
							$submodules[] = [
								'module_name' => $submodule_name,
								'is_child_module_of' => $module_name,
								'has_table' => $has_table,
								'api_json_exists' => $submodule_api_json_exists
							];
						}
					}
				}
				if (!empty($submodules)) {
					$module_info[] = [
						'module_name' => $module_name,
						'api_json_exists' => $api_json_exists,
						'has_table' => $has_table,
						'submodules' => $submodules
					];
				} else {
					$module_info[] = [
						'module_name' => $module_name,
						'api_json_exists' => $api_json_exists,
						'has_table' => $has_table
					];
				}
			}
		}
	}
	if (!empty($table_names)) {
		$module_info[] = [
			'orphaned_tables' => $table_names
		];
	}
	return $module_info;
}
Here is a simple test method that calls it and outputs it as json
function test() {
	$all_mods = $this->list_all_modules();
	json($all_mods);
}
And here is the output of a standard Trongate app with the 'members' module installed
[
    {
        "module_name": "members",
        "api_json_exists": true,
        "has_table": false,
        "submodules": [
            {
                "module_name": "account",
                "is_child_module_of": "members",
                "has_table": false,
                "api_json_exists": false
            },
            {
                "module_name": "join",
                "is_child_module_of": "members",
                "has_table": false,
                "api_json_exists": false
            }
        ]
    },
    {
        "module_name": "tasks",
        "api_json_exists": true,
        "has_table": false
    },
    {
        "module_name": "trongate_administrators",
        "api_json_exists": true,
        "has_table": false
    },
    {
        "module_name": "trongate_comments",
        "api_json_exists": true,
        "has_table": false
    },
    {
        "module_name": "trongate_filezone",
        "api_json_exists": true,
        "has_table": false
    },
    {
        "module_name": "trongate_pages",
        "api_json_exists": true,
        "has_table": false
    },
    {
        "module_name": "trongate_security",
        "api_json_exists": false,
        "has_table": false
    },
    {
        "module_name": "trongate_tokens",
        "api_json_exists": true,
        "has_table": false
    },
    {
        "module_name": "trongate_users",
        "api_json_exists": false,
        "has_table": true,
        "submodules": [
            {
                "module_name": "trongate_user_levels",
                "is_child_module_of": "trongate_users",
                "has_table": true,
                "api_json_exists": false
            }
        ]
    },
    {
        "module_name": "welcome",
        "api_json_exists": false,
        "has_table": false
    }
]


Note: this function assumes the module name matches the table name, as it would if you created a module with the Desktop app, and is normal convention, however, it also adds any orphaned tables to the end of the array.
Founding Member

DaFa

User Level: Founding Member

Date Joined: 30/11/2018

Posted by Dom on Wednesday 7th February 2024 at 13:36 GMT

Hi Simon
That's really neat, and the output is definitely more readable than mine was/ is.

You have of course spotted the obvious flaw/ problem in all this, namely that anyone brining their own database to show is almost certainly going to have differently named tables to the modules themselves. However I don't see that as being too much of a problem until it gets down to creating absolutely realistic data.

Automating that is doable but very complex and I question if it produces any real value. Great as a coding challenge though.

I shall mark your answer as a solution to this as it's more elegant than mine.
Level One Member

Dom

User Level: Level One Member

Date Joined: 12/01/2024

Posted by Dom on Wednesday 7th February 2024 at 13:45 GMT

I realise that third party dependencies are rather frowned upon but it would be great to see something like prism integrated into the Help bar. I always find coloured syntactically highlighted code way easier to read and crucially comprehend.
Level One Member

Dom

User Level: Level One Member

Date Joined: 12/01/2024

Posted by DaFa on Wednesday 7th February 2024 at 14:08 GMT

Thanks! It was fun creating that. I have fixed a minor logic error, where it was reporting the table not existing because I reused the same variable '$has_table' and also switched the order so they are the same in parent and child modules. Also added a test orphan table to show how it will display them at the end of the output.
function list_all_modules() {
	$modules_dir = APPPATH . 'modules';
	$tables = $this->model->query("SHOW TABLES", 'array');
	$table_names = [];
	foreach ($tables as $table) {
		$table_names[] = $table[array_key_first($table)];
	}
	$module_info = [];
	foreach (new DirectoryIterator($modules_dir) as $module_dir) {
		if ($module_dir->isDir() && !$module_dir->isDot() && $module_dir->getFilename() !== 'modules') {
			$module_name = $module_dir->getFilename();
			if (in_array($module_name, $table_names)) {
				$parent_has_table = true;
				unset($table_names[array_search($module_name, $table_names)]);
			} else {
				$parent_has_table = false;
			}
			$controllers_dir = $module_dir->getPathname() . '/controllers';
			if (is_dir($controllers_dir)) {
				$assets_dir = $module_dir->getPathname() . '/assets';
				if (is_dir($assets_dir)) {
					$api_json_exists = file_exists($assets_dir . '/api.json');
				} else {
					$api_json_exists = false;
				}
				$submodules = [];
				foreach (new DirectoryIterator($module_dir->getPathname()) as $submodule_dir) {
					if ($submodule_dir->isDir() && !$submodule_dir->isDot() && $submodule_dir->getFilename() !== 'controllers') {
						$submodule_name = $submodule_dir->getFilename();
						if (in_array($submodule_name, $table_names)) {
							$child_has_table = true;
							unset($table_names[array_search($submodule_name, $table_names)]);
						} else {
							$child_has_table = false;
						}
						$submodule_controllers_dir = $submodule_dir->getPathname() . '/controllers';
						$controllers_exist = is_dir($submodule_controllers_dir);
						$submodule_assets_dir = $submodule_dir->getPathname() . '/assets';
						$submodule_api_json_exists = is_dir($submodule_assets_dir) && file_exists($submodule_assets_dir . '/api.json');
						if ($controllers_exist) {
							$submodules[] = [
								'module_name' => $submodule_name,
								'is_child_module_of' => $module_name,
								'has_table' => $child_has_table,
								'api_json_exists' => $submodule_api_json_exists
							];
						}
					}
				}
				if (!empty($submodules)) {
					$module_info[] = [
						'module_name' => $module_name,
						'has_table' => $parent_has_table,
						'api_json_exists' => $api_json_exists,
						'submodules' => $submodules
					];
				} else {
					$module_info[] = [
						'module_name' => $module_name,
						'has_table' => $parent_has_table,
						'api_json_exists' => $api_json_exists
					];
				}
			}
		}
	}
	if (!empty($table_names)) {
		$module_info[] = [
			'orphaned_tables' => $table_names
		];
	}
	return $module_info;
}

And the output
[
    {
        "module_name": "members",
        "has_table": true,
        "api_json_exists": true,
        "submodules": [
            {
                "module_name": "account",
                "is_child_module_of": "members",
                "has_table": false,
                "api_json_exists": false
            },
            {
                "module_name": "join",
                "is_child_module_of": "members",
                "has_table": false,
                "api_json_exists": false
            }
        ]
    },
    {
        "module_name": "tasks",
        "has_table": true,
        "api_json_exists": true
    },
    {
        "module_name": "trongate_administrators",
        "has_table": true,
        "api_json_exists": true
    },
    {
        "module_name": "trongate_comments",
        "has_table": true,
        "api_json_exists": true
    },
    {
        "module_name": "trongate_filezone",
        "has_table": false,
        "api_json_exists": true
    },
    {
        "module_name": "trongate_pages",
        "has_table": true,
        "api_json_exists": true
    },
    {
        "module_name": "trongate_security",
        "has_table": false,
        "api_json_exists": false
    },
    {
        "module_name": "trongate_tokens",
        "has_table": true,
        "api_json_exists": true
    },
    {
        "module_name": "trongate_users",
        "has_table": true,
        "api_json_exists": false,
        "submodules": [
            {
                "module_name": "trongate_user_levels",
                "is_child_module_of": "trongate_users",
                "has_table": true,
                "api_json_exists": false
            }
        ]
    },
    {
        "module_name": "welcome",
        "has_table": false,
        "api_json_exists": false
    },
    {
        "orphaned_tables": {
            "1": "orphan_table"
        }
    }
]


Let me know if you need more help with your project.

ps. Here is a really cool PHP AI library that could be used to create fake data
https://github.com/openai-php/client
Founding Member

DaFa

User Level: Founding Member

Date Joined: 30/11/2018

Posted by Andrew on Thursday 15th February 2024 at 05:46 GMT

@Dafa, thanks for sharing that github link.

This comment was edited by Andrew on Thursday 15th February 2024 at 05:46 GMT

Early Adopter

Andrew

User Level: Early Adopter

Date Joined: 23/10/2019

×