مقدمه
در بخش اول مقاله یک سری مفاهیم و مسائل که در لاراول به کار رفته است را بیان کردیم، حال قصد داریم به صورت کامل و کاربردی تر مسائل مهمی را در بخش دوم مقاله برایتان بیان کنم که میتواند کمک بسیاری خوبی به شما بکند، لطفا تا انتهای مقاله حتما پیش بروید که مطالب مفیدی دستگیرتان خواهد شد.
۱۵- ماکرو (macro) چیست و چه کاربردی دارد ؟
ما توسط ماکرو ها میتواینم از طریق یک کلاس داخلی در لاراول مثل Request
و Response
یک متد دلخواه را در آن اضافه کنیم و توسعه دهیم. این عمل اضافه کردن در هنگام اجرای اپ به صورت Run-Time
رخ می دهد. با ماکرو، کلاسهایی رو میشه توسعه داد که از یک Trait
به اسم Macroable
استفاده میکنن برای مثال به کد زیر دقت کنید :
نمونه اول :
Request::macro('sayHello', function($argument) {
return 'Hello';
});
نمونه دوم :
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
Collection::macro('toUpper', function () {
return $this->map(function ($value) {
return Str::upper($value);
});
});
با تعریف قطع کد بالا به همین راحتی می توانید یک ماکرو برای خود تعریف کنید ولی فقط نکته اینجاس که باید درون متد boot
در داخل service provider
آن را تعریف کنید. حالا به شکل زیر میتوانید آن را فراخوانی کنید :
$collection = collect(['first', 'second']);
$upper = $collection->toUpper();
----------------------------------------------------------------------
echo Request::sayHello(); // Hello
۱۶- Eager Loading چیست ؟
من در دو بخش مشکل و راه حل مفهوم Eager Loading
را توضیح میدهم که خوب متوجه شوید چرا از این روش استفاده می شود.
خب مشکل چیست ؟ این تکنیک برای حل کردن مشکل N + 1
ساخته شده، شما فرض کنید که ما در داخل دیتابیس دوتا موجودیت به نام های books
و authors
داریم خب بین آنها یک رابطه به وجود آمده است که هر نویسنده کتابی دارد و هر کتاب متعلق به یک نویسنده می باشد، خب مشکل اینجاس که وقتی ما میخواهیم به عنوان مثال کوئری بنویسم که کتاب های که متعلق به نویسنده های مختلف است را برایمان بیاورد، به کد زیر دقت کنید :
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Book extends Model
{
/**
* Get the author that wrote the book.
*/
public function author()
{
return $this->belongsTo(Author::class);
}
}
خب حالا با قطع کد زیر تمام کتاب ها را بر میگردانیم :
use App\Models\Book;
$books = Book::all();
foreach ($books as $book) {
echo $book->author->name;
}
همانطور که در بالا گفتیم کوئری ما N
کتاب را برگرداند، ولی باعث به وجود امدن مشکل N+1
شده، به زبان ساده تر برای لود کردن کتاب های هر نویسنده باید یک کوئری جدا بزند خروجی به شکل زیر می شود :
select * from books where author_id = 1
select * from books where author_id = 2
select * from books where author_id = 3
select * from books where author_id = 4
خب راه حل چیست ؟ با Eager Loading
میتونیم به جای N + 1
کوئری، فقط ۲ تا کوئری داشته باشیم. یک کوئری برای خواندن کاربرها و یک کوئری برای خواندن کتابشون، این تکنیک توسط متدهای with
و load
توی لاراول قابل اجرا هست به کد زیر دقت کنید :
$authors = Author::all()->load('book');
foreach ($authors as $author) {
echo $author->book->name;
}
خروجی کد SQL
به شکل زیر می شود:
select * from authors
...
select * from books where author_id in (1, 2, 3, 4)
نکته آخر اینکه فرق بین متد with
و load
را هم برایتان بگویم،هر دو متد برای پیادهسازی تکنیک Eager Loading
معرفی شدن هنگامی که میخواین به صورت پیوسته (کوئری دوم بلافاصله بعد از کوئری اول زده میشه) کوئری شما اجرا شود از متد with
استفاده کنید به مثال زیر دقت کنید :
$users = Author::with('book')->get();
ولی هنگامی که میخواهید یک عملیاتی را انجام دهید و بعد از اجرای کوئری اول،کوئری دوم را در مکان و زمان دیگر اجرا کنید از متد load
می توانید استفاده کنید که به اون Lazy Eager Loading
هم گفته میشه، به کد زیر دقت کنید :
$authors = Author::all();
// Somewhere in the app
if ($condition) {
$authors->load('books');
}
۱۷- کاربرد Route Model Binding چیست ؟
در لاراول حتما بارها با این مفهوم برخورد کرده اید که در متدی یا constructor
های درون کلاس یک مدلی پاس داده شده است.به عنوان مثال در روت های که تعریف می کنید حتما شده است که $id
یک کاربر را پاس داده اید ولی در قسمت ورودی تابع به شما مدلی از User
بر می گردد :
Route::get('user/{id}', '[email protected]');
متدی هم که در این کنترلر وجود دارد به نام show
به شکل زیر است :
public function show($id)
{
$user = User::findOrFail($id);
dd($user);
}
این عمل خواندن یک شناسه به عنوان پارامتر و اجرا کردن از دیتابیس به صورت خودکار توسط لاراول انجام میشه به این تکنیک میگن Route Model Binding
.
شکل Bind
شده مدل مورد نظر به این صورت تغییر می کند :
public function show(User $user)
{
dd($user);
}
۱۸- Throttle چیست و چطوری اون رو فعال کنیم؟
Throttle
یا به اون Rate Limit
هم گفته می شود که با آن میتوانیم تعیین کنیم در یک بازه زمانی مشخص چه تعداد درخوست مجاز است به سمت برنامه بیاید.
مثلاً یک فرم داریم و کاربر باید بتونه توی 1 دقیقه فقط ۱۰ بار این فرم رو ثبت کنه و اگه بیشتر از ۱۰ بار شد، باید با خطای HTTP 429 (Too Many Requests)
مواجه بشه. یعنی تعداد بیشتر از حد درخواست توی یک بازه زمانی.
برای همین منظور لاراول یک میدل ور برای ما اماده کرده است به نام Throttle
که می توانیم روی روت های که میخواهیم تنظیم کنیم به این صورت :
Route::post('login', 'َ[email protected]')->middleware('throttle:20,1');
۱۹- روت Resource و Named چیست و چه کاربردی دارد ؟
هنگامی روت ها درون برنامه ما ایجاد می شوند ممکن است تعدادشان خیلی زیاد شود و اگر بخواهیم از آنها برای مسیردهی های داخل برنامه ی خودت استفاده کنیم یادآوری و به کار بردن آنها نسبتا سخت میشه و همچنین ممکن بعدا عوض بشوند، برای همین مفهوم Named
به ما کمک می کند تا برای روت های خود یک اسم کوتاه و مختصر بگذاریم به کد زیر دقت کنید :
Route::get('/posts/laravel-project-10221/nz7eX', '...')->name('post');
حالا بجای نوشتن همچین ادرس سختی کافی است فقط نام آن را صدا بزنیم در صفحه های html
خود به شکل زیر :
<form action="{{'{{'}} route('post') }}"></form>
خب مفهومی بعدی که میخواهیم در مورد آن صحبت کنیم Resource
می باشد خیلی موقع ها شما یک عملیات تکرای مثل CRUD
را در نوشتن روت های خود دارید یک روش این است که هر دفع بیاید برای یک موجودیت یا مدل هر چهار عمل را تکرار کنید به شکل زیر :
Route::get('books', '[email protected]');
Route::get('books/{book}', '[email protected]');
Route::post('books', '[email protected]');
Route::patch('books/{book}', '[email protected]');
Route::delete('books/{book}', '[email protected]');
هدف از این مفهوم خلاصه و کم حجم کردن روتهای هست که تعریف کردیم و به راحتی متوانید با قطع کد زیر روت های بالا را مدیریت و خلاصه کنید :
php artisan make:controller BookController --resource
در بخش روت ها به جای تمام آنها کد زیر را داریم :
Route::resource('books', 'BookController');
۲۰- ACL چیست و روش استفاده از آن چگونه است ؟
یکی از بخش های مهم و پرکاربرد می باشد پس خوب توجه کنید، فرض کنید که ما یک فروشگاه اینترنتی داریم که از بخش های مثل مشتریان، محصولات، بلاگ، نظرات تشکیل شده است، خب ما قصد داریم برای کسانی که قرار است با اپ و یا سایت ما کار کنند محدودیت و سیاست گذاری های خاصی بگذاریم، به عنوان مثال میخواهیم بعضی از کاربرها فقط دسترسی بخش نظرات و خواندن آن ها داشته باشند و بعضی ها بتوانن گزارش گیری های مالی را انجام دهند و مثال های از این قبیل که بتوانیم سطوح دسترسی مختلفی داشته باشیم، خب برای اینکار مفهومی به نام ACL
مخفف Access-control list
وجود دارد که میتوانیم بدون دستکاری در جداول بینهایت مجوز و نقش تعریف کنیم.
خب این مفهوم شامل چندین بخش می شود که من تک به تک آنها را توضیح میدهم :
Gate چیست ؟
همانطور که گفتیم میخواهیم فعالیت کاربر را قبل انجام عملی یا دیدن صفحه ای با تعیین سطوح دسترسی محدود کنیم به این معنی که آیا یک کاربر مجوز انجام یک کار خاص رو داره یا نه، که از کلاس (فساد) Gate
استفاده و آن را عبور می دهیم.
بعنوان مثال میخواهیم برسی کنیم که در صورتی که یک کاربر نقش نویسنده را دارد بتواند پست ایجاد کند :
Gate::define('create-post', function ($user) {
return $user->isAuthor;
});
به صورت زیر استفاده می شود :
if (Gate::allows('create-post')) {
// The current user can create posts
}
فقط اینجا یک نکته ای مهم است که ما برای تعریف انواع سطح دسترسی باید دوتا کار انجام دهیم اگر میخواهیم به صورت Gate::define
آن را استفاده کنیم باید آن را توی متد boot
توی فایل AuthServiceProvider
تعریف کنیم.
یا اینکه باید برای مدل های که در برنامه خود داریم از طریق دستور زیر Policy
تعریف کنیم :
php artisan make:policy PostPolicy -m Post
هنگامی که قطع کد بالا را اجرا میکنیم از طریق Command Line
درون پوشه app
یک پوشه ساخته ای می شود به نام Policies
که تمام Policy
های ما آنجا وجود دارد فقط آخرین کاری که باید انجام شود این است که آن را در داخل AuthServiceProvider
معرفی کنیم به این صورت :
protected $policies = [
Post::class => PostPolicy::class
];
در اخر من فقط نمونه های استفاده از این مفهوم رو به عنوان sample code
میزارم تا ازش الگو بگیرید و بدانید در لاراول برای انجام یک کار راه های متعددی وجود دارد.
// ACL (Access Control List)
// if($post->owner_id !== auth()->user()->id) {
// abort(403);
// }
// abort_if( $post->owner_id !== auth()->user()->id , 403);
// abort_unless(auth()->user()->owns($post) ,403);
// $this->authorize('view' , $post);
// if(\Gate::denies('view' , $post)){
// abort(403);
// }
// abort_unless(\Gate::allows('view' , $post) , 403);
// auth()->user()->can('view' , $post);
۲۱- گارد (Guard) چیست؟
برای اینکه بخوایم تعیین کنیم که احرازهویت از طریق چه دستورالعملی صورت بگیره از گارد استفاده می کنیم، بعنوان مثال می خواهیم مشخص کنیم که احرازهویت از طریق رشته ای از Token
صورت بگیره یا از طریق Session
یا Cookie
.
در لاراول ما به صورت کلی دو نوع گارد به نام های web
و api
داریم که web
به صورت پیش فرض در لاراول استفاده می شود که تعیین می کند احرازهویت از طریق Session
یا Cookie
صورت بگیرد و api
برای رشته ای از Token
می باشد .
در قطع کد زیر می توانید مشاهده کنید که به چه صورت گارد مورد استفاده قرار گرفته :
$user = Auth::user();
$user = Auth::guard('api')->user();
$user = Auth::guard('web')->user();
خب اول از گارد پیش فرض خود لاراول استفاده کردیم برای خواندن اطلاعات کاربر در خط دوم و سوم از گارد api
و web
به صورت صریح استفاده کردیم تا اطلاعات کاربر را بخوانیم.
توی فایل config/auth.php
میتونیم گاردها رو ببینیم و همچنین گاردهای دلخواهمون رو تعریف کنیم.
۲۲- فِساد (Facade) چیست؟
یک از مفاهیم مهم دیگر Facade
می باشد که متعدد به آن بر میخورید، اگر میخواهید الگوی کد نویسی لاراول را به خوبی درک کنید حتما به مطالعه Design Pattern
بپردازید که تمام این مفاهیم از طریق این الگو پیاده سازی شده است و در خیلی از فرم ورک های دیگر هم بکار رفته است.
خب همانطور که قبلا اشاره کردیم سرویس ها (کلاس) مثل Session
و DB
و Cookie
درون Service Container
ثبت شده اند، خب حالا برای دسترسی راحت به سرویس ها Service Container
از مفهومی به نام Facade
استفاده می کنیم.
فساد، یک کلاس (رابط) برای دسترسی به این سرویسها بصورت استاتیک هست به عنوان مثال برای دسترسی به کلاس DB
به این صورت عمل می کنیم :
$users = DB::select('select * from users where active = ?', [1]);
۲۳- صف (Queue) چه کاربردی دارد ؟
فرض کنید عملیاتی داریم که زمان اجرای آن طولانی می باشد، به عنوان مثال ارسال ایمیل به صورت انبوه بین تمام کاربران سایت، خب اگر بخواهیم این عمل را به صورت همزمان انجام دهیم و همان لحظه تمام پردازش صورت بگیرید تمام کاربران برای مدتی باید منتظر پاسخ از سرور باشند و انباشته شدن این همه درخواست به سرور هم فشار زیادی می آورد که ممکن است باعث اختلاس در سرور شود.
خب اینجا است که صف وارد عمل می شود تا این مسئله را حل کند، صف زمانی به کار می آید که می خواهیم عملیات سنگین را به یک زمان دیگر موکول کنیم این لایه از چرخه Request
و Response
جدا می باشد تا اختلالی در آنها ایجاد نشود.
۲۴- seeder چیست ؟
حتما به این موضوع بر خورده اید که در هنگام توسعه یک برنامه نیاز دارید که دیتای Fake
ایجاد کنید و روی آنها عملیات های مختلفی را انجام دهید خب در اینجا مفهومی به نام seeder
کمک ما می آید که از طریق Factory
می توانیم به راحتی برای هر چندتا دیتای Fake
از آن استفاده کنیم.
من به عنوان مثال یک بار فرایند ایجاد کردن آن را می گویم :
php artisan make:factory UserTable
خروجی این دستور به صورت زیر می باشد و کلاسی با نامFaker
به آن bind
شده است که میتوانید با یک سرچ ساده استفاده از دیتای آن را متوجه شوید :
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use App\User;
use Faker\Generator as Faker;
$factory->define(User::class, function (Faker $faker) {
return [
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
];
});
بعد باید برای این Factory
یک Seeder
درست کنیم تا آن را با تعداد دفعات دلخواه ما فراخوانی کند :
php artisan make:seeder UsersTableSeeder
خروجی آن به این صورت می باشد :
<?php
use Illuminate\Database\Seeder;
class UsersTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
factory(App\User::class, 10)->create();
}
}
حالا فقط کافی است در فایلی به نام DatabaseSeeder
که در مسیر database/seeds/
وجود دارد آن را فراخوانی کنیم به صورت زیر :
<?php
use App\Profile;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* @return void
*/
public function run()
{
$this->call(UsersTableSeeder::class);
}
}
در آخر فقط کافی است دستور زیر اجرا کنیم :
php artisan db:seed
۲۵- Accessor و Mutator چیست ؟
اگر بخواهیم attributes
های یک مدل رو طبق یک فرمت خاص نمایش دهیم یا ذخیره کنیم از این مفهوم استفاده می کنیم .
: Accessor
برای زمانی که بخواهیم یک attr
را به فرمت خاص نمایش دهیم به عنوان مثال یک attr
را حرف اولش را به صورت Uppercase نمایش دهیم .اگه کاربرا یک Attribute
به اسم name
داشته باشن، اسم Accessor
میشه getNameAttribute
که باید اون رو بصورت یک متد توی مدل User
تعریف کنیم:
<?php
namespace App\Models;
class User extends Model
{
public function getNameAttribute($name)
{
return ucfirst($name);
}
}
: Mutator
فرض میکنیم که میخواهیم یک attr
مقدار لخواه تنظیم کنیم به عنوان مثال میخواهیم هنگامی که password
مقدار میدهیم همیشه عمل Hash
شدن به صورت خودکار اتفاق بیفتد.
<?php
namespace App\Models;
class User extends Model
{
public function setPasswordAttribute($value)
{
$this->attributes['password'] = Hash::make($value);
}
}
جمع بندی
در بخش آخر این مقاله سعی کردم بیشتر مفاهیم کاربردی که در لاراول مطرح است را پوشش بدهم همانطور که قبلا هم گفتم سعی بر درک مفاهیم بود تا مثال های اجرای، ولی بازم تا تونستم مثال های کاربردی زدم تا بهتر درک بشه، امیدوارم این مقاله برای شما مفید باشد.