با سلام و ضمن عرض تبریک سال نو ۱۳۹۶ خدمت تمام هموطنان و نوروزدوستان عزیز جهان، با مطالعهی دو فصل ۱۰-۱ و ۱۰-۲ به این نتیجه دست پیدا کردیم که چگونه یک مدل را ایجاد کرده و با متدها و کالکشنهای آن کار کنیم. خوشبختانه این تسلط به شما کمک خواهد کرد که با بهترین عملکرد مدل خود را بوجود بیاورد و آن را به دیتابیس موردنظر اتصال دهید. اما یک مبحث بسیار مهم و حائز اهمیتی که وجود دارد کار با دیتابیسها و انواع ارسال و دریافت کوئری از آنهاست. لاراول ابزارها و متدهای بسیار راحت و قدرتمندی را در اختیار شما عزیزان قرار داده است تا با استفاده از آن بتوانید به این مهم دست پیدا کنید.
کوئری چیست؟ کوئری در لغت به معنای پرس و جو و در عمل به فرآیندی گفته میشود که با استفاده از آن به درج، حذف، بروزرسانی و فراخوانی یک داده در پایگاه داده میپردازیم.
لاراول ابزاری به نام Query Builder یا سازنده کوئری در اختیار شما قرار میدهد تا با استفاده از یک سری قوانین مشخص به تولید کوئری ها بپردازید. این ابزار عملکرد شمارا بسیار بهبود میدهد و سریعترین روش برای کار کردن با دیتابیس به حساب میآید. ابزار سازندهی کوئری بسیار قدرتمند بوده و انواع پایگاه داده SQLserver, MySQL, Oracle و ... را پوشش میدهد.
همچنین این ابزار از پارامترهای PDO (مخفف PHP Data Objects) برای ارسال و دریافت اطلاعات استفاده میکند تا پایگاه داده شما را از حملات SQL Injection یا تزریق SQL به دور نگهدارد.
مهمترین نکتهای که در حین ساخت یک کوئری با استفاده از لاراول باید رعایت کنید نوشتن DB Facade هست. با استفاده از این Facade اقدام به ساخت و ایجاد یک کوئری میکنیم.
برای بازیابی اطلاعات یک جدول باید از متد table در DB Facade استفاده کرد. این متد نمونهای از سازندهی کوئری را برای جدول موردنظر ایجاد میکند. همچنین میتوان قیدهای متفاوتی را به صورت زنجیرهای به آن متصل کرد. همواره برای بازیابی اطلاعات یک جدول بهتر است از متد get استفاده شود:
<?php namespace App\Http\Controllers; use Illuminate\Support\Facades\DB; use App\Http\Controllers\Controller; class UserController extends Controller { /** * Show a list of all of the application's users. * * @return Response */ public function index() { $users = DB::table('users')->get(); return view('user.index', ['users' => $users]); } }
در این مثال متد get یک کالکشن از Illuminate\Support\Collection که شامل نتایج است را باز میگرداند. هر مقداری که داخل این کالکشن هست یک نمونه از کلاس stdClass (کلاس خالیای که میتوان به آن ویژگیهای جدیدی را اضافه کرد) میباشد. برای دستیابی به ستون یک جدول باید از ویژگی آن ستون استفاده کرد:
oreach ($users as $user) { echo $user->name; }
اگر بخواهیم اطلاعات یک ستون خاص را از یک جدول خاص بازیابی کنیم باید از دستور زیر استفاده کنیم:
$user = DB::table('users')->where('name', 'John')->first(); echo $user->name;
اگر به اطلاعات کامل یک ردیف نیاز ندارید، میتواند یک مقدار واحد از یک رکورد را با استفاده از متد value بدست آورید. این متد مقدار ستون را به صورت مستقیم در اختیار شما قرار میدهد:
$email = DB::table('users')->where('name', 'John')->value('email');
چنانچه میخواهید یک کالکشن از مقادیر یک ستون واحد را در اختیار داشته باشید از متد pluck میتوان استفاده کرد:
$titles = DB::table('roles')->pluck('title'); foreach ($titles as $title) { echo $title; }
درصورتیکه میخواهید با مقادیر بسیار زیادی از دادهها کار کنید، کافیست از متد chunk استفاده نمایید. این متد یک تکه کوچک از نتایج بازیابی شده را در آن واحد بازگردانده و عملیات دستهای روی دادهها انجام میدهد. در نمونهی زیر عملیاتی را برای ۱۰۰ داده به صورت همزمان انجام میدهیم.
DB::table('users')->orderBy('id')->chunk(100, function ($users) { foreach ($users as $user) { // } });
برای متوقف کردن عملیات chunk میتوان از دستور زیر استفاده کرد:
DB::table('users')->orderBy('id')->chunk(100, function ($users) { // Process the records... return false; });
برای انتخاب کردن یک ستون خاص از جدولی خاص میتوان از متد select استفاده کرد:
$users = DB::table('users')->select('name', 'email as user_email')->get();
در مثال فوق با استفاده از دستور Select ستون name و email (با نام مستعار user_email) را انتخاب کرده و سپس با استفاده از متد get کلید و مقدار را باز میگردانیم.
همچنین برای دستیابی به مقادیر غیرتکراری یک ستون از دستور distinct استفاده میکنیم:
$users = DB::table('users')->distinct('name')->get();
فرض کنید در یک کوئری یک ستون مشخص را از یک جدول خاص انتخاب کردهاید. یکدفعه متوجه میشوید که میخواهید یک ستون دیگر از این جدول را انتخاب کنید و با ستون قبلی در دستور SELECT قرار دهید. در این حالت از متد addSelect استفاده خواهید کرد:
$query = DB::table('users')->select('name'); $users = $query->addSelect('age')->get();
در واقع این دستور مشابه دستور زیر است:
$query = DB::table('users')->select('name', 'age')->get();
برخی مواقع نیاز داریم که از یک سری دستورها و عبارتهای خام برای ساخت کوئری استفاده کنیم. این عبارات در فرمت و قالب یک رشته به کوئری تزریق میشوند. بنابراین بسیار مراقب باشید تا دستورهای SQL Injection ایجاد نکنید. در نظر داریم که برای ساخت عبارات خام باید از متد DB::raw بهرهمند شویم:
$users = DB::table('users') ->select(DB::raw('count(*) as user_count, status')) ->where('status', '<>', 1) ->groupBy('status') ->get();
اتصالات داخلی INNER JOIN
با استفاده از ابزار سازندهی کوئریها میتوان انواع اتصالات را ایجاد کرد. سادهترین نوع اتصال که به اتصال داخلی معروف است که میتوان آن را با استفاده از متد join پیادهسازی کرد. اولین آرگومان که به متد join ارسال میشود، نام جدولیست که میخواهیم جدول اصلی را به آن اتصال دهیم. آرگومانهای بعدی به ترتیب شامل نام ستونهاییست که به عنوان انتساب (اتصال داخلی) در دو جدول استفاده میشود. به نمونهی زیر توجه کنید:
$users = DB::table('users') ->join('contacts', 'users.id', '=', 'contacts.user_id') ->join('orders', 'users.id', '=', 'orders.user_id') ->select('users.*', 'contacts.phone', 'orders.price') ->get();
لازم دانستیم که به تعریف اتصال داخلی یا INNER JOIN بپردازیم:
از اتصال داخلی برای انتساب و تساوی مقادیر دو جدول برای دو ستون استفاده میشود. در مثال بالا با دستور join، اطلاعات تماس کاربری که id آن با id موجود در جدول users یکسان بود را استخراج میکند. به عبارت دیگر اشتراک بین دو جدول را در خروجی نمایش میدهد.
اتصال چپ LEFT JOIN
این نوع اتصال علاوه بر اینکه تمام اطلاعات جدول ۱ (چپ) را باز میگرداند بلکه اشتراک بین آن جدول با جدول ۲ را نیز به ازای هر مقدار ستون نمایش میدهد. یعنی اشتراک بین جدول ۱ و ۲ + تمام رکورد جدول ۱ برای رکوردهایی که مقدار آن در ستون جدول ۲ خالی است مقدار null چاپ میشود. برای استفاده از این اتصال باید از متد leftJoin استفاده کرد:
$users = DB::table('users') ->leftJoin('posts', 'users.id', '=', 'posts.user_id') ->get();
اتصال دکارتی یا ضربدری Cross Join
از این نوع اتصال برای برقراری تمام حالتهای ممکن بین دو جدول استفاده میشود. یعنی تمام ستونهای جدول اول در جدول دوم متناظریابی شده (ضرب دکارتی) سپس در خروجی نمایش داده میشود. برای استفاده از این روش باید از متد crossJoin به صورت زیر استفاده کنید:
$users = DB::table('sizes') ->crossJoin('colours') ->get();
اتصالات پیشرفته Advanced Join
گاهی برای تولید یک اتصال پیشرفته باید یک Clouser را به عنوان آرگومان دوم به متد Join استفاده کنیم. این Clouser یک شیء JoinClause را تولید کرده که به شما اجازه میدهد تا قیود و محدودیتهای موردنظر خود را اعمال کنید به مثال زیر توجه بفرمایید:
DB::table('users') ->join('contacts', function ($join) { $join->on('users.id', '=', 'contacts.user_id')->orOn(...); }) ->get();
همچنین میتوان ترکیبی از متدهای where یا orWhere را روی اتصالات خود اجرا کنید تا محدودیتی برای دسترسی به یک سری فیلد خاص اعمال کنید:
DB::table('users') ->join('contacts', function ($join) { $join->on('users.id', '=', 'contacts.user_id') ->where('contacts.user_id', '>', 5); }) ->get();
همانطور که در جریان هستید از این دستور برای ادغام دو یا چند ستون از دو یا چند جدول و نمایش آنها در یک ستون مشترک استفاده میشود. باید در داشته باشید که نوع ستونها باید همواره یکسان باشد تا این دستور اعمال شده و نتیجه را نمایش دهد. این دستور همچنین از نمایش مقادیر تکراری بین دو ستون خودداری میکند. در صورتیکه بخواهیم مقادیر تکراری نمایش دهیم باید از دستور UNION ALL استفاده کنیم:
$first = DB::table('users') ->whereNull('first_name'); $users = DB::table('users') ->whereNull('last_name') ->union($first) ->get();
برای نمایش مقادیر تکراری باید از متد unionAll بجای union استفاده کرد.
دستور Where
همانطور که در مثالهای فوق مشاهده کردید برای اعمال یک محدودیت در کوئریها از دستور where استفاده کردیم. این متد دارای ۳ آرگومان است. اولین آرگومان نام ستون، آرگومان دوم یک عملگر و آرگومان سوم مقداری که میخواهیم به آن دستیابی پیدا کنیم:
$users = DB::table('users')->where('votes', '=', 100)->get();
در مثال فوق تمام مقادیر ستون votes که برابر ۱۰۰ هستند را استخراج میکنیم. همچنین برای خلاصه نویسی عبارات برابر، میتوان دستور فوق را به صورت زیر تغییر داد:
$users = DB::table('users')->where('votes', 100)->get();
علاوه بر این میتوان از عملگرهای گوناگون در این متد بهره برد:
$users = DB::table('users') ->where('votes', '>=', 100) ->get(); $users = DB::table('users') ->where('votes', '<>', 100) ->get(); $users = DB::table('users') ->where('name', 'like', 'T%') ->get();
میتووان به متد where آرایهای از شرایط و محدودیتها را برای یک یا چند ستون ارسال کرد:
$users = DB::table('users')->where([ ['status', '=', '1'], ['subscribed', '<>', '1'], ])->get();
میتوان محدودیت where را با استفاده از عبارت or ترکیب کرد تا در صورت برقرار بودن هر یک از قیود، مقادیر استخراج شود. متد orWhere آرگومانهایی مشابه where دریافت میکند.
$users = DB::table('users') ->where('votes', '>', 100) ->orWhere('name', 'John') ->get();
در این مثال اگر ستون votes بیشتر از ۱۰۰ باشد یا ستون name برابر John باشد تمام اطلاعات باز گردانی خواهد شد.
این دستور بررسی میکند که آیا مقادیر ستون بین دو مقدار مشخص هستند یا خیر:
$users = DB::table('users') ->whereBetween('votes', [1, 100])->get();
این دستور و متد برای مقادیری که بین دو مقدار مشخص نیستند! (خارج از دو مقدار مشخص هستند) مورد استفاده قرار میگیرد:
$users = DB::table('users') ->whereNotBetween('votes', [1, 100]) ->get();
این دستور بررسی میکند که آیا مقادیر ارسال شده به آرگومان دوم آن درون ستون موردنظر هستند؟ در صورت وجود آنها را نمایش میدهد:
$users = DB::table('users') ->whereIn('id', [1, 2, 3]) ->get();
دستور whereNotIn دقیقا عکس دستور whereIn عمل میکند یعنی در صورت عدم وجود مقادیر عبارات را باز میگرداند:
$users = DB::table('users') ->whereNotIn('id', [1, 2, 3]) ->get();
برای بررسی وجود مقدار null در یک ستون از این دستور استفاده میشود:
$users = DB::table('users') ->whereNull('updated_at') ->get();
دستور whereNotNull دقیقا عکس دستور whereNull میباشد و برای بررسی عدم وجود مقدار null در یک ستون استفاده میشود:
$users = DB::table('users') ->whereNotNull('updated_at') ->get();
از دستور whereDate برای مقایسهی ستون تاریخ یک جدول استفاده میشود. مثلا میخواهیم بگوییم که تمام مقادیر جدول users را که ستون تاریخ آن معادل 31-02-2017 است را نمایش بده:
$users = DB::table('users') ->whereDate('created_at', '2016-12-31') ->get();
برای فیلتر کردن بر اساس ماه میتوان از دستور زیر استفاده کرد. عدد ۱۲ نمایانگر ماه December است:
$users = DB::table('users') ->whereMonth('created_at', '12') ->get();
برای فیلتر کردن بر اساس روز نیز میبایست از دستور زیر استفاده کرد:
$users = DB::table('users') ->whereDay('created_at', '31') ->get();
و در نهایت برای فیلتر کردن اطلاعات بر اساس سال باید از دستور زیر بهره برد:
$users = DB::table('users') ->whereYear('created_at', '2016') ->get();
از این دستور برای بررسی برابر بوددن دو ستون استفاده میشود. یعنی مثلا میخواهیم مقادیری را بازگردانیم که در آن نام فرد با فامیلیاش یکسان باشد، آنگاه داریم:
$users = DB::table('users') ->whereColumn('first_name', 'last_name') ->get();
همچنین میتوان از عملگرها در این ستونها استفاده کرد:
$users = DB::table('users') ->whereColumn('updated_at', '>', 'created_at') ->get();
به عنوان آرگومان میتوان به این متد آرایهای با شروط مختلف ارسال کرد:
$users = DB::table('users') ->whereColumn([ ['first_name', '=', 'last_name'], ['updated_at', '>', 'created_at'] ])->get();
درنظر دارید که برای ساخت دستورهای تودرتو where و قیود متفاوت باید یک Clouser به عنوان آرگومان به متدها ارسال کرد. این روش باعث میشود شما به نتایج دقیقتر و پیچیدهتری از اطلاعات بازگردانیشده دست پیدا کنید:
DB::table('users') ->where('name', '=', 'John') ->orWhere(function ($query) { $query->where('votes', '>', 100) ->where('title', '<>', 'Admin'); }) ->get();
این دستور دقیقا مشابه عبارت زیر است:
select * from users where name = 'John' or (votes > 100 and title <> 'Admin')
این متد مشابه دستور where exists در SQL است. متد whereExists بررسی میکند که آیا کوئری موردنظر که به عنوان آرگومان Clouser به آن ارسال میشود، مقداری را باز میگرداند یا خیر؟ تفاوت این دستور با whereIn در این است که در متد whereIn بررسی میکنیم که آیا مقدار در ستون موردنظر وجود دارد یا خیر؟ اما دستور whereExists بررسی میکند آیا یک دستور کوئری که اجرا میشود مقداری را باز میگرداند یا خیر:
DB::table('users') ->whereExists(function ($query) { $query->select(DB::raw(1)) ->from('orders') ->whereRaw('orders.user_id = users.id'); }) ->get();
این دستور دقیقا مشابه دستور زیر است:
select * from users where exists ( select 1 from orders where orders.user_id = users.id )
همانطور که در جریان هستید عدد ۱ به معنای درست بودن همیشگی یک شرط است. وقتی که مینویسیم select 1 یعنی به ازای هر سطری که پیدا میشود یک مقدار ۱ به خروجی ارسال میکند.
لاراول امکاناتی را فراهم کرده است که میتوان نوع دادهی JSON در پایگاه داده را با استفاده از متد where مورد ارزیابی و فیلتر قرار داد، برای اینکار کافیست از عملگر <- استفاده کنیم:
$users = DB::table('users') ->where('options->language', 'en') ->get(); $users = DB::table('users') ->where('preferences->dining->meal', 'salad') ->get();
گاهی نیاز داریم تا اطلاعاتی که به عنوان خروجی در اختیار داریم را مرتب کرده و یا آن را با گروهبندی خاصی ارائه دهیم. در اینصورت باید از دستورهایی که لاراول فراهم کرده است، استفاده کنیم.
از این متد برای مرتب کردن نتایج یک کوئری بدست آمده از یک ستون استفاده میکنیم. اولین آرگومان متد orderBy نام ستون و دومین آرگومان آن نوع ترتیببندی صعودی و نزولی را مشخص میکند که برای صعودی از عبارت asc و برای نزولی از عبارت desc استفاده میشود:
$users = DB::table('users') ->orderBy('name', 'desc') ->get();
از این دو دستور برای مرتب کردن اطلاعات و نتایج بر اساس تاریخ استفاده میکنیم. به صورت پیش فرض اطلاعات بر اساس ستون created_at مرتب میشوند:
$user = DB::table('users') ->latest() ->first();
از عبارت latest برای مرتب سازی بر اساس جدیدترین تاریخ و از oldest برای بروزرسانی بر اساس قدیمی ترین تاریخ استفاده میشود.
از این دستور برای مرتب سازی رندوم یا تصادفی اطلاعات خروجی استفاده میشود:
$randomUser = DB::table('users') ->inRandomOrder() ->first();
دستورهای groupBy و having برای گروهبندی نتایج یک کوئری استفاده میشود. دستورهای having و havingRaw عملکردی مشابه where دارند:
$users = DB::table('users') ->groupBy('account_id') ->having('account_id', '>', 100) ->get();
از دستور havingRaw برای اعمال رشتههای خام به عنوان یک مقدار واحد استفاده میشود. یعنی دستورهایی که به صورت رشته هستند را تبدیل به عدد کرده و عملیات فیلتر کردن را انجام میدهد:
$users = DB::table('orders') ->select('department', DB::raw('SUM(price) as total_sales')) ->groupBy('department') ->havingRaw('SUM(price) > 2500') ->get();
در مثال فوق همانطور که ملاحظه مینکید ابتدا ستون department و مجموع قیمتها total_sales را انتخاب کرده سپس بر اساس department آنها را گروه بندی میکنیم و فیلترینگ را روی این گروه بندی بر اساس تمام خانههایی که بیشتر از ۲۵۰۰ دلار ارزش دارند، انجام میدهد.
از این متد برای محدود کردن یا نادیده گرفتن تعداد نتایج بازگشتی از یک کوئری استفاده میکنیم. در مثال زیر ۱۰ تا مقدار اول را نادیده گرفته و سپس ۵ مقدار را دریافت کرده و نمایش میدهد:
$users = DB::table('users')->skip(10)->take(5)->get();
مشابه این دستور استفاده از limit و offset است که در ادامه به توضیح آنها خواهیم پرداخت:
$users = DB::table('users') ->offset(10) ->limit(5) ->get();
گاهی نیاز دارید یک کوئری زمانیکه چیزی درست بود اعمال شود. برای این کار از متد و دستور when استفاده خواهیم کرد. مثلا در نمونهی زیر زمانی میتوان اطلاعات را در اختیار داشت که مثلا ورودی و نقش کاربر مشخص باشد:
$role = $request->input('role'); $users = DB::table('users') ->when($role, function ($query) use ($role) { return $query->where('role_id', $role); }) ->get();
در صورتیکه مقدار اولین پارامتر true بود ادامهی شرط برقرار میشود و محاسبه صورت میپذیرد. در غیر اینصورت مقدار false بازگردانده شده و محاسبه ای انجام نمیشود.
همینطور میتوان پارامتر سومی به این متد ارسال کرد که به صورت else عمل کند یعنی اگر پارامتر اول false بود آنگاه محجاسبات درون پارامتر سوم اجرا شود:
$sortBy = null; $users = DB::table('users') ->when($sortBy, function ($query) use ($sortBy) { return $query->orderBy($sortBy); }, function ($query) { return $query->orderBy('name'); }) ->get();
برای اضافه کردن یک سطر یا رکورد به یک جدول دیتابیس میتوان از متد insert استفاده کرد:
DB::table('users')->insert( ['email' => 'john@example.com', 'votes' => 0] );
همچنین میتوان با اعمال یک آرایه، چندین ردیف به جدول اضافه کرد:
DB::table('users')->insert([ ['email' => 'taylor@example.com', 'votes' => 0], ['email' => 'dayle@example.com', 'votes' => 0] ]);
در صورتیکه جدول شما دارای یک id با خاصیت افزایشی یا Auto-Increment بود باید از دستور insetGetId استفاده کنید:
$id = DB::table('users')->insertGetId( ['email' => 'john@example.com', 'votes' => 0] );
برای بروزرسانی مقادیر یک رکورد باید از متد update مشابه متد insert استفاده کرد:
DB::table('users') ->where('id', 1) ->update(['votes' => 1]);
بروزرسانی ستونهایی با نوع JSON
برای بروزرسانی این ستونها باید هموراه از عملگر یا اپراتور <- استفاده کنید:
DB::table('users') ->where('id', 1) ->update(['options->enabled' => true]);
بروزرسانی همراه با افزایش یا کاهش
با استفاده از متد increment و decrement میتوان به افزایش یا کاهش یک مقدار و بروزرسانی آن اقدام کرد. این دو متد دارای دو آرگومان هستند که آرگومان اول نام ستون موردنظر را مشخص میکند و آرگومان دوم ضریب مقداری که میخواهیم افزایش پیدا کند:
DB::table('users')->increment('votes'); // مقدار این ستون +۱ تا افزایش پیدا میکند DB::table('users')->increment('votes', 5); // مقدار این ستون +۵ تا افزایش پیدا میکند DB::table('users')->decrement('votes'); // مقدار این ستون -۱ تا افزایش پیدا میکند DB::table('users')->decrement('votes', 5); // مقدار این ستون -۵ تا افزایش پیدا میکند
همچنین میتوان علاوه بر افزایش یک مقدار، ستون دیگری را نیز آپدیت کرد:
DB::table('users')->increment('votes', 1, ['name' => 'John']);
در این مثال علاوه بر افزایش مقدار ستون votes ( افزایش +۱) ستون name نیز با تغییر مقدار آن به John بروزرسانی میشود.
برای حذف کردن یک سطر یا رکورد از متد delete استفاده میشود. معمولا این متد مانند متد update با دستور فیلترینگ where همراه خواهد بود:
DB::table('users')->delete(); DB::table('users')->where('votes', '>', 100)->delete();
درصورتیکه نیاز دارید تمام اطلاعات و رکوردهای یک جدول را حذف کنید و مقدار افزایشی auto-increment را از ابتدا (۰) شروع کنید باید از متد truncate استفاده نمایید:
DB::table('users')->truncate();
بسیار عالی! مجددا به شما عزیزان تبریک میگوییم. حال توانستید با تمام فرمانها و دستورهای ساخت کوئری در لاراول آشنا شوید. تا کنون تسلط بسیاری کسب کردهاید. در فصل آینده به توضیح روابط بین جداول و کارایی آنها در مدلها میپردازیم.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.