Pma 4.0 introduces a completely new navigation panel, this is a complete rewrite in OOP PHP that provides a tree-like structure which is populated via ajax.
When pma is first opened, an initial navigation panel, usually containing some databases, is rendered in Header.class.php. This is the only time that the navigation system is not accessed via ajax. In fact, any non-ajax requests to navigation.php will throw a fatal error.
The rendering of the navigation tree is a 3 stage process which takes place on the server side.
First, information about items such as dbs, tables, etc, is loaded into memory.
Whenever we are rendering a branch of the tree (e.g: when clicking on a branch to open) only that branch, its immediate children and all its parents (but not the children of the parents) are loaded into memory.
Secondly, the tree is grouped based on the values of the following configuration directives:
Lastly, the in-memory structure is rendered into HTML.
The in-memory representation of the tree of items is composed of objects that are either of type Node or are derived from it. All the classes can be found in the 'libraries/navigation/Nodes' folder. However the Node objects should not be instanciated directly, but rather through 'libraries/navigation/NodeFactory.class.php'.
There are two types of Nodes: OBJECT and CONTAINER. Normally, OBJECTs are added as children to CONTAINERs, but this is not a requirement as OBJECTs can hold other OBJECTs as their children.
Almost every node used to child the tree will be derived from a specific class which will extend the base Node class. The only exceptions are the root node which holds the databases and nodes that were created during the grouping of the tree.
Some node classes provide information about their children. This is accomplished via 2 methods: getPresence() and getData(). The former returns an integer to indicate how many children of some specific type there are in the dbms for a node, while the latter returns an array of unique names of children of a node.
The Node class can provide information about databases. The Node_Database class can provide information about: views, tables, functions, procedures and events. The Node_Table class can provide information about: columns, indexes and triggers.
During most requests to navigation.php, it is necessary to pass paths as parameters to the script. There are 2 types of paths: actual paths and virtual paths. An actual path tells the script how to get to a node before the tree has been grouped, while the virtual is obtained after the tree has been grouped.
Grouping itself, is a process of extracting a common prefix from groups of items
// The separator is an underscore
$GLOBALS['cfg']['NavigationTreeDbSeparator'] = "_";
And we have the following dbs:
They would be grouped like this:
As another example, with the default tree separators, if we are trying to expand the "film__actor" table node in the "sakila_backup" db (and we also have a "sakila_original" db, as well as a "film__category"), we will end up sending the following paths to the server:
actual path: 'root.sakila_backup.tables.film__actor' virtual path: 'root.sakila_.backup.Tables.film__.actor'
If no grouping has occured of the tree has occurred, both paths will be identical, except for the parts that point to container, in the above case "Tables" as in the virtual paths container names are localised.
During operations of refreshing the navigation, multiple pairs may be sent to the server, one for each node that has been expanded in the tree.
The implementation of the fast filter has two parts: immediate client side filtering as the user types and additional asynchronous filtering if the user presses Enter. The former is quite straight-forward: a jQuery function that recursively transverses the navigation tree and hides all nodes that don't match the query string. The latter is a bit more complex. Sometimes there may be more than one page with nodes in a branch of the tree and the results that we may be looking for may be on a page that is not loaded. Therefore if the user presses 'Enter', ajax requests are sent to the server to see if this is the case and, if so, the results are updated with the new ones from the server.
All of this functionality is handled in the 'js/navigation.js' file, specifically in the PMA_fastFilter object. The event handlers are bound to all fast filter input boxes on page load.
The implementation of the pagination is simple and relies on the client side to send offsets to the server where these are inserted into the queries that retrieve items from the dbms (e.g: SELECT something FROM somewhere LIMIT $offset, $maxItems).
There are three types of offsets:
- first level offsets: used to paginate databases
- second level offsets: used to paginate tables, views, routines and events
- third level offsets: used to paginate columns, indexes and triggers (of course, third level offsets only make sense if we have a table as a second level parent)
During operations of refreshing the navigation, multiple sets of offsets may be sent to the server, one for each branch that has been paginated in the tree.
In order to refresh the navigation, the navigation tree is transversed backwards on the client side to generate all the actual and virtual paths, as well as the positions in the pagination at various levels, if necessary. Then all this data is sent to the server, together with a "&full=true" parameter to indicate that we want a whole replacement tree rather than a single branch. Upon the successful completion of the post request to the server, the navigation tree is simply replaced by the data contained in the response.
The resizing and scrolling of the navigation panel is implemented in the ResizeHandler and ScrollHandler objects in the 'js/navigation.js' file. Both objects are almost self-contained and only rely on jQuery and the mousewheel plugin.