Tutorial Phalcon 2: Penjelasan INVO

Dalam tutorial phalcon yg kedua ini, kami akan menjelaskan aplikasi yang lebih lengkap untuk memperdalam pembangunan aplikasi dengan Phalcon. INVO adalah salah satu aplikasi yang telah kita buat sebagai sampel. Invo adalah situs kecil yang memungkinkan pengguna untuk menghasilkan faktur, dan melakukan tugas-tugas lain seperti mengelola pelanggan dan produk mereka. Anda dapat mengkloning kode dari Github .

Dan juga, invo dibuat dengan Bootstrap sebagai framework (CSS) di sisi klien. Meskipun aplikasi tidak menghasilkan faktur, masih dapat digunakan sebagai contoh untuk memahami bagaimana framework bekerja.

Struktur proyek 

Setelah Anda clone proyek di root dokumen Anda, Anda akan melihat struktur sebagai berikut:

invo/
    app/
        app/config/
        app/controllers/
        app/library/
        app/models/
        app/plugins/
        app/views/
    public/
        public/bootstrap/
        public/css/
        public/js/
    schemas/

Seperti yang Anda tahu, Phalcon tidak memaksakan struktur file tertentu untuk pengembangan aplikasi. Proyek ini menyediakan struktur MVC sederhana dan sebuah root dokumen publik.

Setelah Anda membuka aplikasi di browser Anda http://localhost/invo Anda akan melihat sesuatu seperti ini:

../_images/invo-1.png

Aplikasi ini dibagi menjadi dua bagian, frontend, yaitu bagian umum di mana pengunjung dapat menerima informasi tentang invo dan meminta informasi kontak. Bagian kedua adalah backend, daerah administratif di mana pengguna yg terdaftar dapat mengelola / produk dan pelanggannya.

Routing 

Invo menggunakan route standar yang built-in dengan komponen Router. Rute-rute ini cocok dengan pola berikut: /:controller/:action/:params. Ini berarti bahwa bagian pertama dari URI adalah controller, kedua action dan sisanya adalah parameter.

maka rute /session/register mengeksekusi controller SessionController dan action registerAction.

Konfigurasi 

Invo memiliki sebuah file konfigurasi yang menentukan parameter umum dalam aplikasi. File ini dibaca dalam beberapa baris pertama dari file bootstrap (public/index.php):

<?php

//Read the configuration
$config = new Phalcon\Config\Adapter\Ini('../app/config/config.ini');

Phalcon\Config memungkinkan kita untuk memanipulasi file dengan cara seakan-akan berorientasi objek. File konfigurasi berisi pengaturan berikut:

[database]
host     = localhost
username = root
password = secret
name     = invo

[application]
controllersDir = /../app/controllers/
modelsDir      = /../app/models/
viewsDir       = /../app/views/
pluginsDir     = /../app/plugins/
libraryDir     = /../app/library/
baseUri        = /invo/

;[metadata]
;adapter = "Apc"
;suffix = my-suffix
;lifetime = 3600

Phalcon belum memiliki pengaturan konvensi yang telah ditentukan (bawaan). Pengaturan bagian (section) membantu kita untuk mengatur opsi yang sesuai. Dalam file ini ada tiga bagian yang akan digunakan nanti.

Autoloader 

Bagian kedua yang muncul dalam file bootstrap (public/index.php) adalah autoloader. Autoloader meregistrasikan seperangkat direktori di mana aplikasi akan mencari class yang akan butuhkan.

<?php

$loader = new \Phalcon\Loader();

$loader->registerDirs(
    array(
        $config->application->controllersDir,
        $config->application->pluginsDir,
        $config->application->libraryDir,
        $config->application->modelsDir,
    )
)->register();

Perhatikan bahwa kode di atas telah meregistrasikan direktori yang didefinisikan dalam file konfigurasi. Satu-satunya direktori yang tidak terdaftar adalah viewsDir, karena mengandung HTML + PHP file tapi tidak ada class.

Penanganan Request 

Jika kita melompat ke akhir file, permintaan tersebut akhirnya ditangani oleh Phalcon\MVC\Application yang menginisialisasi dan mengeksekusi semua yang diperlukan untuk membuat aplikasi berjalan:

<?php

$app = new \Phalcon\Mvc\Application($di);

echo $app->handle()->getContent();

Dependency Injection 

Lihatlah baris pertama dari blok kode di atas, konstruktor class Application menerima variabel $di sebagai argumen. Apa tujuan dari variabel itu? Phalcon adalah kerangka kerja yang sangat dipisahkan (decoupled) jadi kita perlu komponen yang bertindak sebagai perekat untuk membuat semuanya bekerja bersama-sama. Komponen tersebut adalah Phalcon\DI. Ini adalah wadah layanan (service container) yang juga melakukan dependency injection, menginisialisasi semua komponen seperti yang dibutuhkan oleh aplikasi.

Ada banyak cara mendaftar layanan dalam wadah. Dalam invo, sebagian besar layanan telah terdaftar menggunakan fungsi anonim. Berkat ini, objek-objek yang dipakai dalam cara yang malas (lazy-loader), mengurangi sumber daya yang dibutuhkan oleh aplikasi.

Misalnya, dalam kutipan berikut layanan session terdaftar. Fungsi anonim hanya akan dipanggil saat aplikasi membutuhkan akses ke data session:

<?php

//Start the session the first time a component requests the session service
$di->set('session', function() {
    $session = new Phalcon\Session\Adapter\Files();
    $session->start();
    return $session;
});

Di sini, kita memiliki kebebasan untuk mengubah adapter, melakukan inisialisasi tambahan dan banyak lagi. Perhatikan bahwa layanan ini telah didaftarkan menggunakan nama “session”. Ini adalah konvensi yang akan memungkinkan framework untuk mengidentifikasi layanan aktif dalam service container.

Permintaan dapat menggunakan banyak service dan mendaftarkan setiap service satu per satu bisa menjadi tugas yang rumit. Oleh karena itu, framework menyediakan varian Phalcon\DI  bernama Phalcon\DI\FactoryDefault yang bertugas untuk mendaftarkan semua service menyediakan kerangka kerja full-stack.

<?php

// The FactoryDefault Dependency Injector automatically registers the
// right services providing a full-stack framework
$di = new \Phalcon\DI\FactoryDefault();

Perintah tersebut meregistrasikan berbagai service utama dengan komponen yang disediakan oleh framework sebagai standar. Jika kita ingin override definisi beberapa service, kita bisa mengatur kembali seperti yang kita lakukan di atas dengan “session”. Ini adalah alasan adanya variabel $di.

Masuk ke Application 

Fasilitas “log in”  akan memungkinkan kita untuk bekerja pada kontroler backend. Pemisahan antara controller backend dan frontend yang hanya secara logical. Semua pengendali berada di direktori yang sama (app/controllers/).

Untuk masuk ke sistem, pengguna harus memiliki username dan password yang valid. Pengguna disimpan dalam tabel “users” dalam database “invo”.

Sebelum kita dapat memulai session, kita perlu mengkonfigurasi koneksi ke database dalam aplikasi. Sebuah layanan yang disebut “db” sudah diatur dalam wadah pelayanan dengan informasi koneksi. Sama dengan autoloader, kita mengambil lagi parameter dari file konfigurasi untuk mengkonfigurasi service:

<?php

// Database connection is created based on parameters defined in the configuration file
$di->set('db', function() use ($config) {
    return new \Phalcon\Db\Adapter\Pdo\Mysql(array(
        "host" => $config->database->host,
        "username" => $config->database->username,
        "password" => $config->database->password,
        "dbname" => $config->database->name
    ));
});

Di sini, kita membuat return sebuah instance adapter koneksi MySQL. Jika diperlukan, Anda bisa melakukan tindakan tambahan seperti menambahkan logger, profiler atau mengubah adaptor, pengaturan itu seperti yang Anda inginkan.

Berikut bentuk sederhana (app/views/session/index.phtml) yg meminta informasi login. Kami telah menghapus beberapa kode HTML untuk membuat contoh yang lebih ringkas:

<?php echo $this->tag->form('session/start') ?>

    <label for="email">Username/Email</label>
    <?php echo $this->tag->textField(array("email", "size" => "30")) ?>

    <label for="password">Password</label>
    <?php echo $this->tag->passwordField(array("password", "size" => "30")) ?>

    <?php echo $this->tag->submitButton(array('Login')) ?>

</form>

Fungsi SessionController :: startAction (app/controllers/SessionController.php) memiliki tugas untuk memvalidasi data yang dimasukkan dalam form termasuk memeriksa user yang valid dalam database:

<?php

class SessionController extends ControllerBase
{

    // ...

    private function _registerSession($user)
    {
        $this->session->set('auth', array(
            'id' => $user->id,
            'name' => $user->name
        ));
    }

    public function startAction()
    {
        if ($this->request->isPost()) {

            //Receiving the variables sent by POST
            $email = $this->request->getPost('email', 'email');
            $password = $this->request->getPost('password');

            $password = sha1($password);

            //Find the user in the database
            $user = Users::findFirst(array(
                "email = :email: AND password = :password: AND active = 'Y'",
                "bind" => array('email' => $email, 'password' => $password)
            ));
            if ($user != false) {

                $this->_registerSession($user);

                $this->flash->success('Welcome ' . $user->name);

                //Forward to the 'invoices' controller if the user is valid
                return $this->dispatcher->forward(array(
                    'controller' => 'invoices',
                    'action' => 'index'
                ));
            }

            $this->flash->error('Wrong email/password');
        }

        //Forward to the login form again
        return $this->dispatcher->forward(array(
            'controller' => 'session',
            'action' => 'index'
        ));

    }

}

Untuk mempermudah, kami telah menggunakan “sha1” untuk menyimpan password hash dalam database, namun, algoritma ini tidak dianjurkan dalam aplikasi nyata, gunakan “bcrypt” sebagai gantinya.

Perhatikan bahwa beberapa atribut public diakses di controller seperti: $this->flash, $this->session atau $this->request. Ini adalah contoh service yg telah didefinisikan dalam service container sebelumnya. Ketika mereka diakses pertama kali, mereka diinjeksikan sebagai bagian dari controller.

Service ini digunakan bersama, yang berarti bahwa kita akan selalu mengakses instance yang sama terlepas dari tempat di mana kita memanggil mereka.

Sebagai contoh, di sini kita memanggil service “session” dan kemudian kita menyimpan identitas pengguna dalam variabel “auth”:

<?php

$this->session->set('auth', array(
    'id' => $user->id,
    'name' => $user->name
));

Mengamankan Backend 

Backend adalah area pribadi di mana hanya pengguna terdaftar memiliki akses. Oleh karena itu, perlu untuk memeriksa bahwa hanya pengguna terdaftar memiliki akses ke controller ini. Jika Anda belum login ke dalam aplikasi dan Anda mencoba untuk mengakses, misalnya, controller product (area khusus login) Anda akan melihat layar seperti ini:

../_images/invo-2.png

Setiap kali seseorang mencoba untuk mengakses setiap controller/action, aplikasi memverifikasi bahwa role saat ini (di session) memiliki akses ke sana, jika tidak maka akan menampilkan pesan seperti di atas dan meneruskan aliran ke halaman rumah.

Sekarang mari kita cari tahu bagaimana aplikasi menyelesaikan ini. Hal pertama yang harus tahu adalah bahwa ada komponen yang disebut Dispatcher . Itu adalah informasi tentang rute yang diarahkan oleh komponen Routing. Kemudian, ia bertanggung jawab untuk memuat controller yang sesuai dan melaksanakan metode tindakan (action) yang sesuai.

Biasanya, framework menciptakan Dispatcher otomatis. Dalam kasus kami, kami ingin melakukan verifikasi sebelum menjalankan aksi yang diperlukan, memeriksa apakah pengguna memiliki akses ke sana atau tidak. Untuk mencapai hal ini, kami telah mengganti komponen dengan menciptakan fungsi dalam bootstrap:

<?php

$di->set('dispatcher', function() use ($di) {
    $dispatcher = new Phalcon\Mvc\Dispatcher();
    return $dispatcher;
});

Kita sekarang memiliki kendali total atas Dispatcher yg digunakan dalam aplikasi. Banyak komponen dalam framework memicu event yang memungkinkan kita untuk mengubah alur operasi internal mereka. Sebagaimana Dependency Injector bertindak sebagai perekat antar komponen, sebuah komponen baru yang disebut EventsManager memungkinkan kita untuk mencegat event yang dihasilkan (intercept) oleh komponen, menyampaikan event ke pendengar (listener)

Events Management 

Sebuah EventsManager memungkinkan kita untuk melampirkan listener untuk tiap event tertentu. Jenis yang perlu diperhatikan sekarang adalah “dispatch”. Kode berikut akan menyaring semua event yang dihasilkan oleh Dispatcher tersebut:

<?php

$di->set('dispatcher', function() use ($di) {

    //Obtain the standard eventsManager from the DI
    $eventsManager = $di->getShared('eventsManager');

    //Instantiate the Security plugin
    $security = new Security($di);

    //Listen for events produced in the dispatcher using the Security plugin
    $eventsManager->attach('dispatch', $security);

    $dispatcher = new Phalcon\Mvc\Dispatcher();

    //Bind the EventsManager to the Dispatcher
    $dispatcher->setEventsManager($eventsManager);

    return $dispatcher;
});

Plugin Security adalah class yang terletak di (app/plugins/Security.php). Class ini mengimplementasikan metode “beforeDispatch”. Namanya sama dgn salah satu event yg dihasilkan Dispatcher tersebut:

<?php

use Phalcon\Events\Event,
        Phalcon\Mvc\User\Plugin,
        Phalcon\Mvc\Dispatcher,
        Phalcon\Acl;

class Security extends Plugin
{

    // ...

    public function beforeDispatch(Event $event, Dispatcher $dispatcher)
    {
        // ...
    }

}

Listener event selalu menerima parameter pertama yang berisi informasi kontekstual event yang dihasilkan ($event) dan yang kedua merupakan objek yang menghasilkan event itu sendiri ($dispatcher). Plugin tidak harus merupakan extend dari class Phalcon\MVC\User\Plugin, tetapi dengan melakukan hal ini akan mempermudah akses ke layanan yang tersedia dalam aplikasi.

Sekarang, kita sedang memverifikasi role dalam session saat ini, memeriksa apakah pengguna memiliki hak akses menggunakan daftar ACL. Jika pengguna tidak memiliki akses kita mengarahkan ke layar awal seperti yang dijelaskan sebelumnya:

<?php

use Phalcon\Events\Event,
        Phalcon\Mvc\User\Plugin,
        Phalcon\Mvc\Dispatcher,
        Phalcon\Acl;

class Security extends Plugin
{

    // ...

    public function beforeExecuteRoute(Event $event, Dispatcher $dispatcher)
    {

        //Check whether the "auth" variable exists in session to define the active role
        $auth = $this->session->get('auth');
        if (!$auth) {
            $role = 'Guests';
        } else {
            $role = 'Users';
        }

        //Take the active controller/action from the dispatcher
        $controller = $dispatcher->getControllerName();
        $action = $dispatcher->getActionName();

        //Obtain the ACL list
        $acl = $this->getAcl();

        //Check if the Role have access to the controller (resource)
        $allowed = $acl->isAllowed($role, $controller, $action);
        if ($allowed != Acl::ALLOW) {

            //If he doesn't have access forward him to the index controller
            $this->flash->error("You don't have access to this module");
            $dispatcher->forward(
                array(
                    'controller' => 'index',
                    'action' => 'index'
                )
            );

            //Returning "false" we tell to the dispatcher to stop the current operation
            return false;
        }

    }

}

Menyediakan daftar ACL 

Dalam contoh di atas kita telah memperoleh ACL menggunakan perintah
$this->_getAcl(). Perintah
 ini juga diterapkan di Plugin. Sekarang kita akan menjelaskan langkah-demi-langkah bagaimana kita membangun daftar kontrol akses (ACL, access control list):

<?php

//Create the ACL
$acl = new Phalcon\Acl\Adapter\Memory();

//The default action is DENY access
$acl->setDefaultAction(Phalcon\Acl::DENY);

//Register two roles, Users is registered users
//and guests are users without a defined identity
$roles = array(
    'users' => new Phalcon\Acl\Role('Users'),
    'guests' => new Phalcon\Acl\Role('Guests')
);
foreach ($roles as $role) {
    $acl->addRole($role);
}

Sekarang kita mendefinisikan sumber daya (resources) untuk setiap area masing-masing. Nama controller adalah sumber daya dan action mengakses untuk sumber daya masing-masing:

<?php

//Private area resources (backend)
$privateResources = array(
  'companies' => array('index', 'search', 'new', 'edit', 'save', 'create', 'delete'),
  'products' => array('index', 'search', 'new', 'edit', 'save', 'create', 'delete'),
  'producttypes' => array('index', 'search', 'new', 'edit', 'save', 'create', 'delete'),
  'invoices' => array('index', 'profile')
);
foreach ($privateResources as $resource => $actions) {
    $acl->addResource(new Phalcon\Acl\Resource($resource), $actions);
}

//Public area resources (frontend)
$publicResources = array(
  'index' => array('index'),
  'about' => array('index'),
  'session' => array('index', 'register', 'start', 'end'),
  'contact' => array('index', 'send')
);
foreach ($publicResources as $resource => $actions) {
    $acl->addResource(new Phalcon\Acl\Resource($resource), $actions);
}

ACL sekarang memiliki data acuan tentang controller yang ada dan tindakan yang terkait. Role “Users” memiliki akses ke semua sumber daya dari kedua frontend dan backend. Role “Guests” hanya memiliki akses ke area publik:

<?php

//Grant access to public areas to both users and guests
foreach ($roles as $role) {
    foreach ($publicResources as $resource => $actions) {
        $acl->allow($role->getName(), $resource, '*');
    }
}

//Grant access to private area only to role Users
foreach ($privateResources as $resource => $actions) {
    foreach ($actions as $action) {
        $acl->allow('Users', $resource, $action);
    }
}

Hore!, ACL sekarang lengkap.

User  Component

Semua elemen UI dan gaya visual dari aplikasi sebagian besar dibuat menggunakan Bootstrap . Beberapa elemen, seperti perubahan bar navigasi sesuai dengan keadaan aplikasi. Misalnya, di sudut kanan atas, link “Log in / Sign Up” perubahan “Log out” jika pengguna login ke dalam aplikasi.

Ini bagian dari aplikasi diimplementasikan dalam komponen “Elemen” (app/library/Elements.php).

<?php

use Phalcon\Mvc\User\Component;

class Elements extends Component
{

    public function getMenu()
    {
        //...
    }

    public function getTabs()
    {
        //...
    }

}

Class ini extend dr Phalcon\MVC\User\Component, komponen tidak harus extend dr class ini, tetapi hal ini membantu untuk mendapatkan akses yang lebih cepat ke service aplikasi. Sekarang, kita mendaftar class ini dalam service container:

<?php

//Register an user component
$di->set('elements', function(){
    return new Elements();
});

Sebagai controller, plugin atau komponen dalam tampilan, komponen ini juga memiliki akses ke service yang terdaftar dalam container dan dengan hanya mengakses atribut dengan nama yang sama sebagai service terdaftar sebelumnya:

<div class="navbar navbar-fixed-top">
    <div class="navbar-inner">
        <div class="container">
            <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </a>
            <a class="brand" href="#">INVO</a>
            <?php echo $this->elements->getMenu() ?>
        </div>
    </div>
</div>

<div class="container">
    <?php echo $this->getContent() ?>
    <hr>
    <footer>
        <p>© Company 2012</p>
    </footer>
</div>

Bagian yang penting adalah:

<?php echo $this->elements->getMenu() ?>

Bekerja dengan CRUD 

Sebagian besar pilihan yang memanipulasi data (perusahaan, produk dan jenis produk), dikembangkan dengan menggunakan CRUD dasar dan umum (Create, Read, Update dan Delete). Setiap CRUD berisi file-file berikut:

invo/
    app/
        app/controllers/
            ProductsController.php
        app/models/
            Products.php
        app/views/
            products/
                edit.phtml
                index.phtml
                new.phtml
                search.phtml

Setiap controller memiliki action berikut:

<?php

class ProductsController extends ControllerBase
{

    /**
     * The start action, it shows the "search" view
     */
    public function indexAction()
    {
        //...
    }

    /**
     * Execute the "search" based on the criteria sent from the "index"
     * Returning a paginator for the results
     */
    public function searchAction()
    {
        //...
    }

    /**
     * Shows the view to create a "new" product
     */
    public function newAction()
    {
        //...
    }

    /**
     * Shows the view to "edit" an existing product
     */
    public function editAction()
    {
        //...
    }

    /**
     * Creates a product based on the data entered in the "new" action
     */
    public function createAction()
    {
        //...
    }

    /**
     * Updates a product based on the data entered in the "edit" action
     */
    public function saveAction()
    {
        //...
    }

    /**
     * Deletes an existing product
     */
    public function deleteAction($id)
    {
        //...
    }

}

Form Pencarian 

Setiap CRUD dimulai dengan form pencarian. Formulir ini menunjukkan semua field/kolom yg dimiliki tabel (produk), yang memungkinkan pengguna membuat kriteria pencarian dari field manapun. Table “products” memiliki relasi ke tabel  “products_types”. Dalam hal ini, kita telah menyiapkan query record dr tabel dalam rangka memfasilitasi pencarian dengan field-field tersebut:

<?php

/**
 * The start action, it shows the "search" view
 */
public function indexAction()
{
    $this->persistent->searchParams = null;
    $this->view->productTypes = ProductTypes::find();
}

Semua “products_types” di-query dan diteruskan ke tampilan sebagai variabel lokal “productTypes”. Kemudian, dalam tampilan (app/views/index.phtml), kita akan melihat tag “select” diisi dengan hasil tersebut:

<div>
    <label for="product_types_id">Product Type</label>
    <?php echo $this->tag->select(array(
        "product_types_id",
        $productTypes,
        "using" => array("id", "name"),
        "useDummy" => true
    )) ?>
</div>

Perhatikan bahwa $productTypes berisi data yang diperlukan untuk mengisi tag SELECT menggunakan Phalcon\Tag::select. Setelah formulir dikirimkan, action “search” dijalankan di controller, melakukan pencarian berdasarkan data yang dimasukkan oleh pengguna.

Insert dan Update Record 

Sekarang mari kita lihat bagaimana CRUD melakukan insert dan update record. Dari view pada action “new” dan “edit” data dimasukkan oleh pengguna dikirim ke action “create” dan “save” yang melakukan tindakan “membuat” dan “memperbarui” produk masing-masing.

Ketika membuat record baru, create, kita mengambil data yang dikirimkan dan menyimpannya pada instance “product” baru:

<?php

/**
 * Creates a product based on the data entered in the "new" action
 */
public function createAction()
{

    $products = new Products();

    $products->id = $this->request->getPost("id", "int");
    $products->product_types_id = $this->request->getPost("product_types_id", "int");
    $products->name = $this->request->getPost("name", "striptags");
    $products->price = $this->request->getPost("price", "double");
    $products->active = $this->request->getPost("active");

    //...

}

Data difilter sebelum disimpan ke objek. Proses filter ini adalah opsional, ORM melakukan escape input data dan melakukan pemeriksaan tambahan sesuai dengan jenis kolom.

Ketika menyimpan, kita akan tahu apakah data sesuai dengan bussines-rule dan proses validasi pada model Product:

<?php

/**
 * Creates a product based on the data entered in the "new" action
 */
public function createAction()
{

    //...

    if (!$products->create()) {

        //The store failed, the following messages were produced
        foreach ($products->getMessages() as $message) {
            $this->flash->error((string) $message);
        }
        return $this->forward("products/new");

    } else {
        $this->flash->success("Product was created successfully");
        return $this->forward("products/index");
    }

}

Sekarang, untuk pembaruan produk (update), pertama-tama kita harus menampilkan data yg ada saat ini pada pengguna untuk diedit:

<?php

/**
 * Shows the view to "edit" an existing product
 */
public function editAction($id)
{

    //...

    $product = Products::findFirstById($id);

    $this->tag->setDefault("id", $product->id);
    $this->tag->setDefault("product_types_id", $product->product_types_id);
    $this->tag->setDefault("name", $product->name);
    $this->tag->setDefault("price", $product->price);
    $this->tag->setDefault("active", $product->active);

}

Perintah “setDefault” menetapkan nilai default pada form di atribut dengan nama yang sama. Dengan ini, pengguna dapat mengubah nilai apapun dan kemudian dikirim kembali ke database melalui ke action “save”:

<?php

/**
 * Updates a product based on the data entered in the "edit" action
 */
public function saveAction()
{

    //...

    //Find the product to update
    $id = $this->request->getPost("id");
    $product = Products::findFirstById($id);
    if (!$product) {
        $this->flash->error("products does not exist " . $id);
        return $this->forward("products/index");
    }

    //... assign the values to the object and store it

}

Mengubah Judul Secara Dinamis 

Saat Anda menelusuri antara satu pilihan dan yang lain akan melihat bahwa perubahan judul (html-title) dinamis menunjukkan di mana kita saat ini. Hal ini dapat dilakukan di setiap initializer controller:

<?php

class ProductsController extends ControllerBase
{

    public function initialize()
    {
        //Set the document title
        $this->tag->setTitle('Manage your product types');
        parent::initialize();
    }

    //...

}

Perhatikan, bahwa perintah parent::initialize() juga disebutkan, kode diatas untuk menambah data untuk judul:

<?php

class ControllerBase extends Phalcon\Mvc\Controller
{

    protected function initialize()
    {
        //Prepend the application name to the title
        $this->tag->prependTitle('INVO | ');
    }

    //...
}

Akhirnya, judul dicetak pada tampilan utama (app/views/index.phtml):

<!DOCTYPE html>
<html>
    <head>
        <?php echo $this->tag->getTitle() ?>
    </head>
    <!-- ... -->
</html>

Kesimpulan 

Tutorial ini mencakup lebih banyak aspek tentang membangun aplikasi dengan Phalcon, diharapkan Anda telah belajar dan mendapatkan lebih banyak dari framework.