<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Thierry Feuzeu</title>
    <description>The latest articles on DEV Community by Thierry Feuzeu (@tiaya).</description>
    <link>https://dev.to/tiaya</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1601630%2F8855c307-86a6-4ac0-a769-430d2e2c618a.png</url>
      <title>DEV Community: Thierry Feuzeu</title>
      <link>https://dev.to/tiaya</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tiaya"/>
    <language>en</language>
    <item>
      <title>Jaxon DbAdmin: PHP finally gets a next-gen database manager</title>
      <dc:creator>Thierry Feuzeu</dc:creator>
      <pubDate>Thu, 08 Oct 2026 01:42:05 +0000</pubDate>
      <link>https://dev.to/tiaya/jaxon-dbadmin-php-finally-gets-a-next-gen-database-manager-244h</link>
      <guid>https://dev.to/tiaya/jaxon-dbadmin-php-finally-gets-a-next-gen-database-manager-244h</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo7o9sn51eyiqsy1yl51q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo7o9sn51eyiqsy1yl51q.png" alt=" " width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The PHP ecosystem has gifted the IT world with two of the most widely used open-source database management tools: &lt;a href="https://adminer.org" rel="noopener noreferrer"&gt;Adminer&lt;/a&gt; and &lt;a href="https://www.phpmyadmin.net" rel="noopener noreferrer"&gt;PhpMyAdmin&lt;/a&gt;, each having millions of monthly downloads. However, newer solutions have emerged to provide advanced features that these traditional tools do not natively offer, such as simultaneous multi-database browsing, data masking, advanced users and credentials management, audit logging, and modern UI/UX. Alternatives like &lt;a href="https://dbeaver.io" rel="noopener noreferrer"&gt;DBeaver&lt;/a&gt;, &lt;a href="https://www.dbgate.io" rel="noopener noreferrer"&gt;DbGate&lt;/a&gt;, &lt;a href="https://www.bytebase.com" rel="noopener noreferrer"&gt;ByteBase&lt;/a&gt;, and the more recent &lt;a href="https://github.com/TabularisDB/tabularis" rel="noopener noreferrer"&gt;Tabularis&lt;/a&gt; and &lt;a href="https://github.com/t8y2/dbx" rel="noopener noreferrer"&gt;DBx&lt;/a&gt;, just to name those with an open source license, deliver these capabilities. Available as web or desktop applications, their user interfaces are built using modern JavaScript frameworks like React.&lt;/p&gt;

&lt;p&gt;Today, the Jaxon DbAdmin web application bridges this gap.&lt;br&gt;
It positions itself as a modern database management tool built with PHP, delivering the same high-level features as the most advanced alternatives on the market. While the foundational &lt;a href="https://github.com/lagdo/jaxon-dbadmin/releases?page=2#release-v0.32.0" rel="noopener noreferrer"&gt;0.32.0 release&lt;/a&gt; introduced an extensive list of changes, fixes, and architectural improvements, subsequent releases have refined the platform. The features highlighted in this article are available in &lt;a href="https://github.com/lagdo/jaxon-dbadmin/releases?page=1#release-v0.35.1" rel="noopener noreferrer"&gt;version 0.35.1&lt;/a&gt;, the latest release at the time of writing. Finally, we will also take a look at the main features planned for upcoming releases.&lt;/p&gt;
&lt;h4&gt;
  
  
  The Jaxon DbAdmin applications
&lt;/h4&gt;

&lt;p&gt;Jaxon DbAdmin is provided as a &lt;a href="https://www.jaxon-php.org/docs/v5x/extensions/packages.html" rel="noopener noreferrer"&gt;Jaxon package&lt;/a&gt;, which means it implements both the application backend and frontend, and it needs to be installed in an application framework.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Read the &lt;a href="https://github.com/lagdo/jaxon-dbadmin/wiki/01.-Features-and-current-status" rel="noopener noreferrer"&gt;features and current status&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The previous Jaxon DbAdmin releases shipped with a single ready-to-use application built with Laravel. There are now three of them, respectively built with the &lt;a href="https://github.com/lagdo/dbadmin-app-laravel" rel="noopener noreferrer"&gt;Laravel&lt;/a&gt;, &lt;a href="https://github.com/lagdo/dbadmin-app-symfony" rel="noopener noreferrer"&gt;Symfony&lt;/a&gt; and &lt;a href="https://github.com/lagdo/dbadmin-app-slim" rel="noopener noreferrer"&gt;Slim&lt;/a&gt; PHP frameworks.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Read the &lt;a href="https://github.com/lagdo/jaxon-dbadmin/wiki/02.-Installation" rel="noopener noreferrer"&gt;installation documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The user management and authentication feature in Jaxon DbAdmin is provided by the framework it is installed on. Supporting multiple different frameworks then allows the users to choose their preferred authentication strategy and tools, depending on their needs. The Laravel, Symfony and Slim frameworks are able to implement various user authentication strategies, from the most simple to the most advanced.&lt;/p&gt;

&lt;p&gt;The Jaxon DbAdmin applications include a default authentication feature where the user accounts are stored in a database for Laravel and Symfony, or in a json file for Slim.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Read the &lt;a href="https://github.com/lagdo/jaxon-dbadmin/wiki/05.-Authentication" rel="noopener noreferrer"&gt;default authentication documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Althought they are based on different frameworks, the Jaxon DbAdmin applications share the same configuration options and files.&lt;br&gt;
The config options are separated into three files, &lt;code&gt;app.php&lt;/code&gt;, &lt;code&gt;servers.php&lt;/code&gt; and &lt;code&gt;foreigns.php&lt;/code&gt;. In the &lt;code&gt;app.php&lt;/code&gt; and &lt;code&gt;foreigns.php&lt;/code&gt; config files, some options values are provided as closures or anonymous classes.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;servers.php&lt;/code&gt; file can also be provided in &lt;code&gt;json&lt;/code&gt; or &lt;code&gt;yaml&lt;/code&gt; format. In that case the file is renamed to &lt;code&gt;servers.json&lt;/code&gt;, &lt;code&gt;servers.yaml&lt;/code&gt; or &lt;code&gt;servers.yml&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Read the &lt;a href="https://github.com/lagdo/jaxon-dbadmin/wiki/04.-Configuration-files" rel="noopener noreferrer"&gt;configuration documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Jaxon DbAdmin &lt;a href="https://hub.docker.com/r/lagdo/jaxon-dbadmin" rel="noopener noreferrer"&gt;Docker images&lt;/a&gt; are also updated, and they are now tagged both with the framework name and their version number.&lt;/p&gt;
&lt;h4&gt;
  
  
  Database secrets management
&lt;/h4&gt;

&lt;p&gt;The previous releases already allow the users to store their database credentials in a secret management server, instead of the default &lt;code&gt;.env.dbadmin&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;The latest release now natively supports 6 secret management services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://infisical.com/" rel="noopener noreferrer"&gt;Infisical&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/secrets-manager/" rel="noopener noreferrer"&gt;AWS Secrets Manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/security/products/secret-manager" rel="noopener noreferrer"&gt;GCP Secret Manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openbao.org" rel="noopener noreferrer"&gt;OpenBao&lt;/a&gt; (compatible with &lt;a href="https://www.hashicorp.com/fr/products/vault" rel="noopener noreferrer"&gt;HashiCorp Vault&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://azure.microsoft.com/fr-fr/products/key-vault" rel="noopener noreferrer"&gt;Azure Key Vault&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.alibabacloud.com/help/en/kms" rel="noopener noreferrer"&gt;Alibaba Key Management Service&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using a secret manager requires two options in the &lt;code&gt;app.php&lt;/code&gt; config file.&lt;br&gt;
The &lt;code&gt;secret.reader&lt;/code&gt; option defines the name of the class implementing the service client, and the &lt;code&gt;secret.key&lt;/code&gt; defines a class with a function returning the secret location for a given user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;    &lt;span class="s1"&gt;'secret'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'reader'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Provider\Secret\InfisicalConfigProvider&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Provider\Secret\KeyBuilderInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$option&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// $username = Auth::userId(); // Use this to customize the key.&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"users.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$prefix&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$option&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each secret manager also defines custom options to be provided in the &lt;code&gt;.env.dbadmin&lt;/code&gt; file, and optionally a procedure to setup the client.&lt;br&gt;
Jaxon DbAdmin will query the secret service for database credentials on each user request, and will never save any secret locally, not even in a cache.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Read the &lt;a href="https://github.com/lagdo/jaxon-dbadmin/wiki/08.-Using-a-secret-manager-service" rel="noopener noreferrer"&gt;secret service configuration&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  Multi-tab navigation
&lt;/h4&gt;

&lt;p&gt;The list of database servers a Jaxon DbAdmin user can connect to is configured on the server. That list can be customized for each user, and the servers he is allowed to connect to will be displayed in a dropdown list.&lt;/p&gt;

&lt;p&gt;Jaxon DbAdmin allows the user to open multiple tabs, each connected to a chosen database. Each tab can be renamed, and by default a tab is named after the database it is connected to. Connecting to a different database automatically rename the tab accordingly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft6mx5p1bgh7b57edphv9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft6mx5p1bgh7b57edphv9.png" alt="Jaxon DbAdmin tabs" width="799" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When connected to a database, the Query Editor window also allows the user to open multiple tabs. It is convenient when the user needs to run multiple queries on the same database.&lt;/p&gt;

&lt;p&gt;The application window and Query Editor tabs can be saved in the user preferences. They will the be automatically recreated anytime the user opens the application.&lt;/p&gt;
&lt;h4&gt;
  
  
  The Query Editor and Query Builder
&lt;/h4&gt;

&lt;p&gt;Similar to Adminer, Jaxon DbAdmin provides two tools to query database tables: the Query Editor and the Query Builder.&lt;/p&gt;

&lt;p&gt;The Query Editor allows the user to manually edit, then execute SQL queries. As stated earlier, multiple editor tabs can be opened on the same database. The user can also choose its preferred editor between the &lt;a href="https://codemirror.net" rel="noopener noreferrer"&gt;CodeMirror 6&lt;/a&gt; and &lt;a href="https://ace.c9.io" rel="noopener noreferrer"&gt;Ace&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fddppy1ukb43dp1yw8yc7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fddppy1ukb43dp1yw8yc7.png" alt="Jaxon DbAdmin query editor" width="799" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In addition to SQL syntax highlighting and code completion, the Query Editor also provides code completion on the connected database tables and columns names.&lt;/p&gt;

&lt;p&gt;The Query Builder is a visual builder for database queries, which also supports querying and editing table data, and editing table definition.&lt;/p&gt;

&lt;p&gt;The Query Builder displays the selected table rows in a paginated list. Various buttons and dialogs allow the user to select the columns to be displayed, filter rows on column values, and change the results ordering. The corresponding SQL query is displayed in a read-only field, with syntax highlighting enabled.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2pgnrn2yn9c4hns4jnua.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2pgnrn2yn9c4hns4jnua.png" alt="Jaxon DbAdmin query builder rows" width="799" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each data row can be edited in a dialog where each column value is displayed in an HTML input field corresponding to the column type.&lt;br&gt;
The same dialog can be use to insert a new row in the table.&lt;/p&gt;

&lt;p&gt;The Query Builder can also display a table definition, in a page with the table columns, indexes, foreign keys and triggers. From there, the table definition can be altered, in a page that will allow the user to change the table name and properties, and edit, delete or add columns in the table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2ad1z1blg6pw68vpf2eb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2ad1z1blg6pw68vpf2eb.png" alt="Jaxon DbAdmin query builder table edit" width="799" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When editing row data or table definition, the corresponding SQL query can be displayed for prior validation, and even copied to the Query Editor.&lt;/p&gt;
&lt;h4&gt;
  
  
  The foreign keys
&lt;/h4&gt;

&lt;p&gt;In the paginated list of rows of a table, when a column is a foreign key, each value is displayed in a link. A click on that link will move the page to the referenced table, and add a filter on the referenced column and value, so only the referenced row is showed.&lt;/p&gt;

&lt;p&gt;When a table has one or more foreign keys, a toggle button labelled &lt;code&gt;Foreigns&lt;/code&gt; is added on the page. When this button is enabled, the values from the first column with a string type in the referenced table are fetched and displayed instead of the actual column values.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqaxiwpx5udr3vnspamt1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqaxiwpx5udr3vnspamt1.png" alt="Jaxon DbAdmin query builder foreigns" width="799" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The SQL queries for fetching the referenced values can be customized in the &lt;a href="https://github.com/lagdo/jaxon-dbadmin/wiki/04.-Configuration-files" rel="noopener noreferrer"&gt;foreigns.php&lt;/a&gt; config file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'dbadmin-mariadb'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'employees'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'employees'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'emp_no'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="s1"&gt;'select'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$textLength&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"SUBSTR(CONCAT(first_name, ' ', last_name), 1, &lt;/span&gt;&lt;span class="nv"&gt;$textLength&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s1"&gt;'search'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$search&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"LOWER(first_name) LIKE &lt;/span&gt;&lt;span class="nv"&gt;$search&lt;/span&gt;&lt;span class="s2"&gt; OR LOWER(last_name) LIKE &lt;/span&gt;&lt;span class="nv"&gt;$search&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Read the &lt;a href="https://github.com/lagdo/jaxon-dbadmin/wiki/09.-Foreign-fields-in-tables" rel="noopener noreferrer"&gt;foreign fields documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  File storage for database import and export
&lt;/h4&gt;

&lt;p&gt;The database import and export function rely on the &lt;a href="https://flysystem.thephpleague.com" rel="noopener noreferrer"&gt;FlySystem&lt;/a&gt; package for file storage. They are then able to store the SQL files on a any of the supported storage types.&lt;/p&gt;

&lt;p&gt;The SQL files locations are user dependent, which means a user cannot get access to another user files from his account, event when knowing its name. This also apply to the file URL for database export. It is valid only for the user who made the export.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Read the &lt;a href="https://github.com/lagdo/jaxon-dbadmin/wiki/10.-Data-import-and-export" rel="noopener noreferrer"&gt;data import and export documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Input streaming in database import
&lt;/h4&gt;

&lt;p&gt;Generally, the imported files are loaded into the application memory before the SQL queries they contain are executed. The application then needs to have enough available memory to be able to store the entire file. In Adminer for example, the PHP memory limit is updated when processing the imported file.&lt;/p&gt;

&lt;p&gt;Jaxon DbAdmin introduces a new Query Processor, with a new file processing model where the file lines are streamed in input, and the corresponding queries are streamed in output. This results in a dramatic reduction in memory usage, since only a few input lines and a single SQL query are kept in memory at the same same time.&lt;/p&gt;

&lt;p&gt;This screenshot shows how the lines stream in and the queries stream out the Query Processor, on a few lines file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpmqzv0pfq8kx8u7lfh00.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpmqzv0pfq8kx8u7lfh00.png" alt="Jaxon DbAdmin query splitter" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While sequential execution within the new Query Processor can increase total execution time, this challenge is easily solved by modern PHP architectures. By leveraging framework workers, time-consuming queries can be handled asynchronously. This feature will be introduced in an upcoming release.&lt;/p&gt;

&lt;h4&gt;
  
  
  Audit logging
&lt;/h4&gt;

&lt;p&gt;Jaxon DbAdmin can be configured to save all the executed queries in a dedicated database. These queries can then be visualized in a separate audit logging page with restricted access.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6vnaigcngwjg7dk6fhom.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6vnaigcngwjg7dk6fhom.png" alt="Jaxon DbAdmin audit logs" width="799" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The latest releases feature an extension of the data Jaxon DbAdmin records in the audit logs. They now include the client IP, the user agent, and generated ids for the HTTP session and request, the database driver and options, the query code, result, timestamp and duration, the error code and message when the query execution fails, the number of returned or affected rows when the query execution succeeds, and of course, the user who executed the query.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: some of the fields still need to be filled or displayed in the audit logs page.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The new application architecture
&lt;/h3&gt;

&lt;p&gt;Although running behind the scenes, the new application architecture is the foundation designed to make the implementation of all other features both simple and reliable.&lt;/p&gt;

&lt;h4&gt;
  
  
  The database drivers
&lt;/h4&gt;

&lt;p&gt;Since its early releases, the database features are implemented in separate packages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/lagdo/dbadmin-driver" rel="noopener noreferrer"&gt;https://github.com/lagdo/dbadmin-driver&lt;/a&gt;: common classes and interfaces for the database drivers.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/lagdo/dbadmin-driver-pgsql" rel="noopener noreferrer"&gt;https://github.com/lagdo/dbadmin-driver-pgsql&lt;/a&gt;: the database driver for PostgreSQL.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/lagdo/dbadmin-driver-mysql" rel="noopener noreferrer"&gt;https://github.com/lagdo/dbadmin-driver-mysql&lt;/a&gt;: the database driver for MySQL and MariaDB.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/lagdo/dbadmin-driver-sqlite" rel="noopener noreferrer"&gt;https://github.com/lagdo/dbadmin-driver-sqlite&lt;/a&gt;: the database driver for SQLite.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Today, the drivers have evolved to a simpler model where their features are separated into query generation and query execution. The query execution part takes a raw query as input, passes it to the database server, and, depending on the query type and the execution results, returns the appropriate set of data.&lt;/p&gt;

&lt;p&gt;With the exception of a few specific cases, the DDL, DML and DQL functions now generate and return a set of SQL queries, which will then be executed in a separate call. Prior to execution, the generated query can be presented to the user for validation, editing, or even storage for future reuse.&lt;/p&gt;

&lt;p&gt;The usage of DTOs has also been generalized. Instead of arrays or strings, the drivers functions now receive DTOs as input and returns other DTOs as output.&lt;/p&gt;

&lt;p&gt;As stated before, the database features come from Adminer. It means that the actual calls to the database servers, for example to read the list of tables in a database, still run code inherited from Adminer. That code is eventually improved to adapt to PHP 8 changes, and return data in DTOs instead of arrays.&lt;/p&gt;

&lt;h4&gt;
  
  
  The database support layer
&lt;/h4&gt;

&lt;p&gt;On top of the database drivers, a pluggable support layer is introduced, with many new features. The timer for query duration computation and audit logs storage are modules that are plugged into the driver proxy, and automatically called before and after every query execution. They take the query SQL code and results as parameters.&lt;/p&gt;

&lt;p&gt;In addition to the drivers DTOs, another set of DTOs is provided to carry the data between the UI components and the driver. So a database function now takes the user data as input in a UI DTO, converts it to a driver DTO, calls one or more driver functions, and converts the output to another set of UI DTOs. As a result, from the UI to the driver, every Jaxon DbAdmin feature processes well-defined and strongly-typed data structures, which significantly boosts reliability.&lt;/p&gt;

&lt;p&gt;At the core of the query execution feature, a new QueryProcessor class is introduced, serving as the only interface for query execution. It takes a set of queries in a DTO as input, calls the driver functions, and returns the results as a set of DTOs. It also accepts options allowing callers to decide whether to log queries or compute execution duration. In such cases, the corresponding features are enabled in the classes where they are implemented.&lt;/p&gt;

&lt;p&gt;Another notable change is the QueryStream class, which allows the database import feature to stream uploaded file lines instead of loading them all at once into memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Oncoming features
&lt;/h3&gt;

&lt;h4&gt;
  
  
  AI assistant
&lt;/h4&gt;

&lt;p&gt;The AI assistant is on top of the next releases feature list.&lt;br&gt;
Two interaction modes are planned. The &lt;code&gt;Ask mode&lt;/code&gt; where the AI assistant can suggest SQL queries without actually executing any of them, and the &lt;code&gt;Agent mode&lt;/code&gt; where it can also execute queries.&lt;/p&gt;

&lt;p&gt;The Jaxon DbAdmin drivers are able to return detailed data about the database tables and columns. Combined with the Query Processor unique features, there form a solid basis for the AI assistant implementation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Enhanced query builder
&lt;/h4&gt;

&lt;p&gt;Currently, the Query Builder builds select queries only for a single table. In the future releases, it will be able to query multiple tables with joins, and convert complex select queries from SQL code to its internal data structure.&lt;/p&gt;

&lt;h4&gt;
  
  
  UI templates
&lt;/h4&gt;

&lt;p&gt;The application UI is built with the &lt;a href="https://github.com/lagdo/ui-builder" rel="noopener noreferrer"&gt;UI Builder&lt;/a&gt; library. Three Bootstrap 5 templates are currently available, built from the open source &lt;a href="https://github.com/startbootstrap/startbootstrap-sb-admin" rel="noopener noreferrer"&gt;SbAdmin&lt;/a&gt;, &lt;a href="https://coreui.io" rel="noopener noreferrer"&gt;CoreUi 5&lt;/a&gt; and &lt;a href="https://plainadmin.com" rel="noopener noreferrer"&gt;Plain Admin&lt;/a&gt; dashboard templates.&lt;/p&gt;

&lt;p&gt;Other CSS frameworks are planned to be supported by the &lt;a href="https://github.com/lagdo/ui-builder" rel="noopener noreferrer"&gt;UI Builder&lt;/a&gt; library, including TailwindCSS and Bulma. So in addition to Bootstrap 5, other templates based on these frameworks will be available in the future releases.&lt;/p&gt;

&lt;p&gt;Integrating a new admin dashboard is easy, since it doesn't require any changes to the application UI components code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Closing thoughts
&lt;/h3&gt;

&lt;p&gt;The latest releases of Jaxon DbAdmin introduce an extensive array of new features and improvements. On top of the existing database driver packages, a new engine for query generation and execution has been deployed, boosting reliability thanks to the use of DTOs. On the UI/UX front, the usability of every function has been significantly enhanced.&lt;/p&gt;

&lt;p&gt;With its current roadmap, Jaxon DbAdmin will soon match the feature set of the market's most advanced solutions, such as &lt;a href="https://dbeaver.io" rel="noopener noreferrer"&gt;DBeaver&lt;/a&gt;, &lt;a href="https://www.dbgate.io" rel="noopener noreferrer"&gt;DbGate&lt;/a&gt;, &lt;a href="https://www.bytebase.com" rel="noopener noreferrer"&gt;ByteBase&lt;/a&gt;, and the more recent &lt;a href="https://github.com/TabularisDB/tabularis" rel="noopener noreferrer"&gt;Tabularis&lt;/a&gt; and &lt;a href="https://github.com/t8y2/dbx" rel="noopener noreferrer"&gt;DBx&lt;/a&gt;. It will even offer a superior alternative in certain areas, such as its database credentials management and native support for multiple secret managers. While alternative tools often restrict advanced features to paid plans, Jaxon DbAdmin offers all its functionalities completely for free.&lt;/p&gt;

&lt;p&gt;Unlike these desktop-centric alternatives, Jaxon DbAdmin is a pure web application written in PHP, like &lt;a href="https://adminer.org" rel="noopener noreferrer"&gt;Adminer&lt;/a&gt; and &lt;a href="https://www.phpmyadmin.net" rel="noopener noreferrer"&gt;PhpMyAdmin&lt;/a&gt;. It therefore combines the security and centralized advantages of a server-side web application with the usability of a desktop client.&lt;/p&gt;

</description>
      <category>database</category>
      <category>opensource</category>
      <category>php</category>
      <category>software</category>
    </item>
    <item>
      <title>A real world case of recursion done right in PHP</title>
      <dc:creator>Thierry Feuzeu</dc:creator>
      <pubDate>Sun, 20 Sep 2026 09:51:49 +0000</pubDate>
      <link>https://dev.to/tiaya/a-real-world-case-of-recursion-done-right-in-php-428k</link>
      <guid>https://dev.to/tiaya/a-real-world-case-of-recursion-done-right-in-php-428k</guid>
      <description>&lt;p&gt;Nick Cosentino &lt;a href="https://x.com/DevLeaderCa" rel="noopener noreferrer"&gt;@DevLeaderCa&lt;/a&gt; posted this message about recursion on X, ex Twitter, in October 2025, which received over 460 replies.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1984123215170421060-448" src="https://platform.twitter.com/embed/Tweet.html?id=1984123215170421060"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1984123215170421060-448');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1984123215170421060&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;I quote an excerpt: &lt;em&gt;I've found that converting over to an iterative loop based approach is almost always more readable and easier to debug&lt;/em&gt;. He explains that in over 20 years of programming, he has never used recursion, thus questioning its usefulness.&lt;/p&gt;

&lt;p&gt;In this article, I show a real case where I used recursion, and where I think that it is much simpler than its iterative counterpart.&lt;/p&gt;

&lt;p&gt;But before going any further, let see what makes a recursive algorithm powerful.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fibonacci sequence
&lt;/h2&gt;

&lt;p&gt;To understand the value of recursion, let's look at the implementations with and without recursion of an algorithm very often used to illustrate it: the Fibonacci sequence.&lt;/p&gt;

&lt;p&gt;Here's the recursive algorithm.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if n = 0 or n = 1 then
    fib(n) = 1
else
    fib(n) = fib(n − 1) + fib(n − 2)
endif
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's the iterative one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if n = 0 or n = 1 then
    fib(n) = 1
else
    a = 1
    b = 1
    for i in 2 .. n do
        c = a + b
        a = b
        b = c
    endfor
    fib(n) = c
endif
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simple example demonstrates the advantages of recursion: no loops and no intermediate variables.&lt;/p&gt;

&lt;p&gt;This also gives us an indication of when recursion is advantageous, namely when the function can be defined with a simple expression depending on itself. This might not be the case for example when the data being processed is a graph instead of a tree.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our problem
&lt;/h2&gt;

&lt;p&gt;The library we'll be talking about is the &lt;a href="https://github.com/lagdo/ui-builder" rel="noopener noreferrer"&gt;PHP UI Builder&lt;/a&gt;. It builds application UIs using components, as defined by CSS frameworks. It provides a common API which defines the components, and from which specialized libraries can generate HTML code for a given CSS framework, like Bootstrap, Bulma or Tailwind CSS.&lt;/p&gt;

&lt;h3&gt;
  
  
  The UI components
&lt;/h3&gt;

&lt;p&gt;With its hierarchical component structure, a UI is particularly well-suited to a recursive algorithm. A component has properties, but can also contain components that themselves have some properties and can contain other components, and so on.&lt;/p&gt;

&lt;h3&gt;
  
  
  The virtual components
&lt;/h3&gt;

&lt;p&gt;When building a UI from data, the result can sometimes vary depending on their values. Some components will only be created under certain conditions, others are multiplied according to the entries in a list or loop, and others may be an aggregate of different components.&lt;/p&gt;

&lt;p&gt;To represent these cases, our library introduces the concept of virtual components. These are components that take a set of data as input and produce as output a set of zero or more components, which may or may not be virtual.&lt;/p&gt;

&lt;p&gt;There are 4 of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;When&lt;/strong&gt;: takes a condition as input and generates a component if it is met.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Each&lt;/strong&gt;: takes an array of data as input and generates a component for each element.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pick&lt;/strong&gt;: takes a list of conditions as input and generates a different component depending on the first one that is true.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;List&lt;/strong&gt;: aggregates any set of components into one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A UI will ultimately consist of a tree structure of components, each of which can be virtual or real. A real component can have several children, each of which can be virtual or real, and a virtual component generates a list of components, each of which can be virtual or real.&lt;/p&gt;

&lt;p&gt;Here's an example of a function using the UI library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cd"&gt;/**
 * @param array $collations
 *
 * @return string
 */&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;addDbForm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$collations&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;label&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Name'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;setType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'text'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$collations&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;setType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'hidden'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'collation'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$collations&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;label&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Collation'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'collation'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'(collation)'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$collations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_collations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$group&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
                                &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$group&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
                                        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_collations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$collation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
                                            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$collation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                        &lt;span class="p"&gt;)&lt;/span&gt;
                                    &lt;span class="p"&gt;),&lt;/span&gt;
                                    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
                                        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;optgroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_collations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$collation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
                                                &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$collation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                            &lt;span class="p"&gt;)&lt;/span&gt;
                                        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$group&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                    &lt;span class="p"&gt;)&lt;/span&gt;
                                &lt;span class="p"&gt;)&lt;/span&gt;
                            &lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'collation'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;width&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The processing algorithm
&lt;/h3&gt;

&lt;p&gt;To generate a UI from a component tree, we will use the following algorithm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function build(c)
    children = []
    expand(c, children)

    c.elements = []
    for child in children
        if child is a component then
            build(child)
            append child.elements to c.elements
        else
            append child to c.elements
        endif
    endfor
endfunction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function expand(c, children)
    if c is virtual then
        for child in c.children
            expand(child, children)
        endfor
    else
        append c to children
    endif
endfunction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we have here is actually a combination of two recursive functions.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;expand()&lt;/code&gt; function returns the list of real components generated by a virtual component. It will therefore iterate recursively over all its children as long as virtual components are present.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;build()&lt;/code&gt; function, which is the entry point for the process, first calls the &lt;code&gt;expand()&lt;/code&gt; function. Its list of children then consists only of real components, on which it executes recursively.&lt;/p&gt;

&lt;p&gt;The final result is in &lt;a href="https://github.com/lagdo/ui-builder-html/blob/main/src/Builder/Scope.php" rel="noopener noreferrer"&gt;the Scope class&lt;/a&gt;. In their PHP implementation, the recursive &lt;code&gt;expand()&lt;/code&gt; function is 15 lines of code long, and the &lt;code&gt;build()&lt;/code&gt; function is 40.&lt;/p&gt;

&lt;p&gt;Two extremely simple functions, which form the basis that allows the implementation in a single library of both the templating functionalities of libraries such as Twig or Blade, and the HTML code generation of libraries such as Spatie's &lt;a href="https://spatie.be/docs/laravel-html/v3/general-usage/html-builder" rel="noopener noreferrer"&gt;HTML Builder&lt;/a&gt; or Symfony's &lt;a href="https://symfony.com/doc/current/forms.html" rel="noopener noreferrer"&gt;Forms component&lt;/a&gt; (the rendering part).&lt;/p&gt;

&lt;p&gt;The UI library is used to build the &lt;a href="https://github.com/lagdo/jaxon-dbadmin/tree/main/app/Ui" rel="noopener noreferrer"&gt;Jaxon DbAdmin UI&lt;/a&gt;, demonstrating that it allows the construction of complex UIs, calling classes and their methods instead of writing HTML templates.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo4rca0rouvf652rsj13n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo4rca0rouvf652rsj13n.png" alt="An overview of the Jaxon DbAdmin UI" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The limitations of recursion
&lt;/h2&gt;

&lt;p&gt;Nick Cosentino's message rightly points out two drawbacks of recursion, which we will briefly discuss here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Debugging
&lt;/h3&gt;

&lt;p&gt;First, it's absolutely true that recursion is difficult to debug, particularly because mentally unraveling the instructions executed by a recursive algorithm is very complex. However, at the same time, the simplicity of recursive algorithms leaves little chance for a bug to occur.&lt;/p&gt;

&lt;p&gt;Therefore, before choosing recursion, it's important to ensure that the algorithm being implemented can be expressed very simply in a recursive manner. This is indeed the case for the Fibonacci sequence and our library.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory usage
&lt;/h3&gt;

&lt;p&gt;Implementing a recursive algorithm can consume a significant amount of memory, depending on the level of nesting of the calls.&lt;br&gt;
For our UI library, the level of nesting of an application's UI components will always be very limited, and even more if it is built one part at a time, each in a separate recursive function call.&lt;/p&gt;

&lt;p&gt;Moreover, PHP imposes a limit on the number of nested calls within a recursive function, and the consequence of an overflow will be to fail a single call, not to crash the entire server.&lt;/p&gt;

&lt;h2&gt;
  
  
  The closing&amp;nbsp;word
&lt;/h2&gt;

&lt;p&gt;To answer Nick Cosentino's initial question, of course it is possible to create all sorts of complex software without using recursion.&lt;/p&gt;

&lt;p&gt;However, this doesn't mean it can always be replaced by an iterative version, but rather that cases where one can fully leverage all its advantages are not very common.&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>ui</category>
      <category>php</category>
      <category>recursion</category>
    </item>
    <item>
      <title>What if we improve the way developers are given access to databases</title>
      <dc:creator>Thierry Feuzeu</dc:creator>
      <pubDate>Fri, 03 Oct 2025 18:00:41 +0000</pubDate>
      <link>https://dev.to/tiaya/what-if-we-improve-the-way-developers-are-given-access-to-databases-3n0i</link>
      <guid>https://dev.to/tiaya/what-if-we-improve-the-way-developers-are-given-access-to-databases-3n0i</guid>
      <description>&lt;p&gt;Developers often need direct access to the databases used by the applications they work on, in virtually every environment of a project: development, testing, pre-production, and sometimes even in production.&lt;/p&gt;

&lt;p&gt;To do this, they use software such as &lt;a href="https://www.adminer.org/" rel="noopener noreferrer"&gt;Adminer&lt;/a&gt;, &lt;a href="https://www.phpmyadmin.net/" rel="noopener noreferrer"&gt;PhpMyAdmin&lt;/a&gt;, &lt;a href="https://dbeaver.io/" rel="noopener noreferrer"&gt;DBeaver&lt;/a&gt;, and many others, which share a common feature: they authenticate their users with database access parameters.&lt;br&gt;
This pushes teams within companies to share database access credentials with developers, which, in the best-case scenario, is done with a &lt;code&gt;password manager&lt;/code&gt; software.&lt;/p&gt;

&lt;p&gt;This sharing of database credentials can pose several risks.&lt;br&gt;
Accidental leaks of sensitive information, if a developer inadvertently shares this data with third parties.&lt;br&gt;
Reuse of credentials in inappropriate environments.&lt;br&gt;
Uncontrolled and untraceable access, due to the sharing of this information with multiple developers.&lt;br&gt;
Access by unauthorized individuals, when developers leave the company.&lt;/p&gt;

&lt;p&gt;Enforcing good security practices under these conditions will be difficult.&lt;/p&gt;

&lt;h3&gt;
  
  
  Jaxon DbAdmin
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Jaxon DbAdmin&lt;/strong&gt; is a web-based database management application that takes a different approach to user authentication and database connection.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foo8j4tl5xgp54si1fvul.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foo8j4tl5xgp54si1fvul.png" alt="Jaxon DbAdmin dashboard" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The application clearly separates the authentication of its users and the connections to databases. The parameters of the later are stored only in its configuration file on the server.&lt;/p&gt;

&lt;p&gt;When a user logs in, &lt;strong&gt;Jaxon DbAdmin&lt;/strong&gt; reads the list of databases they have access to from its configuration and presents it to them.&lt;br&gt;
And when the user performs an action on a database, &lt;strong&gt;Jaxon DbAdmin&lt;/strong&gt; reads the corresponding database credentials from its configuration, executes the action, and returns the result.&lt;br&gt;
This allows the user to access the database contents without having access to the database credentials.&lt;/p&gt;

&lt;p&gt;It's worth noting that this database access technique isn't exactly new.&lt;br&gt;
On the one hand, applications that use databases already store database credentials this way, and on the other, there are already database management applications that authenticate their users elsewhere than in the database.&lt;br&gt;
&lt;strong&gt;Jaxon DbAdmin&lt;/strong&gt; combines these two approaches to make it its default mode.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Jaxon DbAdmin&lt;/strong&gt; is an open source software distributed under the BSD-3-Clause license: &lt;a href="https://github.com/lagdo/dbadmin-app" rel="noopener noreferrer"&gt;https://github.com/lagdo/dbadmin-app&lt;/a&gt;.&lt;br&gt;
A Docker image is also provided: &lt;a href="https://hub.docker.com/r/lagdo/jaxon-dbadmin" rel="noopener noreferrer"&gt;https://hub.docker.com/r/lagdo/jaxon-dbadmin&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Jaxon DbAdmin&lt;/strong&gt; is still actively developed, and some features are not yet available.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The advantages
&lt;/h3&gt;

&lt;p&gt;Storing database credentials on the server only enhances security.&lt;br&gt;
Developers cannot copy, transfer, or expose credentials, even by mistake.&lt;br&gt;
Each request can be linked to a specific user or role, making it easy to audit who did what, and when.&lt;br&gt;
Login credentials are no longer scattered across development workstations, in configuration files, emails, or other communication tools. They can even be secured in a vault.&lt;/p&gt;

&lt;p&gt;Connection configuration is now managed at the platform or infrastructure level, not in the application code. This makes it easier to manage environments (dev, staging, production).&lt;br&gt;
Since users don't have access to the database credentials, they can be changed regularly without disrupting applications or developers.&lt;br&gt;
Similarly, if the credentials are compromised, the administrator won't have to change them on developer workstations. This is done in a single location, transparently for applications and users.&lt;/p&gt;

&lt;p&gt;There will also be better permission management based on the principle of least privilege: each user only has access to what he needs, regardless of the database credentials actually used.&lt;br&gt;
If a developer leaves, his access to the application can simply be revoked, without having to change the database password and impact all other applications and users.&lt;/p&gt;

&lt;p&gt;Finally, many security standards require strict data access management.&lt;br&gt;
Not sharing database credentials makes it easier to comply with these requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  The challenges
&lt;/h3&gt;

&lt;p&gt;Despite the above advantages, the use of existing tools that offer separate authentication is not common enough because it faces several challenges.&lt;/p&gt;

&lt;p&gt;Setting up and using a secrets management system (vault) or an API Gateway with the necessary permissions is complex. For a small team or on certain projects, this represents a significant overhead in terms of time, skills, and budget.&lt;br&gt;
On the other hand, tools such as &lt;a href="https://dbeaver.io/" rel="noopener noreferrer"&gt;DBeaver&lt;/a&gt; (Community) and &lt;a href="https://www.adminer.org/" rel="noopener noreferrer"&gt;Adminer&lt;/a&gt; are free and open-source.&lt;/p&gt;

&lt;p&gt;Certain complex tasks (analyzing indexes, optimizing queries, performing large-scale exports/imports) are much simpler with a graphical tool than with a command line or REST API.&lt;br&gt;
The same is true for analyzing the structure of a complex database with many tables.&lt;br&gt;
The user-friendliness of the graphical interface of tools like &lt;a href="https://dbeaver.io/" rel="noopener noreferrer"&gt;DBeaver&lt;/a&gt; or &lt;a href="https://www.adminer.org/" rel="noopener noreferrer"&gt;Adminer&lt;/a&gt; is a definite advantage.&lt;/p&gt;

&lt;p&gt;Experienced developers sometimes like to have direct control over the database.&lt;br&gt;
They appreciate the speed of an &lt;em&gt;ad hoc&lt;/em&gt; SQL query to explore a database schema, understand relationships between tables, or test complex logic before writing it in code.&lt;br&gt;
For occasional manipulations, connecting with a graphical tool seems faster.&lt;/p&gt;

&lt;p&gt;It is difficult to replicate an environment where direct access to the database is prohibited on a development workstation.&lt;br&gt;
Developers then need a local database to code on, and in this case, access security isn't a priority.&lt;br&gt;
&lt;a href="https://www.adminer.org/" rel="noopener noreferrer"&gt;Adminer&lt;/a&gt; or &lt;a href="https://dbeaver.io/" rel="noopener noreferrer"&gt;DBeaver&lt;/a&gt; are ideal for this, and the temptation is great to use the same tool for all environments.&lt;/p&gt;

&lt;p&gt;Many developers have always worked with direct database access.&lt;br&gt;
In many companies, sharing database access credentials is a historical and culturally entrenched practice. Changing this requires shifting processes and mindsets.&lt;br&gt;
Furthermore, not all developers are aware of the risks or alternative best practices.&lt;/p&gt;

&lt;h3&gt;
  
  
  In conclusion
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Jaxon DbAdmin&lt;/strong&gt; is a web-based database management application that separates user authentication and database connections, meaning that database credentials are not shared with users accessing them.&lt;br&gt;
Its configuration allows the administrator to define credentials for each user or group of users.&lt;/p&gt;

&lt;p&gt;It will therefore improve the security of access to databases, while maintaining the user-friendliness and simplicity of a graphical interface.&lt;/p&gt;

&lt;p&gt;Its code comes &lt;a href="https://github.com/vrana/adminer/" rel="noopener noreferrer"&gt;from Adminer&lt;/a&gt;, which has been refactored and modernized. Although its UI has also been modernized, it offers similar functions as &lt;a href="https://www.adminer.org/" rel="noopener noreferrer"&gt;Adminer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However, &lt;strong&gt;Jaxon DbAdmin&lt;/strong&gt; is still actively developed, and some features are not yet available.&lt;/p&gt;

</description>
      <category>database</category>
      <category>security</category>
      <category>usermanagement</category>
      <category>webapp</category>
    </item>
    <item>
      <title>Announcing Jaxon version 5</title>
      <dc:creator>Thierry Feuzeu</dc:creator>
      <pubDate>Thu, 25 Sep 2025 17:44:56 +0000</pubDate>
      <link>https://dev.to/tiaya/announcing-jaxon-version-5-2m6k</link>
      <guid>https://dev.to/tiaya/announcing-jaxon-version-5-2m6k</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This post was originally published on the &lt;a href="https://www.jaxon-php.org/blog/2025/07/announcing-jaxon-version-5.html" rel="noopener noreferrer"&gt;Jaxon blog&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After more than two years of development, the version 5 of the Jaxon library is finally out!&lt;/p&gt;

&lt;p&gt;This release is an important step that I think will bring the Jaxon library to the next level, because it implements the features I hoped the library had when I started developing and using it more than ten years ago.&lt;/p&gt;

&lt;p&gt;So what's new in this release?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exported PHP classes are now called components, and they can be of three different types. Some components can be attached to DOM nodes, and used to set their content.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;call factories&lt;/code&gt; in PHP have been enriched, and now support calls to Javascript functions and event handlers, as well as their parameters.&lt;/li&gt;
&lt;li&gt;The command names and formats in responses are now more meaningful, and their processing in Javascript is improved. Deprecated commands have also been removed.&lt;/li&gt;
&lt;li&gt;The dialog libraries now contain only Javascript code, in a single and tiny object. They will therefore be much easier to create and maintain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now let's dive more in depth into each of these new features.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Jaxon components
&lt;/h3&gt;

&lt;p&gt;In the previous versions of Jaxon, the webpage content was set exclusively using the HTML &lt;code&gt;id&lt;/code&gt; attribute.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'dom-element-id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each DOM element with dynamic content in a webpage content needed to be given a unique id, which will then be used to update its content. Keeping track of all those ids in an application was not an easy task.&lt;/p&gt;

&lt;p&gt;Jaxon 5 introduces a new &lt;a href="https://www.jaxon-php.org/docs/v5x/components/node-components.html" rel="noopener noreferrer"&gt;UI component&lt;/a&gt;, which is a Jaxon class that defines the content of a DOM element.&lt;br&gt;
It just needs to implement a method named &lt;code&gt;html()&lt;/code&gt;,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Jaxon\Demo\Calc\App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Calc&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;\Jaxon\App\NodeComponent&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;\Stringable&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'jaxon::demo::calc::calc'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and to be bound to a DOM element in a webpage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="cp"&gt;&amp;lt;?=&lt;/span&gt; &lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Jaxon\Demo\Calc\App\Calc&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;render()&lt;/code&gt; method in the component can then be used to set the DOM element content, for example with an event handler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-primary"&lt;/span&gt; &lt;span class="cp"&gt;&amp;lt;?=&lt;/span&gt;
        &lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Jaxon\Demo\Calc\App\Calc&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Clear&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, a click on the button will display the &lt;code&gt;jaxon::demo::calc::calc&lt;/code&gt; view in the &lt;code&gt;div&lt;/code&gt; element the UI component is bound to.&lt;/p&gt;

&lt;p&gt;The new components now make it very easy to build complex UI with Jaxon.&lt;/p&gt;

&lt;h3&gt;
  
  
  The call and selector factories
&lt;/h3&gt;

&lt;p&gt;Since one of the core features of Jaxon is to write UI components code in PHP on the server, it is important to be able to make calls to Javascript functions, define event handlers, and last but not the least, pass the contents of the webpage as parameters to those calls.&lt;br&gt;
That's the role of the &lt;code&gt;call factories&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;call factories&lt;/code&gt; have been around since the version 2, initially as the &lt;a href="https://www.jaxon-php.org/docs/v2x/advanced/jquery.html" rel="noopener noreferrer"&gt;JQuery PHP API&lt;/a&gt;.&lt;br&gt;
They have evolved a lot since then, and now they can make calls to the exported Jaxon classes, or to any Javascript function or object.&lt;br&gt;
They can also define event handlers on the webpage elements using Javascript or JQuery-style selectors.&lt;br&gt;
Thirdly, they also provides functions to pass any content of the webpage as parameter to Javascript calls.&lt;/p&gt;

&lt;p&gt;In this code from the Jaxon examples, JQuery selectors are used in a Jaxon custom HTML attribute to define calls to Jaxon classes as event handlers on two select lists, and to pass the selected item value as parameter to the handler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row"&lt;/span&gt; &lt;span class="cp"&gt;&amp;lt;?=&lt;/span&gt; &lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.app-color-choice'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'change'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AppTest&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;jq&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;val&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.ext-color-choice'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'change'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ExtTest&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;jq&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;val&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this other example, a command is added in a response to make a call to the &lt;code&gt;Tontine.makeTableResponsive('content-planning-charge-page')&lt;/code&gt; Javascript code in the browser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;jo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Tontine'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;makeTableResponsive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'content-planning-charge-page'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When building an application UI on the server, being able to make Javascript calls and set event handlers in a webpage is paramount, and the &lt;code&gt;call factories&lt;/code&gt; provide Jaxon with a powerful set of features to achieve that quite easily.&lt;/p&gt;

&lt;h3&gt;
  
  
  Javascript library rewrite
&lt;/h3&gt;

&lt;p&gt;The Javascript library rewrite started as a discusion on Github about &lt;a href="https://github.com/jaxon-php/jaxon-js/issues/21" rel="noopener noreferrer"&gt;the forbidden usage of the eval function&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I then decided to rewrite the Javascript library, which today has resulted in many important changes.&lt;/p&gt;

&lt;p&gt;The library now includes a command processor which executes existing Javascript functions from a JSON payload. With this processor, there's no need for the &lt;code&gt;eval&lt;/code&gt; function anymore, and it is also at the foundation of the &lt;code&gt;call factories&lt;/code&gt; described above.&lt;/p&gt;

&lt;p&gt;The library also includes an attribute processor, which handles the Jaxon custom attributes needed to implement the UI components bindings and the event handlers.&lt;br&gt;
In the example above where the &lt;code&gt;Jaxon\Demo\Calc\App\Calc&lt;/code&gt; UI Component is bound to a &lt;code&gt;div&lt;/code&gt; element, the following HTML code is actually generated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;jxn-bind=&lt;/span&gt;&lt;span class="s"&gt;"Jaxon.Demo.Calc.App.Calc"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
...
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://www.jaxon-php.org/docs/v5x/features/responses.html" rel="noopener noreferrer"&gt;response commands&lt;/a&gt; were also rewritten with new names and a new JSON format. Their names are now more meaningful, and the obsolete ones were removed.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Dialog plugin
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://www.jaxon-php.org/docs/v5x/ui-features/dialogs.html" rel="noopener noreferrer"&gt;Dialog plugin&lt;/a&gt; is a special case in the Jaxon universe. It is a Jaxon response plugin, but at the same time it can also be extended with it own plugins.&lt;/p&gt;

&lt;p&gt;In the previous version, adding a dialog plugin required to write functions in both PHP and Javascript.&lt;br&gt;
In the v5 release, only a tiny Javascript object is now necessary.&lt;/p&gt;

&lt;p&gt;This is for example the code of the &lt;a href="https://notifyjs.jpillora.com/" rel="noopener noreferrer"&gt;Notify&lt;/a&gt; library integration, which implements the alert function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="n"&gt;jaxon&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dom&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;jaxon&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dialog&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'notify'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Dialogs options&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;alert&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;alertOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;xTypes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'success'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'info'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;warning&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'warn'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'error'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * Show an alert message
     *
     * @param {object} alert The alert parameters
     * @param {string} alert.type The alert type
     * @param {string} alert.message The alert message
     *
     * @returns {void}
     */&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="n"&gt;alertOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;className&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;xTypes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="n"&gt;xTypes&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"top center"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only feature in PHP classes is to load the Javascript files into the webpage.&lt;/p&gt;

&lt;h3&gt;
  
  
  The examples
&lt;/h3&gt;

&lt;p&gt;A &lt;a href="https://www.jaxon-php.org/docs/v5x/about/example.html" rel="noopener noreferrer"&gt;new example&lt;/a&gt; is introduced to illustrate the Jaxon operation.&lt;/p&gt;

&lt;p&gt;The example now implements a simple calculator, which takes an operation and two operands as parameters, performs the calculation using an injected service, and displays the result.&lt;br&gt;
There are two text zones and a select combo for the inputs, and the result is displayed in a readonly text zone.&lt;/p&gt;

&lt;p&gt;The example is built with the new Jaxon UI components, and also released as a &lt;a href="https://github.com/jaxon-php/jaxon-demo-calc" rel="noopener noreferrer"&gt;Jaxon package&lt;/a&gt;.&lt;br&gt;
This package is used in the &lt;a href="https://github.com/jaxon-php/jaxon-demo-laravel" rel="noopener noreferrer"&gt;Laravel&lt;/a&gt;, &lt;a href="https://github.com/jaxon-php/jaxon-demo-symfony" rel="noopener noreferrer"&gt;Symfony&lt;/a&gt; and &lt;a href="https://github.com/jaxon-php/jaxon-demo-slim" rel="noopener noreferrer"&gt;Slim Framework&lt;/a&gt; demo applications, to show how the Jaxon library can be used to build a cross-framework and full-featured package including both the frontend and backend features.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/lagdo/dbadmin-mono" rel="noopener noreferrer"&gt;DB Admin&lt;/a&gt; package and the &lt;a href="https://github.com/lagdo/tontine" rel="noopener noreferrer"&gt;African Tontine&lt;/a&gt; application are also upgraded to Jaxon 5.&lt;br&gt;
Although the former is still a work in progress, both are real world examples of building complex one page Ajax applications with Jaxon.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;The new Jaxon 5 release is packed with features that make it a good choice for creating complex Ajax applications with PHP on the server side.&lt;br&gt;
There are UI components to build the webpage, custom attributes to enrich the HTML code, and powerful &lt;code&gt;call factories&lt;/code&gt; to make calls to all kind of Javascript functions in PHP.&lt;/p&gt;

&lt;p&gt;In addition to the &lt;a href="https://www.jaxon-php.org/docs.html" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;, there are &lt;a href="https://github.com/jaxon-php/jaxon-examples" rel="noopener noreferrer"&gt;many examples&lt;/a&gt; in PHP or with framework integration, and real world open source applications using the library.&lt;/p&gt;

</description>
      <category>php</category>
      <category>ajax</category>
      <category>uicomponents</category>
      <category>releaseannouncement</category>
    </item>
  </channel>
</rss>
