اگر با فریمورکهای مختلف مثل laravel کار کرده باشید، اینو میدونید که نوشتن یه وب اپلیکیشن یا یه پروژه میتونه خیلی راحتتر از زمانی باشه که میخواهید از پایه با کتابخونههای خودتون بنویسید. یکی از مزیتهای مهم استفاده از فریمورکها، مستندات قوی و جامعه پشتیبان اونهاست که بهشون قدرت میده و شما هم میتونید از این قدرت به نفع خودتون و یا نفع همون جامعه کاربریش استفاده کنید.
در لاراول با استفاده از کتابخونههای قوی کارتون خیلی سرعت دارهو به راحتی پیش میره ولی بعضی موقع این راحتی کار دستتون میده. به عنوان مثال به کد زیر دقت کنید:
class PostController extends BaseController { public function getIndex() { $posts = Post::all(); return View::make('post.index', compact('posts')); }
تو کد بالا ما یه کنترلر Post داریم و یه متد index که میشه صفحه اصلی کنترلرمون و توش با استفاده از مدل Post تمام رکوردها رو ریختیم داخل یه متغیر و اونو پاس دادیم به View مون. تا اینجای کار، لذت بخش و آسون بود.
مشکل کجاست؟
مشکل موقعی پیش میاد که بخواهید کدتونو تست کنید. خب تو این مورد انجامش ممکن نیست، چون Dependencyها یا نیازمندیهای کنترلرمون که از یک کلاس دیگه استفاده میکنه تعیین نشده و نمیشه از اون توی تست استفاده کرد. پس به مشکل میخوریم.
یکی دیگه از مشکلات هم برمیگیرده به زمانی که بخواهیم نوع دیتابیسمونو عوض کنیم. مثلن بخواهیم از mongoDB به جای Mysql یا هر نوع دیتابیس از نوع Sql، استفاده کنیم. خب، توی استفاده از انواع Sql دیگر مثل SqlServer یا Postgesql نباید مشکلی باشه و نیازی نیست کدمونو تغییر بدیم ولی اگر به mongoDB بخواهیم کوچ کنیم مجبوریم تمام قسمتهای کدمونو بازنویسی کنیم که این به دور از اصول توسعه سریع و تمیزه.
راه حل چیه؟
یکی از بهترین راهحل ها استفاده از Repository ها برای جدا کردن مدلها از کنترلرهامون و مدیریت Dependency Injectionهاست. خب اول باید ببینیم Repository چی کار میکنه؟ Repositoryها میان یه واسطی میشن به بخش دیتابیس و کنترلرهامون و مثل یه تونل و فیلتر عمل میکنن تا ما از یک نوع کدنویسی و استاندارد برای ارتباط با پایگاههای داده اعم از (Redis, Sql, NoSql) استفاده کنیم.
به طور کلی Repositoryها به معنای ارتباط دهنده بین دو چیز هستند.
اگر به واژه رابط یا واسط توجه کردهباشد درمییابید که Repository چیزی نیست جز interfaceها. بله، ما به کمک یکی از امکانات شیگرایی برای حل این مشکلات استفاده میکنیم.
حالا کد بالا رو میخواهیم بازنویسی کنیم:
ابتدا یک Repository برای Post ایجاد میکنیم:
interface PostRepository{ public function find($id, array $columns = array('*')); public function all(array $columns = array('*')); }
سپس میاییم یک کلاس دیگر که از این interface ارث برده را ایجاد میکنیم: (توجه داشته باشید تمام کلاسهایی که از interfaceها implement میشوند باید تمام متدهایی که در interface نام برده شدهاند را داخل خودشون پیادهسازی کنند:
ما یک کلاس برای کار با انواع دیتابیس Sql ایجاد میکنیم و همونطور که میدونید برای این کار از کتابخونه Eloqunet لاراول استفاده میکنیم:
class EloquentPostRepository implements PostRepository { public function find($id, array $columns = array('*')) { return Post::find($id, $columns); } public function all(array $columns = array('*')) { return Post::all($columns); } }
حالا کنترلرمونو بازنویسی میکنیم:
class PostController extends BaseController { public function __construct(PostRepository $post) { $this->post = $post; } public function getIndex() { $posts = $this->post->all(); return View::make('post.index', compact('posts')); } }
همونطور که میبینید دیگه کنترلر ما براش فرق نمیکنه که از چه نوع دیتابیسی استفاده کنه. ما به راحتی میتونید کلاس مربوط به اونو بنویسیم و با استفاده از Ioc Container لاراول، اونو به عنوان مدلمون معرفی کنیم.
چیزی که تو این پست نوشتم یه خورده خورده کاری داره مثل bind کردن کلاسمون به interface تا بتونیم تو پروژههامون ازش استفاده کنیم که به صورت تفصیلی تو پستهای آینده در موردش صحبت میکنم. فقط میخواستم یه دیدی از این کار داشته باشید.