چطور در لاراول به شکلی درست کوئری‌ خود را بر اساس URL Query String فیلتر کنیم

نمونه ای از کد غیر اصولیاحتمالا با شرایطی مواجه شده‌اید که می‌خواستید بر اساس پارامترهایی که در query string آدرس وجود دارد، کوئری‌های خود را فیلتر کنید و بعد از انجام اینکار و توسعه بیشتر پروژه، با کدی شبیه به کد تصویر بالا مواجه شدید.این روش خروجی لازم را به شما می‌دهد ولی روش اصولی یا اصطلاحا یک good practice نیست.چرا روش بالا اصولی نیست ؟زمانی که تعداد پارامترهای ‌query string افزایش پیدا می‌کند، تعداد این if ها در کد زیاد شده که این به خودی خود بعد از ماه‌ها توسعه پروژه، شما را با یک کد بزرگ که به سختی قابل نگهداری است روبرو می‌کند.اشکال دیگری که در این کد وجود دارد این هست که اصل دوم از اصول SOLID را نقض می‌کند.بطور خلاصه طبق اصل دوم سالید باید برنامه را طوری طراحی کنیم که برای اضافه کردن امکانی جدید، مجبور به دستکاری کدهای قبلی نباشیم.در واقع اصل دوم سالید تلاش می‌کند ما را از دستکاری کدهایی که به درستی کار می‌کنند منع کند تا اشکالی در برنامه فعلی ایجاد نکنیم.در ادامه توضیح اِشکال مربوط به تصویر ابتدای متن، هر بار که نیاز به فیلتر نمودن پارامتر جدیدی در query string باشد، مجبور هستیم در کدهای قبلی دست برده و تغییراتی در آن ایجاد کنیم، که احتمال تولید باگ نیز به همین ترتیب بالا می‌رود.چه روشی بهتر است ؟برای حل اشکالات بالا، باید روشی طراحی کرد که به ما امکان جداسازی منطق هر بخش از فیلترهای query string را بدهد، به شکلی که اگر نیاز به فیلتر جدیدی داشتیم، در قالب یک متد جدید (جدای از کدهای قبلی) بتوان این فیلتر را اضافه کرد و در نهایت تاثیرش را خروجی کوئری دیتابیس ببینیم.به همین منظور پکیجی برای فریم‌ورک لاراول نوشته‌ام که علاوه بر ایجاد ساختار مورد نظر برای افزودن فیلترها، تعدادی فیلتر پر کاربرد نیز در آن تعبیه شده که در ادامه به توضیح آنها می‌پردازم. پکیج Laravel Filter Query String۱. ابتدا با دستور زیر پکیج را در پروژه خود نصب کنید:$ composer require mehradsadeghi/laravel-filter-querystring ۲. سپس ترِیتِ FilterQueryString را در مدل مورد نظر خود use کنید. همینطور پراپرتی‌ای به نام filters را به مدل خود اضافه کنید که حاوی آرایه‌ایست از فیلرتهای مورد نظر شما.برای نمونه در مدل User چنین کدی خواهیم داشت:use Mehradsadeghi\FilterQueryString\FilterQueryString; class User extends Model { use FilterQueryString; protected $filters = []; ... }۳. برای اِعمال فیلترها در کوئری خود، باید متد filter را در eloquent query استفاده کنید. برای نمونه:User::select('name')->filter()->get();فیلترهای موجودSortComparisonsInLikeWhere clauseبه منظور توضیح هر فیلتر، تصور کنید جدول users ی با این اطلاعات داریم:و کوئری ما در هر مثال نیز به این ترتیب است:User::filter()->get();فیلتر Sortاین فیلتر در واقع معادل order by در sql است که به شکل منعطفی در پکیج FilterQueryString می‌توان از آن استفاده کرد.قاعده استفاده:?sort=field ?sort=field,sort_type ?sort[0]=field1&sort[1]=field2 ?sort[0]=field1&sort[1]=field2,sort_type ?sort[0]=field1,sort_type&sort[1]=field2,sort_typeدر فایل User.php:protected $filters = ['sort'];مثال:https://example.com?sort=created_atخروجی:توجه داشته باشید که وقتی نوع sort را تعیین نمی‌کنید، این مقدار به طور پیش‌فرض asc خواهد بود.مثالی دیگر:https://example.com?sort[0]=age,desc&sort[1]=created_at,descخروجی:فیلترهای Comparisonsاین فیلترها که جنبه مقایسه‌ای دارند شامل ۶ مورد می‌باشند:greatergreater_or_equallessless_or_equalbetweennot_betweenقاعده استفاده:?greater=field,value ?greater_or_equal=field,value ?less=field,value ?less_or_equal=field,value ?between=field,value1,value2 ?not_between=field,value1,value2در فایل User.php:protected $filters = [ 'greater', 'greater_or_equal', 'less', 'less_or_equal', 'between', 'not_between' ];مثالی از فیلتر greater:https://example.com?greater=age,20خروجی:مثالی از فیلتر not_between:https://example.com?not_between=age,21,30خروجی:فیلتر Inاین فیلتر معادل where in در sql است.قاعده استفاده:?in=field,value1,value2,...در فایل User.php:protected $filters = ['in'];مثال:https://example.com?in=name,mehrad,rezaخروجی:فیلتر Likeاین فیلتر معادل like '%value%'در sql است.قاعده استفاده:?like=field,value ?like[0]=field1,value1&like[1]=field2,value2در فایل User.php:protected $filters = ['like'];مثال:https://example.com?like=name,mehخروجی:مثال:https://example.com?like[0]=name,meh&like[1]=username,darخروجی:فیلتر پیش‌فرض Where Clauseبطور کلی زمانی که در query string یکی از فیلترهای معرفی شده در بالا موجود نباشد، پارامتر و مقدار آن در where کوئری قرار می‌گیرد. این فیلتر مناسب زمانی‌ست که می‌خواهید یکی از ستون‌های جدول خود را مستقیما فیلتر کنید.قاعده استفاده:?field=value ?field1=value&field2=value ?field1[0]=value1&field1[1]=value2 ?field1[0]=value1&field1[1]=value2&field2[0]=value1&field2[1]=value2تصور کنید می‌خواهیم ستون‌های name ، username و age را فیلتر کنیم.در فایل User.php:protected $filters = ['name', 'username', 'age'];مثال:https://example.com?name=mehradخروجی:مثال:https://example.com?age=22&username=dariush123خروجی:مثال:https://example.com?name[0]=mehrad&name[1]=dariushخروجی:مثال:https://example.com?name[0]=mehrad&name[1]=dariush&username[0]=mehrad123&username[1]=reza1234خروجی:فیلترهای سفارشیاز طریق فیلترهای سفارشی شما این امکان را دارید تا فیلترهای خود را در قالب متدهایی در مدل خود تعریف کنید. این امکان به رعایت اصل دوم سالید کمک می‌کند، به این صورت که به ازای هر فیلتر جدید، متدی جدید می‌نویسیم و نیازی به ایجاد تغییر در کدهای قبلی نیست.برای نمونه فرض کنید فیلتری با نام all_except نیاز داریم که تمام کاربران به بجز کاربری که نام آن وارد می‌شود را بر‌می‌گرداند:در فایل User.php:protected $filters = ['all_except']; public function all_except($query, $value) { $query->where('name', '!=', $value); }برای آزمایش فیلتر اضافه شده:https://example.com?all_except=mehradتوجه کنید که متدهایی که به عنوان فیلتر تعریف می‌کنید بالاترین اولویت را دارند و حتی این امکان وجود دارد که فیلترهای موجود را override کنید.برای نمونه می‌خواهیم فیلتر in را به گونه‌ای تغییر دهیم که فقط و فقط سه مقدار را بپذیرد:در فایل User.php:protected $filters = ['in']; public function in($query, $value) { $exploded = explode(',', $value); if(count($exploded) != 4) { // throwing an exception or whatever you like to do } $field = array_shift($exploded); $query->whereIn($field, $exploded); }نمونه‌ای دیگر از کاربردهای خوب فیلترهای سفارشی زمانی‌ست که می‌خواهید نام ستون‌های جدول را مستقیما به کاربر (در query string) نمایش ندهید. برای نمونه فرض کنید می‌خواهیم بجای username از by استفاده کنیم:در فایل User.php:protected $filters = ['by']; public function by($query, $value) { $query->where('username', $value); }سپس:https://example.com?by=dariush123خروجی:به عنوان توصیه برای جلوگیری از شلوغ شدن مدل از متدهای فیلترینگ، می‌توانید trait ی ساخته و فیلترهای آن مدل را در آن قرار دهید.همینطور می‌توانید در گیت‌هاب پکیج Laravel Filter Query String را مشاهده کنید.