Forwarded from | کانال ربات تلگرام |
LaraGram یک فریمورک توسعهپذیر، منعطف و مدرن به زبان PHP برای ساخت رباتهای تلگرامه که با الهام از ساختار لاراول طراحی شده.
اگر با لاراول آشنایی دارید، کار با LaraGram براتون راحت، لذتبخش و قابل پیشبینی خواهد بود — و حتی اگه آشنایی ندارید، ساختار منظمش خیلی زود براتون جا میافته.
LaraGram امکانات زیادی درون خودش داره که میتونید سختترین رباتها رو با چند خط کد پیادهسازی کنید، اگر قابلیتی رو هم نداشته باشه میتونید به عنوان پکیج جانبی بهش اضافه کنید یا حتی برای اون پکیج توسعه بدید.
به عنوان مثال، LaraGram مجهز به یک سیستم Update Listener پیشرفته هست که به شما امکاناتی مانند گروهبندی لیسنرها، نامدهی به هر لیسنر، اعمال محدودیت و Middleware بر روی لیسنر، پردازش متن و ورودیها، و... رو میده.
همچنین نسخه بازنویسی شده Eloquent ORM رو در خودش جا داده با پشتیبانی از ۵ دیتابیس مختلف، همراه با تعریف روابط، Migrations، Seeders و Factoryها.
سایر قابلیت های کلیدی اون:
🔐 سیستم کنترل مجوز با قابلیت تعریف Gate و Policy برای مدیریت دقیق سطح دسترسی کاربران به منابع مختلف.
💻 Commander System برای ساخت و اجرای راحت commandها، مدیریت سادهتر پروژه، و زمانبندی اجرای وظایف (Scheduled Tasks).
📨 Queue و Job System برای ساخت صف و اجرای کارها در پسزمینه با زمانبندی دلخواه.
🧰 رابط Redis با امکانات لازم برای توسعههای وابسته به کش، صف و پیامرسانی آنی.
🧠 سیستم کشینگ با پشتیبانی از ۷ درایور مختلف برای ذخیرهسازی دادههای موقتی، بههمراه پیادهسازی Step Manager بر همین بستر.
🔁 کالکشنها برای کار سادهتر و منعطفتر با دادههای Iterable، مشابه کالکشنهای Laravel.
⚙️ Concurrency داخلی با امکان پردازش همزمان چند درخواست در پسزمینه بدون پیچیدگی اضافه.
🔒 ابزارهای امنیتی با پشتیبانی از سیستمهای رمزنگاری (Crypt) و هشینگ (Hash).
📢 Event Dispatcher برای تعریف و مدیریت رویدادها و واکنش به آنها.
🎛 کیبورد بیلدر توسعهیافته با استفاده ساده و انعطاف پذیری بالا.
🧩 موتور قالبسازی پیشرفته الهامگرفته از Blade برای ساخت پیامها بهصورت پویا و قابل نگهداری.
🌍 سیستم چندزبانه (Translation) برای ساخت رباتهایی با پشتیبانی از زبانهای مختلف.
✅ سیستم اعتبارسنجی با قوانین متنوع و امکان تعریف Ruleهای سفارشی.
🤖 پشتیبانی از چند ربات همزمان و امکان تعریف چند کانکشن و مدیریت آنها بهصورت مستقل.
یک مثال ساده برای ایجاد یک کامند بن با user_id به مدت 7 روز، با کنترل دسترسی و شرط ریپلای نشدن کامند:
برخی از ویژگیها با افزونهها و پکیجهای جانبی به LaraGram اضافه میشوند، به عنوان مثال:
⚡️ LaraGram Surge
پکیجی برای اجرای سریعتر رباتها با پشتیبانی از Swoole و OpenSwoole
🔧 LaraGram Installer
برای نصب و راهاندازی سریع و ساده پروژههای LaraGram
🛢 LaraGram MongoDB
درایور پایگاهداده MongoDB برای Eloquent ORM
📚 مستندات رسمی LaraGram نیز از طریق لینک زیر در دسترس است:
🔗 laraxgram.github.io
💬 گروه پرسش و پاسخ:
🔹 @LaraGramChat
📌 پروژه در گیتهاب:
♦️ LaraGram
🔖 #TelegramBot, #ربات, #تلگرام
👤 AmirHossein
💎 Channel: @DevelopixRobot
اگر با لاراول آشنایی دارید، کار با LaraGram براتون راحت، لذتبخش و قابل پیشبینی خواهد بود — و حتی اگه آشنایی ندارید، ساختار منظمش خیلی زود براتون جا میافته.
LaraGram امکانات زیادی درون خودش داره که میتونید سختترین رباتها رو با چند خط کد پیادهسازی کنید، اگر قابلیتی رو هم نداشته باشه میتونید به عنوان پکیج جانبی بهش اضافه کنید یا حتی برای اون پکیج توسعه بدید.
به عنوان مثال، LaraGram مجهز به یک سیستم Update Listener پیشرفته هست که به شما امکاناتی مانند گروهبندی لیسنرها، نامدهی به هر لیسنر، اعمال محدودیت و Middleware بر روی لیسنر، پردازش متن و ورودیها، و... رو میده.
همچنین نسخه بازنویسی شده Eloquent ORM رو در خودش جا داده با پشتیبانی از ۵ دیتابیس مختلف، همراه با تعریف روابط، Migrations، Seeders و Factoryها.
سایر قابلیت های کلیدی اون:
🔐 سیستم کنترل مجوز با قابلیت تعریف Gate و Policy برای مدیریت دقیق سطح دسترسی کاربران به منابع مختلف.
💻 Commander System برای ساخت و اجرای راحت commandها، مدیریت سادهتر پروژه، و زمانبندی اجرای وظایف (Scheduled Tasks).
📨 Queue و Job System برای ساخت صف و اجرای کارها در پسزمینه با زمانبندی دلخواه.
🧰 رابط Redis با امکانات لازم برای توسعههای وابسته به کش، صف و پیامرسانی آنی.
🧠 سیستم کشینگ با پشتیبانی از ۷ درایور مختلف برای ذخیرهسازی دادههای موقتی، بههمراه پیادهسازی Step Manager بر همین بستر.
🔁 کالکشنها برای کار سادهتر و منعطفتر با دادههای Iterable، مشابه کالکشنهای Laravel.
⚙️ Concurrency داخلی با امکان پردازش همزمان چند درخواست در پسزمینه بدون پیچیدگی اضافه.
🔒 ابزارهای امنیتی با پشتیبانی از سیستمهای رمزنگاری (Crypt) و هشینگ (Hash).
📢 Event Dispatcher برای تعریف و مدیریت رویدادها و واکنش به آنها.
🎛 کیبورد بیلدر توسعهیافته با استفاده ساده و انعطاف پذیری بالا.
🧩 موتور قالبسازی پیشرفته الهامگرفته از Blade برای ساخت پیامها بهصورت پویا و قابل نگهداری.
🌍 سیستم چندزبانه (Translation) برای ساخت رباتهایی با پشتیبانی از زبانهای مختلف.
✅ سیستم اعتبارسنجی با قوانین متنوع و امکان تعریف Ruleهای سفارشی.
🤖 پشتیبانی از چند ربات همزمان و امکان تعریف چند کانکشن و مدیریت آنها بهصورت مستقل.
یک مثال ساده برای ایجاد یک کامند بن با user_id به مدت 7 روز، با کنترل دسترسی و شرط ریپلای نشدن کامند:
Bot::onCommand("ban {id}", function (Request $request, $id) {
$request->banChatMember(
chat_id: chat()->id,
user_id: $id,
until_date: now()->addDays(7)->timestamp
);
})->can('administrator')->hasNotReply();برخی از ویژگیها با افزونهها و پکیجهای جانبی به LaraGram اضافه میشوند، به عنوان مثال:
⚡️ LaraGram Surge
پکیجی برای اجرای سریعتر رباتها با پشتیبانی از Swoole و OpenSwoole
🔧 LaraGram Installer
برای نصب و راهاندازی سریع و ساده پروژههای LaraGram
🛢 LaraGram MongoDB
درایور پایگاهداده MongoDB برای Eloquent ORM
📚 مستندات رسمی LaraGram نیز از طریق لینک زیر در دسترس است:
🔗 laraxgram.github.io
💬 گروه پرسش و پاسخ:
🔹 @LaraGramChat
📌 پروژه در گیتهاب:
♦️ LaraGram
🔖 #TelegramBot, #ربات, #تلگرام
👤 AmirHossein
💎 Channel: @DevelopixRobot
🔥12👍6❤5
فصل هفت - Services و Dependency Injection
بخش اول - سرویس کانتینر (قسمت دوم)
Contextual Binding (وابسته به موقعیت)
وقتی یک کلاس یا اینترفیس در یک زمینه (context) خاص نیاز به یک وابستگی (dependency) داره، از کدوم پیادهسازی (implementation) استفاده بشه. این ویژگی بهویژه زمانی مفیده که چند کلاس به یک اینترفیس نیاز دارن، اما هرکدوم باید پیادهسازی متفاوتی از اون اینترفیس رو دریافت کنن.
فرض کن دو کلاس داریم که هرکدوم به
حالا میخواهیم برای هر کلاس پیادهسازی متفاوتی از
با این تنظیمات:
در
در
Binding Primitives
گاهی اوقات ممکنه کلاسی نیاز به تزریق مقادیر سادهای مثل اعداد، رشتهها یا آرایهها داشته باشه. در این مواقع، میتونیم با استفاده از Contextual Binding، این مقادیر رو بهصورت دقیق برای هر کلاس یا متد مشخص کنیم.
فرض کن یک کلاس
برای تزریق مقدار
با این کار، هنگام ساخت نمونهای از
Binding Typed Variadics
میتونیم متدهایی با پارامترهای متغیر (variadic) و تایپشده داشته باشیم. این ویژگی به ما این امکان رو میده که وابستگیهایی با تعداد و نوع مشخص رو بهصورت دقیق تزریق کنیم.
فرض کن یک کلاس
برای تزریق فیلترها، میتونیم از
با این کار، هنگام ساخت نمونهای از
متدهای مرتبط با Contextual Binding
1- متد when
این متد مشخص میکنه که binding در چه کلاسی اعمال بشه.
2- متد needs
با این متد مشخص میکنیم که کدوم وابستگی نیاز به تزریق داره.
3- متد give
با این متد پیادهسازی مورد نظر رو برای وابستگی مشخص میکنیم.
4- متد giveConfig
یک متد راحت برای تزریق مقادیر تنظیماتی به کلاسهاست، بهویژه زمانی که کلاس نیاز به مقادیر ساده (primitive) داره. درواقع نسخهی مخصوص برای
5- متد giveTagged
برای تزریق تمام instanceهای مرتبط با یک tag به کلاسها یا متدها استفاده میشه. این زمانی مفیده که یک کلاس نیاز به مجموعهای از وابستگیها (مثلاً چندین سرویس، middleware، فیلتر و…) داره.
تمام instanceهای تگ شده با
نکته: درمورد تگها در قسمت بعدی توضیح کامل داده میشود.
در قسمتهای بعدی با سایر Injectionها آشنا خواهیم شد.
🔖 #Laravel, #PHP, #لاراول, #فصل_۷
👤 AmirHossein
💎 Channel: @DevelopixLaravel
بخش اول - سرویس کانتینر (قسمت دوم)
Contextual Binding (وابسته به موقعیت)
وقتی یک کلاس یا اینترفیس در یک زمینه (context) خاص نیاز به یک وابستگی (dependency) داره، از کدوم پیادهسازی (implementation) استفاده بشه. این ویژگی بهویژه زمانی مفیده که چند کلاس به یک اینترفیس نیاز دارن، اما هرکدوم باید پیادهسازی متفاوتی از اون اینترفیس رو دریافت کنن.
فرض کن دو کلاس داریم که هرکدوم به
LoggerInterface نیاز دارن:interface LoggerInterface {
public function log(string $message);
}
class FileLogger implements LoggerInterface {
public function log(string $message) {
}
}
class DatabaseLogger implements LoggerInterface {
public function log(string $message) {
}
}حالا میخواهیم برای هر کلاس پیادهسازی متفاوتی از
LoggerInterface تزریق کنیم:$this->app->when(OrderService::class)
->needs(LoggerInterface::class)
->give(FileLogger::class);
$this->app->when(UserService::class)
->needs(LoggerInterface::class)
->give(DatabaseLogger::class);
با این تنظیمات:
در
OrderService، کلاس FileLogger تزریق میشه.در
UserService، کلاس DatabaseLogger تزریق میشه.Binding Primitives
گاهی اوقات ممکنه کلاسی نیاز به تزریق مقادیر سادهای مثل اعداد، رشتهها یا آرایهها داشته باشه. در این مواقع، میتونیم با استفاده از Contextual Binding، این مقادیر رو بهصورت دقیق برای هر کلاس یا متد مشخص کنیم.
فرض کن یک کلاس
OrderProcessor داریم که به یک مقدار حداکثر تعداد سفارش نیاز داره:class OrderProcessor {
public function __construct(
protected int $maxOrders
) {}
public function process() {
}
}برای تزریق مقدار
maxOrders، میتونیم از needs() و give() استفاده کنیم:$this->app->when(OrderProcessor::class)
->needs('$maxOrders')
->give(100);
با این کار، هنگام ساخت نمونهای از
OrderProcessor، مقدار 100 بهعنوان maxOrders به سازنده تزریق میشه.Binding Typed Variadics
میتونیم متدهایی با پارامترهای متغیر (variadic) و تایپشده داشته باشیم. این ویژگی به ما این امکان رو میده که وابستگیهایی با تعداد و نوع مشخص رو بهصورت دقیق تزریق کنیم.
فرض کن یک کلاس
ReportGenerator داریم که به تعدادی فیلتر نیاز داره:class ReportGenerator {
public function __construct(
protected FilterInterface ...$filters
) {}
public function generate() {
}
}برای تزریق فیلترها، میتونیم از
needs() و give() استفاده کنیم:$this->app->when(ReportGenerator::class)
->needs(FilterInterface::class)
->give(function ($app) {
return [
new DateFilter(),
new StatusFilter(),
];
});
با این کار، هنگام ساخت نمونهای از
ReportGenerator، آرایهای از فیلترها بهعنوان وابستگیها تزریق میشه.متدهای مرتبط با Contextual Binding
1- متد when
این متد مشخص میکنه که binding در چه کلاسی اعمال بشه.
2- متد needs
با این متد مشخص میکنیم که کدوم وابستگی نیاز به تزریق داره.
3- متد give
با این متد پیادهسازی مورد نظر رو برای وابستگی مشخص میکنیم.
4- متد giveConfig
یک متد راحت برای تزریق مقادیر تنظیماتی به کلاسهاست، بهویژه زمانی که کلاس نیاز به مقادیر ساده (primitive) داره. درواقع نسخهی مخصوص برای
give() هست که با config() همخوانی داره.$this->app->when(PaymentGateway::class)
->needs('$apiKey')
->giveConfig('services.stripe.key');
5- متد giveTagged
برای تزریق تمام instanceهای مرتبط با یک tag به کلاسها یا متدها استفاده میشه. این زمانی مفیده که یک کلاس نیاز به مجموعهای از وابستگیها (مثلاً چندین سرویس، middleware، فیلتر و…) داره.
$this->app->tag([DateFilter::class, StatusFilter::class], 'filters');
$this->app->when(ReportGenerator::class)
->needs('$filters')
->giveTagged('filters');
تمام instanceهای تگ شده با
'filters' رو به $filters تزریق میکنه.نکته: درمورد تگها در قسمت بعدی توضیح کامل داده میشود.
در قسمتهای بعدی با سایر Injectionها آشنا خواهیم شد.
🔖 #Laravel, #PHP, #لاراول, #فصل_۷
👤 AmirHossein
💎 Channel: @DevelopixLaravel
👍3
فصل هفت - Services و Dependency Injection
بخش اول - سرویس کانتینر (قسمت سوم)
Contextual Attributes
این قابلیت امکان استفاده از PHP Attributes را در سازندهها و متدها فراهم میکند تا تزریق وابستگیها در Service Container به شکل سادهتر، شفافتر و قابل خواندنتری انجام شود.
این ویژگی در عمل جایگزین بسیاری از مواردی است که پیشتر تنها از طریق Contextual Binding (مانند when()->needs()->give()) امکانپذیر بود.
فرض کنید در یک کنترلر نیاز دارید از یک دیسک خاص برای ذخیرهسازی استفاده کنید. پیشتر لازم بود این موضوع را در Service Provider تعریف کنید، اما اکنون کافی است از Attribute استفاده نمایید:
در این مثال، لاراول بهطور خودکار دیسک با نام local را پیدا کرده و به متغیر $filesystem تزریق میکند.
لاراول چندین Attribute داخلی پرکاربرد ارائه کرده است، از جمله:
1- #[Storage('disk-name')]
برای تزریق یک دیسک ذخیرهسازی مشخص.
2- #[Config('config.key')]
برای تزریق مستقیم مقادیر پیکربندی.
3- #[DB('connection-name')]
برای تزریق یک اتصال پایگاهداده خاص.
4- #[Auth('guard-name')]
برای تزریق گارد احراز هویت مشخص.
5- #[Cache('store-name')]
برای تزریق یک cache store.
6- #[Log('channel')]
برای تزریق یک کانال لاگ.
7- #[RouteParameter('param')]
برای تزریق پارامترهای مسیر.
8- #[Tag('tag-name')]
برای تزریق مجموعهای از سرویسهای تَگشده.
9- #[CurrentUser]
برای تزریق مستقیم کاربر احراز هویتشدهٔ فعلی.
مثال:
در این مثال، Attribute CurrentUser باعث میشود که نمونهای از مدل User که معادل کاربر احراز هویتشدهٔ جاری است، بدون هیچ کد اضافهای تزریق گردد.
تعریف Attributes سفارشی
در برخی پروژهها ممکن است نیاز به منطق خاصی داشته باشید. در این حالت میتوانید Attribute سفارشی خود را تعریف کنید.
برای ساخت یک Attribute سفارشی یک کلاس با annotation #[Attribute] تعریف کنید و رابط ContextualAttribute را پیادهسازی کنید.
نمونه استفاده:
Tagging
در سرویس کانتینر، Tagging این امکان را فراهم میکند که چندین کلاس یا binding را تحت یک نام مشترک گروهبندی کنید و بعداً همه آنها را با یک فراخوانی ساده دریافت نمایید. این ویژگی بهویژه زمانی مفید است که چند پیادهسازی از یک interface یا کلاس وجود داشته باشد و بخواهید همه یا تعدادی از آنها را در یک زمان استفاده کنید.
در این مثال، دو کلاس مربوط به پیامرسانی با تگ 'messengers' علامتگذاری میشوند.
در جایی از اپ که نیاز به اجرای عملیات روی همهی پیامرسانها دارید:
تمام instanceهای مربوط به تگ 'messengers' را برمیگرداند.
در قسمتهای بعدی با سایر Injectionها آشنا خواهیم شد.
🔖 #Laravel, #PHP, #لاراول, #فصل_۷
👤 AmirHossein
💎 Channel: @DevelopixLaravel
بخش اول - سرویس کانتینر (قسمت سوم)
Contextual Attributes
این قابلیت امکان استفاده از PHP Attributes را در سازندهها و متدها فراهم میکند تا تزریق وابستگیها در Service Container به شکل سادهتر، شفافتر و قابل خواندنتری انجام شود.
این ویژگی در عمل جایگزین بسیاری از مواردی است که پیشتر تنها از طریق Contextual Binding (مانند when()->needs()->give()) امکانپذیر بود.
فرض کنید در یک کنترلر نیاز دارید از یک دیسک خاص برای ذخیرهسازی استفاده کنید. پیشتر لازم بود این موضوع را در Service Provider تعریف کنید، اما اکنون کافی است از Attribute استفاده نمایید:
use Illuminate\Container\Attributes\Storage;
use Illuminate\Contracts\Filesystem\Filesystem;
class PhotoController extends Controller
{
public function __construct(
#[Storage('local')] protected Filesystem $filesystem
) {}
}
در این مثال، لاراول بهطور خودکار دیسک با نام local را پیدا کرده و به متغیر $filesystem تزریق میکند.
لاراول چندین Attribute داخلی پرکاربرد ارائه کرده است، از جمله:
1- #[Storage('disk-name')]
برای تزریق یک دیسک ذخیرهسازی مشخص.
2- #[Config('config.key')]
برای تزریق مستقیم مقادیر پیکربندی.
3- #[DB('connection-name')]
برای تزریق یک اتصال پایگاهداده خاص.
4- #[Auth('guard-name')]
برای تزریق گارد احراز هویت مشخص.
5- #[Cache('store-name')]
برای تزریق یک cache store.
6- #[Log('channel')]
برای تزریق یک کانال لاگ.
7- #[RouteParameter('param')]
برای تزریق پارامترهای مسیر.
8- #[Tag('tag-name')]
برای تزریق مجموعهای از سرویسهای تَگشده.
9- #[CurrentUser]
برای تزریق مستقیم کاربر احراز هویتشدهٔ فعلی.
مثال:
Route::get('/user', function (#[CurrentUser] User $user) {
return $user;
})->middleware('auth');در این مثال، Attribute CurrentUser باعث میشود که نمونهای از مدل User که معادل کاربر احراز هویتشدهٔ جاری است، بدون هیچ کد اضافهای تزریق گردد.
تعریف Attributes سفارشی
در برخی پروژهها ممکن است نیاز به منطق خاصی داشته باشید. در این حالت میتوانید Attribute سفارشی خود را تعریف کنید.
برای ساخت یک Attribute سفارشی یک کلاس با annotation #[Attribute] تعریف کنید و رابط ContextualAttribute را پیادهسازی کنید.
namespace App\Attributes;
use Attribute;
use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\Container\ContextualAttribute;
#[Attribute(Attribute::TARGET_PARAMETER)]
class AppConfig implements ContextualAttribute
{
public function __construct(
public string $key,
public mixed $default = null
) {}
public static function resolve(self $attribute, Container $container)
{
return $container->make('config')->get($attribute->key, $attribute->default);
}
}
نمونه استفاده:
public function __construct(
#[AppConfig('app.name')] protected string $appName
) {}
Tagging
در سرویس کانتینر، Tagging این امکان را فراهم میکند که چندین کلاس یا binding را تحت یک نام مشترک گروهبندی کنید و بعداً همه آنها را با یک فراخوانی ساده دریافت نمایید. این ویژگی بهویژه زمانی مفید است که چند پیادهسازی از یک interface یا کلاس وجود داشته باشد و بخواهید همه یا تعدادی از آنها را در یک زمان استفاده کنید.
$this->app->bind(SlackMessenger::class, function ($app) {
return new SlackMessenger(/* ... */);
});
$this->app->bind(TwilioMessenger::class, function ($app) {
return new TwilioMessenger(/* ... */);
});
$this->app->tag([SlackMessenger::class, TwilioMessenger::class], 'messengers');در این مثال، دو کلاس مربوط به پیامرسانی با تگ 'messengers' علامتگذاری میشوند.
در جایی از اپ که نیاز به اجرای عملیات روی همهی پیامرسانها دارید:
$messengers = $this->app->tagged('messengers');تمام instanceهای مربوط به تگ 'messengers' را برمیگرداند.
در قسمتهای بعدی با سایر Injectionها آشنا خواهیم شد.
🔖 #Laravel, #PHP, #لاراول, #فصل_۷
👤 AmirHossein
💎 Channel: @DevelopixLaravel
🔥4
فصل هفت - Services و Dependency Injection
بخش اول - سرویس کانتینر (قسمت چهارم)
Extending Bindings
گاهی اوقات شما میخواهید یک سرویس را که قبلاً در Service Container ثبت (bind) شده است، تغییر دهید یا چیزی به آن اضافه کنید. به جای اینکه کل آن را از نو بنویسید، میتوانید از متد extend استفاده کنید. این متد به شما اجازه میدهد تا سرویس اصلی را دریافت کرده، آن را تغییر دهید و سپس نسخه تغییریافته را برگردانید.
Resolving
متد make برای دریافت (resolve) یک نمونه از یک کلاس یا اینترفیس از Service Container استفاده میشود. هر زمان که شما به یک نمونه از کلاسی که در کانتینر ثبت شده نیاز دارید، میتوانید از این متد استفاده کنید.
فرض کنید یک کلاس برای مدیریت کاربران دارید.
تزریق خودکار (Automatic Injection)
لاراول به قدری هوشمند است که میتواند وابستگیهای (dependencies) یک کلاس را به صورت خودکار از روی Type-Hint در سازنده (constructor) یا متدهای آن تشخیص داده و آنها را تزریق کند. این یعنی در اکثر موارد شما نیازی به استفاده از متد make ندارید.
به جای اینکه در مثال قبل به صورت دستی UserManager را make کنیم، میتوانیم آن را به سازنده کنترلر تزریق کنیم.
در این حالت، لاراول به صورت خودکار UserManager را برای شما make کرده و به عنوان آرگومان به سازنده پاس میدهد.
فراخوانی متد و تزریق وابستگی (Method Invocation & Injection)
علاوه بر تزریق وابستگی در سازنده، لاراول میتواند وابستگیها را مستقیماً به متدهای کلاس نیز تزریق کند. این قابلیت به خصوص در کنترلرها بسیار مفید است، زمانی که یک وابستگی فقط در یک متد خاص مورد نیاز است.
فرض کنید یک سرویس برای اعتبارسنجی ورودیها دارید که فقط در متد store از آن استفاده میکنید.
رویدادهای کانتینر (Container Events)
Service Container هنگام دریافت (resolve) هر آبجکت، یک رویداد (event) را فراخوانی میکند. شما میتوانید به این رویداد گوش دهید تا هر زمان که یک آبجکت خاص از کانتینر گرفته شد، یک عملیات دلخواه را انجام دهید. این قابلیت برای دیباگ کردن یا اضافه کردن منطقهای خاص بسیار کاربردی است.
فرض کنید میخواهیم هر زمان که یک آبجکت از کلاس MyService ساخته شد، یک پیام در لاگ ثبت کنیم.
همچنین میتوانید به تمام resolving ها گوش دهید
Rebinding
گاهی اوقات شما نیاز دارید تا بعد از اینکه یک سرویس ساخته و استفاده شد، اگر دوباره bind شد، یک عملیات خاص را انجام دهید. برای مثال، فرض کنید یک سرویس دارید که به اطلاعات کاربر لاگین شده وابسته است. وقتی کاربر لاگ اوت کرده و یک کاربر دیگر لاگین میکند، شما نیاز دارید که این سرویس با اطلاعات کاربر جدید بهروزرسانی شود.
🔖 #Laravel, #PHP, #لاراول, #فصل_۷
👤 AmirHossein
💎 Channel: @DevelopixLaravel
بخش اول - سرویس کانتینر (قسمت چهارم)
Extending Bindings
گاهی اوقات شما میخواهید یک سرویس را که قبلاً در Service Container ثبت (bind) شده است، تغییر دهید یا چیزی به آن اضافه کنید. به جای اینکه کل آن را از نو بنویسید، میتوانید از متد extend استفاده کنید. این متد به شما اجازه میدهد تا سرویس اصلی را دریافت کرده، آن را تغییر دهید و سپس نسخه تغییریافته را برگردانید.
$this->app->singleton(ApiClient::class, function () {
return new ApiClient();
});
$this->app->extend(ApiClient::class, function ($client, $app) {
$client->config['timeout'] = 10;
return $client;
});Resolving
متد make برای دریافت (resolve) یک نمونه از یک کلاس یا اینترفیس از Service Container استفاده میشود. هر زمان که شما به یک نمونه از کلاسی که در کانتینر ثبت شده نیاز دارید، میتوانید از این متد استفاده کنید.
فرض کنید یک کلاس برای مدیریت کاربران دارید.
$this->app->bind('UserManager', function ($app) {
return new UserManager();
});
// Resolving
$userManager = app()->make('UserManager');تزریق خودکار (Automatic Injection)
لاراول به قدری هوشمند است که میتواند وابستگیهای (dependencies) یک کلاس را به صورت خودکار از روی Type-Hint در سازنده (constructor) یا متدهای آن تشخیص داده و آنها را تزریق کند. این یعنی در اکثر موارد شما نیازی به استفاده از متد make ندارید.
به جای اینکه در مثال قبل به صورت دستی UserManager را make کنیم، میتوانیم آن را به سازنده کنترلر تزریق کنیم.
public function __construct(UserManager $userManager)
{
$this->userManager = $userManager;
}
در این حالت، لاراول به صورت خودکار UserManager را برای شما make کرده و به عنوان آرگومان به سازنده پاس میدهد.
فراخوانی متد و تزریق وابستگی (Method Invocation & Injection)
علاوه بر تزریق وابستگی در سازنده، لاراول میتواند وابستگیها را مستقیماً به متدهای کلاس نیز تزریق کند. این قابلیت به خصوص در کنترلرها بسیار مفید است، زمانی که یک وابستگی فقط در یک متد خاص مورد نیاز است.
فرض کنید یک سرویس برای اعتبارسنجی ورودیها دارید که فقط در متد store از آن استفاده میکنید.
public function store(Request $request, UserValidator $validator)
{
// Request and UserValidator injected.
}
رویدادهای کانتینر (Container Events)
Service Container هنگام دریافت (resolve) هر آبجکت، یک رویداد (event) را فراخوانی میکند. شما میتوانید به این رویداد گوش دهید تا هر زمان که یک آبجکت خاص از کانتینر گرفته شد، یک عملیات دلخواه را انجام دهید. این قابلیت برای دیباگ کردن یا اضافه کردن منطقهای خاص بسیار کاربردی است.
فرض کنید میخواهیم هر زمان که یک آبجکت از کلاس MyService ساخته شد، یک پیام در لاگ ثبت کنیم.
$this->app->resolving(MyService::class, function ($service, $app) {
Log::info('MyService resolved!');
});همچنین میتوانید به تمام resolving ها گوش دهید
$this->app->resolving(function ($object, $app) {
Log::info('Resolved: ' . get_class($object));
});Rebinding
گاهی اوقات شما نیاز دارید تا بعد از اینکه یک سرویس ساخته و استفاده شد، اگر دوباره bind شد، یک عملیات خاص را انجام دهید. برای مثال، فرض کنید یک سرویس دارید که به اطلاعات کاربر لاگین شده وابسته است. وقتی کاربر لاگ اوت کرده و یک کاربر دیگر لاگین میکند، شما نیاز دارید که این سرویس با اطلاعات کاربر جدید بهروزرسانی شود.
$this->app->rebinding('UserData', function ($app, $instance) {
// logic
});🔖 #Laravel, #PHP, #لاراول, #فصل_۷
👤 AmirHossein
💎 Channel: @DevelopixLaravel
❤3
Forwarded from | Erfan's Notes |
حدودا یک ماه از ریلیز شدن نسخه 3.0 وباسمبلی (WASM) میگذره و الان فرصت کردم درموردش بخونم، تغییرات مهمی که داشته رو پایین مینویسم.
💠 پشتیبانی از Address Space های 64 بیت
تا قبل از این نسخه، وباسمبلی محدود به آدرسهای i32 بود و نمیتونست بیشتر از 4GB رو آدرسدهی کنه، ولی پشتیبانی از i64 اضافه شده و این محدودیت عملا بینهایت شده، هرچند که همچنان مرورگرها حداکثر اجازه allocate کردن 16GB رو میدن.
💠 پشتیبانی از Memory های چندگانه
تا قبل از این نسخه، هر ماژول وباسمبلی فقط محدود به یک Memory بود و برای تفکیک باید ماژولها Split میشدند، ولی در این نسخه قابلیت داشتن Memory های متعدد برای یک ماژول اضافه شده.
💠 پشتیبانی از Garbage Collection
در این نسخه یک افزونه با عنوان wasm-gc اضافه شده که در سطوح پایین میتونه مموری رو بهصورت خودکار مدیریت کنه، کامپایلرها میتونند Struct ها و آرایهها و بعضی Integer ها رو به صورت تگ شده تعریف کنند و خود wasm وظیفه allocation و lifetime شون رو برعهده بگیره.
💠 پشتیبانی از Tail Call ها
قابلیت Tail Call به وباسمبلی اضافه شده، این ویژگی از زبانهای فانکشنال الگو گرفته، به این معنی که فانکشنها میتونند در آخرین اکشنشون یک فانکشن دیگهای رو کال کنند بدون اینکه فضایی از Stack رو اشغال کنند، این موضوع در کال های Recursive اهمیت زیادی داره.
💠 پشتیبانی از Exception ها
پشتیبانی از Exception های try و catch در وباسمبلی اضافه شده، تا قبل از این برای چنین کاری باید از JS استفاده میشد.
💠 پشتیبانی از String های جاوا اسکریپت
قابلیت رد و بدل کردن مستقیم String های جاوا اسکریپت بدون نیاز به تبدیل دو طرفه اضافه شده، میتونید مقادر String رو به صورت مستقیم بهعنوان externref پاس بدید و سمت wasm تغییرات لازم رو روش اعمال کنید و سمت JS تحویل بگیرید.
و البته کلی قابلیت دیگه که اگر دوست داشتید میتونید اینجا بخونید.
تا قبل از این نسخه، وباسمبلی محدود به آدرسهای i32 بود و نمیتونست بیشتر از 4GB رو آدرسدهی کنه، ولی پشتیبانی از i64 اضافه شده و این محدودیت عملا بینهایت شده، هرچند که همچنان مرورگرها حداکثر اجازه allocate کردن 16GB رو میدن.
تا قبل از این نسخه، هر ماژول وباسمبلی فقط محدود به یک Memory بود و برای تفکیک باید ماژولها Split میشدند، ولی در این نسخه قابلیت داشتن Memory های متعدد برای یک ماژول اضافه شده.
در این نسخه یک افزونه با عنوان wasm-gc اضافه شده که در سطوح پایین میتونه مموری رو بهصورت خودکار مدیریت کنه، کامپایلرها میتونند Struct ها و آرایهها و بعضی Integer ها رو به صورت تگ شده تعریف کنند و خود wasm وظیفه allocation و lifetime شون رو برعهده بگیره.
قابلیت Tail Call به وباسمبلی اضافه شده، این ویژگی از زبانهای فانکشنال الگو گرفته، به این معنی که فانکشنها میتونند در آخرین اکشنشون یک فانکشن دیگهای رو کال کنند بدون اینکه فضایی از Stack رو اشغال کنند، این موضوع در کال های Recursive اهمیت زیادی داره.
پشتیبانی از Exception های try و catch در وباسمبلی اضافه شده، تا قبل از این برای چنین کاری باید از JS استفاده میشد.
قابلیت رد و بدل کردن مستقیم String های جاوا اسکریپت بدون نیاز به تبدیل دو طرفه اضافه شده، میتونید مقادر String رو به صورت مستقیم بهعنوان externref پاس بدید و سمت wasm تغییرات لازم رو روش اعمال کنید و سمت JS تحویل بگیرید.
و البته کلی قابلیت دیگه که اگر دوست داشتید میتونید اینجا بخونید.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
🚀 جلوگیری از مشکل N+1 در Eloquent با eager loading و withCount
خیلی از باگها و افت کارایی در اپهای لاراول از درخواستهای اضافی دیتابیس (مشکل N+1) ناشی میشه. با استفاده از eager loading و withCount میتونید هم تعداد کوئریها رو کم کنید و هم اطلاعاتی مثل تعداد ارتباطها رو بدون کوئریهای اضافی بگیرید. 💡
مثال عملی: فرض کنید مدل Post و رابطه comments دارید. اگر بدون eager loading لیست پستها رو رندر کنید، هر بار که به comments دسترسی پیدا میکنید یک کوئری جدید اجرا میشه — N+1.
راه ساده و بهینه:
نکات عملی و بهترین روشها:
- وقتی فقط تعداد رابطه لازم دارید از withCount استفاده کنید تا دادههای اضافی لود نشه. 🔍
- برای بارگذاری شرطی (مثلاً فقط کامنتهای تاییدشده) از closure در with استفاده کنید:
- برای دیتاستهای بزرگ از chunk یا cursor استفاده کنید تا حافظه پر نشه. ⚠️
اشتباه رایج: افزودن eager loading برای روابطی که هرگز استفاده نمیشن — فقط روابط لازم را بارگذاری کنید تا از overhead جلوگیری کنید.
با این روش ساده، کوئریها بهمراتب کمتر و صفحهها سریعتر بارگذاری میشن. امتحان کنید و بازخورد یا تجربهتون رو به اشتراک بذارید. 🙌
Laravel Docs — Eloquent Relationships
🔖 #Laravel #PHP #لاراول #Laravel #Eloquent #Performance #EagerLoading
👤 Developix
💎 Channel: @DevelopixLaravel
خیلی از باگها و افت کارایی در اپهای لاراول از درخواستهای اضافی دیتابیس (مشکل N+1) ناشی میشه. با استفاده از eager loading و withCount میتونید هم تعداد کوئریها رو کم کنید و هم اطلاعاتی مثل تعداد ارتباطها رو بدون کوئریهای اضافی بگیرید. 💡
مثال عملی: فرض کنید مدل Post و رابطه comments دارید. اگر بدون eager loading لیست پستها رو رندر کنید، هر بار که به comments دسترسی پیدا میکنید یک کوئری جدید اجرا میشه — N+1.
راه ساده و بهینه:
$posts = Post::with('comments')->get();
foreach ($posts as $post) {
// دسترسی به رابطه بدون کوئری اضافی
echo $post->comments->count();
}
// اگر فقط تعداد نیاز دارید، از withCount استفاده کنید:
$posts = Post::withCount('comments')->get();
foreach ($posts as $post) {
echo $post->comments_count; // مقدار از همان کوئری اصلی
}
نکات عملی و بهترین روشها:
- وقتی فقط تعداد رابطه لازم دارید از withCount استفاده کنید تا دادههای اضافی لود نشه. 🔍
- برای بارگذاری شرطی (مثلاً فقط کامنتهای تاییدشده) از closure در with استفاده کنید:
with(['comments' => fn($q) => $q->where('approved', 1)]).- برای دیتاستهای بزرگ از chunk یا cursor استفاده کنید تا حافظه پر نشه. ⚠️
اشتباه رایج: افزودن eager loading برای روابطی که هرگز استفاده نمیشن — فقط روابط لازم را بارگذاری کنید تا از overhead جلوگیری کنید.
با این روش ساده، کوئریها بهمراتب کمتر و صفحهها سریعتر بارگذاری میشن. امتحان کنید و بازخورد یا تجربهتون رو به اشتراک بذارید. 🙌
Laravel Docs — Eloquent Relationships
🔖 #Laravel #PHP #لاراول #Laravel #Eloquent #Performance #EagerLoading
👤 Developix
💎 Channel: @DevelopixLaravel
❤9👍1
در کد بالا در متد index برای صفحهبندی لیست پستها از load کردن تمام رکوردها در Memory و سپس جمعآوری آنها با
🔖 #Laravel #PHP #لاراول
👤 Developix
💎 Channel: @DevelopixLaravel
collect() و forPage() استفاده شده که هم مصرف مموری را بالا میبرد و هم در صورت زیاد بودن تعداد رکوردها باعث افت کارایی میشود. در عوض میتوانید مستقیماً روی QueryBuilder از paginate() استفاده کنید تا صفحهبندی در سطح دیتابیس انجام شود و فقط رکوردهای همان صفحه Query شوند. بهنظر شما در پروژههای واقعی با دیتاست بزرگ، استفاده از کدام روش منطقیتر و مقیاسپذیرتر است؟🔖 #Laravel #PHP #لاراول
👤 Developix
💎 Channel: @DevelopixLaravel
👍10❤1
Forwarded from | کانال توسعهدهندگان PHP |
عملگر پایپ (Pipe Operator) در PHP
عملگر پایپ (
ساختار کلی عملگر پایپ:
در این ساختار:
- مقدار سمت چپ (value) محاسبه شده و سپس به تابع سمت راست (callable) ارسال میشود، و نتیجهٔ آن تابع، خروجی نهایی عبارت خواهد بود.
- تابع سمت راست باید فقط یک پارامتر لازم داشته باشد؛ زیرا همان یک پارامتر از پایپ دریافت میشود.
- نماد (
چند مثال:
پیش از اضافه شدن پایپ تواع پشت سر هم نوشته میشد:
یا از متغیرهای موقتی استفاده میشد:
اما با استفاده از پایپ می توان ساختار تمیزتر و قابل فهمتری را داشته باشیم:
ترتیب اجرای عملیات کاملا روشن و خوانا است:
ابتدا
ترکیب توابع استاندارد با Closure
از آنجایی که گفته شد توابع تنها یک متد داشته باشند، برای توابع با چند متد میتوان از کلوژرها استفاده کرد.
در این مثال
عملیات روی آرایه
چنین زنجیرهای بدون استفاده از پایپ، بهطور معمول شامل متغیرهای واسطه یا تو در تویی توابع خواهد بود؛ اما با استفاده از پایپ، تمامی مراحل به صورت خوانا پشت سر هم نوشته شدهاند.
مزایای استفاده از عملگر پایپ:
- جریان داده از بالا به پایین قابل مشاهده است و عملیات به شکلی خطی بیان میشوند.
- بهجای اینکه توابع داخل یکدیگر قرار بگیرند، هر تابع بهطور مستقل در یک مرحله اجرا میشود.
- نیازی به ایجاد متغیر برای ذخیرهٔ نتیجهٔ هر مرحله نیست.
محدودیتها و نکات مهم:
- توابعی که بیش از یک پارامتر ضروری دارند، مستقیماً با پایپ قابل استفاده نیستند.
- توابعی که پارامترشان با ارجاع (By Reference) دریافت میشود قابل استفاده نیستند (مانند
- اگر تابعی مقدار بازگشتی نداشته باشد (void)، نتیجهٔ عملیات null خواهد بود و استفادهٔ آن در میانهٔ زنجیره صحیح نیست.
به طور کلی عملگر پایپ در PHP 8.5 امکان نگارش کدی خواناتر، مرحلهای و ساختیافته را فراهم میکند.
این عملگر با ارسال خروجی هر مرحله به مرحلهٔ بعد، جریان داده را سادهسازی میکند و برای پردازش رشتهها، آرایهها و دادههای میانمرحلهای بسیار مناسب است.
در مقابل، محدودیتهایی نظیر نیاز به تکپارامتری بودن تابع یا عدم پشتیبانی از توابع دارای ارجاع وجود دارد که باید در استفادهٔ روزمره مورد توجه قرار گیرد.
🔖 #PHP #پی_اچ_پی
👤 AmirHossein
💎 Channel: @DevelopixPHP
عملگر پایپ (
|> ) از PHP 8.5 با هدف سادهسازی جریان داده و افزایش خوانایی کد اضافه شده است. این عملگر امکان میدهد خروجی یک عبارت بهعنوان ورودی تابع بعدی استفاده شود؛ بدون آنکه نیاز به تو در تو کردن فراخوانیها یا استفاده از متغیرهای موقتی باشد.ساختار کلی عملگر پایپ:
$value |> callable;
در این ساختار:
- مقدار سمت چپ (value) محاسبه شده و سپس به تابع سمت راست (callable) ارسال میشود، و نتیجهٔ آن تابع، خروجی نهایی عبارت خواهد بود.
- تابع سمت راست باید فقط یک پارامتر لازم داشته باشد؛ زیرا همان یک پارامتر از پایپ دریافت میشود.
- نماد (
... ) پس از نام تابع، بیانگر این است که پارامتر آن تابع از طریق عملگر پایپ وارد میشود.چند مثال:
پیش از اضافه شدن پایپ تواع پشت سر هم نوشته میشد:
$result = strtolower(trim($title));
یا از متغیرهای موقتی استفاده میشد:
$trim = trim($title);
$result = strtolower($trim);
اما با استفاده از پایپ می توان ساختار تمیزتر و قابل فهمتری را داشته باشیم:
$result = $title
|> trim(...)
|> strtolower(...);
ترتیب اجرای عملیات کاملا روشن و خوانا است:
ابتدا
trim، سپس strtolower.ترکیب توابع استاندارد با Closure
$slug = $title
|> trim(...)
|> (fn($s) => str_replace(' ', '-', $s))
|> strtolower(...);
از آنجایی که گفته شد توابع تنها یک متد داشته باشند، برای توابع با چند متد میتوان از کلوژرها استفاده کرد.
در این مثال
str_replace نیاز به پارامترهای بیشتری است به همین دلیل از arrow-function استفاده شده.عملیات روی آرایه
$clean = $items
|> array_map(fn($x) => trim($x), ...)
|> array_filter(fn($x) => $x !== '', ...)
|> array_values(...);
چنین زنجیرهای بدون استفاده از پایپ، بهطور معمول شامل متغیرهای واسطه یا تو در تویی توابع خواهد بود؛ اما با استفاده از پایپ، تمامی مراحل به صورت خوانا پشت سر هم نوشته شدهاند.
مزایای استفاده از عملگر پایپ:
- جریان داده از بالا به پایین قابل مشاهده است و عملیات به شکلی خطی بیان میشوند.
- بهجای اینکه توابع داخل یکدیگر قرار بگیرند، هر تابع بهطور مستقل در یک مرحله اجرا میشود.
- نیازی به ایجاد متغیر برای ذخیرهٔ نتیجهٔ هر مرحله نیست.
محدودیتها و نکات مهم:
- توابعی که بیش از یک پارامتر ضروری دارند، مستقیماً با پایپ قابل استفاده نیستند.
"hello world" |> explode(' ', ...); // ERROR
"hello world" |> (fn($v) => explode(' ', $v)); // correct- توابعی که پارامترشان با ارجاع (By Reference) دریافت میشود قابل استفاده نیستند (مانند
array_pop یا sort)- اگر تابعی مقدار بازگشتی نداشته باشد (void)، نتیجهٔ عملیات null خواهد بود و استفادهٔ آن در میانهٔ زنجیره صحیح نیست.
به طور کلی عملگر پایپ در PHP 8.5 امکان نگارش کدی خواناتر، مرحلهای و ساختیافته را فراهم میکند.
این عملگر با ارسال خروجی هر مرحله به مرحلهٔ بعد، جریان داده را سادهسازی میکند و برای پردازش رشتهها، آرایهها و دادههای میانمرحلهای بسیار مناسب است.
در مقابل، محدودیتهایی نظیر نیاز به تکپارامتری بودن تابع یا عدم پشتیبانی از توابع دارای ارجاع وجود دارد که باید در استفادهٔ روزمره مورد توجه قرار گیرد.
🔖 #PHP #پی_اچ_پی
👤 AmirHossein
💎 Channel: @DevelopixPHP
❤11👍1👎1
🔎 سوال برای توسعهدهندگان Laravel / PHP
خروجی اجرای این کد PHP چیست؟
به تفاوت بین type casting و type juggling در PHP و تاثیر آن روی کلیدهای آرایه دقت کنید.
خروجی کامل تابع
🔖 #Laravel #PHP #لاراول
👤 Developix
💎 Channel: @DevelopixLaravel
خروجی اجرای این کد PHP چیست؟
به تفاوت بین type casting و type juggling در PHP و تاثیر آن روی کلیدهای آرایه دقت کنید.
$data = [
"01" => "first",
1 => "second",
true => "third",
"1" => "fourth",
];
var_dump($data);
خروجی کامل تابع
var_dump را بنویسید (ساختار آرایه و مقادیر آن).🔖 #Laravel #PHP #لاراول
👤 Developix
💎 Channel: @DevelopixLaravel
👍2
پکیج امروز: Spatie Laravel Ray 🔍
یک دیباگر زنده که لاگها و query و jobها را به اپ دسکتاپ Ray میفرستد؛ مخصوص وقتی که var_dump و dd کل صفحه را منفجر میکنند 🙂
به چه درد میخورد؟
• دیدن لاگها، queryها، exceptionها بهصورت لحظهای
• گروهبندی، فیلتر و استایلدهی به لاگها
• دیباگ queue job، event، mail و حتی cache
نصب
نمونه استفاده در کنترلر
برای پروژههای بزرگ لاراول که لاگخوانی در فایل سخت میشود، Ray جریان دیباگ را خیلی تمیز و قابلردیابی میکند. ارزش دارد یکبار روی یک feature واقعی تست شود و تنظیماتش را مطابق نیاز تیمتان شخصیسازی کنید 🚀
لینکها:
GitHub
Docs
🔖 #Laravel #PHP #لاراول #Laravel #PHP #Spatie #Debugging #Laravel_Ray
👤 Developix
💎 Channel: @DevelopixLaravel
یک دیباگر زنده که لاگها و query و jobها را به اپ دسکتاپ Ray میفرستد؛ مخصوص وقتی که var_dump و dd کل صفحه را منفجر میکنند 🙂
به چه درد میخورد؟
• دیدن لاگها، queryها، exceptionها بهصورت لحظهای
• گروهبندی، فیلتر و استایلدهی به لاگها
• دیباگ queue job، event، mail و حتی cache
نصب
composer require spatie/laravel-ray --dev
php artisan vendor:publish \
--provider\="Spatie\LaravelRay\RayServiceProvider"
نمونه استفاده در کنترلر
use Spatie\LaravelRay\Ray;
public function index()
{
ray('Load products');
$products = Product::with('category')
->where('active', true)
->get();
ray($products)->blue();
return view('products.index', compact('products'));
}
برای پروژههای بزرگ لاراول که لاگخوانی در فایل سخت میشود، Ray جریان دیباگ را خیلی تمیز و قابلردیابی میکند. ارزش دارد یکبار روی یک feature واقعی تست شود و تنظیماتش را مطابق نیاز تیمتان شخصیسازی کنید 🚀
لینکها:
GitHub
Docs
🔖 #Laravel #PHP #لاراول #Laravel #PHP #Spatie #Debugging #Laravel_Ray
👤 Developix
💎 Channel: @DevelopixLaravel