Membuat Halaman Login untuk Admin

Pada tutorial kali ini akan dibahas pembuatan login untuk admin. Kurang lebih contoh tampilan jadinya seperti ini.

Untuk membuatnya, kita akan melalui tahapan-tahapan sebagai berikut:

  1. Setup JI Model dan JI Controller
  2. Pembuatan table a_pengguna dan sample isinya
  3. Pembuatan concern dan model a_pengguna
  4. Pembuatan controller login dan logout
  5. Pembuatan view login
  6. Penambahan validasi "sudah login" pada controller

Kemudian untuk struktur filenya seperti ini:

app/
├── controller/
│ └── admin/
│  ├── login.php
│  ├── logout.php
│  └── home.php
└── view/
 └── admin/
  ├── login
  │ ├── home.php
  │ └── home_bottom.php
  ├── home
  │ ├── home.php
  │ └── home_bottom.php
  └── page/
   └── login.php

Setup JI Model dan JI Controller

JI Controller dan JI Model digunakan sebagai class helper untuk fungsi-fungsi yang akan tersedia secara umum baik pada controller maupun model. Sekarang kita akan merubah pengaturan terlebih dahulu, supaya seme framework memanggil JI_Model dan JI_Controller. Edit file app/config/development.php kemudian ganti pada bagian core_prefix menjadi seperti dibawah ini

$core_prefix = 'ji_';
$core_controller = 'controller';
$core_model = 'model';

Masih di file app/config/development.php lompati ke akhir file, kemudian tambahkan code dibawah ini ini:

$semevar['site_title'] = 'Seme Framework';
$semevar['site_version'] = '1.0.0';
$semevar['admin_site_title'] = 'Admin Page';
$semevar['admin_site_title_suffix'] = ' - '.$semevar['admin_site_title'];

Kemudian setelah itu kita akan buat file baru di app/core/ji_controller.php, isikan seperti code dibawah ini.

<?php
/**
 * Core class for all controller
 *   contains general purpose methods that nice to have in all controllers
 *
 * @version 1.0.0
 *
 * @package Core\Controller
 * @since 1.0.0
 */
class JI_Controller extends \SENE_Controller
{
    protected $user_login = false;
    protected $admin_login = false;
    private $session_current = null;
    
    public function __construct()
    {
        parent::__construct();
        $this->session_current_check();
    }

    public function session_current_check()
    {
        $session = $this->session_current;
        if (!is_null($session) && isset($session->user->id) && isset($session->admin->id)) {
            return  $session;
        }

        $session = $this->getKey();
        if (!is_object($session)) {
            $session = new stdClass();
            $session->user = new stdClass();
            $session->admin = new stdClass();
        }
        if (!isset($session->user)) {
            $session->user = new stdClass();
        }
        if (isset($session->user->id)) {
            $this->user_login = true;
        }

        if (!isset($session->admin)) {
            $this->sessions->admin = new stdClass();
        }
        if (isset($session->admin->id)) {
            $this->admin_login = true;
        }
        $this->session_current = $session;

        return $this;
    }

    protected function config_semevar($key, $default='')
    {
        if (isset($this->config->semevar->{$key})) {
            return $this->config->semevar->{$key};
        } else {
            return $default;
        }
    }

    public function __init()
    {
        $data = array();
        $data['sess'] = $this->session_current;

        return $data;
    }

    public function index() { }
}

Selanjutnya adalah file app/core/ji_model.php, buatlah dan isi seperti code dibawah ini.

<?php
/**
* Define all general method for all tables
*   For class models
*
* @package Core\Model
* @since 1.0
*/
class JI_Model extends \SENE_Model
{
    /** @var string  */
    public $table;

    /** @var string  */
    public $table_alias;

    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Insert a row data
     *
     * @param  array   $d   Contain associative array that represent the pair of column and value
     * @return int          Return last ID if succeed, otherwise will return 0
     */
    public function set($d)
    {
        $this->db->insert($this->table, $d, 0, 0);
        return $this->db->last_id;
    }

    /**
     * Update a row data by supplied ID
     *
     * @param  int      $id    Positive integer
     * @return boolean         True if succeed, otherwise false
     */
    public function update($id, $d)
    {
        $this->db->where("id", $id);
        return $this->db->update($this->table, $d, 0);
    }

    /**
     * Delete row data by ID
     *
     * @param  int      $id    Positive integer
     * @return boolean         True if succeed, otherwise false
     */
    public function del($id)
    {
        $this->db->where("id", $id);
        return $this->db->delete($this->table);
    }

    /**
     * Get single row data by ID
     *
     * @param  int      $id     Positive integer
     * @return stdClass         Will return single row object, otherwise will return empty object
     */
    public function id($id)
    {
        $this->db->where("id", $id);
        return $this->db->from($this->table, $this->table_alias)->get_first('', 0);
    }

    /**
     * Open the database transaction session
     * @return boolean True if succeed, otherwise false
     */
    public function trans_start()
    {
        $r = $this->db->autocommit(0);
        if ($r) {
            return $this->db->begin();
        }
        return false;
    }

    /**
     * Execute `commit` SQL command
     * @return boolean True if succeed, otherwise false
     */
    public function trans_commit()
    {
        return $this->db->commit();
    }

    /**
     * Rollback the database transaction session
     * @return boolean True if succeed, otherwise false
     */
    public function trans_rollback()
    {
        return $this->db->rollback();
    }

    /**
     * Finalize the database transaction session
     * @return boolean True if succeed, otherwise false
     */
    public function trans_end()
    {
        return $this->db->autocommit(1);
    }
}

Pembuatan table a_pengguna dan sample isinya

Pertama-tama kita akan membuat tabel baru untuk penyimpanan data pengguna admin. Data user pada tutorial kali ini akan disimpan dalam sebuah tabel bernama a_pengguna. Pastikan database sudah dibuat sebelumnya dan juga sudah terpilih. Kemudian Copy paste kode SQL dibawah ini dan jalankan di PhpMyAdmin atau MySQL query-nya langsung untuk membuat tabel secara otomatis.

SET FOREIGN_KEY_CHECKS=0;
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";

DROP TABLE IF EXISTS `a_pengguna`;
CREATE TABLE `a_pengguna` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(64) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL ON UPDATE current_timestamp(),
  PRIMARY KEY (`id`),
  UNIQUE KEY `a_pengguna_un` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
SET FOREIGN_KEY_CHECKS=1;

Kemudian setelah itu, kita akan masukan 1 sample akun admin dengan username mimind dan passwordnya itu adalah admin123*. Kemudian Copy paste kode SQL dibawah ini dan jalankan di PhpMyAdmin atau MySQL query-nya langsung untuk menambahkan user admin baru.

insert into `a_pengguna`(
	id,
	username,
	password,
	created_at,
	updated_at)
values(
	null,
	'mimind',
	md5('admin123*'),
	NOW(),
	null
);

Menggunakan enkripsi password dengan MD5 sudah sangat familiar dan menjadi kurang aman, karena sudah banyak library yang bisa membalikan kode MD5 menjadi string originalnya. Pada tutorial ini MD5 digunakan hanya untuk meng-enkripsi secara sederhana saja, supaya password tidak bisa langsung terbaca isinya.

Nanti, kita akan coba upgrade metode enkripsi MD5 menjadi yang lebih baik pada tutorial selanjutnya.

Pembuatan concern dan model a_pengguna

Setelah mengimport tabel a_pengguna kedalam database, sekarang saatnya membuat concern class untuk tabel a_pengguna. Caranya buat file baru di app/model/a_pengguna_concern.php, kemudian copy paste kode dibawah ini dan simpan perubahan pada file tersebut.

<?php
namespace Model;

register_namespace(__NAMESPACE__);
/**
 * Define all general method(s) and constant(s) for a_pengguna table,
 *   can be inherited a Concern class also can be reffered as class constants
 *
 * @version 1.0.0
 *
 * @package Model\Concern
 * @since 1.0.0
 */
class A_Pengguna_Concern extends \JI_Model
{
    public $table = 'a_pengguna';
    public $table_alias = 'ap';

    public function __construct()
    {
        parent::__construct();
        $this->db->from($this->table, $this->table_alias);
    }

    public function username($username)
    {
        return $this->db
            ->from($this->table, $this->table_alias)
            ->where("$this->table_alias.username", $username, 'AND', 'LIKE')
            ->get_first('object', 0);
    }
}

Kemudian sekarang membuat model untuk ruang lingkup admin saja. Ini berarti model file yang ini hanya akan dipakai dibawah controller/admin/ saja. Hal ini dilakukan supaya kode dapat dimaintenance dengan mudah apabila nanti ada perubahan. Selanjutnya kita akan buat file baru di app/model/admin/a_pengguna_model.php, kemudian copy paste kode dibawah ini dan simpan perubahan pada file tersebut.

<?php
namespace Model\Admin;

register_namespace(__NAMESPACE__);
/**
 * Scoped `admin` model for `a_pengguna` table
 *
 * @version 1.0.0
 *
 * @package Model\Admin
 * @since 1.0.0
 */
class A_Pengguna_Model extends \Model\A_Pengguna_Concern
{

    public function __construct()
    {
        parent::__construct();
    }
}

Membuat View dan Controller untuk halaman Login

Sekarang kita akan bikin view untuk tampilan login beserta controllernya (login & logout). Pertama-tama kita akan buat dulu layout view untuk login. Buatlah file baru di app/view/admin/page/login.php, isikan seperti code dibawah ini.

<!DOCTYPE html>
<html class="no-js" lang="en">
	<?php $this->getThemeElement("page/html/head",$__forward); ?>
	<body>
		<!-- Main Container -->
		<?php $this->getThemeContent(); ?>
		<!-- Main Container End -->

		<!-- jQuery, Bootstrap.js, jQuery plugins and Custom JS code -->
		<?php $this->getJsFooter(); ?>

		<!-- Load and execute javascript code used only in this page -->
		<script>
		public base_url = '<?=base_url_admin()?>';
		public Login = function(){
			return {
				init: function(){

				}
			}
		}();
			$(document).ready(function(e){
				<?php $this->getJsReady(); ?>
			});
			<?php $this->getJsContent(); ?>
		</script>
	</body>
</html>

Alasan membuat layout baru adalah supaya wrapper HTML untuk login memiliki desain yang berbeda dengan tampilan admin.

Kemudian sekarang kita akan buat form login. Buatlah file baru di app/view/admin/login/home.php, copy paste isinya dengan kode dibawah ini.

<!-- Login Full Background -->
<!-- For best results use an image with a resolution of 1280x1280 pixels (prefer a blurred image for smaller file size) -->
<img src="https://seme-framework-storage.b-cdn.net/images/background-login-3.jpg" alt="Login Full Background" class="full-bg animation-pulseSlow">
<!-- END Login Full Background -->

<!-- Login Container -->
<div id="login-container" class="animation-fadeIn">
    <!-- Login Title -->
    <div class="login-title text-center">
        <img src="https://seme-framework-storage.b-cdn.net/images/seme-framework-logo.png" class="img-responsive" />
    </div>
    <!-- END Login Title -->

    <!-- Login Block -->
    <div class="block push-bit">
        <div id="flogin_info" class="alert alert-info" role="alert" style="<?php if(!isset($login_message)) echo 'display:none'; ?>"><?php if(isset($login_message)) echo $login_message; ?></div>
        <!-- Login Form -->
        <form action="<?=base_url_admin("login/authentication/"); ?>" method="post" id="form-login" class="form-horizontal form-bordered form-control-borderless">
            <div class="form-group">
                <div class="col-xs-12">
                    <div class="input-group">
                        <span class="input-group-addon"><i class="gi gi-user"></i></span>
                        <input type="text" id="iusername" name="username" class="form-control input-lg" placeholder="Username" />
                    </div>
                </div>
            </div>
            <div class="form-group">
                <div class="col-xs-12">
                    <div class="input-group">
                        <span class="input-group-addon"><i class="gi gi-asterisk"></i></span>
                        <input type="password" id="ipassword" name="password" class="form-control input-lg" placeholder="Password">
                        <input type="hidden" id="ireff" name="reff" value="<?=$reff ?? ''?>" />
                    </div>
                </div>
            </div>
            <div class="form-group form-actions">
                <div class="col-xs-6">
                    <?=$this->config_semevar('admin_site_title')?> <label class="" style="color: grey; font-weight: lighter; font-size: smaller;">version <?=$this->config_semevar('site_version')?></label>
                </div>
                <div class="col-xs-6 text-right">
                    <button type="submit" class="btn btn-sm btn-primary btn-submit">Login <i id="icon-submit" class="fa fa-angle-right"></i></button>
                </div>
            </div>
        </form>
        <!-- END Login Form -->
    </div>
    <!-- END Login Block -->
</div>
<!-- END Login Container -->

Kemudian buat lagi file baru di app/view/admin/login/home_bottom.php, biarkan kosong saja file tersebut.

File app/view/admin/login/home_bottom.php nanti akan kita gunakan untuk menyimpan kode JavaScript, untuk membuat login dengan metode Ajax.

Sekarang kita akan beralih ke controller, pertama-tama buat file controller untuk logout terlebih dahulu di app/controller/admin/logout.php. Kemudian copy paste kode dibawah ini.

<?php
/**
* Controller Class
*   for Admin
* Main objective of this class is to clear session for admin
*
* @version 1.0.0
*
* @package Controller\Admin
* @since 1.0.0
*/
class Logout extends \JI_Controller
{
    public function __construct()
    {
        parent::__construct();
        $this->setTheme('admin');
    }

    public function index()
    {
        $initial_data = $this->__init();
        if (!isset($initial_data['sess']->user->admin)) {
            $this->session_current_check();
            $initial_data = $this->__init();
        }
        $initial_data['sess']->admin = new stdClass();
        $this->login_admin = false;
        $this->setKey($initial_data['sess']);

        flush();
        redir(base_url_admin("login"), 0, 1);
    }
}

Setelah itu kita akan membuat controller untuk login. Pada controller kali ini, ada 2 fungsi utama yaitu menampilkan form login (halaman login). Dan yang kedua, adalah untuk memproses input yang dikirim dari form login. Untuk menampilkan form login ada di method index, sementara untuk proses login ada di authorization. Sekarang kita akan buat file baru di app/controller/admin/login.php, kemudian copy paste kode dibawah ini.

<?php
class Login extends \JI_Controller
{
	public function __construct()
	{
		parent::__construct();
		$this->setTheme('admin');
		$this->load('a_pengguna_concern');
		$this->load('admin/a_pengguna_model', 'apm');
	}

    private function validate_input($key, $min_length=3)
    {
        $value = $this->input->request($key, '');
		if (strlen($value) <= $min_length) {
			$value = false;
		}

        return $value;
    }

    private function validate_admin_session()
    {
        if (isset($this->admin_login) && $this->admin_login == true) {
            return true;
        }

        return false;
    }

    private function validate_username()
    {
        return $this->validate_input('username');
    }

    private function validate_password()
    {
        return $this->validate_input('password');
    }

    private function validate_password_md5($apm, $password)
    {
        if (md5($password) == $apm->password) {
            return true;
        } else {
            return false;
        }
    }

    private function session_current_update($data_initial, $apm)
    {
        $session_current = new stdClass();
        if (!isset($data_initial['sess']->admin)) {
            $this->session_current_check();
        }
        
        $data_initial['sess']->admin = $apm;
        $this->setKey($data_initial['sess']);

        return $data_initial;
    }

	public function index()
	{
		$data = $this->__init();
        if ($this->validate_admin_session()) {
            redir(base_url_admin('login/authorization/'));

            return true;
        }

		$this->setTitle('Login ' . $this->config_semevar('admin_site_title_suffix'));

        $failed_flag = intval($this->input->request('failed', 0));
        if ($failed_flag > 0) {
            $data['login_message'] = 'Invalid username or password';
        }

		$this->putThemeContent("login/home", $data);
		$this->putJsContent('login/home_bottom', $data);
		$this->loadLayout('login', $data);
		$this->render();
	}

	public function authentication()
	{
		//init
		$data = array();
		$initial_data = $this->__init();
		if ($this->validate_admin_session() == true)
        {
            redir(base_url_admin('login/authorization/'));

            return false;
        }
		
		$username = $this->validate_username();
		if (!$username)
        {
            redir(base_url_admin('login/?failed=1'));
            return false;
        }

		$password = $this->validate_password();
		if (!$password)
        {
            redir(base_url_admin('login/?failed=2'));
            return false;
        }

        $apm = $this->apm->username($username);
        if (!isset($apm->id))
        {
            redir(base_url_admin('login/?failed=3'));
            return false;
        }

        if (!$this->validate_password_md5($apm, $password))
        {
            redir(base_url_admin('login/?failed=4'));
            return false;
        }

        $this->session_current_update($initial_data, $apm);

        redir(base_url_admin('login/authorization/'));
        return true;
	}

	public function authorization()
	{
		$data = $this->__init();
        if ($this->validate_admin_session()) {
            redir(base_url_admin());
            
            return true;
        } else {
            redir(base_url_admin('login'));
            
            return false;
        }
	}
}

Penambahan validasi sudah login pada controller

Pada file app/controler/admin/home.php ada beberapa code yang akan diubah. Pertama perubaha pada extends class, dari asalnya SENE_Controller menjadi JI_Controller. Kemudian setelah itu, menambahkan validasi apabila admin belum login. Mari kita lihat code dibawah ini, kemudian sesuaikanlah kodenya seperti dibawah ini.

<?php
/**
* Main Controller Class
*   for Admin
* Mostly for this controller will resulting HTTP Body Content in HTML format
*
* @version 1.0.0
*
* @package Controller\Admin
* @since 1.0.0
*/
class Home extends \JI_Controller
{
    public function __construct()
    {
        parent::__construct();
        $this->setTheme('admin');
    }
    public function index()
    {
        $data = $this->__init();
        if (!$this->admin_login) {
            redir(base_url_admin('login'));

            return false;
        }
        
        $this->setTitle("Home Admin");
        
        $this->putJsContent("home/home_bottom", $data);
        $this->putThemeContent("home/home", $data);

        $this->loadLayout('col-2-left', $data);
        $this->render();
    }
}

Pada class controller ini kita sudah extends dari JI_Controller tidak seperti pada tutorial sebelumnya masih menggunakan class default dari Seme Framework.

Selesai...!

Untuk mencoba hasil dari tutorial ini, buka alamat http://localhost/seme_framework/admin/home pada browser. Harusnya akan muncul halaman login.

Kemudian setelah itu, masukan username: mimind, dan password: admin123*. Jika berhasil harusnya masuk ke halaman admin sample.