GOAL WORKSHOP

Splash

Detail

Main

Tahapan

1. Testing Rest API

2. Membuat Aplikasi Mobile Android Dengan Flutter

3. Membuat Rest API dengan PHP *

Testing Rest API

Endpoint Popular

Endpoint Top

Endpoint Kategori

Endpoint Master Kategori

Endpoint Update HitCount

Extension vscode

Flutter get_cli

 

// To install:
pub global activate get_cli 
// (to use this add the following to system PATH: [FlutterSDKInstallDir]\bin\cache\dart-sdk\bin

flutter pub global activate get_cli

// To create a flutter project in the current directory:
// Note: By default it will take the folder's name as project name
// You can name the project with `get create project:my_project`
// If the name has spaces use `get create project:"my cool project"`
get create project

// To generate the chosen structure on an existing project:
get init

// To create a page:
// (Pages have controller, view, and binding)
// Note: you can use any name, ex: `get create page:login`
// Nota: use this option if the chosen structure was Getx_pattern
get create page:home

// To create a screen
// (Screens have controller, view, and binding)
// Note: you can use any name, ex: `get screen page:login`
// Nota: use this option if the chosen structure was CLEAN (by Arktekko)
get create screen:home 

// To create a new controller in a specific folder:
// Note: you don't need to reference the folder,
// Getx will search automatically for the home folder
// and add your controller there.
get create controller:dialogcontroller on home

// To create a new view in a specific folder:
// Note: you don't need to reference the folder,
// Getx will automatically search for the home folder
// and insert your view there.
get create view:dialogview on home

// To create a new provider in a specific folder:
get create provider:user on home

// To generate a localization file:
// Note: 'assets/locales' directory with your translation files in json format
get generate locales assets/locales

// To generate a class model:
// Note: 'assets/models/user.json' path of your template file in json format
// Note: on  == folder output file
// Getx will automatically search for the home folder
// and insert your class model there.
get generate model on home with assets/models/user.json

//to generate the model without the provider
get generate model on home with assets/models/user.json --skipProvider

//Note: the URL must return a json format
get generate model on home from "https://api.github.com/users/CpdnCristiano"

// To install a package in your project (dependencies):
get install camera

// To install several packages from your project:
get install http path camera

// To install a package with specific version:
get install path:1.6.4

// You can also specify several packages with version numbers

// To install a dev package in your project (dependencies_dev):
get install flutter_launcher_icons --dev

// To remove a package from your project:
get remove http

// To remove several packages from your project:
get remove http path

// To update CLI:
get update
// or `get upgrade`

// Shows the current CLI version:
get -v
// or `get -version`

// For help
get help
pub global activate get_cli 

Mengaktifkan get cli

Membuat Project

get create project

Pilih Flutter Project -> Swift -> Kotlin

Pilih null safe = Yes, dan Linter = Dart

Pilih GetX Pattern dan selanjutnya pilih Yes

Masuk kedalam project dan buka VsCode

Buka Terminal di VsCode, Jalankan

flutter run -v
import 'package:flutter/material.dart';

import 'package:get/get.dart';

import '../controllers/home_controller.dart';

class HomeView extends GetView<HomeController> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('HomeView'),
        centerTitle: true,
      ),
      body: Center(
        child: Text(
          'HomeView is working',
          style: TextStyle(fontSize: 20),
        ),
      ),
    );
  }
}

Flutter Package

dependencies:
  animated_text_kit: ^4.2.1
  cached_network_image: ^3.2.0
  carousel_slider: ^4.0.0
  cupertino_icons: ^1.0.2
  flutter:
    sdk: flutter
  flutter_launcher_icons: ^0.9.2
  flutter_staggered_grid_view: ^0.4.1
  get: 4.6.1
  http: ^0.13.4
  jiffy: ^4.1.0
  lottie: ^1.2.1

dev_dependencies:
  flutter_lints: ^1.0.0
  flutter_test:
    sdk: flutter
  lints: 1.0.1

flutter:
  uses-material-design: true

flutter_icons:
  android: "launcher_icon"
  ios: true
  image_path: "assets/images/logo.png"

  # lokasi image lokal
  assets:
    - assets/images/logo.png
flutter pub get
flutter pub run flutter_launcher_icons:main
String appName = "VISIT BOGOR";
String token = "25d55ad283aa400af464c76d713c07ad";
String baseUrl = "http://flutter.id/apiwisata";
String logoUrl = "https://flutter.id/apiwisata/assets/logo.png";
String lottieLoading =
    'https://assets9.lottiefiles.com/packages/lf20_x62chJ.json';
String lottieError =
    'https://assets8.lottiefiles.com/packages/lf20_pNx6yH.json';
String lottieSPlash =
    "https://assets1.lottiefiles.com/packages/lf20_1pxqjqps.json";

String lottieWelcome =
    "https://assets3.lottiefiles.com/packages/lf20_puciaact.json";

Goal Splash Screen

Splash Screen

get create page:splash

import 'package:get/get.dart';

class SplashController extends GetxController {
  var isLoading = true.obs;

  void loadSplash() {
    isLoading(true);
    Future.delayed(const Duration(milliseconds: 5000), () {
      Get.snackbar("visitbogor", "Welcome");
      Get.toNamed("/home");
      isLoading(false);
    });
  }

  @override
  void onInit() {
    loadSplash();
    super.onInit();
  }
}
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:lottie/lottie.dart';
import 'package:visitbogor/app/data/config.dart';
import '../controllers/splash_controller.dart';

class SplashView extends GetView<SplashController> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: Colors.white,
        body: Obx(() {
          if (controller.isLoading.value) {
            return Center(child: Lottie.network(lottieSPlash));
          } else {
            return Center(child: Lottie.network(lottieWelcome));
          }
        }));
  }
}

Goal Home Screen

json 2 dart

{
            "id_wisata": "1",
            "kategori_name": "Kuliner",
            "title": "Coffee Shop - kafe.in.ciapus",
            "image": "https://flutter.id/apiwisata/assets/1.png",
            "user_create": "ILHAM MAULANA",
            "created_at": "2021-12-15",
            "hit_count": "13"
        },
import 'dart:convert';

//ubah ini
List<WisataModel> wisataModelFromJson(String str) => List<WisataModel>.from(
    json.decode(str).map((x) => WisataModel.fromJson(x)));

//ubah ini
String wisataModelToJson(List<WisataModel> data) =>
    json.encode(List<dynamic>.from(data.map((x) => x.toJson())));

class WisataModel {
  WisataModel({
    required this.idWisata,
    required this.kategoriName,
    required this.title,
    required this.image,
    required this.summary,
    required this.userCreate,
    required this.createdAt,
    required this.hitCount,
  });

  String idWisata;
  String kategoriName;
  String title;
  String image;
  String summary;
  String userCreate;
  String createdAt;
  String hitCount;

  factory WisataModel.fromJson(Map<String, dynamic> json) => WisataModel(
        idWisata: json["id_wisata"],
        kategoriName: json["kategori_name"],
        title: json["title"],
        image: json["image"],
        summary: json["summary"],
        userCreate: json["user_create"],
        createdAt: json["created_at"],
        hitCount: json["hit_count"],
      );

  Map<String, dynamic> toJson() => {
        // ignore: unnecessary_null_in_if_null_operators
        "id_wisata": idWisata,
        // ignore: unnecessary_null_in_if_null_operators
        "kategori_name": kategoriName,
        // ignore: unnecessary_null_in_if_null_operators
        "title": title,
        // ignore: unnecessary_null_in_if_null_operators
        "image": image,
        // ignore: unnecessary_null_in_if_null_operators
        "summary": summary,
        // ignore: unnecessary_null_in_if_null_operators
        "user_create": userCreate,
        // ignore: unnecessary_null_in_if_null_operators
        "created_at": createdAt,
        // ignore: unnecessary_null_in_if_null_operators
        "hit_count": hitCount,
      };
}
import 'package:animated_text_kit/animated_text_kit.dart';
import 'package:flutter/material.dart';

// ignore: must_be_immutable
class HeaderWidget extends StatelessWidget {
  String title;
  int type;

  static const colorizeColors = [
    Colors.purple,
    Colors.blue,
    Colors.yellow,
    Colors.red,
  ];

  static const colorizeTextStyle = TextStyle(
    fontSize: 25,
    fontWeight: FontWeight.w900,
    fontFamily: 'avenir',
  );

  HeaderWidget({required this.type, required this.title});
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16),
      child: Row(
        children: [
          Expanded(
            child: AnimatedTextKit(
              animatedTexts: [
                ColorizeAnimatedText(
                  title,
                  textStyle: colorizeTextStyle,
                  colors: colorizeColors,
                ),
                ColorizeAnimatedText(
                  title,
                  textStyle: colorizeTextStyle,
                  colors: colorizeColors,
                ),
                ColorizeAnimatedText(
                  title,
                  textStyle: colorizeTextStyle,
                  colors: colorizeColors,
                ),
              ],
              isRepeatingAnimation: true,
              onTap: () {
                print("Tap Event");
              },
            ),
          ),
        ],
      ),
    );
  }
}
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
import 'package:visitbogor/app/data/config.dart';
import 'package:visitbogor/app/data/wisata_model.dart';

// ignore: must_be_immutable
class HomeTile extends StatelessWidget {
  WisataModel data;
  HomeTile({required this.data});
  @override
  Widget build(BuildContext context) {
    return Card(
      elevation: 2,
      child: Padding(
        padding: EdgeInsets.all(8),
        child: Stack(
          children: [
            Container(
              height: 180,
              width: double.infinity,
              clipBehavior: Clip.antiAlias,
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(4),
              ),
              child: CachedNetworkImage(
                imageUrl: data.image,
                fit: BoxFit.cover,
                placeholder: (context, url) => Lottie.network(lottieLoading),
                errorWidget: (context, url, error) =>
                    Lottie.network(lottieError),
              ),
            ),
            Positioned(
              right: 5,
              child: Container(
                color: Colors.transparent,
                child: Row(
                  children: [
                    Icon(
                      Icons.visibility,
                      color: Colors.white,
                    ),
                    Text(
                      data.hitCount,
                      style: TextStyle(color: Colors.white),
                    )
                  ],
                ),
              ),
            ),
            Positioned(
              bottom: 5,
              child: Container(
                padding: EdgeInsets.all(8),
                child: Text(
                  data.title,
                  maxLines: 1,
                  style: TextStyle(
                      color: Colors.white,
                      fontFamily: 'avenir',
                      fontWeight: FontWeight.w800),
                  overflow: TextOverflow.ellipsis,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
import 'dart:convert';

import 'package:http/http.dart' as http;
import 'package:visitbogor/app/data/config.dart';
import 'package:visitbogor/app/data/wisata_model.dart';

class HomeServices {
  static var client = http.Client();

  static Future<List<WisataModel>?> fetchPopular() async {
    //http://flutter.id/apiwisata?modul=popular&token=25d55ad283aa400af464c76d713c07ad
    String url = baseUrl + "?modul=popular&token=" + token;
    var response = await client.get(Uri.parse(url));
    if (response.statusCode == 200) {
      var json = jsonDecode(response.body);
      //print(json.toString());
      var jsonString = jsonEncode(json['data']).toString();
      return wisataModelFromJson(jsonString);
    } else {
      return null;
    }
  }

  static Future<List<WisataModel>?> fetchTop() async {
    String url = baseUrl + "?modul=top&token=" + token;
    var response = await client.get(Uri.parse(url));
    if (response.statusCode == 200) {
      var json = jsonDecode(response.body);
      //print(json.toString());
      var jsonString = jsonEncode(json['data']).toString();
      return wisataModelFromJson(jsonString);
    } else {
      return null;
    }
  }

  static Future<List<WisataModel>?> fetchKategori(String id) async {
    String url = baseUrl + "?modul=kategori&id=" + id + "&token=" + token;
    print(url.toString());
    var response = await client.get(Uri.parse(url));
    if (response.statusCode == 200) {
      var json = jsonDecode(response.body);
      //print(json.toString());
      var jsonString = jsonEncode(json['data']).toString();
      return wisataModelFromJson(jsonString);
    } else {
      return null;
    }
  }

  static updateCount(String id) async {
    String url =
        baseUrl + "?modul=hitcount&id=" + id.toString() + "&token=" + token;
    var response = await client.get(Uri.parse(url));
    if (response.statusCode == 200) {
      var json = jsonDecode(response.body);
      //print(json.toString());
      return json;
    } else {
      return null;
    }
  }
}
import 'package:get/get.dart';
import 'package:visitbogor/app/data/wisata_model.dart';
import 'package:visitbogor/app/modules/home/services/home_services.dart';

class HomeController extends GetxController {
  var isLoading = true.obs;
  var isLoading2 = true.obs;
  var wisataList = <WisataModel>[].obs;
  var popularList = <WisataModel>[].obs;

  void fetchPopular() async {
    try {
      isLoading2(true);
      var data = await HomeServices.fetchPopular();
      if (data != null) {
        popularList.value = data;
      }
    } finally {
      isLoading2(false);
    }
  }

  void fetchTop() async {
    try {
      isLoading(true);
      var data = await HomeServices.fetchTop();
      if (data != null) {
        wisataList.value = data;
      }
    } finally {
      isLoading(false);
    }
  }

  void fetchKategori(String id) async {
    try {
      isLoading(true);
      var data = await HomeServices.fetchKategori(id);
      if (data != null) {
        wisataList.value = data;
      }
    } finally {
      isLoading(false);
    }
  }

  void updateHitCount(String id) async {
    await HomeServices.updateCount(id);
  }

  @override
  void onInit() {
    fetchPopular();
    fetchTop();
    super.onInit();
  }
}
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:get/get.dart';
import 'package:lottie/lottie.dart';
import 'package:visitbogor/app/customwidget/header.dart';
import 'package:visitbogor/app/data/config.dart';
import 'package:visitbogor/app/modules/home/views/home_tile.dart';

import '../controllers/home_controller.dart';

class HomeView extends GetView<HomeController> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Column(
          children: [
            //header
            HeaderWidget(type: 1, title: appName),
            //carousel
            Expanded(
              flex: 1,
              child: Obx(() {
                if (controller.isLoading2.value) {
                  return Center(
                    child: Lottie.network(lottieLoading),
                  );
                } else {
                  return CarouselSlider.builder(
                    options: CarouselOptions(
                      autoPlay: true,
                      enlargeCenterPage: true,
                      viewportFraction: 1,
                      aspectRatio: 2.0,
                      initialPage: 2,
                    ),
                    itemCount: controller.popularList.length,
                    itemBuilder: (BuildContext context, int index,
                            int pageViewIndex) =>
                        GestureDetector(
                            onTap: () {
                              controller.updateHitCount(
                                  controller.popularList[index].idWisata);
                              Get.toNamed("/detail", arguments: [
                                {"data": controller.popularList[index]}
                              ]);
                            },
                            child:
                                HomeTile(data: controller.popularList[index])),
                  );
                }
              }),
            ),
            //pembatas
            SizedBox(
              height: 10,
            ),
            //grid view
            Expanded(
              flex: 3,
              child: Obx(() {
                if (controller.isLoading.value) {
                  return Center(
                    child: Lottie.network(lottieLoading),
                  );
                } else {
                  return StaggeredGridView.countBuilder(
                    crossAxisCount: 2,
                    itemCount: controller.wisataList.length,
                    mainAxisSpacing: 16,
                    crossAxisSpacing: 16,
                    staggeredTileBuilder: (index) => StaggeredTile.fit(1),
                    itemBuilder: (context, index) {
                      return GestureDetector(
                          onTap: () {
                            controller.updateHitCount(
                                controller.wisataList[index].idWisata);
                            Get.toNamed("/detail", arguments: [
                              {"data": controller.wisataList[index]}
                            ]);
                          },
                          child: HomeTile(data: controller.wisataList[index]));
                    },
                  );
                }
              }),
            ),
          ],
        ),
      ),
    );
  }
}

Goal Detail Screen

import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:lottie/lottie.dart';
import 'package:visitbogor/app/customwidget/header.dart';
import 'package:visitbogor/app/data/config.dart';
import 'package:visitbogor/app/data/wisata_model.dart';
import '../controllers/detail_controller.dart';
import 'package:jiffy/jiffy.dart';

// ignore: must_be_immutable
class DetailView extends GetView<DetailController> {
  WisataModel data = Get.arguments[0]['data'];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
          child: Column(
        children: [
          Expanded(
            flex: 1,
            child: Stack(
              children: [
                Container(
                  //padding: EdgeInsets.all(16),
                  height: double.infinity,
                  width: double.infinity,
                  child: CachedNetworkImage(
                    imageUrl: data.image,
                    fit: BoxFit.cover,
                    placeholder: (context, url) =>
                        Lottie.network(lottieLoading),
                    errorWidget: (context, url, error) =>
                        Lottie.network(lottieError),
                  ),
                ),
                Positioned(
                    top: 5,
                    child: IconButton(
                      icon: Icon(
                        Icons.arrow_back,
                        color: Colors.white,
                      ),
                      onPressed: () {
                        Get.toNamed("/home");
                      },
                    ))
              ],
            ),
          ),
          Expanded(
            flex: 3,
            child: ListView(
              children: [
                HeaderWidget(type: 2, title: data.title),
                Padding(
                  padding: const EdgeInsets.all(16),
                  child: RichText(
                    text: TextSpan(
                      style: TextStyle(
                        color: Colors.black,
                        fontSize: 15,
                        fontFamily: 'avenir',
                      ),
                      children: <TextSpan>[
                        TextSpan(
                            text: "Upload by :" +
                                data.userCreate.toLowerCase() +
                                ", " +
                                Jiffy(data.createdAt, "yyyy-MM-dd").fromNow()),
                        TextSpan(text: "\n \n" + data.summary),
                      ],
                    ),
                  ),
                ),
              ],
            ),
          )
        ],
      )),
    );
  }
}

Rest API

dbwisata

CREATE TABLE `kategori` (
  `id_kategori` int NOT NULL,
  `kategori_name` varchar(200) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `created_by` int DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `updated_by` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `user` (
  `id_user` int NOT NULL,
  `fullname` varchar(100) DEFAULT NULL,
  `username` varchar(50) DEFAULT NULL,
  `password` varchar(100) DEFAULT NULL,
  `token` varchar(200) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `created_by` int DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `updated_by` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `wisata` (
  `id_wisata` int NOT NULL,
  `id_kategori` int DEFAULT NULL,
  `title` varchar(200) DEFAULT NULL,
  `image` varchar(300) DEFAULT NULL,
  `summary` text,
  `hit_count` int DEFAULT '0',
  `created_at` datetime DEFAULT NULL,
  `created_by` int DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `updated_by` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

File sql

-- phpMyAdmin SQL Dump
-- version 4.9.7
-- https://www.phpmyadmin.net/
--
-- Host: localhost:3306
-- Generation Time: Dec 17, 2021 at 09:30 PM
-- Server version: 8.0.27
-- PHP Version: 7.3.33

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";


/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;

--
-- Database: `dbwisata`
--

-- --------------------------------------------------------

--
-- Table structure for table `kategori`
--

CREATE TABLE `kategori` (
  `id_kategori` int NOT NULL,
  `kategori_name` varchar(200) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `created_by` int DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `updated_by` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

--
-- Dumping data for table `kategori`
--

INSERT INTO `kategori` (`id_kategori`, `kategori_name`, `created_at`, `created_by`, `updated_at`, `updated_by`) VALUES
(1, 'Kuliner', '2021-12-15 22:06:39', 1, '2021-12-15 22:06:41', 1),
(2, 'Wisata', '2021-12-15 22:07:11', 1, '2021-12-15 22:07:13', 1);

-- --------------------------------------------------------

--
-- Table structure for table `user`
--

CREATE TABLE `user` (
  `id_user` int NOT NULL,
  `fullname` varchar(100) DEFAULT NULL,
  `username` varchar(50) DEFAULT NULL,
  `password` varchar(100) DEFAULT NULL,
  `token` varchar(200) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `created_by` int DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `updated_by` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

--
-- Dumping data for table `user`
--

INSERT INTO `user` (`id_user`, `fullname`, `username`, `password`, `token`, `created_at`, `created_by`, `updated_at`, `updated_by`) VALUES
(1, 'ILHAM MAULANA', 'i.maulana', '25d55ad283aa400af464c76d713c07ad', '25d55ad283aa400af464c76d713c07ad', '2021-12-15 00:00:00', 1, '2021-12-15 00:00:00', 1),
(2, 'RAIHAN MAULANA', 'r.maulana', '5e8667a439c68f5145dd2fcbecf02209', '5e8667a439c68f5145dd2fcbecf02209', '2021-12-15 00:00:00', 1, '2021-12-15 00:00:00', 1);

-- --------------------------------------------------------

--
-- Table structure for table `wisata`
--

CREATE TABLE `wisata` (
  `id_wisata` int NOT NULL,
  `id_kategori` int DEFAULT NULL,
  `title` varchar(200) DEFAULT NULL,
  `image` varchar(300) DEFAULT NULL,
  `summary` text,
  `hit_count` int DEFAULT '0',
  `created_at` datetime DEFAULT NULL,
  `created_by` int DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `updated_by` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

--
-- Dumping data for table `wisata`
--

INSERT INTO `wisata` (`id_wisata`, `id_kategori`, `title`, `image`, `summary`, `hit_count`, `created_at`, `created_by`, `updated_at`, `updated_by`) VALUES
(1, 1, 'kafe.in.ciapus', 'https://flutter.id/apiwisata/assets/1.png', 'OFFEE SHOP BARU INSTAGRAMABLE REKOMEN! Coffee Shop baru ini namanya @kafe.in.ciapus lokasinya ada di Kota Batu, Ciapus, tepatnya di Jl. Kapten Yusuf No.188C, Kota Batu, Ciapus, Bogor persis di samping SPBU. Konsepnya minimalis outdoor dengan banyak spot instagramable. Lagi ada PROMO Harga Spesial untuk menu signaturenya Kopi BTS CUMA 18rb dari harga 22rb. @kafe.in.ciapus ini konsep nya sangat unik dan instagramable banget! Area nya 70% oudoor dan punya banyak spot-spot foto terbaik untuk OOTD. Trus ada LIVE MUSIC lho setiap hari sabtu yang bisa kamu nikmati', 41, '2021-12-15 21:50:13', 1, '2021-12-15 21:50:15', 1),
(2, 2, 'Curug Cikuluwung', 'https://flutter.id/apiwisata/assets/2.png', 'Curug Cikuluwung memiliki air berwarna biru yang indah. Bahkan, ada yang menyebut bahwa tempat ini mirip Grand Canyon versi Indonesia dengan air mengalir. Disebut grand canyon karena adanya tebing batu yang ada di kiri sepanjang aliran sungai. Untuk biaya masuk kesini cukup dengan 35rb per orang ya visitors', 43, '2021-12-15 21:57:26', 1, '2021-12-15 21:57:29', 1),
(3, 2, 'Kayumas Vila', 'https://flutter.id/apiwisata/assets/3.png', 'Kayuman Vilas merupakan salah satu objek wisata berupa tempat penginapan di Bogor yang lagi banyak diminati nih visitors. Di sini kamu akan mendapatkan pengalaman menginap dengan suasana vibes ala Bali, selain itu di sini pesona alamnya sangat indah. Untuk menginap harga yang di tawarkan mulai dari 700rb untuk weekdays dan 800rb untuk weekend\r\n—\r\nKayumanvilas\r\nCimande, Kec. Caringin, Kabupaten Bogor, Jawa Barat 16730', 26, '2021-12-17 07:29:29', 1, '2021-12-17 07:29:29', 1),
(4, 2, 'Curug Walet', 'https://flutter.id/apiwisata/assets/4.png', 'Dinamakan curug walet karena daerah curug ini terdapat banyak burung walet, ada di dinding tebing bebatuan maupun pepohonan. Selain itu curug ini juga memiliki 3 tingkatan sehingga tempat ini memiliki keindahan curug yang berbeda\r\n—\r\nCurug Walet\r\nGn. Sari, Kec. Pamijahan, Bogor, Jawa Barat 16810', 20, '2021-12-17 07:29:29', 1, '2021-12-17 07:29:29', 1),
(5, 2, 'Danau Quarry Rumpin', 'https://flutter.id/apiwisata/assets/5.png', 'Danau Quarry Rumpin merupakan bekas pertambangan pasir. Danau indah ini mengandung bahan material batu dan pasir, sehingga dulunya tempat ini adalah bekas lahan pertambangan pasir. Namun saat ini lokasi tersebut sering dikunjungi para traveller karna keindahannya.\r\n—\r\nDanau Quarry Rumpin\r\nKp. Nuggaherang, Tegalega, Kec. Cigudeg, Bogor, Jawa Barat 16660', 34, '2021-12-17 07:29:29', 1, '2021-12-17 07:29:29', 1),
(6, 2, 'Telaga Saat', 'https://flutter.id/apiwisata/assets/6.png', 'Telaga saat ini berada di Desa Tugu Utara, Kecamatan Cisarua, Kabupaten Bogor. Jaraknya sekitar 36 km dari kota Bogor. Lokasinya cukup mudah dijangkau. Jika kamu membawa kendaraan, kamu cukup arahkan kendaraan ke kawasan puncak hingga sampai di Masjid Nurul Iman yang lokasinya berada sebelum masjid At Taawun. Nah, untuk bisa menikmati indahnya Telaga Saat, kamu akan dikenakan tiket masuk sebesar 25rb per orang.\r\n—\r\nTelaga Saat\r\nCibulao, RT.02/RW.06, Tugu Utara, Kec. Cisarua, Bogor, Jawa Barat 16750', 59, '2021-12-17 07:29:29', 1, '2021-12-17 07:29:29', 1),
(7, 2, 'Wisata Javana Sehat', 'https://flutter.id/apiwisata/assets/7.png', 'Wisata Javana Sehat adalah tempat camping di daerah puncak yang menyugukan pemandangan Gn.Gede Pangrango, Gn.Salak, hamparan kebun teh, perbukitan dan pemandangan Telaga saat. Jika ingin camping disini kamu hanya membayar 50rb aja loh visitors. Tidak hanya camping, disini kamu juga bisa melakukan outbound offroad\r\n—\r\nWisata Javana Sehat\r\nTugu Utara, Kec. Cisarua, Bogor, Jawa Barat 16750', 19, '2021-12-17 07:29:29', 1, '2021-12-17 07:29:29', 1),
(8, 2, 'The Highland Park Resort', 'https://flutter.id/apiwisata/assets/8.png', 'Glamping dengan konsep Mongolia dan Indian bisa kamu rasakan ketika kamu ke The Highland Park Resort nih visitors. Menginap disini kamu akan disuguhkan pemandangan Gunung Salah yang sangat indah. Lokasinya pun tidak jauh dari tol jagorawi, kurang lebih 30 menit untuk sampai ke tempat ini. Sudah pernah coba menginap disini visitors?\r\n—\r\nThe Highland Park Resort\r\nJl. Curug Nangka Sinarwangi, Sukajadi, Kec. Tamansari, Bogor, Jawa Barat 16610', 23, '2021-12-17 07:29:29', 1, '2021-12-17 07:29:29', 1),
(9, 1, 'Waroeng Uncal', 'https://flutter.id/apiwisata/assets/9.png', 'REKOMENDASI BASO ACI & SEBLAK PALING RAME DI BOGOR! Ada Waroeng Uncal yang memang udah jadi langganan kita banget nih visitors. Nah, ternyata @waroenguncal ada menu baru loh, yaitu seblak baso aci dan pentol ceker, kamu juga bisa pilih tingkat kepedasannya dari 1-5 dan level 1 sama dengan satu centong sayur sambal loh, kebayang kan pedasnya? Harganya juga terjangkau mulai dari 15rb aja.\r\n.\r\nKalo di @waroenguncal udah gausah di tanya lagi deh ada topping apa aja disini, karna mereka punya 20 macam pilihan topping yang enak-enak loh visitors. Untuk Seblak baso aci isiannya ada tulang balungan yang enak buat di grogoti bersama kuah seblak yang super pedas. Rasa rempah dan aroma daun jeruknya berasa banget. Jangan lupa juga ya untuk pesan baso acinya, yang best seller ada baso aci paket manjiw, kuah dan sambalnya juara banget belum ada lawan deh visitors! Oia, untuk minumannya yang best seller ada es seneng dan aneka sop durian yang menggunakan daging durian medan asli atau kamu mau cobain minuman barunya juga ada, kamu bisa cobain es alpukat milo. Yuk, datang langsung ke Waroeng Uncal atau kamu bisa pesan juga di Grabfood ya!', 3, '2021-12-15 21:50:13', 1, '2021-12-15 21:50:15', 1),
(10, 1, 'Lumpia basah SMANTI', 'https://flutter.id/apiwisata/assets/10.png', 'LUMPIA BASAH LEGEND DEPAN SMAN 3 BOGOR!! Lumpia ini sudah GENERASI KEDUA CITA RASA KHAS NYA TETAP SAMA, LUDES 500 PORSI PER HARI! Visitors masih ingat dengan lumpia basah SMANTI? Lumpia basah ini termasuk lumpia basah legend di Bogor, dulu yang berjualan adalah seorang bapak-bapak namun telah meninggal dunia dan dilanjutkan oleh keluarga nya, jadi bisa dibilang lumpia basah SMANTI yang sekarang ini adalah generasi kedua. Jangan khawatir, meskipun generasi kedua, cita rasa yang khas nya masih tetap terjaga.\r\n\r\nLumpia Basah SMANTI terkenal lezat dengan cita rasa khas dan gurih yang kuat. Di sini hanya ada satu menu lumpia basah seharga 10rb, ORIGINAL tidak ada topping lain, yang membedakan hanya tingkat kepedasan yang bisa kamu sesuaikan dengan selera masing-masing. Satu porsi lumpia basah terdiri dari bengkuang, tauge, dan telur orak arik yang dibungkus dengan kulit lumpia plus olesan saus yang terbuat dari tepung kanji dan gula merah. Lumpia basah SMANTI ini dari dulu hingga sekarang berlokasi di seberang SMAN 3 Bogor dan masih dijajakan dengan gerobak sederhana namun tidak pernah sepi pembeli. Tenang saja, meskipun sehari bisa habis 300 porsi kamu tidak perlu menunggu lama karena penyajian nya bisa dibilang cepat. Karena dijajakan oleh gerobak, biasanya tidak bisa makan ditempat hanya menyediakan untuk di bawa pulang ya! Daripada penasaran mending langsung aja ke lokasi Lumpia Basah SMANTI.', 8, '2021-12-15 21:50:13', 1, '2021-12-15 21:50:15', 1),
(11, 1, 'Bakmi Kane', 'https://flutter.id/apiwisata/assets/11.png', 'BUKA CABANG KE-3 DI BINAMARGA!! Bakmi Kane buka cabang baru nih visitors di Jl. Raya binamarga no.19A Baranangsiang Bogor atau tepatnya di sebelah SMAKBO. Siapa sih yang ga kenal dengan bakmi kekinian satu ini. Bakminya yang enak dan punya banyak varian ini emang udah paling mantep deh. Bakminya sendiri kamu bisa pilih yang asin atau manis. Tersedia juga berbagai macam variasi bakmi dengan level pedas yang kamu inginkan\r\n.\r\nCabang Bakmi Kane di Binamarga ini adalah cabang ketiganya visitors. Nah, di cabang baru ini pastinya tempatnya cozy, bersih dan nyaman banget. Udah gitu instagramable dan seru buat berfoto. Banyak spot bagus dan bikin betah nongkrong lama2. Selain varian bakminya, @bakmikane juga banyak menu lainnya dan aneka menu dessert yang unik. Semua menu di Bakmi kane halal ya. Yuk, langsung aja ke Bakmi Kane, bakmi kekinian paling enak di Bogor\r\n__\r\nBakmi kane @bakmikane\r\nJl. Raya Binamarga No.19A Baranangsiang Kec. Bogor Timur Kota Bogor (sebelah SMAKBO)\r\njam buka: setiap hari, 11.00-21.00\r\nRange Harga:\r\nFood: 10.000-38.500\r\nDessert: 22.000-28.000\r\nDrink: 5.000-27.000\r\nIn Frame:\r\n- Bakmi Kane Spesial 28rb\r\n- Bakmi Sambal Matah Spesial 28rb (tingkat kepedasan 0-6)\r\n- Nasi Bakso Mercon 29rb\r\n- Pangsit Goreng 15rb\r\n- Es Lychee Silky 27rb\r\n- Es Kopi Kane 18rb\r\n- Es Susu Matcha 20rb\r\n', 7, '2021-12-15 21:50:13', 1, '2021-12-15 21:50:15', 1),
(12, 1, 'baso mie agan', 'https://flutter.id/apiwisata/assets/12.png', 'Siapa nih yang udah kangen banget sama baso aci, baso mie dan mie yamin nya @basomieagan ? Langsung aja kunjungi store nya baso mie agan bangbarung dan taman cimanggu karena ada promo beli 3 gratis 1, Visitors! GRATIS 1 MIE YAMIN setiap pembelian 3 menu makanan apapun lho, promo ini berlaku hari Kamis 9 Des 2021 - Minggu 12 Des 2021 khusus dine-in dan takeaway ya. Untuk PROMO pembelian onlinenya, @basomieagan punya promo diskon upto 20% di GOFOOD dan GRABFOOD\r\n.\r\nMenu yang wajib kamu coba di @basomieagan ada baso urat kuah taichan dengan isian baso urat, baso aci, baso kecil, aneka cuanki, ceker, mie/bihun, sayur dengan kuah taichan yang pedesnya rekomen banget. Wajib juga cobain mie yamin pedasnya yg bisa pilih level kepedasan nya dari level petir hingga granat. Kalau ke @basomieagan wajib pesen mie yamin pentol pedas yaitu mie yamin plus pangsit basah dan 5 buah pentol pedas nya dijamin bikin kamu ketagihan. Yuk langsung aja cobain @basomieagan di kedua lokasinya atau pesan online melalui GoFood dan GrabFood!\r\n__\r\nBaso & Mie Agan @basomieagan\r\nPusat: Jl. Taman Cimanggu Raya No.25 Kedung Waringin, Tanah Sereal, Bogor\r\nCabang: Jalan bangbarung raya no.45, tegal gundil, bogor (samping alfamart bangbarung)\r\nJam Buka:\r\nSenin - Kamis 10:00 - 20:00 WIB\r\nJumat - Minggu 10:00 - 21:00 WIB\r\nRange Harga:\r\nMakanan: 15rb - 28rb\r\nMinuman: 3rb - 10rb\r\nIn Frame:\r\nBaso Aci Agan Special Daging + Urat 28k\r\nBaso Aci Agan Special Mercon 28k\r\nBaso Aci Agan 20k\r\nBaso Daging Mercon Kuah Taichan 28k\r\nMie Yamin Pentol 25k\r\nMie Yamin Petir 20k\r\nEs Jeruk 10k\r\nEs Teh Manis 6k', 18, '2021-12-15 21:50:13', 1, '2021-12-15 21:50:15', 1),
(13, 1, 'Kedai Kopi Abdi', 'https://flutter.id/apiwisata/assets/13.png', 'Kedai Kopi Abdi ini memiliki tempat nongkrong dengan view yang keren nih visitors, apalagi view malam disini kamu bisa melihat city light yang indah.\r\n—\r\nKedai Kopi Abdi\r\nCijeruk, Kec. Cijeruk, Bogor, Jawa Barat 16740', 9, '2021-12-15 21:50:13', 1, '2021-12-15 21:50:15', 1),
(14, 1, 'Cafe sudutemu', 'https://flutter.id/apiwisata/assets/14.png', 'CAFE UNTUK NONGKRONG REKOMEN! COZY, INTERNET KENCENG DAN MENU BERLIMPAH KEJU MOZZARELLA DI @sudutemu !!! Kalau kamu tanya cafe rekomen untuk nongkrong dengan suasana outdoor kece, internetnya kenceng, menu2nya enak dan harganya ramah di kantong jawabannya adalah @sudutemu rekomendasi dari visitbogor. Menu yang ada di sini mulai dari indomie juga ada lho, Visitors! Cocok banget buat kamu yang mau NONGKRONG LOW BUDGET. Menu signature dan paling favorit yg harus kamu coba di @sudutemu ada ALL STAR BBQ MOZZA platters berisi sosis, jagung, potato wedges, dan chicken strip lengkap dengan saus BBQ lezat yang bisa kamu pilih level kepedasan nya, menu ini cocok banget buat dinikmatin bareng-bareng Visitors!\r\n.\r\nLalu ada Cheezy Mix berisi sayap ayam, bratwurst, kentang dan saus serta mozzarela melted yg gurih banget! Di sudut temu menu makanan, minuman dan dessert nya rekomen banget untuk dicoba. Semakin seru lagi karena selain menu nya enak-enak, suasana nya cozy, lokasi nya strategis, WIFI nya kenceng dan banyak board games yang bisa dimainin bareng temen-temen bikin nongkrong semakin seru! Langsung aja ajak teman, pacar dan keluarga kamu untuk nongkrong seru di Sudut Temu yuk!\r\n__\r\nSudut Temu by Toastea @sudutemu\r\nJl. Cirahayu No.14 Baranangsiang Bogor (arah pintu parkiran motor botani square)\r\nJam Buka:\r\nSenin - Kamis 12:00 - 21:00 WIB\r\nJumat 13:00 - 22:00 WIB\r\nSabtu - Minggu 12:00 - 22:00 WIB\r\nContact: 081316669110\r\nPrice Range:\r\nFoods: 16K-37k\r\nDrinks: 5K-26k\r\nDessert: 20k - 28k\r\n*exclude tax\r\nIn frame:\r\nEbi curry rice\r\nKatsu curry rice\r\nCheezy winger\r\nBeef patty sizzling\r\nAll star + mozzarella\r\nMatcha latte\r\nChoco blast\r\nMatcha set pudding', 12, '2021-12-15 21:50:13', 1, '2021-12-15 21:50:15', 1);

--
-- Indexes for dumped tables
--

--
-- Indexes for table `kategori`
--
ALTER TABLE `kategori`
  ADD PRIMARY KEY (`id_kategori`);

--
-- Indexes for table `user`
--
ALTER TABLE `user`
  ADD PRIMARY KEY (`id_user`);

--
-- Indexes for table `wisata`
--
ALTER TABLE `wisata`
  ADD PRIMARY KEY (`id_wisata`);

--
-- AUTO_INCREMENT for dumped tables
--

--
-- AUTO_INCREMENT for table `kategori`
--
ALTER TABLE `kategori`
  MODIFY `id_kategori` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;

--
-- AUTO_INCREMENT for table `user`
--
ALTER TABLE `user`
  MODIFY `id_user` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;

--
-- AUTO_INCREMENT for table `wisata`
--
ALTER TABLE `wisata`
  MODIFY `id_wisata` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=15;
COMMIT;

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

db.php

<?php
    define('HOST','localhost');
    define('USER','root');
    define('PASS','');
    define('DB','dbwisata');

    $conn = mysqli_connect(HOST, USER, PASS, DB)or die('Unable to connect');
CREATE TABLE IF NOT EXISTS `kategori` (
  `id_kategori` int(11) NOT NULL AUTO_INCREMENT,
  `kategori_name` varchar(200) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `created_by` int(11) DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `updated_by` int(11) DEFAULT NULL,
  PRIMARY KEY (`id_kategori`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;


INSERT INTO `kategori` (`id_kategori`, `kategori_name`, `created_at`, `created_by`, `updated_at`, `updated_by`) VALUES
	(1, 'Kuliner', '2021-12-15 22:06:39', 1, '2021-12-15 22:06:41', 1),
	(2, 'Wisata', '2021-12-15 22:07:11', 1, '2021-12-15 22:07:13', 1);


CREATE TABLE IF NOT EXISTS `user` (
  `id_user` int(11) NOT NULL AUTO_INCREMENT,
  `fullname` varchar(100) DEFAULT NULL,
  `username` varchar(50) DEFAULT NULL,
  `password` varchar(100) DEFAULT NULL,
  `token` varchar(200) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `created_by` int(11) DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `updated_by` int(11) DEFAULT NULL,
  PRIMARY KEY (`id_user`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;


INSERT INTO `user` (`id_user`, `fullname`, `username`, `password`, `token`, `created_at`, `created_by`, `updated_at`, `updated_by`) VALUES
	(1, 'ILHAM MAULANA', 'i.maulana', '25d55ad283aa400af464c76d713c07ad', '25d55ad283aa400af464c76d713c07ad', '2021-12-15 00:00:00', 1, '2021-12-15 00:00:00', 1),
	(2, 'RAIHAN MAULANA', 'r.maulana', '5e8667a439c68f5145dd2fcbecf02209', '5e8667a439c68f5145dd2fcbecf02209', '2021-12-15 00:00:00', 1, '2021-12-15 00:00:00', 1);


CREATE TABLE IF NOT EXISTS `wisata` (
  `id_wisata` int(11) NOT NULL AUTO_INCREMENT,
  `id_kategori` int(11) DEFAULT NULL,
  `title` varchar(200) DEFAULT NULL,
  `image` varchar(300) DEFAULT NULL,
  `summary` text,
  `hit_count` int(11) DEFAULT '0',
  `created_at` datetime DEFAULT NULL,
  `created_by` int(11) DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `updated_by` int(11) DEFAULT NULL,
  PRIMARY KEY (`id_wisata`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;


INSERT INTO `wisata` (`id_wisata`, `id_kategori`, `title`, `image`, `summary`, `hit_count`, `created_at`, `created_by`, `updated_at`, `updated_by`) VALUES
	(1, 1, 'Coffee Shop - kafe.in.ciapus', 'https://flutter.id/apiwisata/assets/1.png', 'OFFEE SHOP BARU INSTAGRAMABLE REKOMEN! Coffee Shop baru ini namanya @kafe.in.ciapus lokasinya ada di Kota Batu, Ciapus, tepatnya di Jl. Kapten Yusuf No.188C, Kota Batu, Ciapus, Bogor persis di samping SPBU. Konsepnya minimalis outdoor dengan banyak spot instagramable. Lagi ada PROMO Harga Spesial untuk menu signaturenya Kopi BTS CUMA 18rb dari harga 22rb. @kafe.in.ciapus ini konsep nya sangat unik dan instagramable banget! Area nya 70% oudoor dan punya banyak spot-spot foto terbaik untuk OOTD. Trus ada LIVE MUSIC lho setiap hari sabtu yang bisa kamu nikmati', 0, '2021-12-15 21:50:13', 1, '2021-12-15 21:50:15', 1),
	(2, 2, 'Curug Cikuluwung', 'https://flutter.id/apiwisata/assets/2.png', 'Curug Cikuluwung memiliki air berwarna biru yang indah. Bahkan, ada yang menyebut bahwa tempat ini mirip Grand Canyon versi Indonesia dengan air mengalir. Disebut grand canyon karena adanya tebing batu yang ada di kiri sepanjang aliran sungai. Untuk biaya masuk kesini cukup dengan 35rb per orang ya visitors', 0, '2021-12-15 21:57:26', 1, '2021-12-15 21:57:29', 1);

Response Handler

<?php

  require_once('db.php');
  
  //response json ketika success
  function resSuccess($status, $message,$count, $data){
      $response = [
          "status" => $status,
          "message" => $message,
          "hit_date" => date('Y-m-d H:i:s'),
          "user_ip" => $_SERVER['REMOTE_ADDR'],
          "count" => $count,
          "data" => $data
      ];
      header("Content-type: application/json");
      echo json_encode($response);
  }

  //response json ketika error
  function resError($status, $message){
    $response = [
        "status" => $status,
        "message" => $message,
        "hit_date" => date('Y-m-d H:i:s'),
        "user_ip" => $_SERVER['REMOTE_ADDR'],
        "count" => null,
        "data" => null
    ];
    header("Content-type: application/json");
    echo json_encode($response);
  }

Validasi Token

  //mengecek token pada db
  function validToken($conn,$token){
    $q = "SELECT u.token 
            FROM user u
            WHERE u.token='$token'
            LIMIT 1";
    $mq = mysqli_query($conn, $q);
    $r = array();
    $row = $mq->fetch_assoc();
    if($row){
        return true;
    }else{
        return false;
    }
  }

get data from database

  //manampilkan Top 10 Wisata
  function getTop($conn){
      $q = "SELECT 
            w.id_wisata, k.kategori_name, w.title, w.image,w.summary, 
            u.fullname AS user_create, w.created_at, w.hit_count
            FROM wisata w
            LEFT JOIN kategori k ON w.id_kategori = k.id_kategori
            LEFT JOIN user u ON w.created_by = u.id_user
            ORDER BY w.created_at DESC
            LIMIT 50";
      $mq = mysqli_query($conn, $q);
      $r = array();
      while($row = mysqli_fetch_array($mq)){
          array_push($r,array(
              'id_wisata' => $row['id_wisata'],
              'kategori_name' => $row['kategori_name'],
              'title' => $row['title'],
              'image' => $row['image'],
              'summary' => $row['summary'],
              'user_create' => $row['user_create'],
              'created_at' => $row['created_at'],
              'hit_count' => $row['hit_count']
          ));
      }

      return $r;
  }
  
  //manampilkan Popular Wisata
  function getPopular($conn){
      $q = "SELECT 
            w.id_wisata, k.kategori_name, w.title, w.image,w.summary, 
            u.fullname AS user_create, w.created_at, w.hit_count
            FROM wisata w
            LEFT JOIN kategori k ON w.id_kategori = k.id_kategori
            LEFT JOIN user u ON w.created_by = u.id_user
            ORDER BY w.hit_count DESC
            LIMIT 50";
      $mq = mysqli_query($conn, $q);
      $r = array();
      while($row = mysqli_fetch_array($mq)){
          array_push($r,array(
              'id_wisata' => $row['id_wisata'],
              'kategori_name' => $row['kategori_name'],
              'title' => $row['title'],
              'image' => $row['image'],
              'summary' => $row['summary'],
              'user_create' => $row['user_create'],
              'created_at' => $row['created_at'],
              'hit_count' => $row['hit_count']
          ));
      }

      return $r;
  }
  
  //manampilkan Kategori Wisata
  function getKategori($conn,$id_kategori){
      $q = "SELECT 
            w.id_wisata, k.kategori_name, w.title, w.image,w.summary, 
            u.fullname AS user_create, w.created_at, w.hit_count
            FROM wisata w
            LEFT JOIN kategori k ON w.id_kategori = k.id_kategori
            LEFT JOIN user u ON w.created_by = u.id_user
            WHERE w.id_kategori='$id_kategori'
            ORDER BY w.created_at DESC
            LIMIT 50";
      $mq = mysqli_query($conn, $q);
      $r = array();
      while($row = mysqli_fetch_array($mq)){
          array_push($r,array(
              'id_wisata' => $row['id_wisata'],
              'kategori_name' => $row['kategori_name'],
              'title' => $row['title'],
              'image' => $row['image'],
              'summary' => $row['summary'],
              'user_create' => $row['user_create'],
              'created_at' => $row['created_at'],
              'hit_count' => $row['hit_count']
          ));
      }

      return $r;
  }
  
  //menambah hit count
  function hitCount($conn,$id){
      $q = "UPDATE wisata w
            SET w.hit_count = w.hit_count+1
            WHERE w.id_wisata='$id'
            ";
      $mq = mysqli_query($conn, $q);
      return $mq;
  }


    
  //manampilkan Master Kategori
  function getMasterKategori($conn){
      $q = "SELECT 
            k.id_kategori,k.kategori_name
            FROM kategori k
            ORDER BY k.kategori_name ASC
            ";
      $mq = mysqli_query($conn, $q);
      $r = array();
      while($row = mysqli_fetch_array($mq)){
          array_push($r,array(
              'id_kategori' => $row['id_kategori'],
              'kategori_name' => $row['kategori_name']
          ));
      }

      return $r;
  }

Modul

  //validasi & hasil rest api
  isset($_GET['token']) ? $token = $_GET['token'] : $token="";
  isset($_GET['modul']) ? $modul = $_GET['modul'] : $modul="";
  isset($_GET['id']) ? $id = $_GET['id'] : $id="";
  
  if(validToken($conn,$token)){

            switch ($modul) {
              case "top":
                resSuccess(
                    "success",
                    "Success Get Top 10",
                    count(getTop($conn)),
                    getTop($conn)
                );
                break;
              case "popular":
                resSuccess(
                    "success",
                    "Success Get Popular",
                    count(getPopular($conn)),
                    getPopular($conn)
                );
                break;
              case "kategori":
                resSuccess(
                    "success",
                    "Success Get Kategori",
                    count(getKategori($conn,$id)),
                    getKategori($conn,$id)
                );
                break;
              case "hitcount":
                resSuccess(
                    "success",
                    "Success update hitcount",
                    1,
                    hitCount($conn,$id)
                );
                break;
              case "masterkategori":
                resSuccess(
                    "success",
                    "Success Get Master Kategori",
                    count(getMasterKategori($conn)),
                    getMasterKategori($conn)
                );
                break;
              default:
                resSuccess(
                    "success",
                    "Success Get Top 10",
                    count(getTop($conn)),
                    getTop($conn)
                );
            }
      
  }else{
      resError("error","Invalid Token");
  }
WORKSHOP ANDROID IKAUBSI

# Materi Online
  https://flutter.id/ikaubsi/

# Materi Offline (html)
  https://flutter.id/apiwisata/assets/slide.zip

# Source Code Rest Api 
  https://flutter.id/apiwisata/assets/restapi.zip

# Source Code Flutter
  https://flutter.id/apiwisata/assets/flutter.zip

# APK Workshop
  https://flutter.id/apiwisata/assets/visitbogor_apk.zip

WorkshopAndroidIkaubsi

By Maulana Ilham

WorkshopAndroidIkaubsi

Improving User to Creator

  • 898