ตั้งค่า Custom Path ให้กับ โฟลเดอร์ vendor เพื่อใช้งานร่วมกับ Docker

เพื่อความสะดวกในการพัฒนา Application อีกทั้งยังเป็นการปรับปรุงคุณภาพของ Workflow ในขั้นตอนการ Development ปัจจุบันคงไม่มีใครปฎิเสธที่จะใช้งาน Docker และ สำหรับโปรเจค PHP นั้น Docker ช่วยให้พวกเราลดปัญหาเรื่อง Environment จริงตอน Deploy ขึ้น Production ได้เป็นอย่างมาก

ซึ่งการใช้ docker-compose เพื่อ Setup โปรเจคนั้นเป็นวิธีที่พวกเรานิยมใช้กัน โดยมักจะมีโครงสร้างสำหรับโปรเจค PHP ประมาณนี้

Path

|-docker-compose.yml
+-php
|   |-Dockerfile
|   +-src
|   |   |-index.php

docker-compose.yml

version: "3.3"

services:

    php:
        build:
            context: ./php
        container_name: web
        ports:
            - "80:80"
        restart: always
        volumes:
            - ${PROJECT_PATH}/php/src:/var/www/html

สำหรับโปรเจค PHP นั้นเราต้องการให้โฟลเดอร์ vendor อยู่ที่ Path php/src/vendor จึงทำให้เมื่อเราต้องการติดตั้ง Dependencies ยกตัวอย่างเช่น slim/slim จะต้องทำการเปลี่ยน Path ซะก่อน

Terminal

cd php/src
composer require slim/slim

ปัญหาคือเมื่อเราเปิด Terminal จาก Editor เช่น Visual Studio Code เจ้า Default Working Directory จะอยู่ที่ Root Path ดังนั้นหากเราลืมเปลี่ยน Working Directory จะต้องมาเสียเวลา Cleanup ไฟล์ที่เกิดจากการติดตั้งผิดที่ผิดทาง

ตั้งค่า Custom Path สำหรับ vendor

โชคดีที่เราสามารถตั้งค่า Custom Path สำหรับ โฟลเดอร์ vendor ได้ โดยเราจะเพิ่มไฟล์ composer.json ไว้ที่ Root Path

Path

|-composer.json
|-docker-compose.yml
+-php

composer.json

{
    "config": {
        "vendor-dir": "php/src/vendor"
    }
}

หลังจากทำการตั้งค่า Custom Path ให้กับ vendor แล้วเราสามารถใช้คำสั่งติดตั้ง Dependencies จาก Root Path ได้เลย

Terminal

composer require slim/slim

ผลลัพธ์

โฟลเดอร์ vendor เกิดขึ้นในตำแหน่งที่เราต้องการ

Path

|-composer.json
+-php
|   +-src
|   |   |-index.php
|   |   +-vendor

ซึ่งการบันทึกข้อมูล Dependencies ที่ได้ถูกติดตั้งไว้กับโปรเจค ยังคงถูกบันทึกไว้ที่ไฟล์ composer.json ในตำแหน่ง Root Path

composer.json

{
    "config": {
        "vendor-dir": "php/src/vendor"
    },
    "require": {
        "slim/slim": "^3.9"
    }
}

ทดสอบ

ลองทดสอบการเรียกใช้ Dependencies โดยทดลองเขียนโค้ด PHP ง่ายๆ ตามตัวอย่างด้านล่าง

php/src/index.php

<?php

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

use Slim\App;

$app = new App;

var_dump($app);

ปัญหาที่จะตามมาคือการใช้งาน Autoload

สมมุติผมต้องการจะมีไฟล์ HomeController.php ตามโครงสร้างด้านล่าง

Path

|-composer.json
+-php
|   +-src
|   |   |-index.php
|   |   +-app
|   |   |   +-Controllers
|   |   |   |   |-HomeController.php
|   |   +-vendor

ซึ่งผมจะใช้การทำ Autoload แบบ psr-4 เพื่อรองรับการใช้งาน namespace

php/src/app/Controllers/HomeController.php

<?php

namespace App\Controllers;

class HomeController
{
    public function __construct()
    {
        echo 'Hello World';
    }
}

หากยังไม่มีการทำ Autoload โค้ด PHP ด้านล่างจะต้องแสดง Error อย่างแน่นอน

php/src/index.php

<?php

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

use Slim\App;
use App\Controllers\BaseController;

$app = new App;
$c = new BaseController;

ซึ่งในการทำ Autoload นั้นเราจะเพิ่มไฟล์ composer.json ไฟล์ที่ 2 ไว้ที่ Path ตามโครงสร้างด้านล่าง

Path

|-composer.json
+-php
|   +-src
|   |   |-composer.json
|   |   |-index.php

เขียนโค้ดในไฟล์ composer.json ตามตัวอย่างด้านล่าง เพื่อตั้งค่า Autoload

php/src/composer.json

{
  "autoload": {
    "psr-4": {
      "App\\": "app"
    }
  }
}

composer Custom Script

ปัญหาตอนนี้คือหากเราใช้คำสั้ง composer dump-autoload จากตำแหน่ง Root จะไม่มีผลใดๆ เกิดขึ้นเพราะ composer จะอ่านเพียงแค่ไฟล์ composer.json ในตำแหน่ง Working Directory เท่านั้น

ซึ่งเราสามารถจะทำการเปลี่ยน Working Directory ด้วยคำสั่ง cd src/php ก่อนใช้คำสั่ง composer dump-autoload ก็ย่อมได้ แต่มันคงเหมือนจะเป็นการถอยหลังนะสิ เราจึงจะแก้ปัญหานี้ด้วย composer Custom Script

แก้ไขไฟล์ composer.json โดยเพิ่มโค้ดส่วนของ scripts Section ตามตัวอย่างด้านล่าง

composer.json

{
  "config": {
    "vendor-dir": "php/src/vendor"
  },
  "require": {
    "slim/slim": "^3.9"
  },
  "scripts": {
    "autoload": "cd php/src && @composer dump-autoload"
  }
}

การตั้งชื่อ Custom Script นั้นเราสามารถตั้งชื่ออะไรก็ได้ที่ไม่ไปซ้ำกับ Sub Command ของ composer หากเราอยากดูว่า composer มี Sub Command อะไรบ้าง สามารถดูได้คร่าวๆ จากคำสั่ง composer --help

หลังจากแก้ไขโค้ดในไฟล์ composer.json เรียบร้อยแล้ว เราก็สามารถเรียกใช้ Custom Script ได้จากตำแหน่ง Root Path ด้วยตัวอย่างคำสั่งด้านล่างได้เลย

Terminal

composer autoload

เพียงแค่นี้เราก็สามารถกลับไปทดสอบโค้ด PHP ตามตัวอย่างข้างล่างได้อย่างไม่มี Error แล้วหล่ะ

php/src/index.php

<?php

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

use Slim\App;
use App\Controllers\BaseController;

$app = new App;
$c = new BaseController;

บทสรุป

วิธีการที่ได้นำเสนอในบทความนี้ อาจจะมีการตั้งค่าที่ยุ่งย่างพอสมควร แต่เพื่อลดปัญหาในเรียกใช้คำสั่งผิดที่ ผู้เขียนคิดว่าเทคนิตนี้มีความคุ้มค่าที่จะทำ ซึ่งสุดท้ายเราสามารถทำเป็น Repository ไว้เพื่อใช้ในการ Scaffold โปรเจคที่จะเกิดขึ้นใหม่ต่อไป

อย่างไรก็ตามเทคนิคนี้ยังคงมีข้อจำกัดหากเราต้องการจะมีโปรเจค PHP มากกว่า 1 โปรเจค ภายใต้โปรเจค Docker เนื่องจากเราสามารถตั้งค่า Custom Path สำหรับ โฟลเดอร์ vendor ได้เพียงค่าเดียวเท่านั้น