CharityCenter Documentation
Charity Center Controller Documentation
File: /controllers/charityCenterController.php
Purpose: Manages charity centers with CRUD operations and comprehensive reporting
Last Updated: December 20, 2024
Total Functions: 4
Lines of Code: ~187
---
๐ Overview
The Charity Center Controller is a management module for charity organizations that handles the administration of charity centers with associated charities and beneficiaries. It provides:
- โข Charity center creation and management
- โข CRUD operations with soft delete functionality
- โข DataTables integration for dynamic listing
- โข Comprehensive reporting with statistics
- โข Hierarchical relationship tracking (centers โ charities โ beneficiaries)
- โข AJAX-based operations for responsive user interface
Primary Functions
- โ Create and edit charity centers
- โ Display charity center listings with DataTables
- โ Soft delete/restore functionality
- โ AJAX-powered search and filtering
- โ Generate statistical reports
- โ Track relationships between centers, charities, and beneficiaries
- โ Count beneficiaries across different categories
Charity System Hierarchy
Charity Centers
โ
Charities (linked to centers via center_id)
โ
Beneficiaries (linked to charities via charity_id)
โ
Special Category: "Takafol" beneficiaries (charity_id = 0)
Related Controllers
- โข charitiesController.php - Individual charity management
- โข beneficiariesController.php - Beneficiary management
- โข userController.php - User management system
---
๐๏ธ Database Tables
Primary Table (Direct Operations)
| Table Name | Purpose | Key Columns |
|---|---|---|
| **charitiescenter** | Charity center master data | id, name, del, sysdate, user_id, del_date, deluserid, update_date, updateuserid |
| Table Name | Purpose | Key Columns | |
|---|---|---|---|
| **charities** | Individual charities | id, center_id, name, del | |
| **beneficiaries** | Charity beneficiaries | id, charity_id, idnumber, del | |
| **user** | System users | userid, username |
| Del Value | Status | Description | |
|---|---|---|---|
| 0 | Active | Normal active record | |
| 1 | Updated | Record has been updated | |
| 2 | Deleted | Soft deleted record | |
| 5 | Special | Special status (appears in edit scenarios) |
๐ Key Functions
1. Default Action - Add Center Form
Location: Lines 8-12
Purpose: Display form to create new charity center
Process Flow:
1. Display header template
2. Show add form template
3. Set charity-specific CSS flag
4. Display footer template
Template Variables:
- โข
$charity = 1- Enables charity-specific styling and JavaScript
---
2. show - Display Centers List
Location: Lines 13-24
Purpose: Display charity centers list with optional filtering
Function Signature:
// Triggered when: do=show
elseif ($do == "show") {
Process Flow:
1. Authentication Check: Include authentication validation
2. View Mode Detection: Check for special view mode (show=2)
3. Template Preparation: Assign current date and view flags
4. Display: Render show template with DataTables integration
URL Parameters:
- โข
show- View mode (2 for special view)
Template Variables:
- โข
$show- View mode indicator - โข
$date- Current date for filtering - โข
$charity = 1- CSS/JavaScript flag
---
3. edit - Edit Center Form
Location: Lines 25-34
Purpose: Load and display charity center for editing
Function Signature:
// Triggered when: do=edit
elseif ($do == "edit") {
Process Flow:
1. Authentication Check: Validate user permissions
2. Data Loading: Load charity center by ID using RedBeanPHP
3. Template Assignment: Pass center data to template
4. Display: Render edit form with populated data
URL Parameters:
- โข
id- Charity center ID to edit
Data Loading:
$id = filter_input(INPUT_GET, 'id');
$centerdata = R::load('charitiescenter', $id);
$smarty->assign('centerdata', $centerdata);
---
4. savedata - Save Center Data
Location: Lines 72-102
Purpose: Create new charity center or update existing one
Function Signature:
function savedata()
Process Flow:
Input Processing:
$name = filter_input(INPUT_POST, 'name');
$charityid = filter_input(INPUT_POST, 'charityid');
Create vs Update Logic:
if (!$charityid) {
// Create new center
$charitiescenter = R::dispense('charitiescenter');
$charitiescenter->del = 0;
$charitiescenter->sysdate = $today;
$charitiescenter->user_id = $userid;
$charitiescenter->del_date = '';
$charitiescenter->deluserid = '';
} else {
// Update existing center
$charitiescenter = R::load('charitiescenter', $charityid);
$charitiescenter->del = 1;
$charitiescenter->update_date = $today;
$charitiescenter->updateuserid = $userid;
}
Data Assignment and Save:
$charitiescenter->name = $name;
try {
$charityid = R::store($charitiescenter);
echo 1; // Success
} catch (Exception $e) {
echo 0; // Failure
}
Input Parameters:
- โข
name- Charity center name - โข
charityid- ID for updates (empty for new records)
Response:
- โข
1- Success - โข
0- Error/Exception
---
5. showajax - DataTables AJAX Endpoint
Location: Lines 103-167
Purpose: Provide JSON data for DataTables with search, pagination, and sorting
Function Signature:
function showajax()
Process Flow:
Column Definition:
$columns = array('id', 'id', 'name', 'id', 'id');
// Columns: [0=ID, 1=ID, 2=Name, 3=Edit, 4=Delete]
Search Query Building:
$searchQuery = " ";
// Filter by specific ID
if ($data1 != '') {
$searchQuery .= " and charitiescenter.id = " . $data1 . " ";
}
// Filter by delete status
if ($del == '') {
$searchQuery .= " and charitiescenter.del < 2 "; // Show active records
}
// Global search across columns
if (isset($_POST['search']['value']) && $_POST['search']['value'] != "") {
$searchQuery .= 'and ( charitiescenter.id LIKE " % ' . $_POST["search"]["value"] . ' %"
OR charitiescenter.sysdate LIKE "%' . $_POST["search"]["value"] . ' % "
OR charitiescenter.name LIKE "%' . $_POST["search"]["value"] . '%"
)';
}
Sorting and Pagination:
// Sorting
if (isset($_POST["order"])) {
$searchQuery .= 'ORDER BY ' . $columns[$_POST['order']['0']['column']] . ' ' . $_POST['order']['0']['dir'] . ' ';
} else {
$searchQuery .= "ORDER BY charitiescenter.id DESC ";
}
// Pagination
if (isset($_POST['start']) && $_POST['length'] != '-1') {
$searchQuery .= "LIMIT " . intval($_POST['start']) . ", " . intval($_POST['length']);
}
Action Buttons Generation:
foreach ($rResult as $row) {
if ($row["del"] < 2) {
// Active record: Show edit and delete buttons
$sub_array[] = '<a href="charityCenterController.php?do=edit&id=' . $row["id"] . '" type="button" class="btn btn-default btn-lg editicon"></a>';
$sub_array[] = '<a href="javascript:;" data-id="' . $row["id"] . '" data-controll="charityCenterController" type="button" class="btn btn-default btn-lg deleteicon removecontroller" ></a>';
} else if ($row["del"] == 5) {
// Special status: Show only edit button
$sub_array[] = '<a href="charityCenterController.php?do=edit&id=' . $row["id"] . '" type="button" class="btn btn-default btn-lg editicon"></a>';
} else {
// Deleted record: Show details button and "deleted" status
$sub_array[] = '<a href="charityCenterController.php?do=edit&id=' . $row["id"] . '" type="button" class="btn btn-default btn-lm ">ุชูุงุตูู</a>';
$sub_array[] = 'ู
ุญุฐูู ';
}
}
AJAX Parameters:
- โข
del- Delete status filter - โข
data1- Specific ID filter - โข
search[value]- Global search term - โข
order- Sorting configuration - โข
start- Pagination start - โข
length- Page size
Response Format:
{
"draw": 1,
"recordsTotal": 50,
"recordsFiltered": 25,
"data": [
["1", "Center Name", "<edit button>", "<delete button>"],
// ... more rows
]
}
---
6. removecontroller - Soft Delete Center
Location: Lines 170-186
Purpose: Soft delete charity center by setting delete flag
Function Signature:
function removecontroller()
Process Flow:
1. Input Processing: Get center ID from POST
2. Data Loading: Load charity center record
3. Soft Delete: Set del=2, del_date, and deluserid
4. Save: Store updated record
Implementation:
$id = filter_input(INPUT_POST, 'id');
$tables = R::load('charitiescenter', $id);
$tables->del = 2; // Mark as deleted
$tables->del_date = $today; // Record deletion timestamp
$tables->deluserid = $userid; // Record who deleted it
try {
R::store($tables);
echo 1; // Success
} catch (Exception $e) {
echo 0; // Failure
}
Response:
- โข
1- Successfully deleted - โข
0- Error occurred
---
7. report - Statistical Reporting
Location: Lines 41-68
Purpose: Generate comprehensive statistics report for charity centers
Function Signature:
// Triggered when: do=report
elseif ($do == "report") {
Process Flow:
Main Statistics Query:
SELECT cs.id, cs.name,
(SELECT COUNT(charities.id) FROM charities
WHERE charities.del < 2 AND charities.id != 0 AND charities.center_id = cs.id) AS chCount,
(SELECT COUNT(beneficiaries.id) FROM beneficiaries
JOIN charities ON beneficiaries.charity_id = charities.id
WHERE beneficiaries.del < 2 AND charities.del < 2 AND cs.del < 2
AND charities.id != 0 AND charities.center_id = cs.id) AS benCount
FROM charitiescenter cs
WHERE cs.del < 2
Takafol Beneficiaries Calculation:
foreach ($allCenters as $center) {
$tkafolCounter = R::getCell('SELECT COUNT(b.id) FROM beneficiaries b
WHERE b.charity_id = 0
AND b.idnumber IN (
SELECT beneficiaries.idnumber FROM beneficiaries
JOIN charities ON beneficiaries.charity_id = charities.id
WHERE beneficiaries.del < 2 AND charities.del < 2
AND beneficiaries.charity_id != 0 AND charities.center_id = ' . $center["id"] . '
)');
$dataArr[$center['id']]['tkafolCount'] = $tkafolCounter;
}
Global Takafol Count:
$tkafolCount = R::getCell('SELECT COUNT(id) FROM beneficiaries WHERE charity_id = 0');
Report Metrics:
- โข chCount - Number of active charities per center
- โข benCount - Number of beneficiaries linked to charities in each center
- โข tkafolCount - Number of "takafol" beneficiaries (special category with charity_id=0)
- โข Global takafol - Total takafol beneficiaries across all centers
Template Variables:
- โข
$allData- Main statistics for each center - โข
$dataArr- Takafol counts by center - โข
$tkafolCount- Global takafol count
---
๐ Workflows
Workflow 1: Charity Center Creation
---
Workflow 2: Center Listing with DataTables
---
Workflow 3: Statistical Report Generation
---
๐ URL Routes & Actions
| URL Parameter | Function Called | Description | |
|---|---|---|---|
| (no parameters) | Default action | Display add center form | |
| `do=show` | Show listing | Display centers with DataTables | |
| `do=edit` | Edit form | Load center for editing | |
| `do=savedata` | `savedata()` | Create/update center | |
| `do=showajax` | `showajax()` | AJAX endpoint for DataTables | |
| `do=removecontroller` | `removecontroller()` | Soft delete center | |
| `do=report` | Report view | Generate statistical report |
Show Centers (do=show):
- โข
show- View mode (optional, 2 for special view)
Edit Center (do=edit):
- โข
id- Charity center ID
Save Data (do=savedata):
- โข
name- Center name - โข
charityid- ID for updates (empty for new)
AJAX Data (do=showajax):
- โข DataTables parameters (draw, start, length, search, order)
- โข
del- Delete status filter (optional) - โข
data1- Specific ID filter (optional)
Delete Center (do=removecontroller):
- โข
id- Center ID to delete
---
๐งฎ Statistical Calculations
Charity Count per Center
SELECT COUNT(charities.id) FROM charities
WHERE charities.del < 2
AND charities.id != 0
AND charities.center_id = [CENTER_ID]
Beneficiary Count per Center
SELECT COUNT(beneficiaries.id) FROM beneficiaries
JOIN charities ON beneficiaries.charity_id = charities.id
WHERE beneficiaries.del < 2
AND charities.del < 2
AND charities.id != 0
AND charities.center_id = [CENTER_ID]
Takafol Beneficiaries per Center
SELECT COUNT(b.id) FROM beneficiaries b
WHERE b.charity_id = 0
AND b.idnumber IN (
SELECT beneficiaries.idnumber FROM beneficiaries
JOIN charities ON beneficiaries.charity_id = charities.id
WHERE beneficiaries.del < 2
AND charities.del < 2
AND beneficiaries.charity_id != 0
AND charities.center_id = [CENTER_ID]
)
Explanation: Takafol beneficiaries are those with charity_id = 0 but whose ID numbers also appear in the regular beneficiary system linked to actual charities.
---
๐ Security & Permissions
Access Control
// Authentication required for most operations
include_once("../public/authentication.php");
// Session-based user tracking
$userid = $_SESSION['userid'];
Input Validation
// All input filtered through filter_input
$name = filter_input(INPUT_POST, 'name');
$id = filter_input(INPUT_GET, 'id');
$charityid = filter_input(INPUT_POST, 'charityid');
Soft Delete Implementation
// Records never physically deleted, only marked
$tables->del = 2; // Mark as deleted
$tables->del_date = $today; // Timestamp deletion
$tables->deluserid = $userid; // Track who deleted
AJAX Security
- โข POST requests for data modification
- โข Input validation on all AJAX endpoints
- โข No direct SQL in AJAX functions
---
๐ Performance Considerations
Database Optimization Tips
1. Indexes Needed:
- charitiescenter(del) - For active record filtering
- charities(center_id, del) - For center-charity relationships
- beneficiaries(charity_id, del) - For charity-beneficiary relationships
2. Query Optimization:
- Report queries use subqueries which could be optimized with JOINs
- Takafol calculation is complex and could benefit from caching
- DataTables pagination helps with large datasets
3. Potential Issues:
- N+1 query problem in report generation (one query per center for takafol count)
- Complex subqueries in main statistics might be slow with large datasets
Performance Improvements
-- Current approach (N+1 queries)
SELECT id, name FROM charitiescenter;
-- Then for each center:
SELECT COUNT(*) FROM beneficiaries WHERE charity_id = 0 AND idnumber IN (...);
-- Better approach (single query with JOINs)
SELECT cs.id, cs.name,
COUNT(DISTINCT c.id) as charity_count,
COUNT(DISTINCT b.id) as beneficiary_count,
COUNT(DISTINCT bt.id) as takafol_count
FROM charitiescenter cs
LEFT JOIN charities c ON c.center_id = cs.id AND c.del < 2
LEFT JOIN beneficiaries b ON b.charity_id = c.id AND b.del < 2
LEFT JOIN beneficiaries bt ON bt.charity_id = 0 AND bt.idnumber = b.idnumber
WHERE cs.del < 2
GROUP BY cs.id;
---
๐ Common Issues & Troubleshooting
1. DataTables Not Loading
Issue: Table shows "Loading..." indefinitely
Cause: AJAX endpoint returning malformed JSON
Debug:
// Check browser console for AJAX errors
// Verify JSON response format
// Check PHP error logs
2. Incorrect Statistics
Issue: Report shows wrong counts
Cause: Complex JOIN queries or deleted records included
Debug:
-- Check data integrity
SELECT del, COUNT(*) FROM charitiescenter GROUP BY del;
SELECT del, COUNT(*) FROM charities GROUP BY del;
SELECT charity_id, COUNT(*) FROM beneficiaries GROUP BY charity_id;
3. Soft Delete Issues
Issue: Deleted records still appearing
Cause: del field not properly filtered
Fix:
-- Always include del filter in queries
WHERE del < 2 -- Show active only
WHERE del = 2 -- Show deleted only
WHERE del IN (0,1,5) -- Show specific statuses
---
๐งช Testing Scenarios
Test Case 1: Basic CRUD Operations
1. Create new charity center with unique name
2. Verify it appears in listing
3. Edit center name
4. Verify update appears
5. Soft delete center
6. Verify it's marked as deleted but still in database
Test Case 2: DataTables Functionality
1. Create multiple charity centers
2. Test search functionality
3. Test sorting by different columns
4. Test pagination with different page sizes
5. Verify action buttons work correctly
Test Case 3: Statistical Report Accuracy
1. Create test data:
- Multiple centers
- Charities linked to centers
- Beneficiaries linked to charities
- Some takafol beneficiaries
2. Generate report
3. Verify counts match manual calculations
---
๐ Related Documentation
- โข CLAUDE.md - PHP 8.2 migration guide
- โข DataTables Integration - AJAX table functionality
- โข RedBeanPHP ORM - Database operations
- โข Charity Management System - Overall system architecture
---
Documented By: AI Assistant
Review Status: โ Complete
Next Review: When major changes occur