بِسْمِ اللَّهِ Mudah-mudahan tutorial Codeigniter 4 – Melacak Pengunjung Online ini bermanfaat
Visitor adalah pengunjung dunia maya yang sedang membuka website, baik yang tidak sengaja membuka maupun sengaja membuka atau mengunjungi website karena kebutuhan. Visitor adalah bagian yang paling penting sebuah website. Website bisa dikatakan sukses jika memiliki visitor yang banyak. Tutorial kali ini hanya menerjemahkan saja dari Versi originalnya. Kalau diversi originalnya ada ajax, tapi untuk yang disini saya tidak menggunakan ajax. Dan disini akan menampilkan jumlah visitor pada menu sebelah kiri halaman website, atau mungkin Anda punya ide yang lain.
Persiapan Codeigniter 4 – Melacak Pengunjung Online
Pastikan sudah ada:
Editor yang digunakan adalah Visual Studio Code
Project Codeigniter, untuk membuatnya bisa dilihat tutorial Codeigniter 4 Pengenalan dan Instalasi.
Sudah terkoneksi dengan database, silakan lihat Codeigniter 4 – Koneksi ke Database
Pertama
Membuat tabel untuk menyimpan data visitor
CREATE TABLE IF NOT EXISTS `visitors` (
`visitor_id` int unsigned NOT NULL AUTO_INCREMENT,
`no_of_visits` int unsigned NOT NULL,
`ip_address` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`requested_url` tinytext COLLATE utf8mb4_unicode_ci NOT NULL,
`referer_page` tinytext COLLATE utf8mb4_unicode_ci NOT NULL,
`page_name` tinytext COLLATE utf8mb4_unicode_ci NOT NULL,
`query_string` tinytext COLLATE utf8mb4_unicode_ci NOT NULL,
`user_agent` tinytext COLLATE utf8mb4_unicode_ci NOT NULL,
`is_unique` tinyint NOT NULL DEFAULT '0',
`access_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`visitor_id`)
) ENGINE=InnoDB AUTO_INCREMENT=665 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Kedua
Membuat library yang berfungsi untuk menulis ke tabel visitors. Dan memudahkan untuk memanggil fungsi-fungsi yang ada didalamnya dari tempat lain di project Anda. Buat library /app/Libraries/VisitorTracker.php dengan script berikut
<?php
namespace App\Libraries;
use CodeIgniter\Model;
/**
* Description of VisitorTracker
*
* @author https://roytuts.com
*/
class VisitorTracker {
private $db;
private $request;
public function __construct() {
$this->db = \Config\Database::connect();
$this->request = \Config\Services::request();
}
/*
* Defines how many seconds a hit should be rememberd for. This prevents the
* database from perpetually increasing in size. Thirty days (the default)
* works well. If someone visits a page and comes back in a month, it will be
* counted as another unique hit.
*/
//private $HIT_OLD_AFTER_SECONDS = 2592000; // default: 30 days.
/*
* Don't count hits from search robots and crawlers.
*/
private $IGNORE_SEARCH_BOTS = TRUE;
/*
* Don't count the hit if the browser sends the DNT: 1 header.
*/
private $HONOR_DO_NOT_TRACK = FALSE;
/*
* ignore controllers e.g. 'admin'
*/
private $CONTROLLER_IGNORE_LIST = array(
'admin'
);
/*
* ignore ip address
*/
private $IP_IGNORE_LIST = array(
'127.0.0.1'
);
/*
* visitor tracking table
*/
private $visitors = "visitors";
function track_visitor() {
$router = \Config\Services::router();
$track_visitor = TRUE;
if (isset($track_visitor) && $track_visitor === TRUE) {
$proceed = TRUE;
if ($this->IGNORE_SEARCH_BOTS && $this->is_bot()) {
$proceed = FALSE;
}
if ($this->HONOR_DO_NOT_TRACK && !allow_tracking()) {
$proceed = FALSE;
}
foreach ($this->CONTROLLER_IGNORE_LIST as $controller) {
if (strpos(trim($router->controllerName()), $controller) !== FALSE) {
$proceed = FALSE;
break;
}
}
if (in_array($this->request->getIPAddress(), $this->IP_IGNORE_LIST)) {
$proceed = FALSE;
}
if ($proceed === TRUE) {
$this->log_visitor();
}
}
}
private function log_visitor() {
$session = session();
$router = \Config\Services::router();
if ($this->track_session() === TRUE) {
//update the visitor log in the database, based on the current visitor
//id held in $_SESSION["visitor_id"]
$temp_visitor_id = $session->get('visitor_id');
$visitor_id = isset($temp_visitor_id) ? $temp_visitor_id : 0;
$no_of_visits = $session->get('no_of_visits');
$current_page = current_url();
$temp_current_page = $session->get('current_page');
if (isset($temp_current_page) && $temp_current_page != $current_page) {
$page_name = $router->controllerName() . '/' . $router->methodName();
$page_length = strlen(trim($router->controllerName() . '/' . $router->methodName()));
$query_params = trim(substr($this->request->uri, $page_length + 1));
$query_string = strlen($query_params) ? $query_params : '';
$data = array(
'no_of_visits' => $no_of_visits,
'ip_address' => $this->request->getIPAddress(),
'requested_url' => $this->request->uri,
'referer_page' => $this->request->getUserAgent()->getReferrer(),
'user_agent' => $this->request->getUserAgent(),
'page_name' => $page_name,
'query_string' => $query_string
);
$this->db->table($this->visitors)->insert($data);
$session->set('current_page', $current_page);
}
} else {
$page_name = $router->controllerName() . '/' . $router->methodName();
$page_length = strlen(trim($router->controllerName() . '/' . $router->methodName()));
$query_params = trim(substr($this->request->uri, $page_length + 1));
$query_string = strlen($query_params) ? $query_params : '';
$data = array(
'ip_address' => $this->request->getIPAddress(),
'requested_url' => $this->request->uri,
'referer_page' => $this->request->getUserAgent()->getReferrer(),
'user_agent' => $this->request->getUserAgent(),
'page_name' => $page_name,
'query_string' => $query_string
);
$result = $this->db->table($this->visitors)->insert($data);
if ($result === FALSE) {
/**
* find the next available visitor_id for the database
* to assign to this person
*/
$this->ci->session->set_userdata('track_session', FALSE);
} else {
/**
* find the next available visitor_id for the database
* to assign to this person
*/
$session->set('track_session', TRUE);
$entry_id = $this->db->insertID();
$query = $this->db->query('select max(no_of_visits) as counts from ' . $this->visitors . ' limit 1');
$count = 0;
if ($query->resultID->num_rows == 1) {
$row = $query->getRow();
if ($row->counts == NULL || $row->counts == 0) {
$count = 1;
} else {
$count++;
}
}
//update the visitor entry with the new visitor id
//Note, that we do it in this way to prevent a "race condition"
$this->db->table($this->visitors)->where('visitor_id', $entry_id);
$data = array(
'no_of_visits' => $count
);
$this->db->table($this->visitors)->update($data);
//place the current visitor_id into the session so we can use it on
//subsequent visits to track this person
$session->set('no_of_visits', $count);
//save the current page to session so we don't track if someone just refreshes the page
$current_page = current_url();
$session->set('current_page', $current_page);
}
}
}
/**
* check track_session
*
* @return bool
*/
private function track_session() {
$session = session();
return ($session->get('track_session') === TRUE ? TRUE : FALSE);
}
/**
* check whether bot
*
* @return bool
*/
private function is_bot() {
// Of course, this is not perfect, but it at least catches the major
// search engines that index most often.
$spiders = array(
"abot",
"dbot",
"ebot",
"hbot",
"kbot",
"lbot",
"mbot",
"nbot",
"obot",
"pbot",
"rbot",
"sbot",
"tbot",
"vbot",
"ybot",
"zbot",
"bot.",
"bot/",
"_bot",
".bot",
"/bot",
"-bot",
":bot",
"(bot",
"crawl",
"slurp",
"spider",
"seek",
"accoona",
"acoon",
"adressendeutschland",
"ah-ha.com",
"ahoy",
"altavista",
"ananzi",
"anthill",
"appie",
"arachnophilia",
"arale",
"araneo",
"aranha",
"architext",
"aretha",
"arks",
"asterias",
"atlocal",
"atn",
"atomz",
"augurfind",
"backrub",
"bannana_bot",
"baypup",
"bdfetch",
"big brother",
"biglotron",
"bjaaland",
"blackwidow",
"blaiz",
"blog",
"blo.",
"bloodhound",
"boitho",
"booch",
"bradley",
"butterfly",
"calif",
"cassandra",
"ccubee",
"cfetch",
"charlotte",
"churl",
"cienciaficcion",
"cmc",
"collective",
"comagent",
"combine",
"computingsite",
"csci",
"curl",
"cusco",
"daumoa",
"deepindex",
"delorie",
"depspid",
"deweb",
"die blinde kuh",
"digger",
"ditto",
"dmoz",
"docomo",
"download express",
"dtaagent",
"dwcp",
"ebiness",
"ebingbong",
"e-collector",
"ejupiter",
"emacs-w3 search engine",
"esther",
"evliya celebi",
"ezresult",
"falcon",
"felix ide",
"ferret",
"fetchrover",
"fido",
"findlinks",
"fireball",
"fish search",
"fouineur",
"funnelweb",
"gazz",
"gcreep",
"genieknows",
"getterroboplus",
"geturl",
"glx",
"goforit",
"golem",
"grabber",
"grapnel",
"gralon",
"griffon",
"gromit",
"grub",
"gulliver",
"hamahakki",
"harvest",
"havindex",
"helix",
"heritrix",
"hku www octopus",
"homerweb",
"htdig",
"html index",
"html_analyzer",
"htmlgobble",
"hubater",
"hyper-decontextualizer",
"ia_archiver",
"ibm_planetwide",
"ichiro",
"iconsurf",
"iltrovatore",
"image.kapsi.net",
"imagelock",
"incywincy",
"indexer",
"infobee",
"informant",
"ingrid",
"inktomisearch.com",
"inspector web",
"intelliagent",
"internet shinchakubin",
"ip3000",
"iron33",
"israeli-search",
"ivia",
"jack",
"jakarta",
"javabee",
"jetbot",
"jumpstation",
"katipo",
"kdd-explorer",
"kilroy",
"knowledge",
"kototoi",
"kretrieve",
"labelgrabber",
"lachesis",
"larbin",
"legs",
"libwww",
"linkalarm",
"link validator",
"linkscan",
"lockon",
"lwp",
"lycos",
"magpie",
"mantraagent",
"mapoftheinternet",
"marvin/",
"mattie",
"mediafox",
"mediapartners",
"mercator",
"merzscope",
"microsoft url control",
"minirank",
"miva",
"mj12",
"mnogosearch",
"moget",
"monster",
"moose",
"motor",
"multitext",
"muncher",
"muscatferret",
"mwd.search",
"myweb",
"najdi",
"nameprotect",
"nationaldirectory",
"nazilla",
"ncsa beta",
"nec-meshexplorer",
"nederland.zoek",
"netcarta webmap engine",
"netmechanic",
"netresearchserver",
"netscoop",
"newscan-online",
"nhse",
"nokia6682/",
"nomad",
"noyona",
"nutch",
"nzexplorer",
"objectssearch",
"occam",
"omni",
"open text",
"openfind",
"openintelligencedata",
"orb search",
"osis-project",
"pack rat",
"pageboy",
"pagebull",
"page_verifier",
"panscient",
"parasite",
"partnersite",
"patric",
"pear.",
"pegasus",
"peregrinator",
"pgp key agent",
"phantom",
"phpdig",
"picosearch",
"piltdownman",
"pimptrain",
"pinpoint",
"pioneer",
"piranha",
"plumtreewebaccessor",
"pogodak",
"poirot",
"pompos",
"poppelsdorf",
"poppi",
"popular iconoclast",
"psycheclone",
"publisher",
"python",
"rambler",
"raven search",
"roach",
"road runner",
"roadhouse",
"robbie",
"robofox",
"robozilla",
"rules",
"salty",
"sbider",
"scooter",
"scoutjet",
"scrubby",
"search.",
"searchprocess",
"semanticdiscovery",
"senrigan",
"sg-scout",
"shai'hulud",
"shark",
"shopwiki",
"sidewinder",
"sift",
"silk",
"simmany",
"site searcher",
"site valet",
"sitetech-rover",
"skymob.com",
"sleek",
"smartwit",
"sna-",
"snappy",
"snooper",
"sohu",
"speedfind",
"sphere",
"sphider",
"spinner",
"spyder",
"steeler/",
"suke",
"suntek",
"supersnooper",
"surfnomore",
"sven",
"sygol",
"szukacz",
"tach black widow",
"tarantula",
"templeton",
"/teoma",
"t-h-u-n-d-e-r-s-t-o-n-e",
"theophrastus",
"titan",
"titin",
"tkwww",
"toutatis",
"t-rex",
"tutorgig",
"twiceler",
"twisted",
"ucsd",
"udmsearch",
"url check",
"updated",
"vagabondo",
"valkyrie",
"verticrawl",
"victoria",
"vision-search",
"volcano",
"voyager/",
"voyager-hc",
"w3c_validator",
"w3m2",
"w3mir",
"walker",
"wallpaper",
"wanderer",
"wauuu",
"wavefire",
"web core",
"web hopper",
"web wombat",
"webbandit",
"webcatcher",
"webcopy",
"webfoot",
"weblayers",
"weblinker",
"weblog monitor",
"webmirror",
"webmonkey",
"webquest",
"webreaper",
"websitepulse",
"websnarf",
"webstolperer",
"webvac",
"webwalk",
"webwatch",
"webwombat",
"webzinger",
"wget",
"whizbang",
"whowhere",
"wild ferret",
"worldlight",
"wwwc",
"wwwster",
"xenu",
"xget",
"xift",
"xirq",
"yandex",
"yanga",
"yeti",
"yodao",
"zao/",
"zippp",
"zyborg"
);
$agent = strtolower($this->request->getUserAgent());
foreach ($spiders as $spider) {
if (strpos($agent, $spider) !== FALSE)
return TRUE;
}
return FALSE;
}
}
/* End of file VisitorTracker.php */
/* Location: ./app/Libraries/VisitorTracker.php */
Inti dari script diatas adalah fungsi visitor_tracker(), fungsi akan mem-validasi sebelum data pengunjung masuk ke dalam database. Perhatikan bagian berikut dari script diatas
...
function track_visitor() {
$router = \Config\Services::router();
$track_visitor = TRUE;
if (isset($track_visitor) && $track_visitor === TRUE) {
$proceed = TRUE;
if ($this->IGNORE_SEARCH_BOTS && $this->is_bot()) {
$proceed = FALSE;
}
if ($this->HONOR_DO_NOT_TRACK && !allow_tracking()) {
$proceed = FALSE;
}
foreach ($this->CONTROLLER_IGNORE_LIST as $controller) {
if (strpos(trim($router->controllerName()), $controller) !== FALSE) {
$proceed = FALSE;
break;
}
}
if (in_array($this->request->getIPAddress(), $this->IP_IGNORE_LIST)) {
$proceed = FALSE;
}
if ($proceed === TRUE) {
$this->log_visitor();
}
}
}
...
Dari script diatas yang dilihat pertama adalah visitor tracking sedang aktif atau tidak. Selanjutnya akan diperiksa apakah pengunjung ini bot atau pengguna biasa.
Berikutnya script ini akan menerima jika ditemukan DNT pada header http browser. Selanjutnya akan mengabaikan nama controller class yang ditemukan di daftar nama controller yang diabaikan.
Yang terakhir akan memeriksa daftar alamat IP yang diabaikan. Jika semuanya berhasil divalidasi maka waktunya menyimpan detail pengunjung.
$this->request->getIPAddress() digunakan untuk mendapatkan alamat IP di CodeIgniter 4 dari klien atau pengguna
Ketiga
Pada Codeigniter 3 biasa dikenal dengan hooks. Dan hooks ini pada Codeigniter 4 diganti dengan events.
Maka disini Anda perlu mengkonfigurasi events untuk library yang sudah dibuat diatas (VisitorTracker). Silakan update file /app/Config/Events.php
Pertama impor library VisitorTracker dengan script berikut, letakkan pada bagian atas dari file ini
use App\Libraries\VisitorTracker;
Selanjutnya tambahkan script berikut pada bagian paling bawah dari file ini
$visitorTracker = new VisitorTracker();
Events::on('post_controller_constructor', [$visitorTracker, 'track_visitor']);
Disini dibuatkan instance untuk class VisitorTracker dikarenakan class tersebut tidak memiliki static method, jadi method tidak bisa digunakan/dipanggil secara langsung.
Disini menggunakan post_controller_constructor dengan Events::on() untuk menjalankan fungsi track_visitor() setiap kali class tertentu dalam controller dipanggil/diload.
Keempat
Membuat Model \app\Models\VisitorModel.php. Model ini berfungsi untuk mengambil data berdasarkan kriteria tanggal yang diberikan, dengan script berikut
Jika menemukan error ketika menjalakan fungsi yang ada di Model ini berkaitan dengan Group By, untuk pemecahan masalahnya simak dilink ini
Pada Codeigniter Anda bisa memilih dua (2) cara untuk modeling data, dengan model Codeigniter dan entity class
Disini menggunakan model CodeIgniter untuk membuat model yang dibuat sendiri yang extends class model CodeIgniter. Hal ini akan menghasilkan banyak cara yang dibutuhkan ketika berinterkasi dengan tabel dalam database, termasuk mencari data, update data, hapus data, dan lain-lain
<?php
namespace App\Models;
use CodeIgniter\Model;
class VisitorModel extends Model {
protected $table = 'visitors';
function get_site_data_for_today() {
$results = array();
$query = $this->db->query('SELECT SUM(no_of_visits) as visits
FROM ' . $this->table . '
WHERE CURDATE()=DATE(access_date)
LIMIT 1');
if ($query->resultID->num_rows == 1) {
$row = $query->getRow();
$results['visits'] = $row->visits;
}
return $results;
}
function get_site_data_for_last_week() {
$results = array();
$query = $this->db->query('SELECT SUM(no_of_visits) as visits
FROM ' . $this->table . '
WHERE DATE(access_date) >= CURDATE() - INTERVAL DAYOFWEEK(CURDATE())+6 DAY
AND DATE(access_date) < CURDATE() - INTERVAL DAYOFWEEK(CURDATE())-1 DAY
LIMIT 1');
if ($query->resultID->num_rows == 1) {
$row = $query->getRow();
$results['visits'] = $row->visits;
return $results;
}
}
function get_chart_data_for_today() {
$query = $this->db->query('SELECT SUM(no_of_visits) as visits,
DATE_FORMAT(access_date,"%h %p") AS hour
FROM ' . $this->table . '
WHERE CURDATE()=DATE(access_date)
GROUP BY HOUR(access_date)');
if ($query->resultID->num_rows > 0) {
return $query->getResult();
}
return NULL;
}
function get_chart_data_for_month_year($month = 0, $year = 0) {
if ($month == 0 && $year == 0) {
$month = date('m');
$year = date('Y');
$query = $this->db->query('SELECT SUM(no_of_visits) as visits,
DATE_FORMAT(access_date,"%d-%m-%Y") AS day
FROM ' . $this->table . '
WHERE MONTH(access_date)=' . $month . '
AND YEAR(access_date)=' . $year . '
GROUP BY DATE(access_date)');
if ($query->resultID->num_rows > 0) {
return $query->getResult();
}
}
if ($month == 0 && $year > 0) {
$query = $this->db->query('SELECT SUM(no_of_visits) as visits,
DATE_FORMAT(timestamp,"%M") AS day
FROM ' . $this->table . '
WHERE YEAR(access_date)=' . $year . '
GROUP BY MONTH(access_date)');
if ($query->resultID->num_rows > 0) {
return $query->getResult();
}
}
if ($year == 0 && $month > 0) {
$year = date('Y');
$query = $this->db->query('SELECT SUM(no_of_visits) as visits,
DATE_FORMAT(access_date,"%d-%m-%Y") AS day
FROM ' . $this->table . '
WHERE MONTH(access_date)=' . $month . '
AND YEAR(access_date)=' . $year . '
GROUP BY DATE(access_date)');
if ($query->resultID->num_rows > 0) {
return $query->getResult();
}
}
if ($year > 0 && $month > 0) {
$query = $this->db->query('SELECT SUM(no_of_visits) as visits,
DATE_FORMAT(access_date,"%d-%m-%Y") AS day
FROM ' . $this->table . '
WHERE MONTH(access_date)=' . $month . '
AND YEAR(access_date)=' . $year . '
GROUP BY DATE(access_date)');
if ($query->resultID->num_rows > 0) {
return $query->getResult();
}
}
return NULL;
}
}
Kelima
Membuat helper, helper adalah file dengan fungsi-fungsi dalam pemrograman prosedur. Fungsi berikut yaitu generate_years() dan generate_months() secara dinamis menghasilkan tahun dan bulan masing-masing.
Penamaan file helper menggunakan cara konfensional yaitu<nama_file>_Helper.php dan lokasinya ada di folder /app/Helpers. Disini saya memberi nama visitor_helper.php dengan script sebagai berikut
<?php
/**
* dynamically generate year dropdown
* @param int $startYear start year
* @param int $endYear end year
* @param string $id id of the select-option
* @return html
*/
if(!function_exists('generate_years')) {
function generate_years($id = 'year', $startYear = '', $endYear = '') {
$startYear = (strlen(trim($startYear)) ? $startYear : date('Y') - 10);
$endYear = (strlen(trim($endYear)) ? $endYear : date('Y'));
if (!isInt($startYear) || !isInt($endYear)) {
return 'Year must be integer value!';
}
if ((strlen(trim($startYear)) < 4) || (strlen(trim($endYear)) < 4)) {
return 'Year must be 4 digits in length!';
}
if (trim($startYear) > trim($endYear)) {
return 'Start Year cannot be greater than End Year!';
}
//start the select tag
$html = '<select id="' . $id . '" name="' . $id . '">"n"';
$html .= '<option value="">-- Year --</option>"n"';
//echo each year as an option
for ($i = $endYear; $i >= $startYear; $i--) {
$html .= '<option value="' . $i . '">' . $i . '</option>"n"';
}
//close the select tag
$html .= "</select>";
return $html;
}
}
/**
* dynamically generate months dropdown
* @param string $id id of the select-option
* @return html
*/
if(!function_exists('generate_months')) {
function generate_months($id = 'month') {
//start the select tag
$html = '<select id="' . $id . '" name="' . $id . '">"n"';
$html .= '<option value="">-- Month --</option>"n"';
//echo each month as an option
for ($i = 1; $i <= 12; $i++) {
$timestamp = mktime(0, 0, 0, $i);
$label = date("F", $timestamp);
$html .= '<option value="' . $i . '">' . $label . '</option>"n"';
}
//close the select tag
$html .= "</select>";
return $html;
}
}
if(!function_exists('isInt')) {
function isInt($str) {
return preg_match("/^[1-9][0-9]*$/", $str);
}
}
if(!function_exists('visits_today')) {
function visits_today()
{
$modelVisitor = new \App\Models\VisitorModel;
$site_statics_today = $modelVisitor->get_site_data_for_today();
return isset($site_statics_today['visits']) ? $site_statics_today['visits'] : 0;
}
}
if(!function_exists('visits_last_week')) {
function visits_last_week()
{
$modelVisitor = new \App\Models\VisitorModel;
$site_statics_last_week = $modelVisitor->get_site_data_for_last_week();
return isset($site_statics_last_week['visits']) ? $site_statics_last_week['visits'] : 0;
}
}
/* End of file visitor_helper.php */
/* Location: ./app/Helpers/visitor_helper.php */
Keenam
Memanggil helper yang sudah dibuat pada BaseController, hal ini dilakukan karena daftar pengunjung ini akan ditampilkan pada menu. Update pada bagian $helper pada \app\Controllers\BaseController.php dengan script berikut
protected $helpers = ['visitor','form','auth'];
Ketujuh
Saatnya untuk menampilkannya pada view tepatnya di \app\Views\template\index.php. Tambahkan script berikut pada bagian akhir dari Sidebar (setelah Sidebar Toggler)
<div class="col mb-4">
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Pengunjung</h6>
</div>
<div class="card-body">
<h4 class="small font-weight-bold">Hari ini <span class="float-right"><?= visits_today(); ?></span></h4>
<h4 class="small font-weight-bold">Minggu lalu <span class="float-right"><?= visits_last_week(); ?></span></h4>
</div>
</div>
</div>
Akhirnya
Demikian tutorial Codeigniter 4 – Melacak Pengunjung Online. Silakan dilanjutkan dan dikembangkan lagi.
Untuk contohnya bisa dilihat di https://demo.belajardisiniaja.com/
Mudah-mudahan tutorial ini bermanfaat.
What抯 Happening i’m new to this, I stumbled upon this I have discovered It positively useful and it has aided me out loads. I’m hoping to give a contribution & assist other customers like its helped me. Great job.
Thanks, I so glad to hear that. And thanks again for visit my website
I was suggested this web site by my cousin. I’m not sure whether this post is written by him as no one else know such detailed about my difficulty. You are wonderful! Thanks!
Two thumbs up. I am flattered
wonderful post, very informative. I wonder why the other specialists of this sector don’t notice this. You should continue your writing. I’m confident, you have a huge readers’ base already!
thank you for your support. I am flattered
This is very interesting, You are a very skilled blogger. I have joined your feed and look forward to seeking more of your wonderful post. Also, I have shared your web site in my social networks!
Thanks you so much for your attention
I do accept as true with all the concepts you’ve introduced in your post. They’re really convincing and will certainly work. Still, the posts are too quick for novices. May just you please extend them a little from next time? Thanks for the post.
Receive and send emails through your temporary messaging system. Use our webmail or your favorite email software with our absolutely free disposable email. temp mail
I like this site because so much utile stuff on here : D.
you are my inhalation, I have few blogs and very sporadically run out from to post : (.
Thank you, I have recently been searching for info about this topic for ages and yours is the greatest I have discovered till now. But, what about the conclusion? Are you sure about the source?
Thanks so much for giving everyone such a splendid chance to check tips from this site. It really is very useful and as well , stuffed with amusement for me personally and my office acquaintances to search the blog really 3 times in a week to learn the new things you will have. And indeed, I am just always happy with your extraordinary tips and hints you give. Selected two points in this post are without a doubt the finest I’ve ever had.
Hello just wanted to give you a quick heads up. The text in your post seem to be running off the screen in Chrome. I’m not sure if this is a format issue or something to do with browser compatibility but I thought I’d post to let you know. The layout look great though! Hope you get the problem resolved soon. Thanks
Terima kasih, Kodingannya sangat bermanfaat