نسخه:

پرچم لاراول

معرفی

Laravel Pennant یک بسته پرچم ویژگی ساده و سبک است - بدون تراشه. پرچم‌های ویژگی شما را قادر می‌سازد تا به تدریج ویژگی‌های جدید برنامه را با اطمینان اجرا کنید، طراحی‌های رابط A/B جدید را آزمایش کنید، استراتژی توسعه مبتنی بر ترانک را تکمیل کنید، و موارد دیگر.

نصب و راه اندازی

ابتدا Pennant را با استفاده از مدیریت بسته Composer در پروژه خود نصب کنید:

composer require laravel/pennant

در مرحله بعد، باید فایل های پیکربندی و مهاجرت Pennant را با استفاده از vendor:publish دستور Artisan منتشر کنید:

php artisan vendor:publish --provider="Laravel\Pennant\PennantServiceProvider"

در نهایت، باید مهاجرت های پایگاه داده برنامه خود را اجرا کنید. با این کار جدولی ایجاد می شود features که Pennant برای روشن کردن database درایور خود از آن استفاده می کند:

php artisan migrate

پیکربندی

پس از انتشار دارایی های Pennant، فایل پیکربندی آن در آدرس قرار می گیرد config/pennant.php . این فایل پیکربندی به شما امکان می دهد مکانیسم ذخیره سازی پیش فرضی را که توسط Pennant برای ذخیره مقادیر پرچم مشخصه مشخص شده استفاده می شود، مشخص کنید.

Pennant شامل پشتیبانی برای ذخیره مقادیر پرچم ویژگی حل شده در یک آرایه درون حافظه از طریق array درایور است. یا، Pennant می تواند مقادیر پرچم ویژگی حل شده را به طور مداوم در یک پایگاه داده رابطه ای از طریق database درایور ذخیره کند، که مکانیزم ذخیره سازی پیش فرض استفاده شده توسط Pennant است.

تعریف ویژگی ها

برای تعریف یک ویژگی، می توانید از define روش ارائه شده توسط Feature نما استفاده کنید. شما باید یک نام برای ویژگی ارائه دهید، و همچنین یک بسته که برای حل مقدار اولیه ویژگی فراخوانی می شود.

به طور معمول، ویژگی ها در یک ارائه دهنده خدمات با استفاده از Feature نما تعریف می شوند. بسته شدن "محدوده" را برای بررسی ویژگی دریافت خواهد کرد. معمولاً، دامنه کاربر تأیید شده فعلی است. در این مثال، ما یک ویژگی برای عرضه تدریجی یک API جدید برای کاربران برنامه خود تعریف می کنیم:

<?php
 
namespace App\Providers;
 
use App\Models\User;
use Illuminate\Support\Lottery;
use Illuminate\Support\ServiceProvider;
use Laravel\Pennant\Feature;
 
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Feature::define('new-api', fn (User $user) => match (true) {
$user->isInternalTeamMember() => true,
$user->isHighTrafficCustomer() => false,
default => Lottery::odds(1 / 100),
});
}
}

همانطور که می بینید، ما قوانین زیر را برای ویژگی خود داریم:

  • همه اعضای تیم داخلی باید از API جدید استفاده کنند.
  • مشتریان با ترافیک بالا نباید از API جدید استفاده کنند.
  • در غیر این صورت، این ویژگی باید به طور تصادفی به کاربرانی با احتمال 1 در 100 فعال بودن اختصاص داده شود.

اولین باری که این new-api ویژگی برای یک کاربر مشخص بررسی می شود، نتیجه بسته شدن توسط درایور ذخیره سازی ذخیره می شود. دفعه بعد که این ویژگی در برابر همان کاربر بررسی می شود، مقدار از حافظه بازیابی می شود و بسته شدن فراخوانی نمی شود.

برای راحتی، اگر تعریف ویژگی فقط یک قرعه کشی را برمی گرداند، می توانید بسته شدن را به طور کامل حذف کنید:

Feature::define('site-redesign', Lottery::odds(1, 1000));

ویژگی های مبتنی بر کلاس

Pennant همچنین به شما اجازه می دهد تا ویژگی های مبتنی بر کلاس را تعریف کنید. برخلاف تعاریف ویژگی مبتنی بر بسته شدن، نیازی به ثبت ویژگی مبتنی بر کلاس در ارائه‌دهنده خدمات نیست. برای ایجاد یک ویژگی مبتنی بر کلاس، می توانید pennant:feature دستور Artisan را فراخوانی کنید. به طور پیش فرض کلاس ویژگی در app/Features فهرست برنامه شما قرار می گیرد:

php artisan pennant:feature NewApi

هنگام نوشتن یک کلاس ویژگی، فقط باید یک resolve متد را تعریف کنید، که برای حل مقدار اولیه ویژگی برای یک محدوده مشخص فراخوانی می شود. باز هم، دامنه معمولاً کاربر تأیید شده فعلی خواهد بود:

<?php
 
namespace App\Features;
 
use Illuminate\Support\Lottery;
 
class NewApi
{
/**
* Resolve the feature's initial value.
*/
public function resolve(User $user): mixed
{
return match (true) {
$user->isInternalTeamMember() => true,
$user->isHighTrafficCustomer() => false,
default => Lottery::odds(1 / 100),
};
}
}

کلاس‌های ویژگی از طریق ظرف حل می‌شوند ، بنابراین می‌توانید در صورت نیاز وابستگی‌ها را به سازنده کلاس ویژگی تزریق کنید.

سفارشی کردن نام ویژگی ذخیره شده

به طور پیش فرض، Pennant نام کلاس کاملاً واجد شرایط کلاس ویژگی را ذخیره می کند. اگر می خواهید نام ویژگی ذخیره شده را از ساختار داخلی برنامه جدا کنید، می توانید یک $name ویژگی را در کلاس ویژگی مشخص کنید. مقدار این ویژگی به جای نام کلاس ذخیره می شود:

<?php
 
namespace App\Features;
 
class NewApi
{
/**
* The stored name of the feature.
*
* @var string
*/
public $name = 'new-api';
 
// ...
}

بررسی ویژگی ها

برای تعیین اینکه آیا یک ویژگی فعال است، می توانید از active روش روی Feature نما استفاده کنید. به طور پیش‌فرض، ویژگی‌ها در برابر کاربر تأیید شده فعلی بررسی می‌شوند:

<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Laravel\Pennant\Feature;
 
class PodcastController
{
/**
* Display a listing of the resource.
*/
public function index(Request $request): Response
{
return Feature::active('new-api')
? $this->resolveNewApiResponse($request)
: $this->resolveLegacyApiResponse($request);
}
 
// ...
}

اگرچه ویژگی‌ها به‌طور پیش‌فرض با کاربر تأیید شده فعلی بررسی می‌شوند، می‌توانید به راحتی این ویژگی را در مقابل کاربر یا محدوده دیگری بررسی کنید . برای انجام این کار، از for روش ارائه شده توسط Feature نما استفاده کنید:

return Feature::for($user)->active('new-api')
? $this->resolveNewApiResponse($request)
: $this->resolveLegacyApiResponse($request);

Pennant همچنین روش‌های راحتی اضافی را ارائه می‌کند که ممکن است هنگام تعیین فعال یا نبودن یک ویژگی مفید باشد:

// Determine if all of the given features are active...
Feature::allAreActive(['new-api', 'site-redesign']);
 
// Determine if any of the given features are active...
Feature::someAreActive(['new-api', 'site-redesign']);
 
// Determine if a feature is inactive...
Feature::inactive('new-api');
 
// Determine if all of the given features are inactive...
Feature::allAreInactive(['new-api', 'site-redesign']);
 
// Determine if any of the given features are inactive...
Feature::someAreInactive(['new-api', 'site-redesign']);

هنگام استفاده از Pennant خارج از زمینه HTTP، مانند دستور Artisan یا یک کار در صف، معمولاً باید به صراحت محدوده ویژگی را مشخص کنید . از طرف دیگر، می‌توانید یک محدوده پیش‌فرض تعریف کنید که هم برای زمینه‌های HTTP احراز هویت‌شده و هم برای زمینه‌های احراز هویت نشده حساب می‌شود.

بررسی ویژگی‌های مبتنی بر کلاس

برای ویژگی‌های مبتنی بر کلاس، باید نام کلاس را هنگام بررسی ویژگی ارائه کنید:

<?php
 
namespace App\Http\Controllers;
 
use App\Features\NewApi;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Laravel\Pennant\Feature;
 
class PodcastController
{
/**
* Display a listing of the resource.
*/
public function index(Request $request): Response
{
return Feature::active(NewApi::class)
? $this->resolveNewApiResponse($request)
: $this->resolveLegacyApiResponse($request);
}
 
// ...
}

اعدام مشروط

این when روش ممکن است برای اجرای روان یک بسته معین در صورت فعال بودن یک ویژگی استفاده شود. علاوه بر این، بسته شدن دوم ممکن است ارائه شود و در صورت غیرفعال بودن ویژگی اجرا خواهد شد:

<?php
 
namespace App\Http\Controllers;
 
use App\Features\NewApi;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Laravel\Pennant\Feature;
 
class PodcastController
{
/**
* Display a listing of the resource.
*/
public function index(Request $request): Response
{
return Feature::when(NewApi::class,
fn () => $this->resolveNewApiResponse($request),
fn () => $this->resolveLegacyApiResponse($request),
);
}
 
// ...
}

این unless روش به عنوان معکوس روش عمل می کند when و در صورت غیرفعال بودن ویژگی، اولین بسته شدن را اجرا می کند:

return Feature::unless(NewApi::class,
fn () => $this->resolveLegacyApiResponse($request),
fn () => $this->resolveNewApiResponse($request),
);

صفت HasFeatures

ویژگی Pennant HasFeatures ممکن است به مدل برنامه شما User (یا هر مدل دیگری که دارای ویژگی‌ها است) اضافه شود تا روشی روان و راحت برای بررسی مستقیم ویژگی‌ها از مدل ارائه شود:

<?php
 
namespace App\Models;
 
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Pennant\Concerns\HasFeatures;
 
class User extends Authenticatable
{
use HasFeatures;
 
// ...
}

هنگامی که این ویژگی به مدل شما اضافه شد، می توانید به راحتی ویژگی ها را با فراخوانی features روش بررسی کنید:

if ($user->features()->active('new-api')) {
// ...
}

البته، این features روش دسترسی به بسیاری از روش‌های راحت دیگر را برای تعامل با ویژگی‌ها فراهم می‌کند:

// Values...
$value = $user->features()->value('purchase-button')
$values = $user->features()->values(['new-api', 'purchase-button']);
 
// State...
$user->features()->active('new-api');
$user->features()->allAreActive(['new-api', 'server-api']);
$user->features()->someAreActive(['new-api', 'server-api']);
 
$user->features()->inactive('new-api');
$user->features()->allAreInactive(['new-api', 'server-api']);
$user->features()->someAreInactive(['new-api', 'server-api']);
 
// Conditional execution...
$user->features()->when('new-api',
fn () => /* ... */,
fn () => /* ... */,
);
 
$user->features()->unless('new-api',
fn () => /* ... */,
fn () => /* ... */,
);

دستورالعمل تیغه

برای اینکه بررسی ویژگی‌ها در Blade یک تجربه یکپارچه باشد، Pennant @feature دستورالعملی را ارائه می‌دهد:

@feature('site-redesign')
<!-- 'site-redesign' is active -->
@else
<!-- 'site-redesign' is inactive -->
@endfeature

میان افزار

Pennant همچنین شامل یک میان افزار است که ممکن است برای تأیید دسترسی کاربر تأیید شده فعلی به یک ویژگی قبل از فراخوانی مسیر مورد استفاده قرار گیرد. می‌توانید میان‌افزار را به یک مسیر اختصاص دهید و ویژگی‌هایی را که برای دسترسی به مسیر مورد نیاز است، مشخص کنید. اگر هر یک از ویژگی های مشخص شده برای کاربر تأیید شده فعلی غیرفعال باشد، یک 400 Bad Request پاسخ HTTP توسط مسیر برگردانده می شود. چندین ویژگی ممکن است به روش استاتیک منتقل شود using .

use Illuminate\Support\Facades\Route;
use Laravel\Pennant\Middleware\EnsureFeaturesAreActive;
 
Route::get('/api/servers', function () {
// ...
})->middleware(EnsureFeaturesAreActive::using('new-api', 'servers-api'));

سفارشی کردن پاسخ

اگر می‌خواهید پاسخی را که وقتی یکی از ویژگی‌های فهرست‌شده غیرفعال است، توسط میان‌افزار بازگردانده می‌شود، سفارشی کنید، می‌توانید از روش whenInactive ارائه‌شده توسط EnsureFeaturesAreActive میان‌افزار استفاده کنید. به طور معمول، این روش باید در boot روش یکی از ارائه دهندگان خدمات برنامه شما فراخوانی شود:

use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Laravel\Pennant\Middleware\EnsureFeaturesAreActive;
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
EnsureFeaturesAreActive::whenInactive(
function (Request $request, array $features) {
return new Response(status: 403);
}
);
 
// ...
}

کش درون حافظه

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

اگر نیاز به شستشوی دستی کش حافظه دارید، می توانید از flushCache روش ارائه شده توسط Feature نما استفاده کنید:

Feature::flushCache();

محدوده

تعیین محدوده

همانطور که بحث شد، ویژگی ها معمولاً در برابر کاربر تأیید شده فعلی بررسی می شوند. با این حال، این ممکن است همیشه با نیازهای شما مطابقت نداشته باشد. بنابراین، می‌توان محدوده‌ای را که می‌خواهید یک ویژگی را بررسی کنید، از طریق روش Feature نما مشخص کنید for :

return Feature::for($user)->active('new-api')
? $this->resolveNewApiResponse($request)
: $this->resolveLegacyApiResponse($request);

البته دامنه ویژگی ها به «کاربران» محدود نمی شود. تصور کنید یک تجربه صورت‌حساب جدیدی ایجاد کرده‌اید که به جای کاربران فردی، آن را برای کل تیم‌ها عرضه می‌کنید. شاید دوست داشته باشید که قدیمی ترین تیم ها نسبت به تیم های جدیدتر، کندتر عرضه شوند. بسته شدن وضوح ویژگی شما ممکن است چیزی شبیه به زیر باشد:

use App\Models\Team;
use Carbon\Carbon;
use Illuminate\Support\Lottery;
use Laravel\Pennant\Feature;
 
Feature::define('billing-v2', function (Team $team) {
if ($team->created_at->isAfter(new Carbon('1st Jan, 2023'))) {
return true;
}
 
if ($team->created_at->isAfter(new Carbon('1st Jan, 2019'))) {
return Lottery::odds(1 / 100);
}
 
return Lottery::odds(1 / 1000);
});

متوجه خواهید شد که بسته ای که ما تعریف کرده ایم، انتظار یک نیست User ، بلکه در عوض انتظار یک Team مدل است. برای تعیین اینکه آیا این ویژگی برای تیم کاربر فعال است، باید تیم را به for روش ارائه شده توسط Feature نما منتقل کنید:

if (Feature::for($user->team)->active('billing-v2')) {
return redirect()->to('/billing/v2');
}
 
// ...

محدوده پیش فرض

همچنین امکان سفارشی کردن دامنه پیش‌فرض Pennant برای بررسی ویژگی‌ها وجود دارد. به عنوان مثال، ممکن است همه ویژگی‌های شما در مقابل تیم کاربر تأیید شده فعلی به جای کاربر بررسی شود. به جای اینکه مجبور باشید Feature::for($user->team) هر بار که یک ویژگی را بررسی می کنید تماس بگیرید، می توانید تیم را به عنوان محدوده پیش فرض مشخص کنید. به طور معمول، این باید در یکی از ارائه دهندگان خدمات برنامه شما انجام شود:

<?php
 
namespace App\Providers;
 
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\ServiceProvider;
use Laravel\Pennant\Feature;
 
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Feature::resolveScopeUsing(fn ($driver) => Auth::user()?->team);
 
// ...
}
}

اگر هیچ محدوده ای به صراحت از طریق for روش ارائه نشده باشد، بررسی ویژگی اکنون از تیم کاربر تأیید شده فعلی به عنوان محدوده پیش فرض استفاده می کند:

Feature::active('billing-v2');
 
// Is now equivalent to...
 
Feature::for($user->team)->active('billing-v2');

محدوده ناتوان

اگر محدوده‌ای که هنگام بررسی یک ویژگی ارائه می‌دهید، باشد null و تعریف ویژگی null از طریق یک نوع تهی یا با درج در یک نوع اتحادیه پشتیبانی نمی‌شود، Pennant به‌طور خودکار به عنوان مقدار نتیجه ویژگی null برمی‌گردد . false

بنابراین، اگر محدوده‌ای که به یک ویژگی منتقل می‌کنید به طور بالقوه است null و می‌خواهید که حل‌کننده ارزش ویژگی فراخوانی شود، باید آن را در تعریف ویژگی خود در نظر بگیرید. null اگر یک ویژگی را در یک دستور Artisan، کار در صف، یا مسیر احراز هویت نشده بررسی کنید، ممکن است یک محدوده رخ دهد. از آنجایی که معمولاً یک کاربر احراز هویت در این زمینه ها وجود ندارد، دامنه پیش فرض آن خواهد بود null .

اگر همیشه به طور صریح محدوده ویژگی خود را مشخص نمی کنید ، باید اطمینان حاصل کنید که نوع محدوده "nullable" است و null مقدار محدوده را در منطق تعریف ویژگی خود مدیریت کنید:

use App\Models\User;
use Illuminate\Support\Lottery;
use Laravel\Pennant\Feature;
 
Feature::define('new-api', fn (User $user) => match (true) {
Feature::define('new-api', fn (User|null $user) => match (true) {
$user === null => true,
$user->isInternalTeamMember() => true,
$user->isHighTrafficCustomer() => false,
default => Lottery::odds(1 / 100),
});

شناسایی محدوده

درایورهای داخلی array و database ذخیره‌سازی Pennant می‌دانند که چگونه شناسه‌های محدوده را برای همه انواع داده‌های PHP و همچنین مدل‌های Eloquent به درستی ذخیره کنند. با این حال، اگر برنامه شما از یک درایور Pennant شخص ثالث استفاده می کند، ممکن است آن درایور نداند که چگونه یک شناسه برای یک مدل Eloquent یا دیگر انواع سفارشی را در برنامه شما به درستی ذخیره کند.

با توجه به این موضوع، Pennant به شما این امکان را می دهد که با اجرای FeatureScopeable قرارداد بر روی اشیاء موجود در برنامه خود که به عنوان قلمرو قلم استفاده می شوند، مقادیر محدوده را برای ذخیره سازی قالب بندی کنید.

برای مثال، تصور کنید که از دو درایور ویژگی مختلف در یک برنامه استفاده می‌کنید: درایور داخلی database و درایور «Flag Rocket» شخص ثالث. راننده "راکت پرچم" نمی داند چگونه یک مدل Eloquent را به درستی ذخیره کند. در عوض، به یک نمونه نیاز دارد FlagRocketUser . با اجرای toFeatureIdentifier تعریف شده توسط FeatureScopeable قرارداد، می‌توانیم مقدار محدوده قابل ذخیره‌سازی ارائه شده برای هر درایور مورد استفاده توسط برنامه ما را سفارشی کنیم:

<?php
 
namespace App\Models;
 
use FlagRocket\FlagRocketUser;
use Illuminate\Database\Eloquent\Model;
use Laravel\Pennant\Contracts\FeatureScopeable;
 
class User extends Model implements FeatureScopeable
{
/**
* Cast the object to a feature scope identifier for the given driver.
*/
public function toFeatureIdentifier(string $driver): mixed
{
return match($driver) {
'database' => $this,
'flag-rocket' => FlagRocketUser::fromId($this->flag_rocket_id),
};
}
}

محدوده سریال سازی

به‌طور پیش‌فرض، Pennant از یک نام کلاس کاملاً واجد شرایط هنگام ذخیره ویژگی مرتبط با مدل Eloquent استفاده می‌کند. اگر قبلاً از یک نقشه شکل‌بندی Eloquent استفاده می‌کنید، می‌توانید انتخاب کنید که Pennant نیز از نقشه مورف برای جدا کردن ویژگی ذخیره‌شده از ساختار برنامه‌تان استفاده کند.

برای رسیدن به این هدف، پس از تعریف نقشه مورف Eloquent خود در ارائه‌دهنده خدمات، می‌توانید از روش Feature نما استفاده کنید useMorphMap :

use Illuminate\Database\Eloquent\Relations\Relation;
use Laravel\Pennant\Feature;
 
Relation::enforceMorphMap([
'post' => 'App\Models\Post',
'video' => 'App\Models\Video',
]);
 
Feature::useMorphMap();

ارزش ویژگی های غنی

تا به حال، ما ویژگی‌ها را در درجه اول در حالت باینری نشان داده‌ایم، به این معنی که آنها یا «فعال» یا «غیرفعال» هستند، اما Pennant همچنین به شما اجازه می‌دهد مقادیر غنی را نیز ذخیره کنید.

به عنوان مثال، تصور کنید در حال آزمایش سه رنگ جدید برای دکمه «اکنون خرید» برنامه خود هستید. به جای برگرداندن true یا false از تعریف ویژگی، می توانید یک رشته را برگردانید:

use Illuminate\Support\Arr;
use Laravel\Pennant\Feature;
 
Feature::define('purchase-button', fn (User $user) => Arr::random([
'blue-sapphire',
'seafoam-green',
'tart-orange',
]));

می توانید مقدار purchase-button ویژگی را با استفاده از value روش زیر بازیابی کنید:

$color = Feature::value('purchase-button');

دستورالعمل Blade شامل Pennant همچنین ارائه مشروط محتوا را بر اساس مقدار فعلی ویژگی آسان می کند:

@feature('purchase-button', 'blue-sapphire')
<!-- 'blue-sapphire' is active -->
@elsefeature('purchase-button', 'seafoam-green')
<!-- 'seafoam-green' is active -->
@elsefeature('purchase-button', 'tart-orange')
<!-- 'tart-orange' is active -->
@endfeature

هنگام استفاده از مقادیر غنی، مهم است که بدانید یک ویژگی زمانی "فعال" در نظر گرفته می شود که ارزشی غیر از false .

هنگام فراخوانی روش شرطی when ، مقدار غنی ویژگی تا بسته شدن اول ارائه می شود:

Feature::when('purchase-button',
fn ($color) => /* ... */,
fn () => /* ... */,
);

به همین ترتیب، هنگام فراخوانی unless روش شرطی، مقدار غنی ویژگی به بسته دوم اختیاری ارائه می شود:

Feature::unless('purchase-button',
fn () => /* ... */,
fn ($color) => /* ... */,
);

بازیابی چندین ویژگی

این values روش امکان بازیابی چندین ویژگی را برای یک محدوده مشخص می دهد:

Feature::values(['billing-v2', 'purchase-button']);
 
// [
// 'billing-v2' => false,
// 'purchase-button' => 'blue-sapphire',
// ]

یا، می توانید از all روش برای بازیابی مقادیر تمام ویژگی های تعریف شده برای یک محدوده معین استفاده کنید:

Feature::all();
 
// [
// 'billing-v2' => false,
// 'purchase-button' => 'blue-sapphire',
// 'site-redesign' => true,
// ]

با این حال، ویژگی های مبتنی بر کلاس به صورت پویا ثبت می شوند و توسط Pennant شناخته نمی شوند تا زمانی که صریحاً بررسی شوند. این بدان معناست که ویژگی‌های مبتنی بر کلاس برنامه شما ممکن است در نتایجی که توسط all روش برگردانده شده است ظاهر نشوند، اگر قبلاً در طول درخواست فعلی بررسی نشده باشند.

اگر می‌خواهید مطمئن شوید که کلاس‌های ویژگی همیشه هنگام استفاده از all روش گنجانده می‌شوند، می‌توانید از قابلیت‌های کشف ویژگی Pennant استفاده کنید. برای شروع، discover روش را در یکی از ارائه دهندگان خدمات برنامه خود فراخوانی کنید:

<?php
 
namespace App\Providers;
 
use Illuminate\Support\ServiceProvider;
use Laravel\Pennant\Feature;
 
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Feature::discover();
 
// ...
}
}

این discover روش تمام کلاس های ویژگی را در app/Features فهرست برنامه شما ثبت می کند. این all روش اکنون این کلاس ها را در نتایج خود شامل می شود، صرف نظر از اینکه آیا آنها در طول درخواست فعلی بررسی شده اند یا خیر:

Feature::all();
 
// [
// 'App\Features\NewApi' => true,
// 'billing-v2' => false,
// 'purchase-button' => 'blue-sapphire',
// 'site-redesign' => true,
// ]

مشتاق بارگیری

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

برای نشان دادن این موضوع، تصور کنید که ما در حال بررسی هستیم که آیا یک ویژگی در یک حلقه فعال است یا خیر:

use Laravel\Pennant\Feature;
 
foreach ($users as $user) {
if (Feature::for($user)->active('notifications-beta')) {
$user->notify(new RegistrationSuccess);
}
}

با فرض اینکه از درایور پایگاه داده استفاده می کنیم، این کد یک پرس و جو پایگاه داده را برای هر کاربر در حلقه اجرا می کند - صدها کوئری بالقوه را اجرا می کند. با این حال، با استفاده از روش Pennant load ، می‌توانیم این گلوگاه عملکرد بالقوه را با بارگذاری مشتاقانه مقادیر ویژگی برای مجموعه‌ای از کاربران یا محدوده‌ها حذف کنیم:

Feature::for($users)->load(['notifications-beta']);
 
foreach ($users as $user) {
if (Feature::for($user)->active('notifications-beta')) {
$user->notify(new RegistrationSuccess);
}
}

برای بارگیری مقادیر ویژگی فقط زمانی که قبلاً بارگیری نشده اند، می توانید از loadMissing روش زیر استفاده کنید:

Feature::for($users)->loadMissing([
'new-api',
'purchase-button',
'notifications-beta',
]);

به روز رسانی ارزش ها

هنگامی که مقدار یک ویژگی برای اولین بار حل می شود، درایور اصلی نتیجه را در حافظه ذخیره می کند. این اغلب برای اطمینان از تجربه ثابت برای کاربران شما در سراسر درخواست ها ضروری است. با این حال، گاهی اوقات، ممکن است بخواهید به صورت دستی مقدار ذخیره شده این ویژگی را به روز کنید.

برای انجام این کار، می‌توانید از روش‌های activate و deactivate برای تغییر وضعیت «روشن» یا «خاموش» یک ویژگی استفاده کنید:

use Laravel\Pennant\Feature;
 
// Activate the feature for the default scope...
Feature::activate('new-api');
 
// Deactivate the feature for the given scope...
Feature::for($user->team)->deactivate('billing-v2');

همچنین می توان به صورت دستی یک مقدار غنی برای یک ویژگی با ارائه آرگومان دوم برای activate متد تنظیم کرد:

Feature::activate('purchase-button', 'seafoam-green');

برای اینکه به Pennant دستور دهید مقدار ذخیره شده یک ویژگی را فراموش کند، می توانید از forget روش استفاده کنید. هنگامی که ویژگی دوباره بررسی شد، Pennant مقدار ویژگی را از تعریف ویژگی آن حل می کند:

Feature::forget('purchase-button');

به روز رسانی های انبوه

برای به روز رسانی مقادیر ذخیره شده ویژگی به صورت انبوه، می توانید از روش activateForEveryone و استفاده کنید deactivateForEveryone .

به عنوان مثال، تصور کنید اکنون به پایداری ویژگی اطمینان دارید new-api و بهترین 'purchase-button' رنگ را برای جریان پرداخت خود دریافت کرده اید - می توانید مقدار ذخیره شده را برای همه کاربران بر این اساس به روز کنید:

use Laravel\Pennant\Feature;
 
Feature::activateForEveryone('new-api');
 
Feature::activateForEveryone('purchase-button', 'seafoam-green');

همچنین، می‌توانید این ویژگی را برای همه کاربران غیرفعال کنید:

Feature::deactivateForEveryone('new-api');

این فقط مقادیر ویژگی های حل شده ای را که توسط درایور ذخیره سازی Pennant ذخیره شده اند به روز می کند. همچنین باید تعریف ویژگی را در برنامه خود به روز کنید.

ویژگی های پاکسازی

گاهی اوقات، پاک کردن کل یک ویژگی از فضای ذخیره سازی می تواند مفید باشد. این معمولاً در صورتی ضروری است که ویژگی را از برنامه خود حذف کرده باشید یا تنظیماتی را در تعریف ویژگی انجام داده باشید که می‌خواهید برای همه کاربران عرضه کنید.

شما می توانید تمام مقادیر ذخیره شده برای یک ویژگی را با استفاده از purge روش زیر حذف کنید:

// Purging a single feature...
Feature::purge('new-api');
 
// Purging multiple features...
Feature::purge(['new-api', 'purchase-button']);

اگر می‌خواهید همه ویژگی‌ها را از فضای ذخیره‌سازی پاک کنید، می‌توانید purge روش را بدون هیچ استدلالی فراخوانی کنید:

Feature::purge();

از آنجایی که پاک کردن ویژگی‌ها به عنوان بخشی از خط لوله استقرار برنامه شما می‌تواند مفید باشد، Pennant شامل یک pennant:purge دستور Artisan است که ویژگی‌های ارائه شده را از فضای ذخیره‌سازی پاک می‌کند:

php artisan pennant:purge new-api
 
php artisan pennant:purge new-api purchase-button

همچنین می توان تمام ویژگی ها را به جز مواردی که در یک لیست مشخصه مشخص شده اند پاک کرد. برای مثال، تصور کنید می‌خواهید همه ویژگی‌ها را پاک کنید، اما مقادیر ویژگی‌های "new-api" و "purchase-button" را در حافظه نگه دارید. برای انجام این کار، می توانید نام این ویژگی ها را به --except گزینه ارسال کنید:

php artisan pennant:purge --except=new-api --except=purchase-button

برای راحتی، pennant:purge دستور از یک پرچم نیز پشتیبانی می کند --except-registered . این پرچم نشان می دهد که همه ویژگی ها به جز مواردی که به صراحت در یک ارائه دهنده خدمات ثبت شده اند باید پاک شوند:

php artisan pennant:purge --except-registered

آزمایش کردن

هنگام آزمایش کدی که با پرچم‌های ویژگی تعامل دارد، ساده‌ترین راه برای کنترل مقدار بازگشتی پرچم ویژگی در آزمایش‌هایتان این است که به سادگی ویژگی را دوباره تعریف کنید. به عنوان مثال، تصور کنید ویژگی زیر را در یکی از ارائه دهندگان خدمات برنامه خود تعریف کرده اید:

use Illuminate\Support\Arr;
use Laravel\Pennant\Feature;
 
Feature::define('purchase-button', fn () => Arr::random([
'blue-sapphire',
'seafoam-green',
'tart-orange',
]));

برای تغییر مقدار بازگشتی ویژگی در آزمایش‌های خود، می‌توانید ویژگی را در ابتدای آزمایش دوباره تعریف کنید. تست زیر همیشه با موفقیت انجام می شود، حتی اگر Arr::random() پیاده سازی هنوز در ارائه دهنده خدمات وجود داشته باشد:

use Laravel\Pennant\Feature;
 
test('it can control feature values', function () {
Feature::define('purchase-button', 'seafoam-green');
 
expect(Feature::value('purchase-button'))->toBe('seafoam-green');
});
use Laravel\Pennant\Feature;
 
public function test_it_can_control_feature_values()
{
Feature::define('purchase-button', 'seafoam-green');
 
$this->assertSame('seafoam-green', Feature::value('purchase-button'));
}

همین رویکرد ممکن است برای ویژگی‌های مبتنی بر کلاس استفاده شود:

use Laravel\Pennant\Feature;
 
test('it can control feature values', function () {
Feature::define(NewApi::class, true);
 
expect(Feature::value(NewApi::class))->toBeTrue();
});
use App\Features\NewApi;
use Laravel\Pennant\Feature;
 
public function test_it_can_control_feature_values()
{
Feature::define(NewApi::class, true);
 
$this->assertTrue(Feature::value(NewApi::class));
}

اگر ویژگی شما Lottery نمونه ای را برمی گرداند، تعداد انگشت شماری کمک کننده آزمایشی مفید در دسترس هستند .

پیکربندی فروشگاه

PENNANT_STORE می‌توانید با تعریف متغیر محیطی در فایل برنامه، فروشگاهی را که Pennant در طول آزمایش استفاده می‌کند، پیکربندی کنید phpunit.xml :

<?xml version="1.0" encoding="UTF-8"?>
<phpunit colors="true">
<!-- ... -->
<php>
<env name="PENNANT_STORE" value="array"/>
<!-- ... -->
</php>
</phpunit>

افزودن درایورهای دلخواه سفارشی

پیاده سازی درایور

اگر هیچ یک از درایورهای ذخیره سازی موجود Pennant با نیازهای برنامه شما مطابقت نداشت، می توانید درایور ذخیره سازی خود را بنویسید. درایور سفارشی شما باید این Laravel\Pennant\Contracts\Driver رابط را پیاده سازی کند:

<?php
 
namespace App\Extensions;
 
use Laravel\Pennant\Contracts\Driver;
 
class RedisFeatureDriver implements Driver
{
public function define(string $feature, callable $resolver): void {}
public function defined(): array {}
public function getAll(array $features): array {}
public function get(string $feature, mixed $scope): mixed {}
public function set(string $feature, mixed $scope, mixed $value): void {}
public function setForAllScopes(string $feature, mixed $value): void {}
public function delete(string $feature, mixed $scope): void {}
public function purge(array|null $features): void {}
}

اکنون، فقط باید هر یک از این روش ها را با استفاده از یک اتصال Redis پیاده سازی کنیم. برای مثالی از نحوه پیاده‌سازی هر یک از این روش‌ها، نگاهی به Laravel\Pennant\Drivers\DatabaseDriver کد منبع Pennant بیندازید

لاراول همراه با دایرکتوری حاوی پسوندهای شما ارسال نمی کند. شما آزاد هستید که آنها را در هر جایی که دوست دارید قرار دهید. در این مثال، ما یک Extensions دایرکتوری برای قرار دادن RedisFeatureDriver .

ثبت درایور

پس از پیاده سازی درایور شما، آماده ثبت آن در لاراول هستید. برای افزودن درایورهای اضافی به Pennant، می توانید از extend روش ارائه شده توسط Feature نما استفاده کنید. شما باید extend متد را از boot روش یکی از ارائه دهندگان خدمات برنامه خود فراخوانی کنید :

<?php
 
namespace App\Providers;
 
use App\Extensions\RedisFeatureDriver;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\ServiceProvider;
use Laravel\Pennant\Feature;
 
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
// ...
}
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Feature::extend('redis', function (Application $app) {
return new RedisFeatureDriver($app->make('redis'), $app->make('events'), []);
});
}
}

هنگامی که درایور ثبت شد، می توانید از درایور در فایل پیکربندی redis برنامه خود استفاده کنید: config/pennant.php

'stores' => [
 
'redis' => [
'driver' => 'redis',
'connection' => null,
],
 
// ...
 
],

مناسبت ها

Pennant رویدادهای مختلفی را ارسال می کند که می تواند هنگام ردیابی پرچم های ویژگی در سراسر برنامه شما مفید باشد.

Laravel\Pennant\Events\RetrievingKnownFeature

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

Laravel\Pennant\Events\RetrievingUnknownFeature

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

برای مثال، ممکن است برای شما مفید باشد که به این رویداد گوش دهید و report یا در صورت وقوع یک استثنا ایجاد کنید:

<?php
 
namespace App\Providers;
 
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Event;
use Laravel\Pennant\Events\RetrievingUnknownFeature;
 
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Event::listen(function (RetrievingUnknownFeature $event) {
report("Resolving unknown feature [{$event->feature}].");
});
}
}

Laravel\Pennant\Events\DynamicallyDefiningFeature

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