db/sql_preview
< >

return function () {
    $self = url('/@object/@action/' . urlencode('db/sql_preview'));
    $preview = _class('sample_sql_preview', YF_PATH . '.dev/samples/classes/');
    $p = $preview->params($_GET);
    $fields = $preview->TABLES[$p['table']];

    $intro = '<p class="docs-demo-intro">Build a query with the query builder and see the SQL it renders for MySQL / MariaDB and for
        PostgreSQL side by side. Table, fields, operator, order and limit come from fixed lists; only the where value is free text, and it
        is escaped the way the framework expects: <code>db()->es_for_builder()</code> escapes it while
        <code>ESCAPE_BUILDER_VALUES</code> is off (the default, the builder only quotes), and the builder escapes it itself when the flag is
        on; the SQL is the same either way. Try a value like <code>O\'Reilly</code> or <code>a\\b</code>. The queries are only rendered
        with <code>->sql()</code> on database copies that have no connection, never run.
        Classes: <code>yf_db</code>, <code>yf_db_query_builder_mysql</code>, <code>yf_db_query_builder_pgsql</code>.</p>';

    $options = function (array $values, $selected, $empty = null) {
        $html = $empty !== null ? '<option value="">' . $empty . '</option>' : '';
        foreach ($values as $value => $label) {
            $html .= '<option value="' . _prepare_html((string) $value, 0) . '"' . ((string) $value === (string) $selected ? ' selected' : '') . '>' . _prepare_html((string) $label, 0) . '</option>';
        }
        return $html;
    };
    $tables = array_combine(array_keys($preview->TABLES), array_keys($preview->TABLES));
    $fields_kv = array_combine($fields, $fields);
    $checks = '';
    foreach ($fields as $f) {
        $checks .= '<label class="checkbox-inline form-check-inline"><input type="checkbox" class="form-check-input" name="fields[]" value="' . $f . '"'
            . (in_array($f, $p['fields'], true) ? ' checked' : '') . '> ' . $f . '</label> ';
    }
    $limits = [];
    foreach ($preview->LIMITS as $l) {
        $limits[$l] = $l ? $l : 'no limit';
    }
    $form = '<form method="get" action="' . $self . '" class="docs-demo-sql-form">'
        . '<div class="docs-demo-sql-row"><label>Table <select name="table" class="form-control form-select" onchange="this.form.submit()">' . $options($tables, $p['table']) . '</select></label>'
        . '<span class="docs-demo-sql-fields">Fields (none = *): ' . $checks . '</span></div>'
        . '<div class="docs-demo-sql-row"><label>Where <select name="where_field" class="form-control form-select">' . $options($fields_kv, $p['where_field'], '(no condition)') . '</select></label>'
        . '<select name="op" class="form-control form-select" aria-label="Operator">' . $options(array_combine($preview->OPS, $preview->OPS), $p['op']) . '</select>'
        . '<input type="text" name="value" class="form-control" maxlength="' . $preview->MAX_VALUE_LEN . '" placeholder="value" aria-label="Value" value="' . _prepare_html($p['value'], 0) . '"></div>'
        . '<div class="docs-demo-sql-row"><label>Order by <select name="order" class="form-control form-select">' . $options($fields_kv, $p['order'], '(none)') . '</select></label>'
        . '<select name="dir" class="form-control form-select" aria-label="Direction">' . $options(['asc' => 'ASC', 'desc' => 'DESC'], $p['dir']) . '</select>'
        . '<label>Limit <select name="limit" class="form-control form-select">' . $options($limits, $p['limit']) . '</select></label>'
        . '<label class="checkbox-inline form-check-inline"><input type="checkbox" class="form-check-input" name="escape_flag" value="1"' . ($p['escape_flag'] ? ' checked' : '') . '> ESCAPE_BUILDER_VALUES on</label>'
        . '<button type="submit" class="btn btn-primary">Show SQL</button></div></form>';

    $out = '<div class="row docs-demo-sql-out">';
    foreach (['mysql' => 'MySQL / MariaDB', 'pgsql' => 'PostgreSQL'] as $family => $label) {
        $out .= '<div class="col-md-6"><h4>' . $label . '</h4><pre class="docs-demo-sql"><code class="sql">' . _prepare_html($preview->sql($family, $p), 0) . '</code></pre></div>';
    }
    $out .= '</div>';
    $note = '';
    if ($p['where_field'] !== '' && ! $preview->where_applies($p)) {
        $note = '<div class="alert alert-warning">Values with a comma are not rendered here: <code>where()</code> splits its arguments at
            commas, so such a value belongs in <code>where_raw()</code> with <code>db()->escape_key()</code> and <code>escape_val()</code>.</div>';
    } elseif ($p['where_field'] !== '' && $p['value'] === '' && ! in_array($p['op'], ['is null', 'is not null'], true)) {
        $note = '<div class="alert alert-info">An empty value drops the condition: the builder leaves out <code>where()</code> calls whose value is empty.</div>';
    }
    css('.docs-demo-sql-form .docs-demo-sql-row { display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin-bottom:8px; }
        .docs-demo-sql-form label { font-weight:normal; margin:0; }
        .docs-demo-sql-form .form-control { display:inline-block; width:auto; }
        .docs-demo-sql-form .docs-demo-sql-fields .checkbox-inline { margin:0 6px 0 0; padding-left:0; }
        .docs-demo-sql-form label input[type=checkbox] { float:none; margin:0 3px 0 0; position:static; vertical-align:middle; }
        pre.docs-demo-sql code { white-space:pre-wrap; word-break:break-word; }');
    return $intro . $form . '<h4>PHP</h4><pre class="docs-demo-sql"><code class="php">' . _prepare_html($preview->code($p), 0) . '</code></pre>' . $note . $out;
};

Build a query with the query builder and see the SQL it renders for MySQL / MariaDB and for PostgreSQL side by side. Table, fields, operator, order and limit come from fixed lists; only the where value is free text, and it is escaped the way the framework expects: db()->es_for_builder() escapes it while ESCAPE_BUILDER_VALUES is off (the default, the builder only quotes), and the builder escapes it itself when the flag is on; the SQL is the same either way. Try a value like O'Reilly or a\b. The queries are only rendered with ->sql() on database copies that have no connection, never run. Classes: yf_db, yf_db_query_builder_mysql, yf_db_query_builder_pgsql.

Fields (none = *):

PHP

$sql = db()->from('user')
    ->sql(); // ->get_all() would run it

MySQL / MariaDB

SELECT * FROM `y_user`

PostgreSQL

SELECT * FROM "y_user"