Trongate Way Docs

Complete Example and Summary

This page lists all six view files that complete the countries CRUD module. The controller and model were covered in the two preceding pages:

  • Countries.php - Controller (see Building the Controller)
  • Countries_model.php - Model (see Building the Model)
  • create.php - Create/edit form view
  • delete_conf.php - Delete confirmation view
  • manage.php - Paginated list view
  • not_found.php - Record not found view
  • search_modal.php - Search modal form view
  • show.php - Record detail view

Views

create.php

View File
<h1><?= $headline ?></h1>
<?= validation_errors() ?>
<div class="card">
    <div class="card-heading">
        Country Details
    </div>
    <div class="card-body">
        <?php
        echo form_open($form_location);

        echo form_label('Country Title');
        $country_title_attr = [
            'placeholder' => 'Enter Country Title',
            'required'  => true,
            'minlength' => 2,
            'maxlength' => 255
        ];
        echo form_input('country_title', $country_title, $country_title_attr);

        echo form_label('Country Code');
        $country_code_attr = [
            'placeholder' => 'Enter Country Code',
            'required'  => true,
            'minlength' => 2,
            'maxlength' => 2
        ];
        echo form_input('country_code', $country_code, $country_code_attr);

        echo '<div class="text-center">';
        echo anchor($cancel_url, 'Cancel', ['class' => 'button alt']);
        echo form_submit('submit', 'Submit');
        echo '</div>';
        
        echo form_close();
        ?>
    </div>
</div>

delete_conf.php

View File
<h1><?= $headline ?></h1>
<div class="card">
    <div class="card-heading">
        Confirmation Required
    </div>
    <div class="card-body">
        <p>Are you sure?</p>
        <p>You are about to delete a country record. This cannot be undone. Do you really want to do this?</p>
        
        <?php
        echo form_open($form_location);
        echo '<div class="text-center">';
        echo anchor($cancel_url, 'Cancel', array('class' => 'button alt'));
        echo form_submit('submit', 'Yes - Delete Now', array('class' => 'danger'));
        echo form_close();
        echo '</div>';
        ?>
    </div>
</div>

manage.php

View File
<h1>Manage Countries</h1>
<?= flashdata() ?>
<?php if (!empty($search_query)): ?>
    <p>Showing results for <strong><?= out($search_query) ?></strong></p>
<?php endif; ?>
<?php
echo '<p class="flex-row justify-between">';
echo anchor('countries/create', 'Create New Country Record', ['class' => 'button alt']);
if ((count($rows) > 9) || (!empty($search_query))) {
    $btn_attr = [
        'class' => 'alt',
        'mx-get' => 'countries/search_modal',
        'mx-build-modal' => json_encode([
            'id' => 'search-modal',
            'modalHeading' => 'Search Countries',
            'modalFooter' => '<button class="alt" onclick="closeModal()">Cancel</button><button form="search-form">Search</button>'
        ])
    ];
    echo form_button('search_btn', 'Search <i class="tg tg-search"></i>', $btn_attr);
}
echo '</p>';
if (empty($rows)) {
    echo '<p>There are currently no records to display.</p>';
    return;
}
echo Modules::run('pagination/display', $pagination_data);
?>

<div class="table-container">
    <table class="records-table">
        <thead>
            <tr>
                <th colspan="3">
                    <div>
                        <div> </div>
                        <div>Records Per Page: <?php
                        $dropdown_attr['onchange'] = 'setPerPage()';
                        echo form_dropdown('per_page', $per_page_options, $selected_per_page, $dropdown_attr); 
                        ?></div>
                    </div>                    
                </th>
            </tr>
            <tr>
                <th class="text-left">Country Title</th>
            <th class="text-left">Country Code</th>
                <th>Actions</th>
            </tr>
        </thead>
        <tbody>
            <?php foreach($rows as $row): 
                $link_attr = [
                    'mx-get' => 'countries/show/'.$row->id,
                    'mx-select' => '.detail-grid',
                    'mx-build-modal' => json_encode([
                        'id' => 'record-preview-modal',
                        'width' => '640px',
                        'modalHeading' => 'Record Preview',
                        'modalFooter' => '<a href="countries/show/'.$row->id.'" class="button alt mt-0 xs">View Details</a>'
                    ])
                ];
                ?>
                <tr>
                <td><?= anchor('#', out($row->country_title), $link_attr) ?></td>
                <td><?= out($row->country_code) ?></td>                    <td>
                        <div class="actions">
                            <a href="countries/show/<?= $row->id ?>" class="button alt button-round"><i class="tg tg-eye"></i></a>
                            <a href="countries/create/<?= $row->id ?>" class="button alt button-round"><i class="tg tg-pencil"></i></a>
                            <a href="countries/delete_conf/<?= $row->id ?>" class="button alt button-round"><i class="tg tg-trash"></i></a>
                        </div>
                    </td>
                </tr>
            <?php endforeach; ?>
        </tbody>
    </table>
</div>

<?php 
if(count($rows)>9) {
    unset($pagination_data['include_showing_statement']);
    echo Modules::run('pagination/display', $pagination_data);
}
?>

<script>
function setPerPage() {
    const selectedIndex = document.querySelector('select[name="per_page"]').value;
    window.location.href = '<?= BASE_URL ?>countries/set_per_page/' + selectedIndex;
}
</script>

not_found.php

View File
<h1><?= $headline ?></h1>
<div class="card">
    <div class="card-heading">
        <?= $headline ?>
    </div>
    <div class="card-body">
        <p><?= $message ?></p>
        <div class="text-center">
            <?= anchor($back_url, $back_label, array('class' => 'button alt')) ?>
        </div>
    </div>
</div>

search_modal.php

View File
<?php
/**
 * Search modal form for filtering countries.
 *
 * Loaded via MX into a modal when the Search button is clicked.
 */
$searchable_columns = [
    'country_title' => 'Country Title',
    'country_code' => 'Country Code',
];

$form_attr = ['id' => 'search-form'];
echo form_open('countries/submit_search', $form_attr);
?>
    <div class="form-group">
        <?= form_label('Search Query') ?>
        <?= form_input('search_query', '', ['placeholder' => 'Enter search term...', 'autocomplete' => 'off']) ?>
    </div>
    <div class="form-group">
        <?= form_label('Search In') ?>
        <?= form_dropdown('search_column', $searchable_columns) ?>
    </div>
<?= form_close() ?>

show.php

View File
<h1><?= $headline ?></h1>
<?= flashdata() ?>
<div class="card">
    <div class="card-heading">
        Country Details
    </div>
    <div class="card-body">
        <div class="text-right mb-3">
            <?= anchor($back_url, 'Back', array('class' => 'button alt')) ?>
            <?= anchor(BASE_URL.'countries/create/'.$update_id, 'Edit', array('class' => 'button')) ?>
            <?= anchor('countries/delete_conf/'.$update_id, 'Delete',  array('class' => 'button danger')) ?>
        </div>
        <div class="detail-grid">
            <div class="detail-row">
                <div class="detail-label">Country Title</div>
                <div class="detail-value"><?= out($country_title) ?></div>
            </div>
            <div class="detail-row">
                <div class="detail-label">Country Code</div>
                <div class="detail-value"><?= out($country_code) ?></div>
            </div>
        </div>
    </div>
</div>

Summary

In this chapter you built a complete CRUD module for managing countries. Here is what you accomplished:

  • Database: Created a countries table with country_title and country_code columns, and populated it with 23 sample countries.
  • Model: Built Countries_model with methods for fetching, inserting, updating, counting, searching, and preparing records for display.
  • Controller: Built Countries with methods for manage (pagination with search), create/submit (form with validation), show (detail view), and delete confirmation flow.
  • Manage page: A paginated list with two visible columns, search modal, per-page selector, and view/edit/delete action buttons. Country titles link to MX-powered record preview modals.
  • Create and Edit: A single form that handles both creation and updates, with validation rules, client-side constraints, and error display.
  • Show: A detail view of individual records with navigation to edit or delete, plus a .detail-grid class that doubles as the MX preview target.
  • Delete: A two-step delete flow with confirmation screen and not-found handling for already-deleted records.
  • Security: Every public method protected by $this->trongate_security->make_sure_allowed(). Private helper methods eliminate the need for block_url().
  • Search: Column-whitelisted search with SQL injection protection via named parameters in query_bind().

This same pattern - manage, show, create, submit, delete_conf, submit_delete - is used by every admin CRUD module in Trongate. Once you have built one, you have built them all. The only differences are the table schema, the form fields, and the validation rules.

In the next chapter, we will look at exposing these same records through a REST API, where the same make_sure_allowed() pattern takes on new significance for securing endpoints.

We're continually improving the Trongate documentation. If anything is incorrect, unclear, incomplete, or could be better, we'd genuinely appreciate your input.

Share your thoughts in the Documentation Feedback.

Leave Feedback About This Page