فصل ۳: مسیریابی (Routing) در لاراول

20 اسفند 1395
درسنامه درس 4 از سری لاراول
Laravel-Main-routing

با درود. با یکی دیگه از فصل‌های آموزش لاراول به زبان فارسی در خدمت شما عزیزان هستیم. اگر چنانچه فصل ۱ و ۲ این مجموعه آموزشی را مطالعه نکرده‌اید همین الان از منوی بالای صفحه مسیر «برنامه‌نویسی -> Laravel» را انتخاب کرده و از ابتدا شروع کنید. چون فرآیند نصب و معرفی لاراول در دو فصل گذشته آموزش داده‌شده است. خب بسیار عالی! وارد فصل ۳ شدیم. مباحث این فصل شامل بحث کنترلرها (Controllers) و مسیریابی یا مسیردهی (Routing) است که این دو مبحث مهم را به صورت کامل از ۰ تا ۱۰۰ توضیح خواهیم داد و تا جاییکه ممکنه سعی کرده‌ایم تمام جزئیات رآموزش دهیم.

مقدمه

بسیاری از توابع کاربردی در هر نرم‌افزار تحت وب که با فریم‌ورک نوشته می‌شود دریافت درخواست از کاربر و ارسال پاسخ است که معمولا توسط پروتکل‌های HTTP یا HTTPS صورت می‌پذیرد. این بدین معنی‌ست که طراحی مسیرهایی که کاربر وارد آن می‌شود و درخواستی برای سرور ارسال می‌کند بسیار مهم و از اصلی‌ترین و اولین مقدمه‌ی هر نرم‌افزار تحت وب است. بگونه‌ای که بدون مسیرها (Routes) نرم‌افزار شما هیچ قابلیتی نخواهد داشت. در ادامه به توضیح دقیق نحوه‌ی تعریف مسیرها و همچنین کنترل آنها توسط فریم‌ورک قدرتمند لاراول می‌پردازیم.

تعریف مسیر (Route)

در لاراول ۵.۴ مسیرها (Routes) در پوشه‌ی routes تعریف شده‌اند. البته در نسخه‌های قبلی این مسیر به صورت app/Http/routes.php بود. ساده‌ترین راه‌ برای تعریف یک مسیر انطباق یک URI‌ مانند "/" به یک Clouser‌ (بستار) است:

یک سوال: URI چیست؟
پاسخ: URI‌ به مجموعه‌ی URLها و URN ها گفته می‌شود. برای روشن‌تر شدن این موضوع یک مثال خیلی مشخص می‌زنیم:

تعریف URI:‌ هر آدرسی که منبع مشخصی نداشته باشد. (مثل آدرس درایو‌ها در ویندوز و ...) به نمونه‌ی زیر توجه کنید:

Files/main/page.html 

تعریف URL:‌ هر آدرسی که دارای منابع باشند مانند منبع http, https و ftp و ... . (مثل آدرس وب سایت‌ها و ...) به نمونه‌ی زیر توجه کنید:

http://www.roxo.ir/laravel/page.html

تعریف URN:‌ هر آدرسی که منبع آن یک نام باشد. (مثل بارکد کتاب‌ها و ...) به نمونه‌ی زیر توجه کنید:

ISBN 1-2345-678-9

نکته قابل توجه:‌ هر URL می‌تواند یک URI‌ باشد و هر URN نیز مجددا می‌تواند یک URI‌ باشد ولی هیچگاه یک URL‌ نمی‌تواند یک URN باشد یا بالعکس.

یک سوال: Clouser چیست؟
پاسخ: توابعی هستند که عنوان شیء، پارامتر و متغییر به سایر توابع، متدها یا رویدادها ارسال می‌شوند.

حال که با تعاریف Clouser و URI آشنا شدید اولین مثال لاراولی خود را ارائه داده تا شما را با مسیردهی لاراول آشنا کنیم. بنابراین مسیر routes/web.php را باز کرده و دستورهای موجود در آن را به حالت غیرفعال (کلید میانبر /+ctrl) درآورده و کدهای زیر را اضافه کنید:

Route::get("/",function(){
    return "Hello Roxo";
});

این دستور به سیستم مسیردهی لاراول فرمان می‌دهد که هر گاه کاربر مسیر /http://www.roxo.ir را وارد کرد پیغام Hello Roxo را نمایش دهد. یا به اصطلاح هرگاه کاربر مسیر روت اصلی وب سایت را مشاهده کرد پیام موردنظر چاپ شود. شاید این سوال برای شما پیش بیاید که چرا بجای استفاده از Echo یا Print‌ از دستور return برای نمایش رشته‌ها استفاده کردیم. نکته قابل توجه این است که درخواست‌ها و پاسخ‌های کاربران از یک کانال به نام MiddleWare یا میان‌افزار عبور می‌کنند و سپس به کاربر بازگردانده می‌شوند. بنابراین دستور return‌ مستقیما به کاربر ارسال نمی‌شود. بلکه از فیلتر میان‌افزار یا MiddleWare می‌گذرد.

همچنبن توجه داشته باشید که در فایل web.php و یا هر فایل Route دیگری می‌توان چندصد Route‌ تعریف کرد. به مثال زیر توجه کنید:

Route::get('/', function () {
    return view('welcome');
});

Route::get('about', function () {
    return view('about');
});

Route::get('products', function () {
    return view('products');
});

Route::get('services', function () {
    return view('services');
});

فراخوانی استاتیک (ساکن)

اگر شما تجربه‌ی برنامه‌نویسی به زبان PHP را داشته باشید، شاید از نحوه‌ی فراخوانی استاتیک متدها (مثلا متد get) در کلاس‌ها (مثلا کلاس ::Route) حیرت‌زده باشید. ولی در حقیقت این متدها Static نیستند بلکه لاراول از مفهومی به نام Facade استفاده می‌کند. به عنوان نمونه اگر شما مثال اول را با بدون Facade بنویسید به صورت زیر خواهد بود:

$router->get('/', function(){
    return 'Hello Roxo';
});

یک سوال: Facade چیست؟
پاسخ: یک Interface است که وابستگی‌های خارجی یک کلاس را به حداقل رسانده و باعث خواناتر شدن کد‌ها و برنامه‌ها می‌شود.

همراهان گرامی جهت تسط به مباحث مسیردهی لاراول باید به متدهای پروتکل‌ HTTP تسلط کامل داشته باشید. برای این منظور مقاله‌ای به زبان ساده و روان آماده کرده‌ایم که مطالعه آن خالی از لطف نیست:

متدهای مسیردهی (Route)

همانطور که در بالا اشاره کردیم برای متوجه شدن مفاهیم متدهای پروتکل HTTP حتما مقاله فوق را مطالعه بفرمایید. در نتیجه لاراول نیز با استفاده از Facade مسیردهی (::Route) تمام متدها را به زیباترین شکل ممکن پشتیبانی می‌کند. متدها به شرح زیر می‌باشند:

  • ()Route::get : متدی برای خواندن صفحات
  • ()Route::post : متدی برای ایجاد و ذخیره اطلاعات
  • ()Route::put : متدی برای آپدیت و بروزرسانی اطلاعات
  • ()Route::delete : متدی برای حذف اطلاعات

کنترل کردن مسیردهی

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

Route::get('/', 'WelcomController@inedx');

این عبارت و دستور در واقع درخواست را به آدرس URI‌ که معادل App\Http\Controllers\WelcomeController و در نهایت متد ()indexارسال می‌کند. این متد دقیقا مشابه یک Clouser در مثال‌های بالاست.

پارامترهای مسیردهی (Route)

اگر داخل URI‌ یک پارامتر وجود داشته باشد می‌توان آن را مستقیما به Clouser یا متد ارسال کرد. به مثال زیر توجه کنید:

Route::get('users/{id}/friends', function ($id) {
    //
});

همچنین می‌توان از پارامترهای پیشفرض استفاده کرد. به عنوان مثال پارامتر پیشفرض id$ در مثال فوق برابر fallbackId است:

Route::get('users/{id?}', function ($id = 'fallbackId') {
	//
});

و می‌توان از عبارات باقاعده (Regular Expression) استفاده کرد. این عبارات مانند یک شرط عمل می‌کنند بدین صورت که اگر شرط برقرار باشد Route مخصوص به آن اجرا می‌شود:

Route::get('users/{id}', function ($id) {
	//
})->where('id', '[0-9]+');

Route::get('users/{username}', function ($username) {
	//
})->where('username', '[A-Za-z]+');

Route::get('posts/{id}/{slug}', function ($id, $slug) {
	//
})->where(['id' => '[0-9]+', 'slug' => '[A-Za-z]+']);

برای خروجی فوق اگر کاربر آدرس users/abc را وارد کند route دوم اجرا خواهد شد و اگر عبارت posts/abc/123 در مرورگر وارد شود Route اول اجرا خواهد شد.

اسامی مسیرها (Route)

در حالت پیش‌فرض می‌توان با استفاده از تابع ()url‌ هر جای نرم‌افزار خود از مسیردهی ها استفاده کنید. به عنوان مثال:

<a href="<?php echo url('/'); ?>">

اما لاراول برای شما امکانی را فراهم کرده است که برای هر Route‌ یا مسیر خود یک نام دلخواه انتخاب کرده تا در صورت لزوم مسیرهای پیچیده را با نام اختصاری آنها معرفی کنید. به مثال زیر توجه کنید:

Route::get('members/{id}', [
	'as' => 'members.show',
	'uses' => 'MembersController@show'
]);

// فایل View
<a href="<?php echo route('members.show', ['id' => 14]); ?>">

در این مثال به برخی مفاهیم باید اشاره کرد. اولین مبحث تخصیص آرگومان دوم به شکل آرایه به متد get در سیستم مسیردهی Route است. لاراول آرگومان دوم Route را چک می‌کند و اگر Clouser بود آن را به صورت هوشمند اجرا می‌کند یا اگر رشته باشد آن را به عنوان یک معرف کنترل و متدهای همراه آن، می‌شناسد یا در صورت وجود یک آرایه (مانند مثال فوق) لاراول انتظار دریافت پارامترهایی دارد تا بتواند روی سیستم مسیردهی اعمال کند. پس در زیر به صورت خلاصه و تیتروار انواع پارامترهایی که به عنوان آرگومان دوم به Route و متدهای مرتبط به آن ارسال می‌شوند را بررسی می‌کنیم:

  1. Clouser، که در لحظه اجرا می‌شود
  2. String یا رشته، به کنترلر مربوطه و متد آن اشاره می‌کند
  3. Array‌ یا آرایه، تنظیمات روتر را درنظر می‌گیرد و سپس به کنترلر موردنظر و متد آن اشاره می‌کند

همانطور که در مثال فوق مشاهده می‌کنید یک نام مستعار برای این مسیردهی (route) تحت عنوان members.show اختصاص داده شده است. که این نام‌گذاری از یک قاعده و قانون کلی تبعیت می‌کند که توسط لاراول ایجاد شده است. این قانون بدین صورت است: resourcePlural.action، یعنی قسمت اول نام کنترلر به صورت جمع و camel-case است و قسمت دوم نام اکشن یا متد.

بنابراین با استفاده از ویژگی (Property)تحت عنوان "as" نام این روت (route) یا مسیر را "members.show" قرار داده ایم. سپس با استفاده از ویژگی "uses" نام کنترلر مربوطه را به این مسیر معرفی کرده‌ایم.

در نظر داریم که اختصاص نام مستعار مسیر برای یک Coluser امکان‌پذیر است. به مثال زیر توجه کنید:

Route::get('/members/{id}/edit', [
	'as' => 'members.edit',
	function ($id) {
		//
	}
]);

بسیار عالی! حال به معرفی توابع موردنیاز برای لینک دادن به این مسیرها، اشاره می‌کنیم. این توابع معمولا در view مورد استفاده قرار می‌گیرد:

()url

این تابع و متد زمانی استفاده می‌شود که روت‌ها پارامتری نداشته باشند. به عبارت دیگر نام مستعاری برای آنها تعریف نشده باشد.

()route

این تابع زمانی استفاده می‌شود که روت و مسیر ما دارای نام مستعار باشد و یا پارامتری برای آن تعریف کرده باشیم.

یک مثال جامع برای مبحث پارامترهای Route ارائه می‌دهیم تا این مبحث رو به اتمام برسانیم:

درنظر بگیرید که شما آدرسی به صورت زیر دارید:

users/{userId}/comments/{commentId}

حال می‌خواهیم برای userId=1 و commentsId=2، انواع پارامترها را به همراه کاربرد آنها در View‌ نمایش دهیم:

Route::get('users/{userId}/comments/{commentId}, [
    'as' => 'users.comments.show',
    'uses' => 'UsersCommentsController'
]);
===================================
حالت ۱:
    route('users.comments.show', [1, 2])
    خروجی: // http://www.roxo.ir/users/1/comments/2
===================================
حالت ۲:
    route('users.comments.show', ['userId' => 1, 'commentId' => 2])
    خروجی: // http://www.roxo.ir/users/1/comments/2
===================================
حالت ۳:
    route('users.comments.show', ['commentId' => 2, 'userId' => 1])
    خروجی: // http://www.roxo.ir/users/1/comments/2
===================================
حالت ۴:
    route('users.comments.show', ['userId' => 1, 'commentId' => 2, 'opt' => 'a'])
    خروجی: // http://www.roxo.ir/users/1/comments/2?opt=a

گروه‌‌بندی مسیرها (Route Groups)

گروه‌بندی مسیرها یکی از امکانات فوق‌العاده‌ای‌ست که لاراول در اختیار شما قرار می‌دهد تا از تکرار یک سری پسوند و فضای نام‌ها (namespace) خودداری نمایید. همچنین از این گروه‌بندی‌ها برای اعمال برخی کامپوننت‌های امنیتی و تایید هویت کاربری (authentication) بر روی یک دسته مسیر (routes) استفاده می‌شود. این امر از تکرار بسیاری از route ها جلوگیری می‌کند و مسیردهی اپلیکیشن شما را بسیار منظم و مرتب می‌کند.

برای تعریف کردن یک یا چندین گروه باید از متد group به صورت زیر استفاده کنید:

Route::group([], function () {
	Route::get('hello', function () {
		return 'Hello';
	});
	Route::get('world', function () {
		return 'World';
	});
});

مثال بالا هیچ فرقی با حالت route عادی ندارد. زیرا مقدار آرگومان اول group‌ نباید هرگز خالی باشد.

گروه‌بندی با استفاده از Middleware

همانطور که قبلا اشاره کردیم، میان‌افزارها (Middleware) را می‌توان به عنوان یک فیلتر و با استفاده از عبارت group یا Route::middleware به مسیردهی خود اضافه کنیم. به مثال زیر توجه کنید:

Route::group(['middleware' => 'auth'], function () {
	Route::get('dashboard', function () {
		return view('dashboard');
	});
	Route::get('account', function () {
		return view('account');
	});
});

در این مثال میان‌افزار تایید هویت (authentication) به مسیرهای http://www.roxo.ir/dashboard و http://www.roxo.ir/account‌ اعمال شده است که باعث می‌شود کاربر بدون ورود و عضویت نتواند وارد بخش‌های داشبورد و اکانت شود.

گروه‌بندی با استفاده از پسوندها

گاهی نیاز داریم که برای نرم‌افزار خود یک API تولید کنیم و آن را در اختیار سایر افراد قرار دهیم در این صورت باید مسیر API‌ به صورت http://www.roxo.ir/api/help باشد. حال برای اینکه مسیر api/ را مدام تکرار نکنیم از یک پسوند استفاده کرده و مسیرهای درون آن را گروه‌بندی می‌کنیم. این مفهوم را با مثال زیر شفاف‌تر توضیح می‌دهیم:

Route::group(['prefix' => 'api'], function () {
	Route::get('/', function () {
		//
	});
	Route::get('help', function () {
		//
	});
});

در این حالت همانطور که مشاهده می‌کند پسوند api/ به تمام زیر گروه‌ها اضافه می‌شود.

گروه‌بندی برای ساب دامین

این امر همانند پسوند دادن به نرم‌افزار است با این تفاوت که ساب دامین را به عنوان یک پسوند به مسیرها اعمال می‌کنیم. مثال زیر را مشاهده کنید:

Route::group(['domain' => 'api.roxo.ir'], function () {
	Route::get('/', function () {
		//
	});
});

گروه‌بندی برای فضای‌نام‌ها

برای کنترلرهایی که فضای‌نام (namespaces) مشترکی دارند می‌توان مسیردهی را گروه‌بندی کرد. در مثال زیر کنترلرهای موجود در فولدر API با فضای نام تعیین شده دیگر تکرار نخواهند شد:

// بدون اعمال کردن namespace
// App\Http\Controllers\ControllerA
Route::get('/', 'ControllerA@index');

// همراه با اعمال namespace
Route::group(['namespace' => 'API'], function () {
	// App\Http\Controllers\API\ControllerB
	Route::get('/', 'ControllerB@index');
});

گروه‌بندی کردن نام‌های مستعار

از این نوع گروه‌بندی برای جلوگیری از تکرار URI از نام‌های مستعار استفاده می‌کنیم. بنابراین عبارت users/comments/5 توسط یک نام مستعار مشابه users.comments.show نمایش داده خواهد شد. به مثال زیر دقت کنید:

Route::group(['as' => 'users.', 'prefix' => 'users'], function () {
	Route::group(['as' => 'comments.', 'prefix' => 'comments'], function () {
		Route::get('{id}', ['as' => 'show', function () {
			//
		}]);
	});
});

// در نهایت کل این مسیر در خروجی با عبارت:
//users.comments.show‌ 
// در دسترس است.

به شما تبریک می‌گوییم، با آموزش فوق شما به راحتی می‌توانید مسیردهی‌های وب اپلیکیشن خود را ایجاد کرده و در نهایت امر به تولید یک محصول کارآمد و بدون نقص بپردازید. مسیردهی (routing) به شما کمک می‌کند تا نرم‌افزاری تمیز و بدون نقص ایجاد کرده تا در صورت امکان در برهه‌ای از زمان بتوانید آن را بروزرسانی کنید و دچار سردرگمی نشوید! در جلسه‌ی آینده به توضیح مفصل کنترلرها می‌پردازیم. با ما همراه باشید.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری لاراول توصیه می‌کند:
نویسنده شوید

دیدگاه‌های شما (14 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

بتی
26 آذر 1398
سلام من چند نوبت نرم افزارهامو uninstall کردم و دوباره نصب کردم ولی درست نمیشه .مشکل اینه که هر کدوم از فایل های لاراول رو توی PHPstorm ران می کنم همشون ارور می دن و می گه کلاس مورد نظر پیدا نمی شه چی کار کنم ؟ نرم افزار zamp به درستی نصب شده و لاراول هم روش بالا میاد اما توی phpstorm مشکل داره

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

بتی
23 آذر 1398
من فهمیدم که چرا میشه : چون به محض اینکه یک تکه کد را وارد web.php می کنم در قسمت کد زنی اگر به صورت پیغام درش بیاورم با یک نوع فونت نوشته میشه و اگر در حالت معمولی و غیر کد بودن باشه نوع فونت مربوط به ان تغییر می کند و در نتیجه برای فایل های کانفیگ و غیره ناشناخته میشه دو این میشه که می گه Route تعریف نشده است و کلاسش پیدا نمیشه و خطا می ده حالا میشه به من بگید که چطوری نوع فونت را ثابت نگه داریم چه پیغام باشه چه جزئی از کد

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

بتی
23 آذر 1398
من نرم افزارها ی قسمت دوم رو نصب کردم و پروژه ساختم اما مشکل اینه که وقتی Web.php v رو بالا میارم برای ادیت ، خطای fatal error می ده و می گه کلاس Route not found اررور 255 می تونید کمکم کنید چون توی وب هم گشتم و نوشته بود توی فایل app باید 'Route' => Illuminate\Support\Facades\Route::class, این مورد باشه که برای من بود ممنون می شم راهنماییم کنید

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

محمد
20 خرداد 1398
بسیار مفید و کامل دست گلتون درد نکنه

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

monire
15 اردیبهشت 1398
بسیار عالی . متشکرم. موفق باشید

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

محمد
21 اسفند 1397
سلام ممنون از آموزش، یه سوالی دارم اگر کنترلری داشته باشیم و هیچ روتی برای نداشته باشیم به هیچ وجه قابل دسترس نیست؟ از جنبه امنیتی منظورمه مثلا من روت زیر رو دارم Route::middleware('can:accessAdmin')->group(function() { Route::get('/admin', 'AdminController@dashboard'); Route::get('/admin/blog', 'admin\PostController@post'); }); حالا داخل کنترلر PostController باید هویت سنجی کنم و role یوزر رو بسنجم؟ بهترین راهش چیه؟

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

mohammadavood
08 دی 1397
سلام ممنون به خاطر آموزش باکیفیت و رایگانتون!! تو تکه کد بالای «گروه‌‌بندی مسیرها (Route Groups)» جای کوتیشن تو خط اول اشتباه وارد شده . یعنی: {Route::get('users/{userId'}/comments/{commentId باید بشه '{Route::get('users/{userId}/comments/{commentId

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

روکسو
08 دی 1397
ممنون اصلاح شد.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

امید احمدیانی
01 آذر 1397
سلام خیلی عالیه ویرایش متن: "این عبارت و دستور در واقع درخواست را به آدرس URI‌ که معادل App\Http\Controllers\WelcomeController و در نهایت متد ()welcome ارسال می‌کند. این متد دقیقا مشابه یک Clouser در مثال‌های بالاست." به جای welcome() بنویسید index() با تشکر

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

روکسو
02 آذر 1397
درود بر شما ویرایش مطلب موردنظر انجام شد.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

zahra
09 آبان 1397
بسیار عالی و پرمحتوا

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

حامد
29 شهریور 1397
خیلی عالی بود

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

علی
25 تیر 1397
سلام،همه route ها رو توضیح دادید به غیر patch! دقیقا فرقش با put چیه؟!

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

روکسو
28 تیر 1397
سلام منوی سایت بخش برنامه نویسی و در نهایت وب سرویس یک پست به نام متدهای HTTP هست لطفا مطالعه بفرمایید

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

حمید رضا
04 آذر 1396
سلام وقت بخیر با تشکر باید واقعا خسته باشید بگم که میشه به جرات گفت بهترین منبعی هست که تا حالا دیدم. با اینکه مدت ها منابع فارسی و لاتین زیادی دیدم حتی در Youtube به زبان فارسی ولی این بهترین منبعی هست که تا الان باهاش روبرو شدم. کمال تشکر رو از شما دارم. موفق باشید.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

طراحی سایت
17 آبان 1396
سلام ادمین عزیز واقعا زحمت میکشی این همه مطالب مفید میزاری .. دمت گرم ;)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

رادین
25 اردیبهشت 1396
گفتم نظر ندم بی انصافیه عالی و کارآمد تشکر

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.