Using JForm

Joomla's Form class loads xml form definitions and provides a bunch of tools for rendering and validating form fields. It's used for all extension xml configs and can also be used by your own component in both the front and back end.

Documentation of Available Fields: https://docs.joomla.org/Standard_form_field_types

Some other links to look at:

To actually catch the data from the request then see Using values from the Joomla Input

Creating a Basic Wrapper around JForm

If you're making adhoc forms it can be useful to make your own wrapper around jform to make it nicer to work with. A very rough implementation would be as follows.

Usage

I'm Assuming this is in a component, but you would need to fix some paths where appropriate.

Example Form

File is called text.xml (note also the name of the fields 'mydatalevel' which will be the data level name when we bind the preexisting data to the form later)

<?xml version="1.0" encoding="UTF-8"?>
<form>
    <fields name="mydatalevel">
        <fieldset name="firstset">
            <field
            type="text"
            name="title"
            id="title"
            label="Title"
            class="form-control"
            size="80"
            maxLength="255"
            required="true" />
        </fieldset>
    </fields>
</form>

Displaying the Form

We load up our wrapper, feed in some preexising data (which would be populated out of a database or session etc.), and have the wrapper render the form for us with a CSRF token and submit button.

require_once(JPATH_COMPONENT . '/helpers/MyFormWrapper.php');
$pathToMyXMLFile = JPATH_COMPONENT . "/forms/test.xml";
$data = ['mydatalevel' => ['title'=>'Hi there']];
$PfForm = new PfForm($pathToMyXMLFile,$data);

echo $PfForm->renderForm(true);

The Wrapper

<?php

Class MyFormWrapper
{
    protected $xml_path_or_string;
    protected $data;
    protected $form;
    protected $attr = ['method' => 'POST', 'action' => ''];

    public function __construct($xml_path_or_string = "",$data = [],$attr = [])
    {
        if(count($attr)){
            $this->attr = array_merge($this->attr,$attr);
        }
        if(!empty($xml_path_or_string)){
            $this->setPath($xml_path_or_string);
        }
        if(!empty($data)){
            $this->bind($data);
        }
    }

    public function wrapFields($fields_html)
    {
        $attr = "";
        foreach($this->attr as $k => $v){
            $attr .= $k . ' ="' . $v . '" ';
        }
        $html  = '<form '.$attr.'>';
        $html .= $fields_html;
        $html .= '<button type="submit">Submit</button>';
        $html .= JHtml::_( 'form.token' );
        $html .= "</form>";
        return $html;
    }

    public function setPath($xml_path_or_string)
    {
        $this->form = JForm::getInstance('something_unique', $xml_path_or_string);
    }

    public function renderForm($with_fieldset_titles = 1)
    {   
        $html = "";
        foreach ($this->form->getFieldsets() as $fieldsets => $fieldset){
            $html .= $this->renderFieldSet($fieldset,$with_fieldset_titles);
        }
        return $this->wrapFields($html);
    }

    public function renderFieldSet($fieldset,$with_fieldset_titles = 1)
    {
        $html = "";
        if($with_fieldset_titles){
            $html .= "<h3>" . $fieldset->name . "</h3>";
        }
        foreach($this->form->getFieldset($fieldset->name) as $field){
            if ($field->hidden){
                $html .= $field->input;
            } else {
                $html .= '<div class="form-group">';
                $html .= $field->label;
                $html .= $field->input;
                $html .= "</div>";
            }
        }
        return $html;
    }

    public function bind($data = []){
        $this->form->bind($data);
    }
}