Doctrine ORM از نسخه دوم معماریشو از نوع ActiveRecord به DataMapper تغییر داد. اگه نمیدونید فرق این دو تا چیه این بلاگ پست رو بخونید. همونطور که میدونید لاراول به صورت پیشفرض از ORM بسیار خوب و قوی Eloquent که توسط تیم همین فریمورک طراحی شده استفاده میکنه که معماریش ActiveRevordایه ولی اگه شما هم مثل من به سرتون زده که از یه ORM دیگه استفاده کنید، ادامه بدید به خوندن این پست وگرنه فکر نمیکنم فایدهای داشته باشه براتون.
من از Doctrine تو فریمورکهای Yii و Symfony قبلا استفاده کردم و تقریبا بهش مسلطم واسه همین قصدم اینه که به دلایلی مثل کد تمیزتر و جدا بودن لایه Data Persistence از لایه Domain و … از این ORM توی پروژههای لاراولیم هم استفاده کنم. خب برای این کار چند راه داریم یکیش و سختترینش اینه که Doctrine رو با Composer نصب کنیم و خودمون شروع کنیم به integrate کردنش با لاراول. خب وقتی قبلا افرادی بودن که همین قضیه به فکرشون رسیده و این کارو انجام دادن، چرا باید دوباره خودمون چرخو از اول اختراع کنیم؟ LaravelDoctrine یه پکیجیه که این کارو برامون آسون میکنه و برای داشتن Doctrine روی لاراول به ترتیب زیر عمل میکنیم:
نصب Doctrine
ابتدا پکیج laravel-doctrine/orm رو با توجه به نسخه لاراولمون نصب میکنیم. من خودم از لاراول نسخه ۵٫۱٫* استفاده میکنم، نسخهای که باید از این پکیج نصب کنم به این ترتیبه:
1 |
composer require "laravel-doctrine/orm:1.0.*" |
شما هم میتونید با توجه به نسخه لاراولتون، نسخه مناسب خودتونو نصب کنید:
بعد از اینکه نصب شد، Service Provider این پکیج را به آرایه Providerهامون توی فایل config/app.php اضافه میکنیم:
1 |
LaravelDoctrine\ORM\DoctrineServiceProvider::class |
در مرحله بعدی باید فایل تنظیمات laravel-doctrine/orm رو با فرمان زیر به دایرکتوری configs اضافه میکنیم:
1 |
php artisan vendor:publish --tag="config" |
بعد اینکه این کارها رو انجام دادیم، تقریبا همه چی آماده س برای شروع، فقط قبلش یه دایرکتوری برای Entity هامون درست میکنیم که همینجوری توی دایرکتوری app ول نباشن، من دایرکتوری app/Entities رو دست کردم و بعدش توی فایل config/doctrine.php ، توی خط ۳۲، base_path('app') رو به base_path('app/Entities') تغییر دادم.
ایجاد Entity
برای مثال من میخوام Entity کاربر درست کنم، برای این منظور، یه فایل به نام User.php توی دایرکتوری app/Entities درست میکنم و Mappingشو به صورت Annotation بهش میدم. توی داکترین به چند شکل میشه Mapping تعریف کرد، Annotation , XML, YAML , PHP که به صورت پیشفرض من از Annotation استفاده کردم. هر User من میتونه بینهایت BlogPost داشته باشه که اونو هم داخل کلاس نوشتم. توجه داشته باشید که هر Entity میتونه مستقیم یه جدول از دیتابیس باشه یا نباشه.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
<?php namespace App\Entities; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\Mapping as ORM; /** * Class User * @package App\Entities * @ORM\Entity * @ORM\Table(name="users") */ class User { /** * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer", options={"unsigned":true}) */ protected $id; /** * @ORM\Column(type="string") */ protected $fullName; /** * @ORM\Column(type="string", options={"unique":true}) */ protected $email; /** * @ORM\Column(type="string", nullable=true) */ protected $password; /** * @ORM\OneToMany(targetEntity="BlogPost", mappedBy="author", cascade={"persist"}) * @var ArrayCollection|BlogPost[] */ protected $blogPosts; public function __construct() { $this->blogPosts = new ArrayCollection(); } /** * @return mixed */ public function getId() { return $this->id; } /** * @return mixed */ public function getFullName() { return $this->fullName; } /** * @param mixed $fullName */ public function setFullName($fullName) { $this->fullName = $fullName; } /** * @return mixed */ public function getEmail() { return $this->email; } /** * @param mixed $email */ public function setEmail($email) { $this->email = $email; } /** * @return mixed */ public function getPassword() { return $this->password; } /** * @param mixed $password */ public function setPassword($password) { $this->password = $password; } /** * @return mixed */ public function getBlogPosts() { return $this->blogPosts; } public function addBlogPost(BlogPost $blogPost) { if (!$this->blogPosts->contains($blogPost)) { $blogPost->setAuthor($this); $this->blogPosts->add($blogPost); } } } |
در ادامه کلاس BlogPost رو هم ایجاد میکنم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
<?php namespace App\Entities; use Doctrine\ORM\Mapping as ORM; /** * Class User * @package App\Entities * @ORM\Entity * @ORM\Table(name="blogPosts") */ class BlogPost { /** * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer", options={"unsigned":true}) */ protected $id; /** * @ORM\Column(type="string") */ protected $title; /** * @ORM\Column(type="text") */ protected $content; /** * @ORM\ManyToOne(targetEntity="User", inversedBy="blogPosts") * @var User */ protected $author; /** * @return mixed */ public function getId() { return $this->id; } /** * @return mixed */ public function getTitle() { return $this->title; } /** * @param mixed $title */ public function setTitle($title) { $this->title = $title; } /** * @return mixed */ public function getContent() { return $this->content; } /** * @param mixed $content */ public function setContent($content) { $this->content = $content; } /** * @return User */ public function getAuthor() { return $this->author; } /** * @param User $author */ public function setAuthor(User $author) { $this->author = $author; } } |
تا اینجای کار، ما فقط دو تا کلاس ساده PHP ایجاد کردیم و با دیتابیس کاری نداشتیم حالا برای ایجاد جداول دیتابیسمون یا اعمال تغییرات باید دستور زیر رو توی خط-فرمان اجرا کنیم:
1 |
php artisan doctrine:schema:update |
حالا اگه به دیتابیس سر بزنیم میبینم که دو تا جدول با مشخصاتی که از طریق Annotation توی کلاسمون تعریف کردیم ایجاد شده. به همین راحتی. دیگه نیازی به نوشتن مایگرشن هم نیست براش، اگه توی تیم کار میکنید فقط کافیه بعد از گرفتن تغییرات جدید، دستور بالا رو یه بار اجرا کنید.
کار با Entity ها
در Doctrine برای کار با Entity ها ما به طور پیشفرض با چند ابزار مواجه هستیم، Entity Manager, Entity Repository و Query Builder. که به وسیله Entity Manager همه عملیات رو روی Entity انجام میدیم. Entity Manager ابزارهای لازم برای کوئری گرفتن از دیتابیس و ایجاد و ویرایش بر روی دیتابیس رو بر عهده داره. در ادامه یه مثال میزنم که خوب متوجه بشید:
برای ایجاد یک کاربر و یه سری بلاگپست کد زیر رو مینویسیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
$user = new \App\Entities\User(); $user->setFullName('Morteza Parvini'); $user->setEmail('email@example.com'); $blogPost1 = new \App\Entities\BlogPost(); $blogPost1->setTitle('my post1 title'); $blogPost1->setContent('content of my first post ...'); $blogPost2 = new \App\Entities\BlogPost(); $blogPost2->setTitle('my post2 title'); $blogPost2->setContent('content of my second post ...'); $user->addBlogPost($blogPost1); $user->addBlogPost($blogPost2); EntityManager::persist($user); EntityManager::flush(); |
همونطور که میبینید، تو کد بالا با Entity ها مثل Objectهای معمولی PHP رفتار کردیم و دقیقا هم کار و رفتارشون همینه و عملیات ارتباط با دیتابیس و ذخیره کردن و تبدیل محتوای آبجکتها توسط EntityManager مدیریت میشه.
حالا برای دریافت اطلاعات از دیتابیس باید از Repository یا Query Builder استفاده کنیم. هر Entity میتونه Repository مخصوص به خودشو داشته باشه یا اینکه از EntityRepository پیشفرض Doctrine استفاده میکنیم. در هر صورت کاری که باید بکنیم اینه:
1 2 3 4 5 6 7 8 9 |
$manager = app('em'); $user = $manager->getRepository(\App\Entities\User::class)->find(1); $user = $manager->getRepository(\App\Entities\User::class)->findOneBy(['email' => 'email.example.com']); $blogPosts = $user->getBlogPosts(); $blogPosts[0]->getTitle(); $blogPosts[0]->getContent(); $user->getFullName(); |
متغیر $manager رو از Container باید بگیریم که یا باید کلاس EntityManager رو inject کنیم تو متد یا کنترلرمون یا اینکه باید از به وسیله app('em') یا app(EntityManager::calss) میتونیم همه جا داشته باشیمش.
توصیه میکنم برای آشنایی بیشتر مستندات Doctrine ORM رو بخونید. در آخر اگه سوالی داشتید و میتونستم جواب بدم، خوشحال میشم کمکتون کنم.