# **Chapter 4: Understanding the Event-Oriented Paradigm** ## **4.1 The Paradigm Shift: From Routes to Events** SartajPHP framework don't use a **route-to-controller** model where URLs are mapped to controller methods. SartajPHP revolutionizes this with an **event-oriented** approach where URLs trigger **PageEvents** in applications. ### **4.1.1 The Traditional MVC Limitation** Traditional MVC. 1. **Routing complexity** grows with application size 2. **Controller bloat** as all user-related logic piles up 3. **URL fragility** when refactoring 4. **Limited reuse** of logic across different entry points ### **4.1.2 The SartajPHP uses Gate‑Driven Event Architecture (GDEA)** In SartajPHP, the simple user management: Browser send Request to SartajPHP Framework which translate URL into PageEvents and pass to your registered Gate file `UserGate.php` register inside `reg.php` file with registerGate function. Framework find and load your Gate Class and transfer request as events. Framework don't use autoload to load your Gate classes, so your Gate classes should be registered in `reg.php` file. If your Gate class don't handle Browser Requested Events then Browser received empty output but if Browser requested to non Registered Gate then receive error output page not found. ```php // apps/UserGate.php class UserGate extends \Sphp\tools\BasicGate { public function page_new() { // List users (GET /user.html) $this->renderUserList(); } public function page_submit() { // Handle form submission (POST /user.html) $this->processUserForm(); } public function page_view() { // View user (GET /user-view-123.html) $this->showUserDetails($this->page->evtp); } public function page_event_search($evtp) { // Search users (GET /user-search-john.html) $this->searchUsers($evtp); } public function page_event_export($evtp) { // Export users (GET /user-export-csv.html) $this->exportUsers($evtp); } } ``` **Key advantages:** - **No routing configuration** - URLs map directly to methods - **Natural organization** - Events group related functionality - **Easy discovery** - See all supported actions in one class - **Flexible extension** - Add new events without configuration ## **4.2 PageEvents: The Core Abstraction** ### **4.2.1 Built-in PageEvents** SartajPHP recognizes several built-in PageEvents that handle common web patterns: | PageEvent | URL Pattern | HTTP Method | Typical Use | |-----------|-------------|-------------|-------------| | `page_new()` | `gate.html` | GET | Initial page load, listing data | | `page_submit()` | `gate.html` | POST | Form submission handler | | `page_insert()` | `gate.html` | POST | Create new records (auto-triggered) | | `page_update()` | `gate.html` | POST | Update existing records (auto-triggered) | | `page_view()` | `gate-view-id.html` | GET | View single record | | `page_delete()` | `gate-delete-id.html` | GET | Delete record | | `page_event_*()` | `gate-*-param.html` | GET/POST | Custom application events | ### **4.2.2 The page_submit() Dispatcher** The `page_submit()` method is special—it acts as a dispatcher: ```php public function page_submit() { // 1. Validate all Components if (getCheckErr()) { // Validation failed, return to form $this->setFrontFile($this->frtMain); return; } /** 2. Determine if this is insert or update * you can also use page_insert and page_update events * which auto trigger with form submission type. * Manually you can check Record ID */ if ($this->frtMain->form2->getRecID() == "") { $this->insertMe(); } else { $this->updateMe(); } } ``` This automatic dispatch eliminates boilerplate code for form handling. ### **4.2.3 Custom PageEvents** Any method starting with `page_event_` becomes a custom PageEvent: ```php // URL: blog-search-php.html public function page_event_search($evtp) { // $evtp = "php" $results = $this->searchPosts($evtp); $this->displayResults($results); } // URL: user-activate-abc123.html public function page_event_activate($evtp) { // $evtp = "abc123" $this->activateUser($evtp); $this->page->forward(getGateURL("user")); } // URL: shop-filter-price-asc-category-electronics.html public function page_event_filter($evtp) { // $evtp = "price-asc-category-electronics" $filters = explode('-', $evtp); // Process complex filter parameters } ``` ## **4.3 URL-to-Event Mapping Rules** Understanding how URLs map to PageEvents is crucial for effective SartajPHP development. ### **4.3.1 Basic Mapping Rules** ``` URL Pattern: <gate>-<event>-<parameter>.html │ │ │ │ │ │ │ └── Always .html extension │ │ │ │ │ └── Event parameter (optional) │ │ │ └── Event name (or built-in event) │ └── Gate (registered application name) ``` **Examples:** - `index.html` → `IndexGate.php::page_new()` - `blog-view-123.html` → `BlogGate.php::page_view()` with `$evtp = "123"` - `shop-category-electronics.html` → `ShopGate.php::page_event_category("electronics")` - `admin-user-delete-456.html` → `AdminGate.php::page_event_user("delete-456")` ### **4.3.2 Complex Parameter Handling** For multi-part parameters, use delimiter conventions: ```php // URL: products-filter-price-100-200-category-electronics.html public function page_event_filter($evtp) { // $evtp = "price-100-200-category-electronics" // Parse parameters $parts = explode('-', $evtp); $filters = []; $currentKey = null; foreach ($parts as $part) { if (in_array($part, ['price', 'category', 'brand', 'rating'])) { $currentKey = $part; $filters[$currentKey] = []; } elseif ($currentKey) { $filters[$currentKey][] = $part; } } // $filters = ['price' => ['100', '200'], 'category' => ['electronics']] $this->applyFilters($filters); } ``` ### **4.3.3 Alternative: Query Parameters** While SartajPHP prefers path-based parameters, query strings can be used for optional data: ```php // URL: search.html?q=keyword&page=2&sort=date public function page_new() { $query = $this->Client->request('q'); $page = (int)$this->Client->request('page', 1); $sort = $this->Client->request('sort', 'relevance'); $this->performSearch($query, $page, $sort); } ``` ## **4.4 Application Lifecycle in Detail** Understanding the complete lifecycle helps you place code in the right events. ### **4.4.1 Complete Lifecycle Sequence** ``` ┌─────────────────────────────────────────────────────────┐ │ Request Received │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ Engine Initialization │ │ - Load framework core │ │ - Parse CLI arguments (if console) │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ Router Analysis │ │ - Extract Gate from URL │ │ - Identify event type │ │ - Parse event parameters │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ Application Loading │ │ - Check registration in reg.php │ │ - Load Gate.php file │ │ - Create Gate instance │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ prerun.php │ │ - Security headers │ │ - Global initialization │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ Gate::onstart() │ │ - Gate-specific setup │ │ - FrontFile creation │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ FrontFile Parsing │ │ - Parse .front file │ │ - Create Components │ │ - Execute Parse Phase Fusion │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ Gate::onfrontinit() │ │ - FrontFile initialized callback │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ Gate::onfrontprocess() │ │ - FrontFile ready callback, after onaftercreate │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ Gate::onready() │ │ - Gate ready to process │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ Gate::onrun() │ │ - Pre-PageEvent processing │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ PageEvent Execution │ │ - page_new() or page_event_*() or page_submit() │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ FrontFile Execution │ │ - Execute Render Phase Fusion │ │ - Process runtime attributes │ │ - Generate HTML output │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ Gate::onrender() │ │ - Final output modification │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ MasterFile Merge │ │ - Apply master template │ │ - Inject menus, headers, footers │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ Cache Store │ │ - Check cacheability │ │ - Store if cacheable │ └───────────────────────────┬─────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────┐ │ Response Sent │ └─────────────────────────────────────────────────────────┘ ``` ### **4.4.2 Lifecycle Event Purposes** **onstart()** - Setup phase ```php public function onstart() { // Check authentication, Should be First line // who has Application's Gate Permit, set by SigninGate.php in session with // setSession('ADMIN', intval($authcookie['sid'])) $this->getAuthenticate("MEMBER,ADMIN"); // check permissions if you use permssion system $this->page->getAuthenticatePerm("view,add"); // Initialize FrontFiles $this->frtMain = new FrontFile($this->mypath . "/fronts/main.front"); // Load configuration $this->config = $this->loadConfig(); // Connect to external services $this->apiClient = new ApiClient(); } ``` **onready()** - Preparation phase ```php public function onready() { // All Components are created // FrontFile is parsed but not rendered // Good place for Component initialization $searchBox = $this->frtMain->getComponent("txtSearch"); $searchBox->fu_setDefaultValue($this->getLastSearch()); } ``` **onrun()** - Pre-processing phase ```php public function onrun() { // Executes before PageEvent // Good for authentication checks, logging if (!$this->hasPermission("view_page")) { // this will stop futher execution immediately with // call of SphpBase::engine()->exitMe(); $this->page->forward(getGateURL("unauthorized")); } $this->logAccess(); } ``` **onrender()** - Final modification phase ```php public function onrender() { // After FrontFile rendering, before MasterFile // You Can't modify Front File final output // but you change master file or add Javascript,CSS code if ($this->page->getAuthenticateType() != "ADMIN") { // Inject analytics code addHeaderJSCode("ga('send', 'pageview');"); } // or file links addFileLink("mytheme.css"); // or add Front Places for master addFrontPlace("banner",__DIR__ . "/banner2.front"); } ``` ## **4.5 Designing Event-Oriented Applications** ### **4.5.1 Single Responsibility Applications** Instead of monolithic controllers, create focused applications: ``` apps/ ├── UserGate.php # User management ├── BlogGate.php # Blog system ├── ShopGate.php # E-commerce ├── CartGate.php # Shopping cart ├── CheckoutGate.php # Checkout process ├── AdminGate.php # Administration └── ApiGate.php # JSON API ``` Each app handles a cohesive set of related events. ### **4.5.2 Event Naming Conventions** Use consistent naming for discoverability: ```php // User management events public function page_event_login($evtp) {} // user-login.html public function page_event_register($evtp) {} // user-register.html public function page_event_profile($evtp) {} // user-profile-edit.html public function page_event_password($evtp) {} // user-password-reset.html // Content management events public function page_event_create($evtp) {} // blog-create.html public function page_event_edit($evtp) {} // blog-edit-123.html public function page_event_publish($evtp) {} // blog-publish-123.html public function page_event_archive($evtp) {} // blog-archive-123.html // E-commerce events public function page_event_add($evtp) {} // cart-add-456.html public function page_event_update($evtp) {} // cart-update-456.html public function page_event_remove($evtp) {} // cart-remove-456.html public function page_event_clear($evtp) {} // cart-clear.html ``` ### **4.5.3 Parameter Design Patterns** **Simple ID parameters:** ```php // URL: product-view-123.html public function page_view() { $productId = (int)$this->page->evtp; // Use $productId } ``` **Slug-based parameters:** ```php // URL: blog-post-my-awesome-article.html public function page_event_post($evtp) { $slug = $evtp; // "my-awesome-article" $post = $this->getPostBySlug($slug); } ``` **Action-object patterns:** ```php // URL: admin-user-activate-abc123.html public function page_event_user($evtp) { // $evtp = "activate-abc123" list($action, $userId) = explode('-', $evtp, 2); switch ($action) { case 'activate': $this->activateUser($userId); break; case 'deactivate': $this->deactivateUser($userId); break; case 'promote': $this->promoteUser($userId); break; } } ``` **Multi-parameter patterns:** ```php // URL: search-query-php-category-tutorials-sort-date.html public function page_event_search($evtp) { $params = []; $parts = explode('-', $evtp); for ($i = 0; $i < count($parts); $i += 2) { if (isset($parts[$i + 1])) { $params[$parts[$i]] = $parts[$i + 1]; } } // $params = ['query' => 'php', 'category' => 'tutorials', 'sort' => 'date'] $this->performSearch($params); } ``` ## **4.6 Advanced Event Patterns** ### **4.6.1 Event Chaining** Events can trigger other events: ```php public function page_event_process($evtp) { // Process data $result = $this->processData($evtp); if ($result->needsReview()) { // Chain to review event in another App $this->page->forward(getEventURL("review", $result->id,"gate")); } else { // Chain to complete event handler inside this Gate $this->page_event_complete($result->id); } } ``` ### **4.6.2 Event Middleware** Add pre/post processing to events: ```php public function page_event_download($evtp) { // Pre-processing $this->logDownloadAttempt(); if (!$this->validateDownloadAccess($evtp)) { return $this->page->forward(getGateURL("access-denied")); } // Main event logic $file = $this->getDownloadFile($evtp); $this->serveFile($file); // Post-processing $this->recordDownloadComplete(); } ``` ### **4.6.3 Event Broadcasting** NativeGates can broadcast events to multiple clients: ```php // In a NativeGate (chat server) public function page_event_message($evtp) { $message = $this->Client->post("message"); $sender = $this->Client->post("user"); // Broadcast to all connected clients $this->sendAll([ 'type' => 'message', 'sender' => $sender, 'message' => $message, 'timestamp' => time() ]); } ``` ## **4.7 Error Handling in Events** ### **4.7.1 Structured Error Responses** ```php public function page_event_api($evtp) { try { $data = $this->processApiRequest($evtp); $this->JSServer->addJSONReturnBlock([ 'success' => true, 'data' => $data, 'timestamp' => time() ]); } catch (ValidationException $e) { $this->JSServer->addJSONReturnBlock([ 'success' => false, 'error' => 'validation_failed', 'message' => $e->getMessage(), 'fields' => $e->getErrors() ]); } catch (AuthenticationException $e) { $this->JSServer->addJSONReturnBlock([ 'success' => false, 'error' => 'authentication_required', 'message' => 'Please log in to continue' ]); } catch (Exception $e) { // Log full error for debugging error_log("API error: " . $e->getMessage()); // Generic error for client $this->JSServer->addJSONReturnBlock([ 'success' => false, 'error' => 'server_error', 'message' => 'An unexpected error occurred' ]); } } ``` ### **4.7.2 User-Friendly Error Pages** ```php public function page_event_process($evtp) { if (!$this->validateInput($evtp)) { $this->setFrontFile($this->frtError); $this->frtError->getComponent("errorMessage") ->setInnerHTML("Invalid input provided"); /** * inform all Framework about error, * So Further processing can effect. Alert Component can * automatically display all user errors in Front File. just use * <alert id="app" runat="server"></alert> */ setErr("app","Invalid input provided"); return; } try { $this->processData($evtp); } catch (DatabaseException $e) { $this->setFrontFile($this->frtError); $this->frtError->getComponent("errorMessage") ->setInnerHTML("Database error. Please try again."); $this->debug->write_log("Database error:-" . $e->getMessage()); //send error message on console window of browser $this->debug->println("Database error:-" . $e->getMessage()); } } ``` ## **4.8 Testing Event-Oriented Applications** ### **4.8.1 Unit Testing Events** ```php // tests/UserAppTest.php class UserAppTest extends \PHPUnit\Framework\TestCase { public function testLoginEvent() { // Mock the App $app = $this->createMock(UserApp::class); // Test successful login $_POST['username'] = 'testuser'; $_POST['password'] = 'password123'; $app->expects($this->once()) ->method('authenticateUser') ->with('testuser', 'password123') ->willReturn(true); // Would need framework-specific testing setup } } ``` ### **4.8.2 Integration Testing URLs** ```php // tests/UrlMappingTest.php class UrlMappingTest extends \PHPUnit\Framework\TestCase { public function urlProvider() { return [ ['index.html', 'index', 'new', ''], ['blog-view-123.html', 'blog', 'view', '123'], ['shop-category-electronics.html', 'shop', 'category', 'electronics'], ]; } /** * @dataProvider urlProvider */ public function testUrlParsing($url, $ExpectedGate, $expectedEvent, $expectedParam) { $result = parseSartajUrl($url); $this->assertEquals($ExpectedGate, $result['gate']); $this->assertEquals($expectedEvent, $result['event']); $this->assertEquals($expectedParam, $result['parameter']); } } ``` ## **4.9 Performance Considerations** ### **4.9.1 Event-Specific Caching** ```php // In cachelist.php // Cache public events longer addCacheList("blog-view", 3600, "ce"); // 1 hour for blog views addCacheList("shop-product", 1800, "ce"); // 30 minutes for products // Cache personalized events shorter or not at all addCacheList("user-profile", 300, "ce"); // 5 minutes for profiles // No cache for cart, so no need any code // In the App public function page_event_expensive($evtp) { $cacheKey = "expensive_" . md5($evtp); // read database through cache $result = $this->dbEngine->fetchQuery("SELECT * FROM users WHERE status=1",300); $str1 = ""; foreach($result["news"] as $key=>$row){ $str1 .= '<p>'. $row["user_name"] .'<p>'; } $divuser = $this->frtMain->getComponent("divuser"); $divuser->setInnerHTML($str1); $divuser->setAttribute("class","card-body"); $divuser->setPreTag('<div class="card">'); $divuser->setPostTag('</div>'); if ($cached = SphpBase::sphp_api()->readFromCache($cacheKey,300)) { // 5 minutes $this->frtMain->div1->setInnerHTML($cached); return; } $result = $this->expensiveOperation($evtp); SphpBase::sphp_api()->saveToCache($cacheKey, $result); $this->frtMain->div1->setInnerHTML($result); } ``` ### **4.9.2 Lazy Event Initialization** ```php public function onstart() { // Don't load heavy resources unless needed if ($this->page->getEvent() === 'report') { $this->reportGenerator = new HeavyReportGenerator(); $this->chartLibrary = new ChartingLibrary(); } } public function page_event_report($evtp) { // Resources already loaded in onstart() $report = $this->reportGenerator->generate($evtp); $charts = $this->chartLibrary->render($report); $this->displayReport($report, $charts); } ``` ## **4.10 Real-World Example: Blog System** Let's build a complete blog system using event-oriented design: ```php // apps/BlogGate.php class BlogGate extends \Sphp\tools\BasicGate { private $frtMain; private $frtPost; private $frtAdmin; public function onstart() { // GUEST (No login), MEMBER and ADMIN can access this App $this->getAuthenticate("GUEST,MEMBER,ADMIN"); // only few events are secure for ADMIN, So loose security type App $this->frtMain = new FrontFile($this->mypath . "/fronts/blog_main.front"); $this->frtPost = new FrontFile($this->mypath . "/fronts/blog_post.front"); $this->frtAdmin = new FrontFile($this->mypath . "/fronts/blog_admin.front"); } // Public events public function page_new() { // List blog posts (GET /blog.html) $posts = $this->getRecentPosts(10); $this->frtMain->getComponent("postList")->setInnerHTML($posts); $this->setFrontFile($this->frtMain); } public function page_view() { // View single post (GET /blog-view-123.html) $postId = (int)$this->page->getEventParameter(); $post = $this->getPost($postId); if (!$post) { $this->page->forward(getGateURL("not-found")); return; } $this->frtPost->getComponent("postTitle")->setInnerHTML($post->title); $this->frtPost->getComponent("postContent")->setInnerHTML($post->content); $this->setFrontFile($this->frtPost); // Increment view count $this->incrementViews($postId); } public function page_event_category($evtp) { // Posts by category (GET /blog-category-php.html) $posts = $this->getPostsByCategory($evtp); $this->frtMain->getComponent("postList")->setInnerHTML($posts); $this->frtMain->getComponent("pageTitle") ->setInnerHTML("Posts in category: " . $evtp); $this->setFrontFile($this->frtMain); } public function page_event_search($evtp) { // Search posts (GET /blog-search-keywords.html) $posts = $this->searchPosts($evtp); $this->frtMain->getComponent("postList")->setInnerHTML($posts); $this->frtMain->getComponent("pageTitle") ->setInnerHTML("Search results for: " . $evtp); $this->setFrontFile($this->frtMain); } // Admin events (protected) public function page_event_admin($evtp) { // Admin dashboard (GET /blog-admin.html) if (!$this->page->getAuthenticateType() == "ADMIN") { $this->page->forward(getGateURL("login")); return; } $stats = $this->getBlogStats(); $this->frtAdmin->getComponent("stats")->setInnerHTML($stats); $this->setFrontFile($this->frtAdmin); } public function page_event_create($evtp) { // Create post form (GET /blog-create.html) $this->requireAdmin(); $this->setFrontFile($this->frtAdmin); } public function page_submit() { // Handle post creation/update (POST /blog.html) $this->requireAdmin(); if (getCheckErr()) { // Validation failed $this->setFrontFile($this->frtAdmin); return; } if ($this->page->isInsertMode()) { $postId = $this->createPost(); $this->page->forward(getEventURL("edit", $postId)); } else { $this->updatePost(); $this->page->forward(getEventURL("admin")); } } public function page_event_publish($evtp) { // Publish post (GET /blog-publish-123.html) $this->requireAdmin(); $this->publishPost((int)$evtp); $this->page->forward(getEventURL("admin")); } // Helper methods private function requireAdmin() { if (!$this->page->getAuthenticateType() == "ADMIN") { $this->page->forward(getGateURL("login")); } } private function getRecentPosts($limit) { $db = SphpBase::dbEngine(); return $db->executeQuery( "SELECT * FROM posts WHERE status = 'published' ORDER BY published_at DESC LIMIT $limit"); } // ... other helper methods } ``` ## **4.11 Chapter Summary** The event-oriented paradigm in SartajPHP represents a fundamental shift in how we think about web application architecture: 1. **Natural Mapping**: URLs directly correspond to application events 2. **Reduced Boilerplate**: No routing configuration needed 3. **Better Organization**: Related functionality grouped in event methods 4. **Flexible Extension**: New features added as new events 5. **Clear Lifecycle**: Well-defined execution order Key takeaways: - Use `page_new()` for initial page loads - Use `page_submit()` for form handling (auto-dispatches to insert/update) - Use `page_event_*()` for custom functionality - Design URLs that make sense for users and developers - Group related events in focused applications This paradigm enables building maintainable, scalable applications that grow gracefully with your needs. In the next chapter, we'll explore the FrontFile system and how to create dynamic, reusable UI components. --- *Next: Chapter 5 dives into the FrontFile system, showing how to create pure HTML templates enhanced with server-side capabilities.*