Custom File - Rest API or Doctrine ORM

Hi,

I have a custom file that’s opened by a button on the list page, which updates two values in my database using a REST API.
Lavori->Row_Rendered:

    <button type="button" class="btn-default btn dropdown-toggle btn-custom" data-title="Modifica" data-bs-toggle="dropdown" data-bs-auto-close="true"><i data-phrase="ButtonListOptions" class="fa-solid fa-edit ew-icon"><span class="visually-hidden">Modifica</span></i></button><ul class="dropdown-menu ew-dropdown-menu ew-list-options btn-custom">
    <li><a class="ew-row-link ew-edit dropdown-item" data-ew-action="modal" data-ajax="true" data-url="editlavori?x_id='. $this->id->CurrentValue .'" href="#"><i class="fa-solid fa-pen ew-icon me-2"><span class="visually-hidden">Lavori</span></i>Lavori</a></li>
    </ul>
    </div>';

Custom FIle->editlavori.php ->Include Common File → YES
Custom File → editlavori.php → Custom Template → Content:

<label for="note">Note:</label>
<textarea name="note" id="note"></textarea><br />
<br>
<label for="urgente">Urgente?</label>
<input type="radio" id="si" name="urgente" value="1">
<label for="si">Si</label>
<input type="radio" id="no" name="urgente" value="0">
<label for="no">No</label><br />
<br>

    <button type="button" class="btn btn-primary ew-btn" id="btn-id" data-bs-dismiss="modal">Invia Dati</button>
	<button type="button" class="btn btn-default ew-btn" data-bs-dismiss="modal">Chiudi</button>
<?php
$id = isset($_GET['x_id']) ? $_GET['x_id'] : 0;
?>
<script>
$(document).ready(function() {
	$('.modal-footer').hide();
    loadExistingData();
    $('#btn-id').click(function(event) {
        sendCurlRequest();
    });
});
function loadExistingData() {
    var key = <?php echo json_encode($id); ?>;
    var object = "lavori";

    $.ajax({
        url: ew.getApiUrl(["view", object, key]),
        type: "GET",
        success: function(data) {
            console.log("Dati ricevuti:", data);
            $("#note").val(data.lavori.note || '');
            $("input[name='urgente'][value='" + data.lavori.urgente + "']").prop('checked', true);
        },
        error: function(error) {
            console.error("Errore nel caricamento dei dati esistenti: " + JSON.stringify(error));
        }
    });
}

function sendCurlRequest() {
    var note = $("#note").val();
    var urgente = $("input[name='urgente']:checked").val();
    var object = "lavori";
    var key = <?php echo json_encode($id); ?>;

    $.ajax({
		url: ew.getApiUrl(["edit", object, key]),
        type: "POST",
        data: {
            "note": note,
            "urgente": urgente
        },
        success: function (data) {
            console.log("Richiesta riuscita: " + data);
            window.location.reload();
        },
        error: function (error) {
            console.error("Errore: " + JSON.stringify(error));
            window.location.reload();
        }
    });
}
</script>

In version 2024, it was working perfectly, updating only the two values as intended. However, in version 2025, it sets every column to NULL except for the two columns that the script is supposed to update, which receive the correct values.
Do you have any idea why this might be happening?

Now, as a test, I’m trying to do the same thing with Doctrine ORM, but I’m stuck on this as well.

<?php
$em = EntityManager();
$id = isset($_GET['x_id']) ? $_GET['x_id'] : 0;
$note = '';
$urgente = null;
$lavoro = $em->find(Entity\Lavori::class, $id);
if ($lavoro) {
    $note = $lavoro->getNote();
    $urgente = $lavoro->getUrgente();
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $note = $_POST['note'];
    $urgente = $_POST['urgente'];
    if ($lavoro) {
        $lavoro->setNote($note);
        $lavoro->setUrgente($urgente);
        $em->persist($lavoro);
        $em->flush();
        echo "<script>alert('Dati aggiornati con successo!'); window.location.href = 'your_redirect_url.php';</script>";
    }
}
?>

<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Gestione Lavori</title>
</head>
<body>
    <form method="POST" action="">
        <label for="note">Note:</label>
		<input type="hidden" name="<?= $TokenNameKey ?>" value="<?= $TokenName ?>">
		<input type="hidden" name="<?= $TokenValueKey ?>" value="<?= $TokenValue ?>">
        <textarea name="note" id="note"><?php echo htmlspecialchars($note); ?></textarea><br />
        <br>
        <label for="urgente">Urgente?</label>
        <input type="radio" id="si" name="urgente" value="1" <?php if ($urgente == 1) echo 'checked'; ?>>
        <label for="si">Si</label>
        <input type="radio" id="no" name="urgente" value="0" <?php if ($urgente == 0) echo 'checked'; ?>>
        <label for="no">No</label><br />
        <br>
        <button type="submit" class="btn btn-primary ew-btn">Invia Dati</button>
        <button type="button" class="btn btn-default ew-btn" onclick="window.history.back()">Chiudi</button>
    </form>
</body>
</html>

The connection, the GET request, and the population of my HTML work correctly, but the POST request is not functioning. It closes the editlavori.php window, sets a filter in the lavori list, and doesn’t update any values.

The REST API always uses the same page classes generated for the table. If the Edit page is selected with other fields and those fields are missing from your new data, they will be updated as null. If you want to have different behavior for API, you may use Row_Updating server event to disable other fields, e.g.

if (IsApi()) {
     $this->MyField->Disabled = true;
    // Other unused fields in API
}

Alternatively, you may also use Doctrine ORM because it updates directly without using page classes. However, your content should only contains the form, no <html> and other page tags because you have already selected “Include Common Files”.

if (IsApi()) {
     $this->MyField->Disabled = true;
    // Other unused fields in API
}

this looks good, but i should use something similar to:

if (IsApi() && $this->PageID === "editlavori.php")
or
if (IsApi() && $this->PageID === "editlavori")

because otherwise i think will broke my other api request. Both of them are not working. Do you have any idea how to solve this?

As for Docrtine ORM i’ve modyfied my code from:

<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Gestione Lavori</title>
</head>
<body>
    <form method="POST" action="">
        <label for="note">Note:</label>
		<input type="hidden" name="<?= $TokenNameKey ?>" value="<?= $TokenName ?>">
		<input type="hidden" name="<?= $TokenValueKey ?>" value="<?= $TokenValue ?>">
        <textarea name="note" id="note"><?php echo htmlspecialchars($note); ?></textarea><br />
        <br>
        <label for="urgente">Urgente?</label>
        <input type="radio" id="si" name="urgente" value="1" <?php if ($urgente == 1) echo 'checked'; ?>>
        <label for="si">Si</label>
        <input type="radio" id="no" name="urgente" value="0" <?php if ($urgente == 0) echo 'checked'; ?>>
        <label for="no">No</label><br />
        <br>
        <button type="submit" class="btn btn-primary ew-btn">Invia Dati</button>
        <button type="button" class="btn btn-default ew-btn" onclick="window.history.back()">Chiudi</button>
    </form>
</body>
</html>

to

    <form method="POST" action="">
        <label for="note">Note:</label>
		<input type="hidden" name="<?= $TokenNameKey ?>" value="<?= $TokenName ?>">
		<input type="hidden" name="<?= $TokenValueKey ?>" value="<?= $TokenValue ?>">
        <textarea name="note" id="note"><?php echo htmlspecialchars($note); ?></textarea><br />
        <br>
        <label for="urgente">Urgente?</label>
        <input type="radio" id="si" name="urgente" value="1" <?php if ($urgente == 1) echo 'checked'; ?>>
        <label for="si">Si</label>
        <input type="radio" id="no" name="urgente" value="0" <?php if ($urgente == 0) echo 'checked'; ?>>
        <label for="no">No</label><br />
        <br>
        <button type="submit" class="btn btn-primary ew-btn">Invia Dati</button>
        <button type="button" class="btn btn-default ew-btn" onclick="window.history.back()">Chiudi</button>
    </form>

but i’m still unable to save the data in the database. the data are shown in the format correctly, but when i click “Invia Dati” it close the modal but don’t modify the data in the database. i’m debugging it, but looks like the data are sent correctly to the post, so im a bit out of ideas.

It “closed the modal”? Did you try to run your Custom File in modal dialog? If so, you did not post related code.

The custom file is opened by the press of a button in my table list view. I use this code in Lavori-> Table Specific->Common->Row_Rendered:

    $this->a_consuntivi->ViewValue = '<div class="btn-group btn-group-sm ew-btn-dropdown">
    <button type="button" class="btn-default btn dropdown-toggle btn-custom" data-title="Modifica" data-bs-toggle="dropdown" data-bs-auto-close="true"><i data-phrase="ButtonListOptions" class="fa-solid fa-edit ew-icon"><span class="visually-hidden">Modifica</span></i></button><ul class="dropdown-menu ew-dropdown-menu ew-list-options btn-custom">
    <li><a class="ew-row-link ew-edit dropdown-item" data-ew-action="modal" data-ajax="true" data-url="editconsuntivi?x_id='. $this->id->CurrentValue .'" href="#"><i class="fa-solid fa-pen ew-icon me-2"><span class="visually-hidden">Consuntivi</span></i>Consuntivi</a></li>
    <li><a class="ew-row-link ew-edit dropdown-item" data-ew-action="modal" data-ajax="true" data-url="editassegnazioni?x_id='. $this->id->CurrentValue .'" href="#"><i class="fa-solid fa-pen ew-icon me-2"><span class="visually-hidden">Asseganzioni</span></i>Assegnazioni</a></li>
    <li><a class="ew-row-link ew-edit dropdown-item" data-ew-action="modal" data-ajax="true" data-url="editlavori?x_id='. $this->id->CurrentValue .'" href="#"><i class="fa-solid fa-pen ew-icon me-2"><span class="visually-hidden">Lavori</span></i>Lavori</a></li>
    </ul>
    </div>';

Each <li> represents a different custom file with a unique API action or, if I manage to implement it, Doctrine ORM. The concept remains the same: it opens a simple modal dialog where only 2 or 3 values can be edited. This allows my users to make small edits in a large table without having to search for each value across different pages. It also gives me control over who can modify what, with permissions set in the user pages and some logic in the main edit button visibility.

In version 2024, I used a REST API, and it worked well, editing the intended data without affecting the rest. The only issue was that data loading on the page was a bit slow, but it wasn’t a major concern. After upgrading to version 2025, I noticed a change: the API now requires a full set of all data fields for editing. If any data is not included in the edit, it is set to NULL.
After your suggestion:

if (IsApi()) {
     $this->MyField->Disabled = true;
    // Other unused fields in API
}

i modified my base code (REST API), this disabled the edit for fields not involved in the edit. However, I need to find a way to separate the exclusions between the various API calls. I considered using PageID in Lavori->Row_Updating, but it doesn’t seem to work.

if (IsApi() && $this->PageID === "editlavori.php") { ... }
or
if (IsApi() && $this->PageID === "editlavori") { ... }

set a name (data-modal-id="modal_lavori") to the modal dialog:

<li><a class="ew-row-link ew-edit dropdown-item" data-ew-action="modal" data-ajax="true" data-url="editlavori?x_id='. $this->id->CurrentValue .'" data-modal-id="modal_lavori" href="#"><i class="fa-solid fa-pen ew-icon me-2"><span class="visually-hidden">Lavori</span></i>Lavori</a></li>

and used this in Lavori->Row_Updating:

if (IsApi() && Param("modal_id") === "modal_consuntivi") { ... }

Passed the value of modal_id with Ajax:

$.ajax({
		url: ew.getApiUrl(["edit", object, key]),
        type: "POST",
        data: {
            "note": note,
            "urgente": urgente,
            "modal_id": "modal_consuntivi"
        },
        success: function (data) {
            console.log("Richiesta riuscita: " + data);
            window.location.reload();
        },
        error: function (error) {
            console.error("Errore: " + JSON.stringify(error));
            window.location.reload();
        }

and used this in Lavori->Row_Updating:

if (IsApi() && Param("modal_id") === "modal_consuntivi") { ... }
or
if (IsApi() && isset($_POST["modal_id"]) && $_POST["modal_id"] === "modal_consuntivi") { ... }

with the same result, every value except the edited one in the selected DB row is deleted.

Thanks for your help!

For Doctrine ORM, I managed to make it work in a test project. The table is called Test and it has two columns: id (identity) and prova. I had to use two custom files. The first one is for the frontend and loading data into the fields. I decided to use AJAX to send the data to the backend:
edit.php -> Include common file YES

<?php
// Recive data with Doctrine ORM
$em = EntityManager();
// id is from the list page passed with the link ( data-url="edit?x_id='. $this->id->CurrentValue .'")
$id = isset($_GET['x_id']) ? $_GET['x_id'] : 0;
$test = $em->find(Entity\test::class, $id);
if ($test) {
    $prova = htmlspecialchars($test->getProva());
} else {
    echo "<script>alert('Errore: ID not Found!');</script>";
    $prova = '';
}
?>

<h1>TEST</h1>
<form id="edit" method="POST">
    <input type="hidden" name="<?= $TokenNameKey ?>" value="<?= $TokenName ?>">
    <input type="hidden" name="<?= $TokenValueKey ?>" value="<?= $TokenValue ?>">
    <label for="prova">Prova:</label>
    <textarea name="prova" id="prova"><?php echo $prova; ?></textarea>
    <button type="button" id="submitBtn" class="btn btn-primary ew-btn">Send Data</button>
    <button type="button" class="btn btn-default ew-btn" data-bs-dismiss="modal">Close</button>
</form>

<script>
$(document).ready(function () {
    $("#submitBtn").on("click", function () {
        var prova = $("#prova").val();
        var id = <?php echo json_encode($id); ?>;

        // Use AJAX to send data to the backend
        $.ajax({
            url: "processingedit", // Endpoint backend (second custom file)
            type: "POST",
            data: {
                id: id,
                prova: prova,
                "<?= $TokenNameKey ?>": "<?= $TokenName ?>",
                "<?= $TokenValueKey ?>": "<?= $TokenValue ?>"
            },
            success: function (data) {
				console.log("Request executed: " + data);
				window.location.reload();
            },
            error: function (xhr, status, error) {
                console.error("Error in AJAX request:", {
                    status: xhr.status,
                    responseText: xhr.responseText,
                    message: error
                });
            }
        });
    });
});
</script>

and the second custom file to manage the backend to save the data in the database:
processingedit.php -> Include common file YES

<?php
$em = EntityManager();

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
	throw new Exception('Method Not Allowed.', 405);
}

// Retrive POST data
$id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
$prova = isset($_POST['prova']) ? trim($_POST['prova']) : '';

// Find by ID
$test = $em->find(Entity\Test::class, $id);
if (!$test) {
	throw new Exception('ID not Found.', 404);
}

//Update and save
$test->setProva($prova);
$em->persist($test);
$em->flush();

echo json_encode(['success' => true, 'message' => 'Data Updated!']);
?>

To avoid creating two files for each instance, it should be possible to add a new variable in the Ajax POST request to identify the source of the request. Then, use an if statement in the processingedit.php file to determine which variables to retrieve from the POST data and update accordingly. However, I haven’t tested this approach yet.

  1. If you use modal dialog, your form from your Custom File is opened in the List page, you need to set the action attribute of your form or the form will submitted back to the List Page by default.
  2. You should check IsGet() and IsPost() so that you don’t need two files.

Hi, as always thanks for your help!
This time i’m not sure i’m understanding you:

  1. after the edit (with API or Doctrine) the list page should refresh, action is not used to send to another page after, in my case, POST?

I’ve managed to wrap everything in a single file thanks to your suggestion:

<?php

$em = EntityManager();

$prova = '';
$id = 0;

if (isGet()) {
    $id = isset($_GET['x_id']) ? (int)$_GET['x_id'] : 0;
    $test = $em->find(Entity\Test::class, $id);
    if ($test) {
        $prova = htmlspecialchars($test->getProva());
    } else {
        echo "<script>alert('Errore: Lavoro non trovato!');</script>";
    }
}

if (isPost()) {
    $id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
    $prova = isset($_POST['prova']) ? trim($_POST['prova']) : '';

    $test = $em->find(Entity\Test::class, $id);
    if (!$test) {
        echo json_encode(['success' => false, 'message' => 'Lavoro non trovato.']);
        exit;
    }

    $test->setProva($prova);
    $em->persist($test);
    $em->flush();

    echo json_encode(['success' => true, 'message' => 'Dati aggiornati con successo!']);
    exit;
}
?>

<!-- Interfaccia HTML -->
<h1>TEST</h1>
<form id="modifica" method="POST">
    <input type="hidden" name="<?= $TokenNameKey ?>" value="<?= $TokenName ?>">
    <input type="hidden" name="<?= $TokenValueKey ?>" value="<?= $TokenValue ?>">
    <label for="prova">Prova:</label>
    <textarea name="prova" id="prova"><?php echo $prova; ?></textarea>
    <button type="button" id="submitBtn" class="btn btn-primary ew-btn">Invia Dati</button>
    <button type="button" class="btn btn-default ew-btn" data-bs-dismiss="modal">Chiudi</button>
</form>

<script>
$(document).ready(function () {
    $("#submitBtn").on("click", function () {
        var prova = $("#prova").val();
        var id = <?php echo json_encode($id); ?>;

        $.ajax({
            url: "edit", // CHANGE THIS TO SAME CUSTOM FILE NAME (e.g. edit)
            type: "POST",
            data: {
                id: id,
                prova: prova,
                "<?= $TokenNameKey ?>": "<?= $TokenName ?>",
                "<?= $TokenValueKey ?>": "<?= $TokenValue ?>"
            },
            success: function (data) {
				console.log("Richiesta riuscita: " + data.message);
				window.location.reload();
            },
            error: function (xhr, status, error) {
                console.error("Errore nella richiesta AJAX:", {
                    status: xhr.status,
                    responseText: xhr.responseText,
                    message: error
                });
            }
        });
    });
});
</script>