# **Chapter 7: Two Front Files Pattern - Pagination and Edit Form** > **File Extension Reference:** > - Gate Files: `*.gate.php` (e.g., `Index.gate.php`) > - Front Files: `*.front` (e.g., `index_main.front`) > - Master Files: `*.mast.php` (e.g., `default/master.mast.php`) > - Front Places: `*.place.front` or `*.place.php` --- ## **7.1 The Two Front Files Pattern** A common pattern in SartajPhp is using **two Front Files** in one Gate: 1. **List Front File** - Displays data with Pagination Component 2. **Edit Front File** - Form for inserting/updating records This pattern provides: - Clean separation between viewing and editing - Reusable pagination for any table - Automatic database operations - Built-in form validation ## **7.2 When to Use This Pattern** Use when your application needs: - List view with pagination - Add new record functionality - Edit existing record functionality - Delete functionality ## **7.3 Step 1: Register the Gate** ```php <?php // reg.php registerGate("product", __DIR__ . "/apps/Product.gate.php"); ``` ## **7.4 Step 2: Create the Gate Class** ```php <?php // apps/Product.gate.php use Sphp\tools\BasicGate; class Product extends BasicGate { private $frtList; // For displaying list with pagination private $frtEdit; // For add/edit form public function onstart() { // Allow guests to view, members to edit $this->getAuthenticate("GUEST,MEMBER,ADMIN"); // Create two FrontFile objects $this->frtList = new \Sphp\tools\FrontFile($this->mypath . "/fronts/product_list.front"); $this->frtEdit = new \Sphp\tools\FrontFile($this->mypath . "/fronts/product_edit.front"); } // Page Events will go here } ``` ## **7.5 Step 3: Create List Front File with Pagination** Create `apps/fronts/product_list.front`: ```html <!-- product_list.front - List View with Pagination --> <title id="pageTitle" runat="server">Product List</title> <div class="container"> <div class="row"> <div class="col"> <h1>Products</h1> </div> <div class="col-auto"> <!-- Add New Button --> <a href="product-create.html" class="btn btn-primary"> Add New Product </a> </div> </div> <!-- Pagination Component - Handles listing and paging --> <div id="productGrid" runat="server" path="uikit/data/Pagination.php" dtable="products" fun-setFieldNames="name,price,category" fun-setPerPageRows="10" fun-setAJAX="" on-init="true"> <!-- Template for each row - what shows in the table --> <div class="row align-items-center"> <div class="col-md-4"> <!-- Display name from current row --> <strong>##{$productGrid->getRow('name')}#</strong> </div> <div class="col-md-2"> $##{$productGrid->getRow('price')}# </div> <div class="col-md-3"> ##{$productGrid->getRow('category')}# </div> <div class="col-md-3"> <!-- Action links --> <a href="product-view-##{$productGrid->getRow('id')}#.html" class="btn btn-sm btn-info">View</a> <a href="product-edit-##{$productGrid->getRow('id')}#.html" class="btn btn-sm btn-warning">Edit</a> </div> </div> </div> <!-- Page links (next, previous, page numbers) --> <div id="pageLinks" runas="holder" sphp-comp="productGrid" sphp-comp-prop="page_links"></div> </div> ``` ### **Key Pagination Component Attributes:** | Attribute | Purpose | |-----------|---------| | `dtable` | Database table name | | `fun-setFieldNames` | Fields to display (comma-separated) | | `fun-setPerPageRows` | Rows per page | | `fun-setAJAX` | Enable AJAX pagination | | `path` | Component path (built-in) | ## **7.6 Step 4: Create Edit Front File with Form** Create `apps/fronts/product_edit.front`: ```html <!-- product_edit.front - Add/Edit Form --> <title id="pageTitle" runat="server">Product Form</title> <div class="container"> <h1>##{$formTitle}#</h1> <!-- Product Form --> <form id="productForm" runat="server" action="product.html"> <!-- Product Name --> <div class="mb-3"> <label class="form-label">Product Name *</label> <input id="txtName" runat="server" type="text" class="form-control" dtable="products" dfield="name" placeholder="Enter product name" fui-setRequired="" fui-setMinLen="3" fui-setMaxLen="100" fui-setForm="productForm"> </div> <!-- Price --> <div class="mb-3"> <label class="form-label">Price *</label> <input id="txtPrice" runat="server" type="number" class="form-control" dtable="products" dfield="price" placeholder="0.00" fui-setRequired="" fui-setNumeric="" fui-setForm="productForm"> </div> <!-- Category Dropdown --> <div class="mb-3"> <label class="form-label">Category</label> <select id="sltCategory" runat="server" class="form-select" dtable="products" dfield="category" fui-setForm="productForm"> <option value="">Select Category</option> <option value="Electronics">Electronics</option> <option value="Clothing">Clothing</option> <option value="Books">Books</option> <option value="Home">Home</option> </select> </div> <!-- Description --> <div class="mb-3"> <label class="form-label">Description</label> <textarea id="txaDescription" runat="server" class="form-control" rows="4" dtable="products" dfield="description" placeholder="Product description"></textarea> </div> <!-- Alert for errors --> <alert id="formErrors" runat="server" fun-setShowAll=""></alert> <!-- Buttons --> <div class="mb-3"> <button type="submit" class="btn btn-primary">Save</button> <a href="product.html" class="btn btn-secondary">Cancel</a> <!-- Delete button (only shows when editing) --> #{if $isEditMode}# <a href="product-delete-##{$recordId}#.html" class="btn btn-danger float-end" onclick="return confirm('Delete this product?')">Delete</a> #{endif}# </div> </form> </div> ``` ### **Key Form Component Attributes:** | Attribute | Purpose | |-----------|---------| | `dtable` | Database table to bind to | | `dfield` | Database field name | | `fui-setRequired` | Field is required | | `fui-setForm` | Bind to form for validation | ## **7.7 Step 5: Add Page Events to Handle Everything** Now add the Page Events in your Gate: ```php <?php // apps/Product.gate.php - Complete use Sphp\tools\BasicGate; class Product extends BasicGate { private $frtList; private $frtEdit; public function onstart() { $this->getAuthenticate("GUEST,MEMBER,ADMIN"); $this->frtList = new \Sphp\tools\FrontFile($this->mypath . "/fronts/product_list.front"); $this->frtEdit = new \Sphp\tools\FrontFile($this->mypath . "/fronts/product_edit.front"); } /** * URL: product.html * Display list with pagination */ public function page_new() { $this->setFrontFile($this->frtList); } /** * URL: product-create.html * Show empty form for new record */ public function page_event_create($evtp) { // Set mode properties for FrontFile $this->frtEdit->addProp("formTitle", "Add New Product"); $this->frtEdit->addProp("isEditMode", false); $this->frtEdit->addProp("recordId", ""); $this->setFrontFile($this->frtEdit); } /** * URL: product-view-5.html * View existing record (same as edit but read-only) */ public function page_view() { $recordId = (int)$this->page->evtp; // Load data into form $this->loadRecordToForm($recordId, false); $this->setFrontFile($this->frtEdit); } /** * URL: product-edit-5.html * Show form populated with existing data */ public function page_event_edit($evtp) { $recordId = (int)$evtp; // Load data into form $this->loadRecordToForm($recordId, true); $this->setFrontFile($this->frtEdit); } /** * URL: product.html (POST) * Handle form submission - insert or update */ public function page_submit() { // Check validation errors if (getCheckErr()) { // Show form again with errors $this->frtEdit->addProp("formTitle", "Please Fix Errors"); /** * you can also use setErr to inform other about error in working. This is not * PHP error so it will not stop PHP execution. */ $this->setFrontFile($this->frtEdit); return; } // Determine insert or update based on record ID if ($this->frtEdit->getComponent("txtId")->getValue() == "") { // No ID = Insert $this->page->insertData($this->frtEdit->getComponent("productForm")); } else { // Has ID = Update $this->page->updateData($this->frtEdit->getComponent("productForm")); } // Redirect back to list $this->page->forward(getGateURL("product")); } /** * URL: product-delete-5.html * Delete a record */ public function page_event_delete($evtp) { // Require higher permission $this->getAuthenticate("MEMBER,ADMIN"); $recordId = (int)$evtp; // Delete from database $this->dbEngine->executeQuery("DELETE FROM products WHERE id = $recordId"); // Redirect to list $this->page->forward(getGateURL("product")); } /** * Helper: Load record into form */ private function loadRecordToForm($recordId, $isEditMode) { $this->dbEngine->connect(); // Fetch from database $result = $this->dbEngine->executeQuery( "SELECT * FROM products WHERE id = $recordId" ); $row = $this->dbEngine->row_fetch_assoc($result); $this->dbEngine->disconnect(); if (!$row) { $this->page->forward(getGateURL("product")); return; } // Set form values $this->frtEdit->getComponent("txtId")->fi_setDefaultValue($row['id']); $this->frtEdit->getComponent("txtName")->fi_setDefaultValue($row['name']); $this->frtEdit->getComponent("txtPrice")->fi_setDefaultValue($row['price']); $this->frtEdit->getComponent("sltCategory")->fi_setDefaultValue($row['category']); $this->frtEdit->getComponent("txaDescription")->fi_setDefaultValue($row['description']); // Set properties for display $this->frtEdit->addProp("formTitle", $isEditMode ? "Edit Product" : "View Product"); $this->frtEdit->addProp("isEditMode", $isEditMode); $this->frtEdit->addProp("recordId", $recordId); } } ``` ## **7.8 URL Mapping Summary** | URL | Event | Action | |-----|-------|--------| | `product.html` | page_new | Show list with pagination | | `product.html` (POST) | page_submit | Save form data | | `product-create.html` | page_event_create | Show empty form | | `product-view-5.html` | page_view | Show record (read-only) | | `product-edit-5.html` | page_event_edit | Show populated form | | `product-delete-5.html` | page_event_delete | Delete record | ## **7.9 How Database Operations Work** ### **Automatic Insert** ```php // Gets form Components with dtable/dfield and inserts $this->page->insertData($this->frtEdit->getComponent("productForm")); ``` This looks at Components in the form: - `txtName` with `dtable="products" dfield="name"` - `txtPrice` with `dtable="products" dfield="price"` - etc. And generates: `INSERT INTO products (name, price, ...) VALUES (?, ?, ...)` ### **Automatic Update** ```php // Gets form Components and updates by record ID $this->page->updateData($this->frtEdit->getComponent("productForm")); ``` Generates: `UPDATE products SET name=?, price=? WHERE id=?` ### **Loading Data for Edit** SartajPHP provides two approaches for loading data into form components: #### **Approach 1: Using $this->page->viewData() (Recommended)** ```php // Auto-populate form from record using database binding // Components need dfield and dtable attributes $this->page->viewData($this->frtEdit->getComponent("productForm"), $recordId); ``` **How viewData works internally:** ```php // viewData iterates over all form children components $compList = $form->getAllChildren(); foreach ($compList as $compid => $comp) { // Only process components with dfield attribute and not marked blnDontFill if ($comp->dfield != '' && !$comp->blnDontFill) { // Get table from dtable attribute or use default tblName $table = ($comp->dtable != '') ? $comp->dtable : $this->page->tblName; // Build SELECT query and fetch data // Set component value from database row } } ``` **Component flags for database binding:** - `dfield`: Database field name to bind with - `dtable`: Database table name (optional, uses `$this->page->tblName` by default) - `blnDontFill`: When set, component won't be filled from database (use `fur-unsetDataFill="true"` in FrontFile) #### **Approach 2: Manual fetch** ```php // Manual approach - fetch then set each component $this->dbEngine->connect(); $result = $this->dbEngine->executeQuery("SELECT * FROM products WHERE id = $id"); $row = $this->dbEngine->row_fetch_assoc($result); $this->dbEngine->disconnect(); // Set each component manually $this->frtEdit->getComponent("txtName")->fi_setDefaultValue($row['name']); $this->frtEdit->getComponent("txtPrice")->fi_setDefaultValue($row['price']); ``` #### **FrontFile Component Database Binding** ```html <form id="productForm" runat="server"> <!-- dfield = database field, dtable = database table --> <input id="txtName" runat="server" dfield="name" dtable="products" /> <input id="txtPrice" runat="server" dfield="price" dtable="products" type="number" /> <textarea id="txaDesc" runat="server" dfield="description" dtable="products"></textarea> <!-- Skip database fill for this component --> <input id="txtCalculated" runat="server" dfield="total" fur-unsetDataFill="true" /> </form> ``` ### **Using Dynamic URLs** Always use `getThisGateURL()`,`getEventURL()`, `getGateURL()` for dynamic URLs instead of hardcoded paths: In FrontFile: ```html <a href="##{getThisGateURL()}#">Add New Product</a> ``` **Why use getThisGateURL()?** - Generates domain-binded absolute URLs - Works even if you change the Gate registration name in `registerGate()` - Handles URL extensions dynamically (configured in comp.php) ## **7.10 Complete File Structure** ``` myproject/ ├── reg.php │ ├── apps/ │ ├── Product.gate.php │ │ │ └── fronts/ │ ├── product_list.front # Pagination view │ └── product_edit.front # Add/Edit form │ └── masters/ └── default/ ├── master.mast.php └── menu.place.front ``` ## **7.11 Chapter Summary** 1. **Two Front Files Pattern** - List for viewing, Form for editing 2. **Pagination Component** - Use built-in `Pagination.php` with `dtable` and `dtable` attributes 3. **Form Components** - Use `dtable` and `dfield` for database binding 4. **Page Events**: - `page_new()` - Show list - `page_event_create()` - Empty form - `page_event_edit(id)` - Populated form - `page_view(id)` - Read-only view - `page_submit()` - Save (auto insert/update) - `page_event_delete(id)` - Delete 5. **Database Operations** - `page->insertData()`, `page->updateData()`, `page->viewData()` ## **7.12 What's Next?** Next, learn about Master Files - how to create page layouts with headers, footers, and menus. --- *Next: Chapter 8 - Master Files: Creating Page Layouts*