Do you have used Elvis Operator in PHP?

I am not a good PHP programmer, I use the language to work on projects that are not product base. And usually I work on the project myself. Because I do it by myself, I code it according to what I learned myself.

I am not know what the version of PHP before, is your code working in PHP version 5.6 or 7+ version?

I am not aware thats. But now, i need to know that deeply because we create product base mean customer will not break our product. I now always care about other programmer write their code.

Continue reading Do you have used Elvis Operator in PHP?

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.