ایجاد Package سفارشی لاراول: راهنمای گام به گام

در این مطلب، راهنمای گام به گام ایجاد package سفارشی در لاراول را بررسی خواهیم کرد. در فریم ورک لاراول، Packageها راه بسیار خوب و آسانی برای ایجاد یک سری کد reuseable(قابل استفاده مجدد) و قابل اشتراک‌گذاری هستند. ممکن است قبلاً با Package های لاراولی زیادی برخورد کرده باشید، چه رسمی و چه آن پکیج‌هایی که توسط جامعه برنامه‌نویسان بصورت غیر رسمی توسعه داده و نگهداری می‌شوند. برخی از آنها ساده و برخی بسیار پیچیده‌اند. اما آیا تا به حال به این فکر کرده اید که چگونه می توانید کد “خود” را تحت عنوان یک Package معرفی کرده و آن را با دیگران به اشتراک بگذارید؟

مثالی که در این آموزش دنبال می‌کنیم، ایجاد Package ای است که نقل قول های الهام بخش بصورت random تولید می‌کند و دیگران می توانند با استفاده از Composer این بسته را در Package های لاراول خود اضافه کرده و نصب کنند. اگرچه این Package پیشگامانه و جدید نیست، اما بیشتر مفاهیم اساسی در مورد چیدمان و ساختار یک Package سفارشی لاراول را در درون خود جای داده است.


طرح و ساختار Package

ساختار Package ها بسیار ساده است. در این آموزش Package ای با نام inspire ایجاد خواهیم کرد. اگر کسی از آن در پروژه لاراول خود استفاده کند، پس از بازدید از مسیر /inspire، یک نقل قول الهام‌بخش تصادفی دریافت می‌کند. نقل قول ها را از وب سرویسی که در آدرس https://api.goprogram.ai/inspiration قرار دارد و با هربار فراخوانی یک متن تصادفی در قالب JSON ارائه می‌دهد، دریافت خواهیم کرد.

همانطور که گفتم تمام مراحل کار بسیار ساده هستند. تنها بخشی که ممکن است سخت به نظر برسد، نوشتن کد خوب است. برای شروع به یک پروژه جدید لاراول نیاز دارید.

پس از نصب یک پروژه‌ی جدید لاراول، باید برای Package خود یک نام مناسب انتخاب کنیم. این نام معمولاً از دو بخش تشکیل شده است: نام vendor و نام Package. به عنوان مثال نام laravel/framework را در نظر بگیرید. در اینجا vendor لاراول  و framework نام package است.

من معمولاً بسته های خود را با فرمت <my-github-username>/<package-name> نام گذاری می کنم. می توانید از نام سازمان، نام شرکت، نام خود یا هر چیزی که می خواهید استفاده کنید. برای سادگی، نام پروژه را yusef22/inspire بگذارید که در آن yusef22 نام کاربری GitHub من است.

اجرای دستور  mkdir -p packages/yusef22/inspire/src  در دایرکتوری اصلی پروژه شما، باید دایرکتوری های زیر را ایجاد کند.

├── packages
│   └── yusef22
│       └── inspire
│           ├── src

ساختار دایرکتوری باید مانند شکل بالا باشد. دایرکتوری src محل قرار دادن تمام کدهای PHP مربوط به package ماست.


مقدار دهی اولیه(Initialize) برای ایجاد Package سفارشی در لاراول

برای مقداردهی اولیه یک Package سفارشی لاراول، با دستور cd به پوشه packages/yusef22/inspire منتقل شوید و دستور composer init را اجرا کنید. با این کار فرآیند مقدار دهی اولیه آغاز می شود. یک سری سوالات از شما پرسیده خواهد شد. مطمئن شوید که نام، توضیحات و اطلاعات سازنده را به درستی بنویسید. پس از آن، می‌توانید کلید Enter را فشار دهید و default را برای همه گزینه‌ها بپذیرید، به جز زمانی که از شما می‌پرسد که آیا می‌خواهید dependencies و dev-dependencies خود را به صورت تعاملی اضافه کنید یا خیر. برای این دو، n را بنویسید و Enter را بزنید تا پاسخ منفی دهید.

هنگامی که سوالات تمام شد، یک پوشه vendor جدید و فایل composer.json را در دایرکتوری inspire پیدا خواهید کرد. فایل composer.json را (به خاطر داشته باشید که این فایل composer.json جدا از فایل composer.json پروژه شما است) را در ویرایشگر کد انتخابی خود باز کنید و به دنبال بخش require{} ​​بگردید. این جایی است که شما باید هر بسته دیگری را که بسته شما به آن نیاز دارد ثبت نام کنید. خوب در این مورد، برای درخواست HTTP به بسته guzzlehttp/guzzle نیاز دارید. بنابراین، بخش نیازمندی {} را به صورت زیر ویرایش کنید:

"require": {
  "guzzlehttp/guzzle": "^7.0.1"
}

اکنون، وضعیت نهایی فایل composer.json باید به صورت زیر باشد:

{
    "name": "yusef22/inspire",
    "autoload": {
        "psr-4": {
            "Yusef22\\Inspire\\": "src/"
        }
    },
    "authors": [
        {
            "name": "Yusef Shiri",
            "email": "yusef22@gmail.com"
        }
    ],
    "require": {
        "guzzlehttp/guzzle": "^7.0.1"
    }
}

مطمئن شوید که نیازمندی‌های Package شما در بخش require به درستی لیست شده است و بخش autolaod نیز مطابق تصویر بالا پر شده است. “Yusef22\\Inspire\\”: “src/” به Composer دستور می دهد که دایرکتوری src را به عنوان فضای نام Yusef22\Inspire در نظر بگیرد.


ایجاد کلاس Inspire در Package سفارشی لاراول

ایجاد کلاس برای یک Package سفارشی لاراول اولین گام برای توسعه است. یک فایل جدید Inspire.php در دایرکتوری src ایجاد کنید و کد زیر را در آن قرار دهید:

<?php

namespace Yusef22\Inspire;

use Illuminate\Support\Facades\Http;

class Inspire {
    public function justDoIt() {
        $response = Http::get('https://inspiration.goprogram.ai/');

        return $response['quote'] . ' -' . $response['author'];
    }
}

همانطور که می بینید، تابعی به نام justDoIt در داخل این کلاس وجود دارد که یک درخواست به https://api.goprogram.ai/inspiration/  می‌فرستد و پاسخی به این شکل دریافت می‌کند:

{"quote": "The greatest discovery of all time is that a person can change their future by merely changing their attitude.", "author": "Oprah Winfrey"}

سپس این تابع رشته JSON را به یک رشته معمولی فرمت می کند و آن را در جواب نهایی کنترلر می فرستد. این تمام چیزی است که در این کلاس وجود دارد. اکنون می توانید یک نمونه از این کلاس ایجاد کنید و تابع justDoI را برای دریافت یک نقل قول الهام بخش فراخوانی کنید.


تست عملکرد کلاس Inspire

قبل از ادامه، بیایید کلاس Inspire را آزمایش کنیم. برای انجام این کار، ابتدا فایل composer.json را در دایرکتوری اصلی پروژه خود باز کنید و به قسمت autoload بروید. از قبل باید سه دایرکتوری در این کلید وجود داشته باشد. بعد از آنها یک خط جدید به شرح زیر اضافه کنید:

"autoload": {
        "psr-4": {
            "App\\": "app/",
            "Database\\Factories\\": "database/factories/",
            "Database\\Seeders\\": "database/seeders/",
            "Yusef22\\Inspire\\": "packages/yusef22/inspire/src/"
        }
    },

اکنون برای ایجاد یک فایل autoload.php به روز شده، که بتواند Package ما را مسیریابی کند، دستور زیر را در دایرکتوری اصلی پروژه اجرا کنید:

composer dump-autoload

این دستور فایل autoload.php را با در نظر گرفتن Package شما بازسازی می کند. اگر می‌خواهید درباره بارگیری خودکار اطلاعات بیشتری کسب کنید، این پیوند را دنبال کنید.

اکنون فایل routes/web.php را باز کرده و مسیر GET زیر را در آن ثبت کنید:

Route::get('inspire', function(Yusef22\Inspire\Inspire $inspire) {
    return $inspire->justDoIt();
});

برنامه را با استفاده از دستور php artisan serve اجرا کنید و به مسیر /inspire در مرورگر انتخابی خود بروید. اگر همه چیز خوب پیش رفت، باید یک نقل قول الهام‌بخش تصادفی روی صفحه نمایش خود ببینید.


اضافه کردن Service Provider به Package سفارشی

اکنون که کلاس به خوبی کار می کند، وقت آن است که یک Service Provider به بسته اضافه کنید. این Service Provider به‌عنوان نوعی نقطه ورود به بسته شما کار خواهد کرد. در داخل پوشه‌ی src که در Package ایجاد کردیم، یک فایل جدید در مسیر src/Providers/InspirationProvider.php ایجاد کنید و کد زیر را در آن قرار دهید:

<?php

namespace Yusef22\Inspire\Providers;

use Illuminate\Support\ServiceProvider;

class InspirationProvider extends ServiceProvider
{
    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
}

فعلاً متد boot را خالی نگه دارید، به زودی کدی را برای بارگیری routeها و viewهای بسته در اینجا اضافه خواهید کرد. در دایرکتوری اصلی پروژه، فایل config/app.php را باز کنید و به آرایه providers بروید. در آن آرایه، باید بخشی برای provider های package ها وجود داشته باشد. خط کد زیر را در آن بخش اضافه کنید:

/*
 * Package Service Providers...
 */
Yusef22\Inspire\Providers\InspirationProvider::class,

با این کار کلاس InspirationProvider به عنوان یکی از service provider ها برای پروژه اصلی ثبت می شود.


اضافه کردن Controller به Package

در بخش قبلی، مسیر /inspire را در پروژه‌ی اصلی به عنوان بخشی از کد پروژه ثبت کردید. اما بهتر این است که این مسیر در داخل Package تعریف شود. به این ترتیب، هر کسی که از این بسته استفاده می کند، از همان ابتدا این مسیر را بدون نوشتن کد می‌تواند در پروژه خود داشته باشد.

بنابراین ممکن است بخواهید عملکرد مسیریابی را به همراه یک کلاس کنترلر جدید به بسته منتقل کنید. اضافه کردن یک کنترلر برای چنین کار ساده ای ممکن است زیاد باشد، اما من می خواهم به شما نشان دهم که چگونه می توانید کنترلرها را در بسته های خود قرار دهید.

یک فایل جدید src/Controllers/InspirationController.php با کد زیر ایجاد کنید:

<?php
namespace Yusef22\Inspire\Controllers;

use Illuminate\Http\Request;
use Yusef22\Inspire\Inspire;

class InspirationController
{
    public function __invoke(Inspire $inspire) {
        $quote = $inspire->justDoIt();

        return $quote';
    }
}

همانطور که می بینید، این کار مانند مسیری که در بخش قبلی ایجاد کرده اید، اما به روشی کمی متفاوت انجام می دهد. تابع __invoke() که کل این کنترلر هدف یک مسیر واحد را خدمت می کند.


اضافه کردن Route ها به Package

اکنون که کنترلر در جای خود قرار دارد، زمان ایجاد مسیر GET است. برای انجام این کار، یک فایل جدید src/routes/web.php ایجاد کنید و کد زیر را در آن قرار دهید:

<?php

use Yusef22\Inspire\Controllers\InspirationController;
use Illuminate\Support\Facades\Route;

Route::get('inspire', InspirationController::class);

همانطور که می بینید، یک route واحد در اینجا وجود دارد که به Controller ای که در قسمت قبل ایجاد کردید اشاره دارد. در این لحظه، می توانید فایل routes/web.php مربوط به پروژه اصلی را باز کنید و مسیری که قبلا به آن اضافه شده بود را حذف کنید. زیرا اگر همان مسیر روی یک Package و همزمان روی فایل مسیر‌های پروژه ( routes/web.php ) وجود داشته باشد، آن مسیری که در پروژه تعریف شده است، اولویت دارد.

پس از افزودن مسیر، فایل src/Providers/InspirationProvider.php خود را باز کرده و متد boot آن را به صورت زیر ویرایش کنید:

<?php
namespace Yusef22\Inspire\Providers;

use Illuminate\Support\ServiceProvider;

class InspirationProvider extends ServiceProvider
{
    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        $this->loadRoutesFrom(__DIR__.'/../routes/web.php');
    }
}

تغییری که ایجاد کردیم باعث می شود که وقتی این Service Provider توسط پروژه شما load می شود، مسیر /inspire به طور خودکار در پروژه ثبت شود. حال اگر پروژه را با استفاده از php artisan serve اجرا کنید و از مسیر /inspire دیدن کنید، متوجه می‌شوید که مانند قبل همه چیز خوب کار می کند.


افزودن view ها به Package

بخش پایانی اضافه کردن چند view به بسته است. باز هم، ممکن است برای package ای به این سادگی بیش از حد اضافی باشد، اما هدف این است که به شما نشان دهم که چگونه می‌توانید view ها را به بسته خود اضافه کنید. یک فایل جدید src/views/index.blade.php ایجاد کنید و محتوای زیر را در آن قرار دهید:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Inspire</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>

<body>
    <div class="container">
        <h1>{{ $quote }}</h1>
    </div>
</body>
</html>

کد بالا یک view بسیار ساده که نقل قول را در قالب یک صفحه HTML که بر پایه Bootstrap ساخته شده است را نشان می دهد. هر چند افزودن view کافی نیست. شما باید این view را به کمک Service Provider مربوط به Package خود، بارگیری کنید. برای انجام این کار، فایل src/Providers/InspirationProvider.php را باز کرده و کد آن را به صورت زیر ویرایش کنید:

<?php
namespace Yusef22\Inspire\Providers;

use Illuminate\Support\ServiceProvider;

class InspirationProvider extends ServiceProvider
{
    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        $this->loadRoutesFrom(__DIR__.'/../routes/web.php');
        $this->loadViewsFrom(__DIR__.'/../views', 'inspire');
    }
}

متد loadViewsFrom دو پارامتر دارد. اولی دایرکتوری است که در آن view های خود را قرار داده‌اید و دومی فضای نام است. فضای نام باید نام بسته شما باشد.

در نهایت فایل src/Controllers/InspirationController.php را باز کرده و کد آن را به صورت زیر ویرایش کنید:

<?php
namespace Yusef22\Inspire\Controllers;

use Illuminate\Http\Request;
use Yusef22\Inspire\Inspire;

class InspirationController
{
    public function __invoke(Inspire $inspire) {
        $quote = $inspire->justDoIt();

        return view('inspire::index', compact('quote'));
    }
}

همانطور که می بینید، دستور return این بار برای بازگرداندن نمای HTML تغییر یافته است. به خاطر داشته باشید، باید فضای نام و سپس :: و نام فایل view را وارد کنید. در غیر این صورت لاراول view را پیدا نخواهد کرد. پس از انجام این کار، همه چیز را ذخیره کنید و برنامه خود را با استفاده از دستور php artisan serve اجرا کنید و از مسیر /inspire بازدید کنید. این بار شما باید یک HTML رندر شده را به جای یک رشته مشاهده کنید. خب، فرآیند ایجاد Package سفارشی لاراول به اتمام رسید. در ادامه به نحوه‌ی استفاده از آن در پروژه‌های جدید می‌پردازیم.


اشتراک گذاری Package سفارشی خود با دیگران

اکنون که بسته شما آماده است، ممکن است بخواهید آن را با دیگران به اشتراک بگذارید. برای این کار می‌توانید از Packagist استفاده کنید، اما برای بسته‌ای به این گنگی، بهتر است پلتفرم Pakagist را تبدیل به زباله دان نکنیم. بیایید فعلا از GitHub برای اشتراک گذاری بسته خود استفاده کنیم.

با دستور cd به پوشه packages/yusef22/inspire منتقل شوید و مجموعه دستورات زیر را اجرا کنید:

// packages/yusef22/inspire

echo "/vendor/" > .gitignore
git init
git checkout -b master
git add .
git commit -m "Initial commit"
git tag 1.0.0

این دایرکتوری بسته را به یک مخزن git تبدیل می کند، همه فایل ها را اضافه می کند، یک commit اولیه ایجاد می کند و کد منبع را به عنوان نسخه ۱٫۰٫۰ تگ می کند. اکنون به GitHub بروید و یک مخزن Public که دسترسی به کدهای داخل آن برای عموم آزاد است جدید ایجاد کنید.

مخزنی که قرار است از آن استفاده کنم مخزن yusef22/inspire است. دستورات زیر وظیفه انتشار بسته را در مخزن انجام می دهند:

// packages/yusef22/inspire

git remote add origin git@github.com:yusef22/inspire.git
git push -u origin --all
git push -u origin --tags

این بسته هم اکنون توسط سایرین در پروژه های خود قابل نصب است.


نصب Package خود در یک پروژه جدید لاراول

برای آزمایش نصب بسته، به یک پروژه جدید لاراول نیاز دارید. یک پروژه جدید در جایی در رایانه خود با نام need-inspiration ایجاد کنید.

laravel new needs-inspiration

به‌طور پیش‌فرض، Composer بسته‌ها را از Packagist واکشی و نصب می‌کند، بنابراین باید کمی در فایل composer.json پروژه جدید خود تغییرات اعمال کنید. در نتیجه فایل composer.json پروژه جدید را باز کنید و موارد زیر را در آن اضافه کنید:

"repositories": [
    {
        "type": "vcs",
        "url": "https://github.com/yusef22/inspire"
    }
],
//
"require": {
    //
    "yusef22/inspire": "^1.0"
}

فایل composer.json ویرایش شده باید چیزی شبیه این باشد:

{
    "name": "laravel/laravel",
    "type": "project",
    "description": "The Laravel Framework.",
    "keywords": ["framework", "laravel"],
    "license": "MIT",
    // here you go
    "repositories": [
        {
            "type": "vcs",
            "url": "https://github.com/yusef22/inspire"
        }
    ],
    "require": {
        "php": "^7.3|^8.0",
        "fruitcake/laravel-cors": "^2.0",
        "guzzlehttp/guzzle": "^7.0.1",
        "laravel/framework": "^8.75",
        "laravel/sanctum": "^2.11",
        "laravel/tinker": "^2.5",
        "yusef22/inspire": "^1.0"
    },
    "require-dev": {
        "facade/ignition": "^2.5",
        "fakerphp/faker": "^1.9.1",
        "laravel/sail": "^1.0.1",
        "mockery/mockery": "^1.4.4",
        "nunomaduro/collision": "^5.10",
        "phpunit/phpunit": "^9.5.10"
    },
    // ... so on
}

اکنون composer برای بروز رسانی Package ها به repository ما نیز رجوع می‌کند. برای اینکه package ما نیز دریافت شود و در پوشه‌ی vendor قرار گیرد تا بتوانیم از آن استفاده کینم، باید دستور زیر را اجرا کنیم:

composer update

خروجی این دستور باید به صورت زیر باشد:

$ composer require yusef22/inspire
Using version ^1.0 for yusef22/inspire
./composer.json has been updated
Running composer update yusef22/inspire
Loading composer repositories with package information
Updating dependencies                                 
Lock file operations: 1 install, 0 updates, 0 removals
  - Locking yusef22/inspire (1.0.0)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
  - Downloading yusef22/inspire (1.0.0)
  - Installing yusef22/inspire (1.0.0): Extracting archive
Package swiftmailer/swiftmailer is abandoned, you should avoid using it. Use symfony/mailer instead.
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi
Discovered Package: facade/ignition
Discovered Package: fruitcake/laravel-cors
Discovered Package: laravel/sail
Discovered Package: laravel/sanctum
Discovered Package: laravel/tinker
Discovered Package: nesbot/carbon
Discovered Package: nunomaduro/collision
Package manifest generated successfully.
۷۷ packages you are using are looking for funding.
Use the `composer fund` command to find out more!
> @php artisan vendor:publish --tag=laravel-assets --ansi --force
No publishable resources for tag [laravel-assets].
Publishing complete.

همانطور که مشاهده می کنید، بسته با موفقیت نصب شده است. اکنون فایل config/app.php را باز کرده و به آرایه providers بروید. در آن آرایه، باید بخشی برای service provider های بسته‌ها وجود داشته باشد. خط کد زیر را در آن بخش اضافه کنید:

/*
 * Package Service Providers...
 */
Yusef22\Inspire\Providers\InspirationProvider::class,

با این کار کلاس InspirationProvider به عنوان یکی از ارائه دهندگان خدمات برای این پروژه ثبت می شود. برنامه را با استفاده از php artisan serve شروع کنید و برای الهام گرفتن از مسیر /inspire دیدن کنید. همچنین، از آنجایی که ما منطق دریافت نقل قول الهام را در یک کلاس جداگانه به جای مستقیم در کنترلر قرار داده ایم، می توانید از کلاس Yusef22\Inspire\Inspire.php در هر نقطه از پروژه استفاده کنید. من می توانستم آن را به یک نما تبدیل کنم، اما دوست ندارم مسائل را بی جهت پیچیده کنم. پس همین است.


سخن آخر

حال که آموزش نحوه‌ی ایجاد Package سفارشی در لاراول به پایان رسید، بابت زمانی که برای مطالعه صرف کردید تشکر می‌کنم. امیدوارم از این آموزش لذت برده باشید و در آینده Package‌های مفیدی توسعه دهید. آن چیزی که در این مطلب بررسی کردیم تمام راه نیست و در دنیای واقعی بسته ها می توانند بسیار پیچیده تر از این باشند. اگر راهنمایی بیشتری نیاز داشتید، می‌توانید به مستندات لاراول در مورد توسعه Package ها نیز مراجعه کنید.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *