Multiple .env di Laravel

Multiple .env di Laravel

Singkat cerita saya butuh konfigurasi yang berbeda-beda di tiap sub-domain, tapi dengan menggunakan core aplikasi yang sama. Maksudnya gini, kalau membuat sub-domain, maka ada folder khusus untuk sub-domain. Isi sub-domain ini adalah isi dari folder public. Misalnya begini struktur folder-nya

~/company1.example.com
  company2.example.com
  company3.example.com
  main_app

Nah, lokasi aplikasi utama ada di main_app. Sedangkan company1/2/3 maunya menggunakan core-nya si main_app tapi dengan konfigurasi environment dan database yang berbeda. Gimana caranya? :/

Update bootstrap/app.php

Tambahkan ini sebelum return $app;

if (is_readable(getcwd().'/.env')) {
    $app->useEnvironmentPath( getcwd() );
}

Yup, selesai 😀

Sekarang kalau menuju ke http://company1.example.com, maka akan menggunakan konfigurasi dari company1.example.com/.env

Weits, jangan lupa juga file .env diamankan. Kalau menggunakan Apache HTTPD

<Files ".env">
    Order Allow,Deny
    Deny from all
</Files>

Nginx

location ~ /\.env
{ 
    deny all; 
}

How to use “Laravel facade” outside of Laravel

How to use “Laravel facade” outside of Laravel

I love Laravel facade. With Laravel facade the code be more beautiful and readable than traditional approach. But, what if you don’t use Laravel? How to use the “facade”?

For example, I want to use Laravel “File” facade inside of my application.

First, I need to install illuminate/filesystem via composer.

composer require illuminate/filesystem

Then, just load the composer autoloader and create a new class like this

<?php

require 'vendor/autoload.php';

use Illuminate\Filesystem\Filesystem;

class File
{
    public static function __callStatic($name, $args)
    {
        $self = new Filesystem;

        return $self->{$name}(...$args);
    }
}

Finally, we can use File:: like in Laravel.

File::allFiles('./');

File::get('somefile.txt');

UPDATE 23 Aug 2016 – illuminate/config

For example I have folder structure like this

\ - assets/
  - config/
     \ - database.php
       - site.php
       - mail.php

Inside of database.php, site.php, mail.php just simple array. Example of database.php

<?php

return [
    'hostname' => 'localhost',
    'username' => 'root',
    'password' => '',
    'database' => 'test',
];

Here’s the main file to load all configurations.

<?php

require 'vendor/autoload.php';

use Illuminate\Config\Repository;

class Config
{
    protected static $instance;

    public static function __callStatic($method, $args)
    {
        if (! static::$instance) {
            static::$instance = new Repository(
                (new self)->loadConfig('config')
            );
        }

        return static::$instance->{$method}(...$args);
    }

    protected function loadConfig($configPath)
    {
        $items = [];

        foreach(scandir($configPath) as $file) {
            if (substr($file, -4) === '.php') {
                $filename = str_replace('.php', '', $file);
                $items[$filename] = require $configPath . '/' . $file;
            }
        }

        return $items;
    }
}

To get the configuration, just use Config::

Config::get('database.username');             // root
Config::set('database.username', 'newuser');  // Set new value
Config::get('database.username');             // newuser

Config::all();  // All configs

How to use Illuminate/Validation outside of Laravel

How to use Illuminate/Validation outside of Laravel

Ok, judulnya english, tapi isinya Indonesia. 😐

Kebetulan hari ini ada pertanyaan di Facebook Group Laravel Indonesia tentang bagaimana cara menggunakan Validator-nya Laravel tapi bukan di dalam project Laravel?

Ini dia enaknya Laravel, kita bisa menggunakan component-nya di luar Laravel itu sendiri. Ngg… sebenernya bukan cuma Laravel sih. Symfony, ZF, CakePHP, Aura, dll juga bisa digunakan component-nya di luar framework-nya sendiri. Mungkin pengecualian untuk Yii. Component di Yii terlalu terikat dengan framework-nya. Sehingga sangat sulit menggunakan component di Yii tanpa Yii. 😐 Itu salah satu alasan saya kurang suka Yii. Karena nggak mau berbagi dengan komunitas PHP secara keseluruhan.

Ok, kembali ke topik utama. Saya anggap kita bikin project from scratch. Pertama bikin folder project, setelah itu gunakan composer init untuk generate file composer.json

Ambil illuminate/validation dengan composer require illuminate/validation.

Buat file baru index.php dan load composer autoloader.

<?php
require __DIR__.'/vendor/autoload.php';

Kalau kita lihat di vendor/Illuminate/Validation/, ada file Factory.php. Factory adalah istilah yang umum digunakan untuk membuat suatu object.

Tambahkan code berikut.

<?php
require __DIR__.'/vendor/autoload.php';

$validator = new Illuminate\Validation\Factory();

Hmm, untuk membuat Validation\Factory kita membutuhkan Translator dari Symfony. Ini digunakan untuk menentukan error message dalam bahasa apa. Jadi kita tambahkan Symfony Translator

<?php
require __DIR__.'/vendor/autoload.php';

$trans = new Symfony\Component\Translation\Translator($locale = 'en');
$validator = new Illuminate\Validation\Factory($trans);

Nah, sekarang $validator sudah bisa digunakan 😀 Mari kita coba

<?php
require __DIR__.'/vendor/autoload.php';

$trans = new Symfony\Component\Translation\Translator($locale = 'en');
$validator = new Illuminate\Validation\Factory($trans);

$validation = $validator->make([
  'name' => ''
  'email' => 'hello'
],[
  'name' => 'required'
  'email' => 'required|email'
]);

var_dump($validation->fails()); // Seharusnya true

Ok, that’s it 😀 Selamat bereksperimen! Kalau ada pertanyaan, jangan ditanyakan. 😐

Multiple Authentication di Laravel 5.2

Multiple Authentication di Laravel 5.2

Pada kisah sebelumnya saya sebenarnya ingin mencari konfigurasi multiple authentication. Tapi malah nyasar ke token authentication.

Dengan multi-auth ini kita bisa membuat fitur login dengan table yang berbeda-beda dengan lebih mudah. Misalnya ada table dokter, pasien, dan staff.

Di Laravel 4, anda bisa menggunakan ollieread/multiauth untuk menggunakan multiple auth. Atau Kbwebs/MultiAuth untuk Laravel 5.0 dan 5.1.

Tetapi pada Laravel 5.2, Taylor Otwell menambahkan fitur ini ke core Laravel

So, gimana cara menggunakan multiple auth ini? Sangat mudah, tidak jauh berbeda dengan dua kisah sebelumnya. Kita bisa baca informasi pada config/auth.php

Screen Shot 2015-12-18 at 22.00.12

Jadi kita cukup menambahkan guards dan providers.

Persiapan

Contoh misalnya kita ingin ada tiga jenis user:

  • Doctor
  • Patient
  • Staff

Buat model yang mirip dengan app/User.php. Kita bisa copy dan edit nama class-nya. Jangan lupa buat juga  migrations untuk model tersebut. Isinya bisa copy dari /database/migrations/2014_10_12_000000_create_users_table.php, atau anda bisa buat sendiri sesuai dengan kebutuhan.

Setelah itu edit config/auth.php, tambahkan tiga guards dan tiga providers baru.

'guards' =>  [
    'doctor' =>  [
        'driver'   => 'session',
        'provider' => 'doctor',
    ],

    'patient' =>  [
        'driver'   => 'session',
        'provider' => 'patient',
    ],

    'staff' =>  [
        'driver'   => 'session',
        'provider' => 'staff',
    ],
],

// ... 

'providers' =>  [
    'doctor' =>  [
        'driver' => 'eloquent',
        'model'  => App\Doctor::class,
    ],

    'patient' =>  [
        'driver' => 'eloquent',
        'model'  => App\Patient::class,
    ],

    'staff' =>  [
        'driver' => 'eloquent',
        'model'  => App\Staff::class,
    ],
],

Login

Persiapan selesai. Saatnya simulasi untuk login. Eits, jangan lupa tambahkan user pada masing-masing model.

Berikut adalah file app/Http/routes.php

Route::get('/login', function() {

    $auth = auth()->guard('doctor'); // Atau \Auth::guard('doctor')

    $credentials = [
        'nip' =>  '04324321', // Nomor Induk Pegawai
        'password' =>  'sate',
    ];

    if ($auth->attempt($credentials)) {
        return 'Yay! Berhasil login (^o^)/';
    }

    return 'Gagal login.';

});

Well done 😀

UPDATE 19 Jan 2016 23:33:56

Pada Laravel 5.2 terbaru, tepatnya 5.2.10, kita bisa gunakan

auth('doctor')

lebih singkat daripada

auth()->guard('doctor')

Detailnya bisa dilihat di https://github.com/laravel/framework/commit/2edaa2791317e1ff0cf80a5d8539e983154082c1.

UPDATE 18 Maret 2016 08:26

Source code demo bisa dilihat di https://github.com/mul14/laravel-52-multiple-auth-demo

Simple Token Authentication API di Laravel 5.2

Simple Token Authentication API di Laravel 5.2

Setelah sebelumnya kita bahas cara menggunakan fitur baru di Laravel 5.2 yaitu autentikasi dengan token, sekarang kita bahas bagaimana cara menggunakannya bersamaan dengan metode autentikasi session.

Pada tulisan sebelumnya, by default kita mengubah konfigurasi “guard” yang default-nya adalah “web” menjadi “api”. Sekarang kita coba menggunakan api guard tanpa mengubah konfigurasi default.

Contoh pada app/Http/routes.php

Route::get('/', function() {

    if (auth()->check()) {
        return auth()->user();
    }

    abort(403, "You're not authorized to access this page.");
});

Route::get('api', function() {

    $auth = auth()->guard('api'); // Switch guard ke "api" driver

    if ($auth->check()) {
        return $auth->user();
    };

    abort(403, "You're not authorized to access this public REST API.");
});

Hmm, gimana kalau kita ubah menjadi middleware? Buat middleware baru dengan nama ApiAccess

./artisan make:middleware ApiAccess

Edit file app/Http/Middleware/ApiAccess.php

public function handle($request, Closure $next)
{
    $auth = auth()->guard('api');

    if ($auth->check()) {
        return $next($request);
    };

    abort(403, "You're not authorized to access this public REST API.");
}

Lalu tambahkan pada app/Http/Kernel.php

'auth.api' => \App\Http\Middleware\ApiAccess::class,

Karena sudah jadi middleware, sekarang kita lebih mudah menggunakannya. Misalnya pada app/Http/routes.php

$options = [
    'prefix' => 'api', 
    'namespace' => 'Api', 
    'middleware' => 'auth.api',
];

Route::group($options, function() {

    Route::get('/', 'UserController@index');

    Route::get('{user_id}', 'UserController@show');

});

Well done. Sekarang kita bisa menggunakan metode login seperti biasanya dan juga menggunakan metode autentikasi stateless dengan menggunakan token sederhana.

Validasi Token API di Laravel 5.2

Validasi Token API di Laravel 5.2

So, ceritanya saya lihat source code Laravel pada config/auth.php. Maksudnya sih mau melihat informasi tentang multiple auth. Itu lho, yang bisa menggunakan multiple table. Tapi ternyata malah menemukan hal baru :O

Screen Shot 2015-12-18 at 11.03.25
config/auth.php pada Laravel 5.2

Bisa dilihat ada metode autentikasi yang baru, yaitu token.

Karena penasaran, setelah Jumatan, saya langsung cari dimana code yang mengimplementasikan metode autentikasi ini. Dengan menggunakan command line fuzzy search pada terminal, saya langsung menemukan file yang dicari, yaitu Illuminate/Auth/TokenGuard.php.

Setelah baca-baca sedikit, akhirnya saya tau cara menggunakannya 😀 Sepertinya “saat ini” belum dibahas di dokumentasi resmi.

Cara Menggunakan

Pertama edit file users migrations. Tambahkan kolom api_token. Kira-kira menjadi seperti ini

public function up()
{
    Schema::create('users', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->string('email')->unique();
        $table->string('password', 60);

        $table->string('api_token')->unique(); // Kita tambahkan ini

        $table->rememberToken();
        $table->timestamps();
    });
}

Lalu lakukan migrasi database dengan ./artisan migrate.

Sekarang kita buat user baru dan api_token-nya. Terserah mau masukin dari artisan tinker, factory model, phpmyadmin, whatever.

Ubah metode autentikasi default pada config/auth.php dari web menjadi api

'defaults' => [
    'guard' => 'api', // Ubah ini
    'passwords' => 'users',
],

Sekarang pada file app/Http/routes.php, tambahkan ini

Route::get('/', function() {
    return auth()->user();
});

Ketika akses url /, tampilan akan kosong. Tapi ketika ditambahkan ?api_token=123 dengan asumsi api_token di database juga 123, maka akan muncul user tersebut.

Screen Shot 2015-12-18 at 13.52.48

PENTING!!!

Demi keamanan, jangan lupa masukkan api_token ke dalam hidden property yang ada di App/User.php, bersama dengan password dan remember_token.

NEW LARAVEL 5.1 ACL – PART #2

NEW LARAVEL 5.1 ACL – PART #2

Melanjutkan bahasan yang tertunda sebelumnya, kali ini kita akan bahas sedikit persiapan dan penggunaan fitur authorization.

Migrations

Pertama kita buat dulu Model dan migrations.

php artisan make:model -m Role

php artisan make:model -m Permission

Pada directory database/migrations akan ada dua file baru. Edit kedua file tersebut, lalu tambahkan name field. Kira-kira seperti ini pada method up()

// xxxx_xx_xx_xxxxxx_create_roles_table

Schema::create('roles', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');
    $table->timestamps();
});

// xxxx_xx_xx_xxxxxx_create_permissions_table

Schema::create('permissions', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');
    $table->timestamps();
});

Buat migrations baru untuk menghubungkan antara permissions dan roles, juga roles dan users

php artisan make:migration --create=permission_role create_permission_role_table

php artisan make:migration --create=role_user create_role_user_table

Edit kedua migrations tersebut

// xxxx_xx_xx_xxxxxx_create_permission_role_table

Schema::create('permission_role', function (Blueprint $table) {
    $table->unsignedInteger('permission_id');
    $table->unsignedInteger('role_id');

    $table->foreign('permission_id')->references('id')->on('permissions')->onDelete('cascade');
    $table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade');

    $table->primary(['permission_id', 'role_id']);
});

// xxxx_xx_xx_xxxxxx_create_role_user_table

Schema::create('role_user', function (Blueprint $table) {
    $table->unsignedInteger('role_id');
    $table->unsignedInteger('user_id');

    $table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade');
    $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');

    $table->primary(['role_id', 'user_id']);
});

Model

Jika anda sudah pernah membaca artikel saya sebelumnya, ini tidak jauh berbeda. Tambahkan 4 method pada app/User.php

class User extends Model implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract {

    use Authenticatable, Authorizable, CanResetPassword;

    // ...

    public function roles()
    {
        return $this->belongsToMany(Role::class);
    }

    public function hasRole($name)
    {
        foreach($this->roles as $role) {
            if ($role->name == $name) {
                return true;
            }
        }

        return false;
    }

    public function assignRole($role)
    {
        if (is_string($role)) {
            $role = Role::where('name', $role)->first();
        }

        return $this->roles()->attach($role);
    }

    public function revokeRole($role)
    {
        if (is_string($role)) {
            $role = Role::where('name', $role)->first();
        }

        return $this->roles()->detach($role);
    }

    // ...

}

Tambahkan beberapa method pada app/Role.php

class Role extends Model
{
    public function permissions()
    {
        return $this->belongsToMany(Permission::class);
    }

    public function users()
    {
        return $this->belongsToMany(User::class);
    }

    public function addPermission($permission)
    {
        if (is_string($permission)) {
            $permission = Permission::where('name', $permission)->first();
        }

        return $this->permissions()->attach($permission);
    }

    public function removePermission($permission)
    {
        if (is_string($permission)) {
            $permission = Permission::where('name', $permission)->first();
        }

        return $this->permissions()->detach($permission);
    }
}

Seeder

Tahap ini sebenarnya tidak harus. Tapi untuk mempermudah, kita buat seeder langsung di database/seeders/DatabaseSeeder.php. Jika anda lebih prefer seeder yang terpisah, anda bisa membuat dengan php artisan make:seeder

Tambahkan pada method run(), sehingga kira-kira menjadi seperti ini

public function run()
{
    // Basic roles data
    App\Role::insert([
        ['name' => 'admin'],
        ['name' => 'manager'],
        ['name' => 'editor'],
    ]);

    // Basic permissions data
    App\Permission::insert([
        ['name' => 'access.backend'],
        ['name' => 'create.user'],
        ['name' => 'edit.user'],
        ['name' => 'delete.user'],
        ['name' => 'create.article'],
        ['name' => 'edit.article'],
        ['name' => 'delete.article'],
    ]);

    // Add a permission to a role
    $role = App\Role::where('name', 'admin')->first();
    $role->addPermission('access.backend');
    $role->addPermission('create.user');
    $role->addPermission('edit.user');    
    $role->addPermission('delete.user');
    // ... Add other role permission if necessary

    // Create a user, and give roles
    $user = App\User::create([
        'email' => 'admin@example.com',
        'password' => bcrypt('anything_you_want'),
    ]);

    $user->assignRole('admin');

    // ... Add other user and assign them to roles
}

Jalankan migrations dan seeder

php artisan migrate --seed

AuthServiceProvider

Edit file app/Providers/AuthServiceProvider.php, tambahkan pada boot() method hingga kira-kira menjadi seperti ini

public function boot(GateContract $gate)
{
    $this->registerPolicies($gate);

    $permissions = App\Permission::all();

    foreach($permissions as $permission) {
        $gate->define($permission->name, function($user) use ($permission) {
            return $permission;
        });
    }
}

Usage

Sekarang kita bisa melakukan pengecekan permission/otoritasi dengan beberapa cara. Secara global kita bisa menggunakan Gate facade.

Route::get('/backend', function() {

    if (Gate::check('access.backend')) {
        return view('backend');
    }

    return abort(403);

});

Pada Controller kita bisa menggunakan Gate facade atau authorize method.

class UsersController extends Controller 
{
    public function create()
    {
        $this->authorize('create.user');
    }
}

Pada Model kita bisa menggunakan Gate facade atau can method.

class UsersController extends Controller 
{
    public function create()
    {
        if (auth()->user()->can('create.user')) {
            return view('users.create');
        }

        return abort(403);
    }
}

Pada View kita bisa menggunakan @can, sebagai contoh suatu menu hanya bisa dilihat oleh yang memiliki permission access.backend

// resources/views/welcome.blade.php

@can('access.backend')
  <a href="/backend">Go to backend</a>
@endcan

Conclusie

Pembuatan manual fitur otorisasi pada Laravel sebenarnya sangat mudah. Tapi dengan adanya fitur otorisasi bawaan, menjadikan proses pembuatan ini menjadi jauh lebih mudah.

Jika ada pertanyaan, ketidakjelasan, kebingungan, silahkan tanya pada kolom komentar. Source code menyusul! Source code bisa diakses di https://github.com/mul14/laravel-acl-demo-2

Artikel ini akan di-update karena dapat pencerahan lebih terang 😐