Firebird Documentation IndexFirebird 3.0 Developer's GuideDeveloping Web Applications with PHP and Firebird → Creating Controllers and Configuring Routing
Firebird Home Firebird Home Prev: TransactionsFirebird Documentation IndexUp: Developing Web Applications with PHP and FirebirdNext: The Result

Creating Controllers and Configuring Routing

Table of Contents

Using Controllers to Route Requests
A Customer Controller
A Product Controller
A Controller for Invoices
Changing the Routes

The Laravel framework has a powerful routing subsystem. You can display your routes both for simple callback functions and for the controller methods. The simplest sample routes look like this:

Route::get('/', function () {
  return 'Hello World';
});

Route::post('foo/bar', function () {
  return 'Hello World';
});
      

In the first example, we register the handler of the GET request for the website root for the POST request with the route /foo/bar in the second.

You can register a route for several types of HTTP requests. For example:

Route::match(['get', 'post'], 'foo/bar', function () {
  return 'Hello World';
});
      

You can extract some part of the URL from the route for use as a parameter in the handling function:

Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) {
  //
});
      

The parameters of a route are always enclosed in braces.

You can find more details about routing configuration in the Routing chapter of the documentation. Routes are configured in the app/Http/routes.php file in Laravel 5.2 and in the routes/wep.php file in Laravel 5.3.

Using Controllers to Route Requests

Instead of directing the processing of all requests from a single routing file, we can use Controller classes to group related request handlers into separate classes. Controllers are stored in the app/Http/Controllers folder.

All Laravel controllers must extend the basic class of the controller App\Http\Controllers\Controller that exists in Laravel by default. You can read more details about writing controllers at https://laravel.com/docs/5.2/controllers.

A Customer Controller

First, we'll write our Customer controller.

<?php

/*
* Customer controller
*/
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Customer;

class CustomerController extends Controller
{
  /**
   * Show customer list
   *
   * @return Response
   */
  public function showCustomers()
  {
    // get the first 20 customers
    // sorted alphabetically
    $customers = Customer::select()
               ->orderBy('NAME')
               ->take(20)
               ->get();
    var_dump($customers);
  }
}
        

Now we have to link the controller methods to the route. For this, add the following line to routes.php (web.php):

Route::get('/customers', 'CustomerController@showCustomers');
        

The controller name is separated from the method name with the @ character.

To build a quick interface with grids and edit dialog boxes, we will use the zofe/rapyd package that was enabled earlier. Classes from the zofe/rapyd package take up the role of building standard queries to Eloquent ORM models. We will change the customer controller so that it shows data on the grid, allows filtering and record insertions, updates and deletes by way of the edit dialog boxes.

<?php
/*
 * Customer Controller
 */
namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Customer;

class CustomerController extends Controller {

  /**
   * Displays the list of customers
   *
   * @return Response
   */
  public function showCustomers() {
    // Connect widget for search
    $filter = \DataFilter::source(new Customer);
    // Search will be by the name of the supplier
    $filter->add('NAME', 'Name', 'text');
    // Set capture for search button
    $filter->submit('Search');
    // Add the filter reset button and assign it caption
    $filter->reset('Reset');
    // Create a grid to display the filtered data
    $grid = \DataGrid::source($filter);
    // output columns
    // Field, label, sorted
    $grid->add('NAME', 'Name', true);
    $grid->add('ADDRESS', 'Address');
    $grid->add('ZIPCODE', 'Zip Code');
    $grid->add('PHONE', 'Phone');
    // Add buttons to view, edit and delete records
    $grid->edit('/customer/edit', 'Edit', 'show|modify|delete');
    // Add the Add Customer button
    $grid->link('/customer/edit', "Add customer", "TR");
    $grid->orderBy('NAME', 'asc');
    // set the number of records per page
    $grid->paginate(10);
    // display the customer template and pass the filter and grid to it
    return view('customer', compact('filter', 'grid'));
  }

  /**
   * Add, edit and delete a customer
   *
   * @return Response
   */
  public function editCustomer() {
    if (\Input::get('do_delete') == 1)
      return "not the first";
    // create an editor
    $edit = \DataEdit::source(new Customer());
    // Set title of the dialog, depending on the type of operation
    switch ($edit->status) {
      case 'create':
        $edit->label('Add customer');
        break;
      case 'modify':
        $edit->label('Edit customer');
        break;
      case 'do_delete':
        $edit->label('Delete customer');
        break;
      case 'show':
        $edit->label("Customer's card");
        // add a link to go back to the list of customers
        $edit->link('customers', 'Back', 'TR');
        break;
    }
    // set that after the operations of adding, editing and deleting,
    // you need to return to the list of customers
    $edit->back('insert|update|do_delete', 'customers');
    // We add editors of a certain type, assign them a label and
    // associate them with the attributes of the model
    $edit->add('NAME', 'Name', 'text')->rule('required|max:60');
    $edit->add('ADDRESS', 'Address', 'textarea')
         ->attributes(['rows' => 3])
         ->rule('max:250');
    $edit->add('ZIPCODE', 'Zip code', 'text')->rule('max:10');
    $edit->add('PHONE', 'Phone', 'text')->rule('max:14');
    // display the template customer_edit and pass it to the editor
    return $edit->view('customer_edit', compact('edit'));
  }
}
        

blade Templates

By default, Laravel uses the blade template engine. The view function finds the necessary template in the resources/views directory, makes the necessary changes to it and returns the text of the HTML page, at the same time passing to it any variables that are supplied in the template. You can find the description of the blade template syntax at https://laravel.com/docs/5.2/blade.

The Template for Displaying Customers

The template for displaying customers looks like this:

@extends('example')

@section('title', 'Customers')

@section('body')
<h1>Customers</h1>
<p>
  {!! $filter !!}
  {!! $grid !!}
</p>
@stop
          

This template is inherited from the example template and redefines its body section. The $filter and $grid variables contain the HTML code for filtering and displaying data on the grid. The example template is common for all pages.

@extends('master')
@section('title', 'Example of working with Firebird')

@section('body')
<h1>??????</h1>
  @if(Session::has('message'))
  <div class="alert alert-success">
    {!! Session::get('message') !!}
  </div>
  @endif
  <p>Example of working with Firebird.<br/>
  </p>
@stop

@section('content')
@include('menu')
@yield('body')

@stop
          

This template is itself inherited from the master template and also enables the menu template. The menu is quite simple and consists of three items: Customers, Products and Invoices.

<nav class="navbar main">
  <div class="navbar-header">
    <button type="button" class="navbar-toggle"
            data-toggle="collapse" data-target=".main-collapse">
      <span class="sr-only"></span>
      <span class="icon-bar"></span>
      <span class="icon-bar"></span>
      <span class="icon-bar"></span>
    </button>
  </div>
  <div class="collapse navbar-collapse main-collapse">
    <ul class="nav nav-tabs">
      <li @if (Request::is('customer*'))
          class="active"@endif>{!! link_to("customers", "Customers") !!}</li>
      <li @if (Request::is('product*'))
          class="active"@endif>{!! link_to("products", "Products") !!}</li>
      <li @if (Request::is('invoice*'))
          class="active"@endif>{!! link_to("invoices", "Invoices") !!}</li>
    </ul>
  </div>
</nav>
          

The master template enables css styles and JavaScript files with libraries.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>@yield('title', 'An example of a Web application on Firebird')</title>
  <meta name="description" content="@yield('description',
        'An example of a Web application on Firebird')" />
  @section('meta', '')
  <link href="http://fonts.googleapis.com/css?family=Bitter" rel="stylesheet"
        type="text/css" />
  <link href="//netdna.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"
        rel="stylesheet">
  <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css"
        rel="stylesheet">
  {!! Rapyd::styles(true) !!}
</head>
<body>
  <div id="wrap">
    <div class="container">
      <br />
      <div class="row">
        <div class="col-sm-12">
          @yield('content')
        </div>
      </div>
    </div>
  </div>
  <div id="footer">
  </div>
  <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js">
  </script>
  <script src="//netdna.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js">
  </script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.pjax/1.9.6/jquery.pjax.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/riot/2.2.4/riot+compiler.min.js"></script>
  {!! Rapyd::scripts() !!}
</body>
</html>
          

The customer_edit template:

@extends('example')
@section('title', 'Edit customer')
@section('body')
  <p>
    {!! $edit !!}
  </p>
@stop
          

A Product Controller

Implementation of the product controller is similar to what we did for the customer controller:

<?php

/*
 * Product Controller
 */
namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Product;

class ProductController extends Controller {

  /**
   * Displays a list of products
   *
   * @return Response
   */
  public function showProducts() {
    // Connect widget for search
    $filter = \DataFilter::source(new Product);
    // The search will be by product name
    $filter->add('NAME', 'Name', 'text');
    $filter->submit('Search');
    $filter->reset('Reset');
    // Create a grid to display the filtered data
    $grid = \DataGrid::source($filter);
    // output grid columns
    // Field, label, sorting
    $grid->add('NAME', 'Name', true);
    // Set the format with 2 decimal places
    $grid->add('PRICE|number_format[2,., ]', 'Price');
    $grid->row(function($row) {
      // Press the money values to the right
      $row->cell('PRICE')->style("text-align: right");
    });
    // Add buttons to view, edit and delete records
    $grid->edit('/product/edit', 'Edit', 'show|modify|delete');
    // Add the Add product button
    $grid->link('/product/edit', "?????????? ??????", "TR");
    // set sorting
    $grid->orderBy('NAME', 'asc');
    // set the number of records per page
    $grid->paginate(10);
    // display the customer template and pass the filter and grid to it
    return view('product', compact('filter', 'grid'));
  }

  /**
   * Add, edit and delete products
   *
   * @return Response
   */
  public function editProduct() {
    if (\Input::get('do_delete') == 1)
      return "not the first";
    // create editor
    $edit = \DataEdit::source(new Product());
    // Set the title of the dialog, depending on the type of operation
    switch ($edit->status) {
      case 'create':
        $edit->label('Add product');
        break;
      case 'modify':
        $edit->label('Edit product');
        break;
      case 'do_delete':
        $edit->label('Delete product');
        break;
      case 'show':
        $edit->label("Product's card");
        $edit->link('products', 'Back', 'TR');
        break;
    }
    // set that after the operations of adding, editing and deleting, 
    // you need to return to the list of products
    $edit->back('insert|update|do_delete', 'products');
    // We add editors of a certain type, assign them a label and 
    // associate them with the attributes of the model
    $edit->add('NAME', 'Name', 'text')->rule('required|max:100');
    $edit->add('PRICE', 'Price', 'text')->rule('max:19');
    $edit->add('DESCRIPTION', 'Description', 'textarea')
         ->attributes(['rows' => 8])
         ->rule('max:8192');
    // display the template product_edit and pass it to the editor
    return $edit->view('product_edit', compact('edit'));
  }
}
        

A Controller for Invoices

Table of Contents

The Invoice Editor

The invoice controller is more complex and includes an additional function to pay an invoice. Paid invoices are highlighted in a different color. While viewing an invoice, you can also see its items. While editing an invoice, you can edit its items as well. Here is the code for the controller with detailed comments.

<?php
/*
 * Invoice controller
 */
namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Invoice;
use App\Customer;
use App\Product;
use App\InvoiceLine;

class InvoiceController extends Controller {

  /**
   * Show invoice list
   *
   * @return Response
   */
  public function showInvoices() {
    // The invoice model will also select the related suppliers
    $invoices = Invoice::with('customer');
    // Add a widget for search.
    $filter = \DataFilter::source($invoices);
    // Let's filter by date range
    $filter->add('INVOICE_DATE', 'Date', 'daterange');
    // and filter by customer name
    $filter->add('customer.NAME', 'Customer', 'text');
    $filter->submit('Search');
    $filter->reset('Reset');
    // Create a grid to display the filtered data
    $grid = \DataGrid::source($filter);
    // output grid columns
    // Field, caption, sorted
    // For the date we set an additional function that converts 
    // the date into a string
    $grid->add('INVOICE_DATE|strtotime|date[Y-m-d H:i:s]', 'Date', true);
    // for money we will set a format with two decimal places
    $grid->add('TOTAL_SALE|number_format[2,., ]', 'Amount');
    $grid->add('customer.NAME', 'Customer');
    // Boolean printed as Yes/No
    $grid->add('PAID', 'Paid')
         ->cell(function( $value, $row) {
                  return $value ? 'Yes' : 'No';
                });
    // set the function of processing each row
    $grid->row(function($row) {
      // The monetary values are pressed to the right
      $row->cell('TOTAL_SALE')->style("text-align: right");
      // paint the paid waybills in a different color
      if ($row->cell('PAID')->value == 'Yes') {
        $row->style("background-color: #ddffee;");
      }
    });
    // Add buttons to view, edit and delete records
    $grid->edit('/invoice/edit', '??????????????', 'show|modify|delete');
    // Add the button for adding invoices
    $grid->link('/invoice/edit', "?????????? ?????", "TR");
    
    $grid->orderBy('INVOICE_DATE', 'desc');
    // set the number of records per page
    $grid->paginate(10);
    // display the customer template and pass the filter and grid to it
    return view('invoice', compact('filter', 'grid'));
  }

  /**
   * Add, edit and delete invoice
   *
   * @return Response
   */
  public function editInvoice() {
    // get the text of the saved error, if it was
    $error_msg = \Request::old('error_msg');
    // create an invoice invoice editor
    $edit = \DataEdit::source(new Invoice());
    // if the invoice is paid, then we generate an error when trying to edit it
    if (($edit->model->PAID) && ($edit->status === 'modify')) {
      $edit->status = 'show';
      $error_msg = 'Editing is not possible. The account has already been paid.';
    }
    // if the invoice is paid, then we generate an error when trying to delete it
    if (($edit->model->PAID) && ($edit->status === 'delete')) {
      $edit->status = 'show';
      $error_msg = 'Deleting is not possible. The account has already been paid.';
    }
    // Set the label of the dialog, depending on the type of operation
    switch ($edit->status) {
      case 'create':
        $edit->label('Add invoice');
        break;

      case 'modify':
        $edit->label('Edit invoice');
        break;

      case 'do_delete':
        $edit->label('Delete invoice');
        break;

      case 'show':
        $edit->label('Invoice');
        $edit->link('invoices', 'Back', 'TR');
        // If the invoice is not paid, we show the pay button
        if (!$edit->model->PAID)
          $edit->link('invoice/pay/' . $edit->model->INVOICE_ID,
                      'Pay', 'BL');
        break;
    }
    // set that after the operations of adding, editing and deleting, 
    // we return to the list of invoices
    $edit->back('insert|update|do_delete', 'invoices');
    // set the "date" field, that it is mandatory
    // The default is the current date
    $edit->add('INVOICE_DATE', '????', 'datetime')
         ->rule('required')
         ->insertValue(date('Y-m-d H:i:s'));
    // add a field for entering the customer. When typing a customer name, 
    // a list of prompts will be displayed
    $edit->add('customer.NAME', 'Customer', 'autocomplete')
         ->rule('required')
         ->options(Customer::lists('NAME', 'CUSTOMER_ID')
                           ->all());
    // add a field that will display the invoice amount, read-only
    $edit->add('TOTAL_SALE', 'Amount', 'text')
         ->mode('readonly')
         ->insertValue('0.00');
    // add paid checkbox
    $paidCheckbox = $edit->add('PAID', 'Paid', 'checkbox')
                         ->insertValue('0')
                         ->mode('readonly');
    $paidCheckbox->checked_output = 'Yes';
    $paidCheckbox->unchecked_output = 'No';
    // create a grid to display the invoice line rows
    $grid = $this->getInvoiceLineGrid($edit->model, $edit->status);
    // we display the invoice_edit template and pass the editor and grid to 
    // it to display the invoice invoice items
    return $edit->view('invoice_edit', compact('edit', 'grid', 'error_msg'));
  }

  /**
   * Payment of invoice
   *
   * @return Response
   */
  public function payInvoice($id) {
    try {
      // find the invoice by ID
      $invoice = Invoice::findOrFail($id);
      // call the payment procedure
      $invoice->pay();
    } catch (\Illuminate\Database\QueryException $e) {
      // if an error occurs, select the exclusion text
      $pos = strpos($e->getMessage(), 'E_INVOICE_ALREADY_PAYED');
      if ($pos !== false) {
        // redirect to the editor page and display the error there
        return redirect('invoice/edit?show=' . $id)
             ->withInput(['error_msg' => 'Invoice already paid']);
      } else
        throw $e;
    }
    // redirect to the editor page
    return redirect('invoice/edit?show=' . $id);
  }

  /**
   * Returns the grid for the invoice item
   * @param \App\Invoice $invoice
   * @param string $mode
   * @return \DataGrid
   */
  private function getInvoiceLineGrid(Invoice $invoice, $mode) {
    // Get invoice items
    // For each ivoice item, the associated product will be initialized
    $lines = InvoiceLine::with('product')
                ->where('INVOICE_ID', $invoice->INVOICE_ID);
    // Create a grid for displaying invoice items
    $grid = \DataGrid::source($lines);
    // output grid columns
    // Field, caption, sorted
    $grid->add('product.NAME', 'Name');
    $grid->add('QUANTITY', 'Quantity');
    $grid->add('SALE_PRICE|number_format[2,., ]', 'Price')
         ->style('min-width: 8em;');
    $grid->add('SUM_PRICE|number_format[2,., ]', 'Amount')
         ->style('min-width: 8em;');
    // set the function of processing each row
    $grid->row(function($row) {
      $row->cell('QUANTITY')->style("text-align: right");
      // The monetary values are pressed to the right
      $row->cell('SALE_PRICE')->style("text-align: right");
      $row->cell('SUM_PRICE')->style("text-align: right");
    });
    if ($mode == 'modify') {
      // Add buttons to view, edit and delete records
      $grid->edit('/invoice/editline', '??????????????', 'modify|delete');
      // Add a button to add an invoice item
      $grid->link('/invoice/editline?invoice_id=' . $invoice->INVOICE_ID,
                  "Add item", "TR");
    }
    return $grid;
  }

  /**
   * Add, edit and delete invoice items
   *
   * @return Response
   */
  public function editInvoiceLine() {
    if (\Input::get('do_delete') == 1)
      return "not the first";
    $invoice_id = null;
    // create the editor of the invoice item
    $edit = \DataEdit::source(new InvoiceLine());
    // Set the label of the dialog, depending on the type of operation
    switch ($edit->status) {
      case 'create':
        $edit->label('Add invoice item');
        $invoice_id = \Input::get('invoice_id');
        break;

      case 'modify':
        $edit->label('Edit invoice item');
        $invoice_id = $edit->model->INVOICE_ID;
        break;

      case 'delete':
        $invoice_id = $edit->model->INVOICE_ID;
        break;

      case 'do_delete':
        $edit->label('Delete invoice item');
        $invoice_id = $edit->model->INVOICE_ID;
        break;
    }
    // make url to go back
    $base = str_replace(\Request::path(), '', strtok(\Request::fullUrl(), '?'));
    $back_url = $base . 'invoice/edit?modify=' . $invoice_id;
    // set the page to go back
    $edit->back('insert|update|do_delete', $back_url);
    $edit->back_url = $back_url;
    // add a hidden field with an invoice code
    $edit->add('INVOICE_ID', '', 'hidden')
         ->rule('required')
         ->insertValue($invoice_id)
         ->updateValue($invoice_id);
    // Add a field for entering the goods. When you type the product name, 
    // a list of prompts is displayed.
    $edit->add('product.NAME', 'Name', 'autocomplete')
         ->rule('required')
         ->options(Product::lists('NAME', 'PRODUCT_ID')->all());
    // Field for input quantity
    $edit->add('QUANTITY', 'Quantity', 'text')
         ->rule('required');
    // display the template invoice_line_edit and pass it to the editor
    return $edit->view('invoice_line_edit', compact('edit'));
  }
}
        

The Invoice Editor

The invoice editor has a view that is not standard for zofe/rapyd because we want to display a grid with invoice items. To do that, we change the invoice_edit template as follows:

@extends('example')

@section('title','Edit invoice')

@section('body')
  <div class="container">
    {!! $edit->header !!}
    @if($error_msg)
      <div class="alert alert-danger">
        <strong>??????!</strong> {{ $error_msg }}
      </div>
    @endif
    {!! $edit->message !!}
    @if(!$edit->message)
      <div class="row">
        <div class="col-sm-4">
          {!! $edit->render('INVOICE_DATE') !!}
          {!! $edit->render('customer.NAME') !!}
          {!! $edit->render('TOTAL_SALE') !!}
          {!! $edit->render('PAID') !!}
        </div>
      </div>
      {!! $grid !!}
    @endif
    {!! $edit->footer !!}
  </div>
@stop
          

Changing the Routes

Now that all controllers are written, we are going to change the routes so that our website opens the list of invoices on the start page. Be aware that routes are configured in the file app/Http/routes.php in Laravel 5.2 and in routes/wep.php in Laravel 5.3.

Route::get('/', 'InvoiceController@showInvoices');
Route::get('/customers', 'CustomerController@showCustomers');
Route::any('/customer/edit', 'CustomerController@editCustomer');
Route::get('/products', 'ProductController@showProducts');
Route::any('/product/edit', 'ProductController@editProduct');
Route::get('/invoices', 'InvoiceController@showInvoices');
Route::any('/invoice/edit', 'InvoiceController@editInvoice');
Route::any('/invoice/pay/{id}', 'InvoiceController@payInvoice');
Route::any('/invoice/editline', 'InvoiceController@editInvoiceLine');
        

Here the /invoice/pay/{id} route picks up the invoice identifier from the URL and sends it to the payInvoice method. The rest of the routes should be self-explanatory.

Prev: TransactionsFirebird Documentation IndexUp: Developing Web Applications with PHP and FirebirdNext: The Result
Firebird Documentation IndexFirebird 3.0 Developer's GuideDeveloping Web Applications with PHP and Firebird → Creating Controllers and Configuring Routing