Belajar Laravel: Validasi nomor telpon dengan package Propaganistas

Validasi nomor telpon sebenarnya susah susah mudah. Sebelum membahas susah susah mudahnya itu ada baiknya kenapa kita perlu melakukan validasi terhadap nomor telpon yang kadang perlu dilakukan di aplikasi yang kita buat.

Continue reading Belajar Laravel: Validasi nomor telpon dengan package Propaganistas

Blade Error Directive Pada Laravel 5.8.13

Sore ini dapat email dari Laravel News, terdapat beberapa perubahan pada Laravel 5.8.13 yang salah satunya adalah Directive Blade terbaru untuk menangani error. Walaupun mungkin Blade ini sudah mulai ditinggalkan oleh frontend developer karena beralih ke vuejs atau reactjs, namun saya tertarik membahasnya di tulisan pendek ini.

Continue reading Blade Error Directive Pada Laravel 5.8.13

Laravel: Unit Testing dengan SQLite Memory

Lama tidak menulis artikel, hari ini coba untuk membahas tulisan sederhana bagaimana kita membuat unit testing di Laravel dengan menggunakan SQLite di dalam memory. Kenapa harus membuat unit testing? kenapa harus menyimpannya pada SQLite? dan kenapa menggunakan memory? Yuk kita bahas satu per satu ya.

Kenapa membuat Unit Testing?

Sebagai developer atau programmer, kita tentu diharapkan dapat membuat aplikasi yang minim error. Bagaimana agar minim error? tentu kita harus mengenal apa itu error dan bagaimana error itu bisa muncul. Ketika error sudah kita kenali, kita buat kode untuk menghindari error tersebut.

Pekerjaan ini tentu memakan waktu kita sebagai developer yang biasa membuat aplikasi, lalu test dan deliver ke pengguna. Apa ada pembaca disini yang membuat aplikasi lalu langsung di deliver ke pengguna? 😀 Jangan ya..

Di proses test yang biasanya kita lakukan secara manual, sekarang kita ubah proses itu dengan cara membuat unit testing / kode kecil untuk melakukan testing. Kode-kode tersebut akan dijalankan sebelum proses deploy (aplikasi siap digunakan oleh user). Jadi sebelum user menggunakan aplikasi yang kita buat, kita (programmer) pantau terlebih dahulu proses testing ini.

Bagaimana jika flow aplikasi berubah?

Jika aplikasi berubah, kita harus ubah juga kode unit testing sesuai dengan perubahan tersebut. Ini tentunya menambah pekerjaan lagi buat programmer ya. Tapi langkah ini diambil untuk memastikan produk kita sesuai dengan rencana dan meminimalisir error yang muncul.

Kenapa menggunakan SQLite untuk Unit Testing?

Sebelum membahas kenapa menggunakan SQLite, pastikan kita sudah mengetahui apa itu SQLite dan manfaat SQLite dalam database di aplikasi kita. SQLite biasanya digunakan untuk menyimpan data secara local di lingkungan aplikasi dengan beberapa keterbatasan dan kelebihan yang ada pada SQLite. Saya tidak akan membahas jauh tentang SQLite ya.

Alasan kenapa menggunakan SQLite untuk unit testing adalah untuk memudahkan kita melakukan testing yang berkaitan dengan database. Ketika kita programmer sudah bekerja bersama programmer lain, kita kadang tidak mungkin memiliki database dengan data yang sama.

Ada beberapa programmer yang sedang mengerjakan feature tertentu dengan data di database yang mungkin berbeda. Namun perlu diingat, pada suatu waktu pasti feature dari tiap programmer akan disatukan dalam satu kode yang sama sehingga tiap-tiap programmer harus memiliki data yang sama persis. Ketika itu terjadi, tentu saja proses testing juga tetap harus berjalan.

Lalu kenapa tidak menggunakan MySQL? Atau RDBMS Lainnya?

Awalnya saya juga menggunakan MySQL karena aplikasi yang sedang berjalan juga menggunakan MySQL. Namun setelah mencari informasi jika kita menggunakan SQLite itu lebih kecil dan lebih cepat melakukan testing dengan data yang sama. Kadang beberapa unit kecil testing flow yang kita tulis hanya beberapa proses kecil yang juga bisa dilakukan dengan SQLite.

Dengan keterbatasan SQLite, temen-temen juga perlu memperhatikan itu ketika melakukan testing jangan sampai ada query atau data atau mungkin join table yang terbatasi oleh SQLite. Beberapa batasan dari SQLite ini bisa temen-temen lihat di link berikut ini: https://www.sqlite.org/limits.html

Apa maksud SQLite dengan Memory?

Kalau temen-temen pernah menggunakan SQlite, kita tentu store atau simpan data di salah satu file di hardisk atau storage. Nah SQLite ini juga bisa menyimpan tanpa file di storage, kita bisa simpan itu di memory. Mungkin ada yang kepikiran seperti redis ya, eh wait, ada yang belum tahu apa itu redis? Silahkan baca tulisan sederhana saya tentang redis di sini: https://www.adiputra.web.id/mengenal-redis-teori-sederhana/

Temen-temen bisa baca dokumentasi SQLite dengan Memory di link berikut ini: https://www.sqlite.org/inmemorydb.html

Dengan adanya memory ini, tentu akan lebih mempercepat proses testing karena unit testing yang kita lakukan mengolah data dimana data itu berada di memory.

Setting SQLite Memory pada Unit Testing Laravel

Setelah kita ketahui beberapa info diatas, yuk sekarang kita coba untuk membuat unit testing sederhana di Laravel. Saya ingin mencoba membuat unit testing untuk CRUD Kupon. Sebelumnya pastikan juga temen-temen sudah menginstall SQLite ya. Yang diperlukan pada proses ini adalah:

  1. Membuat migration table
  2. Membuat factory dengan Faker
  3. Melakukan Setting pada PHPUnit
  4. Membuat unit testing
  5. Jalankan testing

Membuat Migration Table

Sebelum memulai, tentunya pasti kita install laravel di komputer kita ya, jangan komputer orang lain apalagi komputer mantan #eh #becanda

Buat migration table, saya ingin membuat migration table kupon seperti berikut ini:

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateCouponsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('coupon', function (Blueprint $table) {
            $table->increments('coupon_id');
            $table->unsignedInteger('shop_id')->index();
            $table->string('coupon_name',45)->index();
            $table->tinyInteger('coupon_type');
            $table->string('coupon_code',45)->index();
            $table->decimal('coupon_amount');
            $table->decimal('coupon_minpurchase');
            $table->date('coupon_expire')->nullable();
            $table->date('coupon_start')->nullable();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('coupon');
    }
}

Ada yang bertanya mungkin kenapa kita ingin membuat unit testing tapi harus membuat migration dulu?

Kalau temen-temen ingin membuat unit testing tanpa data yaa tidak perlu ada migration. Migration disini berguna untuk menyetting struktur table di SQLite yang kita buat nanti. Jadi tanpa migration, unit testing kita akan selalu gagal karena struktur table tidak ditemukan 🙂

Membuat Factory dengan Faker

Ada yang belum mengerti Factory di Laravel? mudah-mudahan sudah mengerti ya. Gunanya membuat Factory di Laravel adalah agar kita bisa meng-generate data dengan mudah di struktur table yang sebelumnya kita buat dengan migration. Factory ini akan kita panggil nanti di unit testing yang akan kita buat. Jika ada temen-temen yang ingin membaca apa itu factory mungkin bisa baca dokumentasi pada Laravel disini: https://laravel.com/docs/5.8/database-testing#generating-factories

Faker sendiri adalah salah satu package yang memudahkan kita mengisi data yang mirip dengan keadaan kolom. Karena kita diatas membahas factory dimana data akan di generate. Tentu saja generate tersebut tidak secara otomatis. Kita harus isi data tersebut lalu proses pengisian biasanya kita lakukan dengan memanggil method random yang ada pada PHP ataupun Helper di Laravel. Nah dengan Faker, kita bisa mengisi data yang mirip dengan kondisi dari kolom data. Misalnya data kolom nomor telp secara random, data kolom nama secara random, data kolom email secara random.

Temen-temen yang ingin lihat package Faker ini bisa lihat disini tanpa harus gugel: https://github.com/fzaninotto/Faker

Lalu berikut adalah factory yang saya buat dengan nama CouponFactory.php:

<?php

use Faker\Generator as Faker;

$factory->define(App\Models\Coupon::class, function (Faker $faker) {

    $coupon_ongoing = $faker->randomElement(array(0,1));
    $coupon_start = null;
    $coupon_expire = null;

    if($coupon_ongoing == 0) {
        $start = $faker->dateTimeBetween('+1 days', '+90 days');

        $coupon_start = $start->format('Y-m-d');
        $coupon_expire = $faker->dateTimeBetween($start, $start->format('Y-m-d H:i:s') . '+60 days')->format('Y-m-d');
    }

    return [
        'coupon_id' => $faker->randomDigit,
        'shop_id' => $faker->randomDigit,
        'coupon_name' => $faker->name,
        'coupon_code' => $faker->name,
        'coupon_type' => $faker->randomElement(array(1,2)),
        'coupon_amount' => $faker->randomDigit,
        'coupon_quantity' => $faker->randomDigit,
        'coupon_minpurchase' => $faker->randomDigit,
        'coupon_start' => $coupon_start,
        'coupon_expire' => $coupon_expire
    ];
});

Melakukan Setting pada PHPUnit

Setelah kita membuat Factory, sekarang kita setting di PHPUnit. PHPUnit adalah package yang memudahkan kita melakukan proses testing. Ada beberapa method bawaan dari PHPUnit yang memudahkan kita untuk mengecek data, atau kondis dari flow aplikasi yang kita buat.

Laravel sendiri sudah secara default mengikutsertakan PHPUnit di framework ini. Dalam artian, temen-temen tidak perlu menginstall package atau data ketika menggunakan PHPUnit ini. Yang perlu temen-temen lakukan adalah tambahkan data pada file phpunit.xml dengan tag default seperti ini (pada laravel 5.7)

<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
         backupStaticAttributes="false"
         bootstrap="vendor/autoload.php"
         colors="true"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         processIsolation="false"
         stopOnFailure="false">
    <testsuites>
        <testsuite name="Unit">
            <directory suffix="Test.php">./tests/Unit</directory>
        </testsuite>

        <testsuite name="Feature">
            <directory suffix="Test.php">./tests/Feature</directory>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist processUncoveredFilesFromWhitelist="true">
            <directory suffix=".php">./app</directory>
        </whitelist>
    </filter>
    <php>
        <env name="APP_ENV" value="testing"/>
        <env name="BCRYPT_ROUNDS" value="4"/>
        <env name="CACHE_DRIVER" value="array"/>
        <env name="SESSION_DRIVER" value="array"/>
        <env name="QUEUE_DRIVER" value="sync"/>
        <env name="MAIL_DRIVER" value="array"/>
    </php>
</phpunit>

Menjadi seperti ini:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
         backupStaticAttributes="false"
         bootstrap="vendor/autoload.php"
         colors="true"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         processIsolation="false"
         stopOnFailure="false">
    <testsuites>
        <testsuite name="Feature">
            <directory suffix="Test.php">./tests/Feature</directory>
        </testsuite>

        <testsuite name="Unit">
            <directory suffix="Test.php">./tests/Unit</directory>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist processUncoveredFilesFromWhitelist="true">
            <directory suffix=".php">./app</directory>
        </whitelist>
    </filter>
    <php>
        <env name="APP_ENV" value="testing"/>
        <env name="CACHE_DRIVER" value="array"/>
        <env name="SESSION_DRIVER" value="array"/>
        <env name="QUEUE_DRIVER" value="sync"/>
        <env name="MAIL_DRIVER" value="array"/>
        <env name="DB_CONNECTION" value="sqlite"/>
        <env name="DB_DATABASE" value=":memory:"/>
    </php>
</phpunit>

Kalau temen-temen lihat, yang utama adalah penambahan key name DB_CONNECTION = sqlite dan DB_DATABASE = :memory:

Kedua tambahan key name tersebut untuk menyetting database connection dan db name kita saat melakukan testing.

Laravel otomatis akan membaca data yang ada pada phpunit.xml ketika kita melakukan test. Temen-temen juga bisa menggunakan file .env.testing jika mau dan perlu beberapa setting ya.

Membuat Unit Testing

Setelah itu, sekarang kita coba membuat unit testing coupon untuk mendapatkan list seperti kode berikut ini:

<?php
namespace Tests\Feature\V3;

use App\Models\Shop;
use App\Models\User;
use App\Models\Coupon;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
use Laravel\Passport\Passport;

class CouponTest extends TestCase
{
    use RefreshDatabase;

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

    /**
     * @test
     */
    public function user_want_get_resource_coupon_list()
    {
        Passport::actingAs(
            factory(User::class)->create(),
            ['all'],
            'api'
        );
        
        $this->shop = factory(Shop::class)->create();

        factory(Coupon::class)->create([
            'shop_id' => $this->shop->shop_id
        ]);

        $response = $this->get('/api/shop/' . $this->shop->shop_id . '/coupon');

        $response
            ->assertSuccessful()
            ->assertSee('shop_id')
            ->assertSee('coupon_name')
            ->assertSee('coupon_code');
    }

}

Jika temen-temen lihat diatas, saya memanggil factory Coupon::class yang telah kita buat sebelumnya. Lalu ada beberapa factory lain seperti factory User dan Shop yang tidak saya bahas.

Intinya dari kode unit testing diatas adalah, kita membuat data dengan factory coupon, setelah data dibuat maka tentu akan masuk ke SQLite Memory. Dan setelah itu kita coba panggil API Restful untuk menghandle data list coupon yang kita baru saja buat.

Kode Controller dan Route untuk menghandle data API Restful tidak saya tulis disini. Saya hanya menjelaskan flow ketika kita menggunakan SQLite Memory pada Unit testing yang kita buat.

Sampai disini, kritik saran atau ada yang tidak sesuai, boleh komentar di kolom komentar ya. Saya juga masih belajar 🙂

Belajar PHP Mencoba RatchetPHP untuk menangani Websocket

RatchetPHP library dapat membantu kita untuk membuat websocket, realtime aplikasi. Websocket sendiri kadang diperlukan di beberapa kasus aplikasi yang menginginkan data dan flow proses dilakukan secara realtime. Contoh sederhana adalah kita sedang chatting, ada proses yang dilakukan secara realtime.

Kasus ini sebenarnya sudah dikenalkan di salah satu fitur keren dari NodeJS. Dengan NodeJS kita dapat mudah melakukan hal tersebut dengan bahasa Javascript. Namun, mungkin ada beberapa kasus dimana kita tidak bisa menggunakan JS untuk menghandle proses websocket ini dan harus menggunakan PHP karena mungkin semua aplikasi dibagun di atas PHP.

Saya mencoba mengikuti dokumentasi dari RatchetPHP dan mengikuti beberapa tutorial yang ada di internet. Berikut saya coba tulis di blog agar nanti bisa mudah di implementasikan kembali tanpa harus browsing-browsing lagi.

Tahapan-nya:
1. PHP versi 7
Versi PHP yang bisa menjalankan Ratchetphp ini >=5.4.2. Untuk temen-temen yang masih dibawah itu belum bisa menggunakan RatchetPHP ini. Saya sarankan menggunakan PHP Versi 7+
2. Install ZeroMQ. Distributed system message.
Ini seperti RabbitMQ yang gunanya menghantarkan data/message. Karena RatchetPHP ini menggunakan ZeroMQ, saya mengikuti untuk memasang ZeroMQ di ubuntu saya. Panduan Installasi bisa mengikut di link ini: https://gist.github.com/katopz/8b766a5cb0ca96c816658e9407e83d00

#!/usr/bin/bash

# Download zeromq
# Ref http://zeromq.org/intro:get-the-software
wget https://github.com/zeromq/libzmq/releases/download/v4.2.2/zeromq-4.2.2.tar.gz

# Unpack tarball package
tar xvzf zeromq-4.2.2.tar.gz

# Install dependency
sudo apt-get update && \
sudo apt-get install -y libtool pkg-config build-essential autoconf automake uuid-dev

# Create make file
cd zeromq-4.2.2
./configure

# Build and install(root permission only)
sudo make install

# Install zeromq driver on linux
sudo ldconfig

# Check installed
ldconfig -p | grep zmq

# Expected
############################################################
# libzmq.so.5 (libc6,x86-64) => /usr/local/lib/libzmq.so.5
# libzmq.so (libc6,x86-64) => /usr/local/lib/libzmq.so
############################################################


3. Install ext-zmq. extension untuk ZeroMQ.
Extension ini untuk menghubungkan antara PHP dengan ZeroMQ. Temen-temen bisa lihat di link ini https://eole-io.github.io/sandstone-doc/install-zmq-php-linux

Setelah semua di install buat folder berisi composer json seperti berikut ini:

{
    "name": "adiputra/rachetphp-test",
    "description": "rachetphp testing",
    "require": {
        "cboden/ratchet": "^0.4.1",
        "react/zmq": "0.2.*|0.3.*"
    },
    "autoload": {
        "psr-4": {
            "Adiputra\\Rachetphp\\": "src"
        }
    },
    "authors": [
        {
            "name": "Adiputra",
            "email": "tuts.adiputra@gmail.com"
        }
    ]
}

Setelah itu jalankan composer install untuk menginstall beberapa library dari Ratchetphp ini.

Setelah itu kita buat folder src lalu di dalam folder src kita buat folder bin dan file server.php. Isi file server.php seperti berikut ini:

<?php
require dirname(__DIR__) . '../../vendor/autoload.php';

use React\EventLoop\Factory;
use React\ZMQ\Context;

$loop = React\EventLoop\Factory::create();
$broadcast = new Adiputra\Rachetphp\Broadcast;

// Listen for the web server to make a ZeroMQ push after an ajax request
$context = new React\ZMQ\Context($loop);
$pull = $context->getSocket(ZMQ::SOCKET_PULL);
$pull->bind('tcp://127.0.0.1:5555'); // Binding to 127.0.0.1 means the only client that can connect is itself
$pull->on('message', array($broadcast, 'onNewObject'));

// Set up our WebSocket server for clients wanting real-time updates
$webSock = new React\Socket\Server('0.0.0.0:8080', $loop); // Binding to 0.0.0.0 means remotes can connect
$webServer = new Ratchet\Server\IoServer(
    new Ratchet\Http\HttpServer(
        new Ratchet\WebSocket\WsServer(
            $broadcast
        )
    ),
    $webSock
);

$loop->run();

File server.php gunanya untuk membuat server serta melisten atau menerima apakah ada data yang dikirimkan oleh client atau tidak.

Setelah itu kita buat file client yaitu index.html seperti kode dibawah ini:

<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

<div id="messages"></div>

<div>
    <form>
        <input type="text" id="msgText">
        <input type="button" id="msgSend" value=">">
    </form>
</div>
<script>
    var conn = new WebSocket('ws://localhost:8080');
    var userId = 51;
    
    conn.onopen = function (e) {
        console.log("Connection established!");
        console.log(e.data);
    };

    conn.onmessage = function (e) {
        console.log(e.data);

        var response = JSON.parse(e.data);
        if(response['user_id'] == userId) {
            var chat = document.createElement("div");
            var cwind = document.getElementById("messages");
            cwind.appendChild(chat);
            cwind.lastElementChild.innerHTML = response["message"];
        }
    };

    var button = document.getElementById("msgSend");

    button.onclick = function () {
        var val = document.getElementById("msgText").value;
        conn.send(JSON.stringify({'user_id': userId, 'message': val}));
    };

</script>
</body>

File index.html ini adalah file client yang nanti akan menerima data dari server ke client atau sebaliknya dari client ke server.

Setelah itu saya buat file Broadcast.php seperti kode dibawah ini:

<?php
namespace Adiputra\Rachetphp;

use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;

class Broadcast implements MessageComponentInterface {
    protected $clients;

    public function __construct() {
        $this->clients = new \SplObjectStorage;
    }

    public function onOpen(ConnectionInterface $conn) {
        // Store the new connection to send messages to later
        $this->clients->attach($conn);

        echo "New connection! ({$conn->resourceId})\n";
    }

    public function onMessage(ConnectionInterface $from, $msg) {
        $numRecv = count($this->clients) - 1;
        echo sprintf('Connection %d sending message "%s" to %d other connection%s' . "\n"
            , $from->resourceId, $msg, $numRecv, $numRecv == 1 ? '' : 's');

        foreach ($this->clients as $client) {
            if ($from !== $client) {
                // The sender is not the receiver, send to each client connected
                $client->send($msg);
            }
        }
    }

    public function onClose(ConnectionInterface $conn) {
        // The connection is closed, remove it, as we can no longer send it messages
        $this->clients->detach($conn);

        echo "Connection {$conn->resourceId} has disconnected\n";
    }

    public function onError(ConnectionInterface $conn, \Exception $e) {
        echo "An error has occurred: {$e->getMessage()}\n";

        $conn->close();
    }

    public function onNewObject($msg)
    {
        echo "on new object fire \n";
        foreach ($this->clients as $client) {
            $client->send($msg);
        }
    }
}

Isi file tersebut adalah membaca beberapa event yang terjadi. Temen-temen yang pernah menggunakan Socket IO pasti memahami beberapa event disini ya. Secara garis besar terdapat event ketika telah terbuka / masuk (on open). Ketika sedang tertutup (on close) dan ketika terjadi pengiriman data (on message). Beberapa method seperti on error untuk menghandle ketika terjadi error pada class Ratchetphp ini. Ke-empat method itu adalah bawaan utama atau default yang saya baca pada documentasi RatchetPHP

Terdapat method tambahan yaitu method onNewObject. Kegunaan method itu untuk mengirimkan kembali ke client data setelah menerima data.

Sampai disini kawan-kawan bisa menjalankan dengan cara:
1. Jalankan server.php
Menjalankan server.php dengan cara mengetikan php src/bin/server.php. Pastikan ketika menjalankan ini tidak memunculkan error.
2. Jalankan index.html di beberapa browser
Untuk menerima dan mengirimkan data dari client ke client lain. Kawan-kawan bisa membuka file index.html di beberapa browser seperti browser firefox dan chromium.

Sampai disini seharusnya sudah jalan dengan baik ya 🙂

Saya coba membuat file baru dengan nama SendMessage.php:

<?php
$userId = 51;

$entryData = array(
    'user_id' => $userId,
    'message' => "Send message for user id: " . $userId
);

$context = new ZMQContext();
$socket = $context->getSocket(ZMQ::SOCKET_PUSH, 'adiputra');
$socket->connect("tcp://localhost:5555");
$socket->send(json_encode($entryData));

File ini gunanya untuk mengakses langsung file PHP lalu mengirimkan data ke socket agar client bisa membaca data yang dikirimkan dengan PHP.

Temen temen bisa mencoba dengan mengetikkan perintah php src/SendMessage.php untuk mencobanya.

Belajar PHP – Menggunakan array_unique

Seri belajar PHP hari ini adalah bagaimana kita menggunakan array_unique pada PHP. Pada kasus tertentu kita mendapatkan data yang berasal baik itu dari database atau user input pada form yang telah kita sediakan, namun memiliki nilai yang duplicate atau bisa disebut sama. Untuk menghindari data yang kita kelola itu duplikat, pada PHP kita bisa menggunakan array_unique ini.

Pada dokumentasi PHP, array unique ini sudah ada pada versi PHP4 dan pada Versi PHP 7 masih bisa kita gunakan lho.

array_unique ini digunakan untuk menghindari duplikat dari nilai sebuah array.

array_unique ( array $array [, int $sort_flags = SORT_STRING ] )

Fungsi array_unique ini juga mengeluarkan nilai yang unique pada array sehingga kita bisa membuat variable baru untuk memisahkan antara variable yang duplikat dengan variable yang sudah unique.

<?php
$input = array(1,2,"1",3,"3","5",5,1);
$result = array_unique($input);
print_r($result);
?>

Hasil dari kode diatas seperti ini.

Array
(
    [0] => 1
    [1] => 2
    [3] => 3
    [5] => 5
)

Bisa kawan-kawan lihat, hasil mengeluarkan nilai yang unique.

Kawan-kawan juga bisa lihat array_unique ini juga bisa membandingkan tipe integer dan string ya. Lalu keluaran dari array_unique ini mengeluarkan key dimana array value itu berasal, maksudnya key 0 => 1, key 1 => 2, key 3 => 3 dan seterusnya. Jadi output tidak selalu berurutan.

array_unique ini belum bisa melakukan proses pada array multiple. Sebagai contoh berikut ini.

<?php
$input = [
    0 => [
        "id" => 1,
        "value" => "nilai 1"
    ],
    1 => [
        "id" => 2,
        "value" => "nilai 2"
    ],
    2 => [
        "id" => 3,
        "value" => "nilai 3"
    ],
    3 => [
        "id" => 4,
        "value" => "nilai 2"
    ]
];

$result = array_unique($input);
var_dump($result);
?>

Jika kawan-kawan menjalankan kode berikut, akan muncul error seperti ini.

<br />
<b>Notice</b>:  Array to string conversion in <b>[...][...]</b> on line <b>21</b><br />
<br />
<b>Notice</b>:  Array to string conversion in <b>[...][...]</b> on line <b>21</b><br />
<br />
<b>Notice</b>:  Array to string conversion in <b>[...][...]</b> on line <b>21</b><br />
<br />
<b>Notice</b>:  Array to string conversion in <b>[...][...]</b> on line <b>21</b><br />
<br />
<b>Notice</b>:  Array to string conversion in <b>[...][...]</b> on line <b>21</b><br />
<br />
<b>Notice</b>:  Array to string conversion in <b>[...][...]</b> on line <b>21</b><br />
<br />
<b>Notice</b>:  Array to string conversion in <b>[...][...]</b> on line <b>21</b><br />
<br />
<b>Notice</b>:  Array to string conversion in <b>[...][...]</b> on line <b>21</b><br />
<br />
<b>Notice</b>:  Array to string conversion in <b>[...][...]</b> on line <b>21</b><br />
<br />
<b>Notice</b>:  Array to string conversion in <b>[...][...]</b> on line <b>21</b><br />
<br />
<b>Notice</b>:  Array to string conversion in <b>[...][...]</b> on line <b>21</b><br />
<br />
<b>Notice</b>:  Array to string conversion in <b>[...][...]</b> on line <b>21</b><br />
array(1) {
  [0]=>
  array(2) {
    ["id"]=>
    int(1)
    ["value"]=>
    string(7) "nilai 1"
  }
}

Error tersebut karena array_unique berusaha untuk mengkonversi nilai array menjadi sebuah string namun ternyata nilai adalah array. Sehingga muncullah error seperti itu.

Lalu bagaimana cara agar value “nilai 2” ini tidak duplikat?

Ada beberapa cara, salah satunya kita gunakan array_column sehingga menjadi seperti berikut ini.

<?php
$input = [
    0 => [
        "id" => 1,
        "value" => "nilai 1"
    ],
    1 => [
        "id" => 2,
        "value" => "nilai 2"
    ],
    2 => [
        "id" => 3,
        "value" => "nilai 3"
    ],
    3 => [
        "id" => 4,
        "value" => "nilai 2"
    ]
];

$result = array_unique(array_column($input, 'value'));
var_dump($result);
?>

keluaran akan menjadi seperti berikut:

array(3) {
  [0]=>
  string(7) "nilai 1"
  [1]=>
  string(7) "nilai 2"
  [2]=>
  string(7) "nilai 3"
}

Sesuai dengan yang kita mau ya, karena fungsi array_column adalah mengambil multiple array menjadi satu array dengan menginputkan key yang ingin dikeluarkan. Jika kawan-kawan mencoba tanpa array_unique akan seperti ini.

<?php
$input = [
    0 => [
        "id" => 1,
        "value" => "nilai 1"
    ],
    1 => [
        "id" => 2,
        "value" => "nilai 2"
    ],
    2 => [
        "id" => 3,
        "value" => "nilai 3"
    ],
    3 => [
        "id" => 4,
        "value" => "nilai 2"
    ]
];

$result = array_column($input, 'value');
var_dump($result);
?>

// result
array(4) {
  [0]=>
  string(7) "nilai 1"
  [1]=>
  string(7) "nilai 2"
  [2]=>
  string(7) "nilai 3"
  [3]=>
  string(7) "nilai 2"
}

Sampai disini, mudah-mudahan bermanfaat untuk seri belajar php array unique 😉

 

 

Fitur Baru Laravel 5.7: Symfony Dump Server

Belum lama dapat info kalau Laravel sudah update versi lagi dari yang 5.6 ke versi terbaru adalah 5.7. Beberapa programmer ada yang senang, ada juga yang mungkin dan sangat banyak saya lihat di group baik itu group facebook dan group telegram yang mengatakan kurang lebih “Baru belajar 5.5, udah keluar lagi 5.7” dan banyak tipe programmer yang tidak seneng melihat perubahan yang cepat pada Laravel ini.

Continue reading Fitur Baru Laravel 5.7: Symfony Dump Server

Cara Mengambil Data Wilayah Indonesia dengan PHP Laravel

Beberapa aplikasi membutuhkan data wilayah seperti Provinsi, Kota, Daerah. Sebagai contoh yang sering kita pakai ada untuk mengambil data pengiriman barang melalui jasa shipping. Beberapa aplikasi yang pasti memerlukan data wilayah ini tidak lain biasanya adalah aplikasi yang membutuhkan lokasi detail user ataupun pengguna. Continue reading Cara Mengambil Data Wilayah Indonesia dengan PHP Laravel

PHPUnit : Cara Mengambil Value Dari Hasil Test Fungsi Lain

PHPUnit salah satu tool yang menarik untuk dipelajari terutama untuk kita yang ingin project atau aplikasi kita bebas dari bug. Mungkin ada beberapa kita pernah dengar istilah TDD, Test Driven Development? Atau mungkin pernah mendengar Continuous Integration? Nah dari sini mulai belajar bagaimana code yang kita buat itu sebelumnya harus menjalankan unit testing sehingga kedepan aplikasi kita lancar walaupun ada panambahan fitur baru. Continue reading PHPUnit : Cara Mengambil Value Dari Hasil Test Fungsi Lain

Membuat Tanggal Carbon menjadi Tanggal Indonesia di Laravel

Ada beberapa kawan-kawan menanyakan, bagaimana cara agar tanggal pada Carbon itu menjadi tanggal Indonesia? Karena sebagai informasi kawan-kawan, carbon itu defaultnya adalah bahasa inggris, jadi kita perlu ubah agar tanggal indonesia dengan carbon pada aplikasi kita menjadi lebih sesuai.

Berikut ini saya akan jelaskan cara agar tanggal di carbon menjadi tanggal waktu Indonesia. Kawan-kawan di luar indonesia juga bisa melakukan setting tanggal menurut wilayahnya sendiri. Continue reading Membuat Tanggal Carbon menjadi Tanggal Indonesia di Laravel

Cara Setting Sederhana Laravel dan Docker

Berawal dari satu project yang memiliki environtment development yang berbeda. Saya mencoba membiasakan setup project entah dengan bahasa apapun dengan docker. Lalu apa sebenarnya docker itu? dan masalah seperti apa yang menyebabkan kita menggunakan docker?

Jawabannya itu sederhana, misalnya di local laptop kita terinstall MySQL versi 5.7. Sedangkan project hanya berjalan lancar di versi 5.6.

Apa yang harus kita lakukan? Jawaban-nya itu biasanya kita downgrade mysql menjadi versi 5.6. Tapi project lainnya yang ada di local laptop dan menggunakan MySQL versi 5.7 kita bagaimana? Dilema 😀

Saat inilah kita harus mencoba docker. Continue reading Cara Setting Sederhana Laravel dan Docker

Membuat Queue sederhana dengan Database pada Laravel

Queue adalah salah satu teknik agar proses pada aplikasi kita dapat berjalan secara mengantri. Kenapa dibuat mengantri? kadang beberapa proses dilakukan sesuai dengan antrian sehingga berjalan sesuai flow yang direncanakan.

Pada laravel, proses queue sudah sangat mudah dilakukan. Pada artikel ini saya coba jelaskan contoh simple membuat queue pada proses tertentu. Diharapkan dengan membaca artikel ini, pembaca mengetahui proses kerja queue dengan database pada Laravel. Untuk production, saya sarankan untuk menggunakan redis atau amazon sqs atau driver lainnya yang lebih baik dari database.

# Langkah 1
Membuat proyek laravel, saat artikel ini dibuat, saya menggunakan Laravel 5.5 LTS. Jika dikemudian hari laravel sudah berubah, mohon disesuaikan dengan dokumentasi. Saya membuat folder laravel-jobtable

# Langkah 2
Setelah kita menginstall laravel 5.5, kita buat database pada mysql local kita. Pada artikel ini saya buat “laravel-jobtable”.

# Langkah 3
Setelah membuat database tersebut, kita sesuaikan di konfigurasi laravel dengan mysql database di file .env


[crp]


# Langkah 4
Setelah langkah ketiga telah dilakukan, mari kita coba jalankan proses migration untuk menyimpan queue laravel pada database dengan cara mengetikan pada terminal seperti pada gambar dibawah ini.

php artisan queue:job
php artisan queue:job

Setelah berhasil lanjutkan dengan php artisan migrate.

migrate job success
migrate job success

Setelah berhasil, cek database kita. Apa benar database sudah terinstall table-table yang telah berhasil di migrate.

tables current
tables current

Lalu kita lihat ada table jobs, data masih kosong dan struktur seperti gambar dibawah ini.

empty jobs
empty jobs

Nanti table jobs inilah yang berisi queue/atrian yang akan kita jalankan pada proyek laravel kita. Lanjut ke langkah berikutnya ya.

# Langkah 5
Pada langkah ini, mari kita coba membuat dua proses untuk melakukan atrian. Proses ini saya sebut juga job. Pada laravel, kita dapat mudah mengetikan perintah “php artisan make:job “. Pada artikel ini saya buat JobPertama dan JobKedua sebagai percobaan.

laravel job pertama dan kedua
laravel job pertama dan kedua

Baik, mari kita edit kedua job tersebut. File kedua job tersebut berada pada folder /app/Jobs/.

Lalu saya coba menuliskan job untuk write log untuk melihat apakah benar job tersebut berjalan sesuai yang diharapkan atau tidak.

File /app/Jobs/JobPertama.php

<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;

use Log;

class JobPertama implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    private $data;
    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct($data)
    {
        $this->data = $data;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        Log::info('Ini log dari JobPertama dengan data ' . $this->data);
    }
}

 

File /app/Jobs/JobKedua.php

<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;

use Log;

class JobKedua implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    private $data;
    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct($data)
    {
        $this->data = $data;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        Log::info('Ini log dari JobKedua dengan data ' . $this->data);
    }
}

Pada kedua job tersebut, saya menambakan Log untuk menuliskan log dari job yang telah kita buat. Ada flag darimana log tersebut dibuat sehingga kita bisa mengetahui apa benar proses sesuai dengan yang kita harapkan?

# Langkah 6

Setelah kita buat job, saatnya kita panggil job tersebut. Job akan berjalan sesuai dengan antrian/queue yang telah kita buat. Cara panggilnya mudah, kita buka controller atau lokasi yang ingin menggunakan job pertama dan job kedua ini lalu sisipkan kode seperti pada contoh ini.

Sebelumnya kita buat dulu controller dengan nama misalnya HomeController. Karana default laravel 5.5 belum ada controllernya.

Berikut kode dari HomeController.

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Jobs\JobPertama;
use App\Jobs\JobKedua;

class HomeController extends Controller
{
    public function index() {

        JobPertama::dispatch('belajar queue dengan');
        JobKedua::dispatch('laravel');

        return "berhasil";

    }
}

Pada HomeController itu kita include JobPertama dan JobKedua, lalu kita panggil dengan <namajob>::dispatch(<data yang ingin dilempar ke job>).

Data yang ingin dilempar ke job itu nanti di handle di construct method job tersebut. Dan prosesnya ada di method handle 🙂

Setelah itu mari kita panggil HomeController tersebut dengan menyetting route web ‘/’ mengarah ke HomeController method index.

Langkah 7:

Setelah itu, jangan lupa file .env terdapat konfigurasi untuk menyetting driver queue yang ingin kita gunakan. Defaultnya laravel menggunakan sync. Ganti QUEUE_DRIVER=sync menjadi QUEUE_DRIVER=database.

Langkah 8:

Jalankan artisan serve 🙂 lalu lihat pada /storage/logs/laravel.log.

Jika selesai halaman akan memunculkan “berhasil”. Apakah ada data yang tersimpan pada log? Dan saat itu juga, mari kita lihat database table jobs. Apakah ada perubahan atau tidak ya?

Yap ada data ya

job menunggu perintah
job menunggu perintah

Pada gambar diatas terlihat ada 2 job yang tersimpan. Pada table tersebut adalah list dari job yang akan dijalankan oleh laravel. Karena kita belum memanggil satu fungsi untuk menjalankan job tersebut. Maka job tersebut akan terlist pada table tersebut.

Mari kita coba jalankan perintah untuk memanggil job dengan perintah “php artisan queue:work”.

Lalu sekarang cek kembali table jobs tersebut. Sudah hilang ya data-datanya. Itu karena data jobs telah terpanggil untuk dijalankan. Sekarang coba lihat file /storage/logs/laravel.log.

hasil log queue

Yap, sekarang sudah ada log nya yaa..

Begitulah kira-kira cara kerja job queue.

  • Kita buat job
  • Dijalankan HomeController
  • Proses di HomeController hanya untuk menuliskan job ke table jobs
  • Setelah HomeController berhasil menuliskan job tersebut. Proses HomeController selesai.
  • JobPertama dan JobKedua dijalankan oleh laravel queue:work dengan membaca table jobs tersebut di background proses.
  • Jadi ada 2 proses, proses di HomeController dan proses di background

Coba kita bayangkan ada proses pengiriman email pada HomeController. Dimana pengiriman email tersebut belum menggunakan job.

  • Jalankan HomeController
  • Jalankan proses yang ada HomeController salah satunya adalah pengiriman email.
  • Waktu pengiriman email kisaran 3-5 detik tergantung dengan koneksi
  • Sampai pengiriman email berhasil, HomeController berhasil dipanggil
  • Jadi ada proses dan waktu menunggu pengiriman email dari proses HomeController tersebut.

Jika kita buat job dalam pengiriman email akan lebih simpel karena proses di HomeController hanya menyimpan job pengiriman email ke table jobs. Sedangkan proses pengiriman email dilakukan di background (secara terpisah). Sehingga tidak memakan waktu yang lama.

Pada proses ini tidak terlihat, silahkan coba buat job untuk pengiriman email ke mailtrap.io untuk mencoba.

Nyobain Elasticsearch dengan PHP

Elasticsearch mungkin bukan hal yang baru, tapi bagi saya ini baru. Udah lama denger tapi belum sempet pakai karena sepertinya belum ada kesempatan untuk menggunakannya.

Biasa request dari klien atau atasan, “Kok ini lemot ya..kok ini lama ya keluarnya”. Nah dari situ baru kita belajar “Gemana nih biar cepet..”.

Kasus sederhana adalah proses pencarian produk. E-commerce yang besar seperti shopee, BL, Tokped, Blibli pasti punya ribuan atau mungkin ratusan ribu atau lebih ya.. Ibarat kita nyari satu halaman dalam buku, semakin banyak halaman buku, semakin lama kita mencari keyword dalam tiap halaman yang kita inginkan.

Kalau pake database biasa, rada ngap-ngapan mungkin si database, perlu di tune-up lagi, dinaikin fisik servernya atau mungkin dibuat menyamping (scaling vertical) dengan berbagai cara dilakukan.

Salah satu form pencarian suggestion dari blibli. Ada keyword2 yang biasa dicari oleh customer lain lalu ada produk populer. Keren ya..

Dan ini saya pernah buat dengan MySQL alurnya adalah ketika customer mencari produk, saya simpan di table mysql, lalu ketika ada customer lain mencari kembali, saya munculkan data-data yang pernah orang cari. Mirip ya..

Tapi bayangin kalo ada 1000 customer dalam 1 hari. Lalu dalam 1 bulan, lalu dalam 1 tahun, kebayang yaa..itu table gendut banget pasti..

Untuk itu, biasanya data-data pencarian ini yang tidak masuk transaksi akan disimpan di data non transaksi engine seperti dengan mongodb, elasticsearch atau dengan tool lain bukan disimpan di RDBMS.

Beres cerita dikit soal penggunaan elasticsearch mari kita masuk ke proses selanjutnya.

Install Elasticsearch

Sebelum masuk cara panggil elasticsearch dengan PHP, ya perlu install dan isi elasticsearch dulu di vps atau local laptop kita. Ada banyak cara install elasticsearch di google. Monggo bisa coba di link berikut:

  1. Install di Ubuntu biasanya untuk server. https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-elasticsearch-on-ubuntu-16-04
  2. Install di Mac. Ini cara install paling mudah pake homebrew. Biasa buat testing-testing sebelum masuk ke server. https://chartio.com/resources/tutorials/how-to-install-elasticsearch-on-mac-os-x/
  3. Install di Windows. Elasticsearch bisa digunakan di windows. Belum pernah coba tapi mungkin bisa. https://www.elastic.co/guide/en/elasticsearch/reference/current/windows.html

Baik, setelah berhasil di install. Mari kita belanjar konsep-konsep sederhana dulu pada elasticsearch.

Konsep Dasar Elasticsearch.

Sebelum masuk ke konsep dasar, mungkin ada yg nanya “Kenapa mesti elasticsearch? kenapa ga pakai solr?, kenapa ga pakai redis?” dan lainnya. Jujur saya ga bisa jawab tapi saya sempet nyoba install solr dan coba pakai rada repot. Enaknya elasticsearch ini ada API Rest buat coba-coba. Lalu data yang dikirim dan diterima dalam bentuk JSON 🙂

Konsep dasar Elasticsearch ini saya kumpulkan ada di beberapa artikel berikut:

  • Codementor. Belajar dari konsep sampai cara install dan penggunaan.
  • Joelabrahamsson. Beberapa tool ada yg ga bisa dipakai. Saya ganti dengan postman untuk testing.
  • Dokumentasi Resmi. Belajar dari doc resmi lebih baik

Yang mesti diingat dalam konsep dasar elasticseach ini adalah naming-nya seperti apa itu index, apa itu document, apa itu type dan korelasinya di RDBMS.

Konekin ke PHP
Setelah diinstall dan coba diisi datanya. Elasticsearch bisa kita coba panggil dengan PHP. Sebenernya bisa dibaca-baca disini https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/_quickstart.html. Lalu Elasticsearch juga sudah membuatkan client untuk PHP di github ini: https://github.com/elastic/elasticsearch-php. Jadi tinggal colok composer, setting dikit sesuai dengan local dimana elasticsearch ini di install. Dan tadaa…bisa kok..belum muncul ya..

Saya coba buat satu folder dengan nama esphp, lalu saya buat satu file dengan nama composer.json dan isi file tersebut untuk menambahkan si elasticsearch php client.

{
        "require": {
            "elasticsearch/elasticsearch": "~6.0"
        }
    }

Pada kode tersebut dan saat tulisan ini dibuat, ES (elasticsearch) sedang aktif di versi 6.1. Jadi saya tulis require ke versi 6.

Jika selesai, ketik di terminal “composer install”, untuk menginstall depedency elasticsearch tersebut. Pastikan dulu ya composer sudah terinstall di laptop/komputer kita.

Setelah berhasil, nanti ada folder vendor yang berisi folder-folder depedency dari elasticsearch. Abaikan saja lalu kita buat index.php untuk memanggil elasticsearch di laptop kita dengan PHP. Isi kode index.php:

<?php

require 'vendor/autoload.php';

$hosts = [
    'localhost'
];

$client = Elasticsearch\ClientBuilder::create()
            ->setHosts($hosts)
            ->setRetries(2)
            ->build();

$params = [
    'index'  => 'movies',
    'type'   => 'movie',
    'id'     => 2,
    'client' => [ 'ignore' => 404 ]
];

echo json_encode( $client->get($params) );

Di kodingan tersebut saya seperti mengkonek-an PHP dengan Mysql. Ambil data client PHP dulu, setting host elasticsearch, buat inisialiasi object PHP dan panggil/query apa yang mau kita panggil di $params. Lalu munculkan dengan json (karena defaultnya adalah sebuah array jika di PHP) hasil query kita dengan json_encode.

Jika dilihat ada index, type, id itu adalah ambil data id 2, dari table/type movie di database/index movies elasticsearch.

Sederhana yaa..

Data movies itu saya ambil dari data link joelabrahamsson.

Mudah-mudahan menarik untuk dipelajari 🙂