در این مقاله درمورد آموزش رابطه ها در لاراول بحث خواهیم کرد. معمولا جدولها در دیتابیس یا پایگاه داده، با هم در ارتباط هستند. مثلا در یک وبلاگ، یک نوشته میتواند چندین دیدگاه یا کامنت داشته باشد. در عین حال همین نوشته با کاربری که آن را نوشته و ثبت کرده است نیز ارتباطی دیگر دارد.
مقدمه
در فریمورک لاراول (Laravel)، مدیریت ارتباط بین مدلها یکی از مهمترین ویژگیها در طراحی ساختار دیتابیس و منطق برنامه است. لاراول با استفاده از Eloquent ORM این امکان را فراهم میکند تا ارتباط بین جداول پایگاه داده به صورت شیءگرایانه و بسیار ساده تعریف و استفاده شود.
روابط (Relationships) در لاراول به توسعهدهنده این اجازه را میدهند که بین مدلهای مختلف مانند کاربران، پستها، سفارشها و محصولات، وابستگیهای منطقی ایجاد کند. این روابط میتوانند از نوع یک به یک (One to One)، یک به چند (One to Many)، چند به چند (Many to Many)، رابطه از طریق مدل میانی (Has Many Through) یا رابطه چندشکلی (Polymorphic Relations) باشند.
با استفاده از این سیستم، توسعهدهنده میتواند به سادگی دادههای مرتبط را واکشی کرده، شرطگذاری کند، و عملیات مختلف را بدون نیاز به نوشتن کوئریهای SQL پیچیده انجام دهد. در نتیجه، روابط در لاراول نهتنها کد را خواناتر و تمیزتر میکنند، بلکه توسعه و نگهداری پروژه را نیز آسانتر میسازند.
آموزش رابطه ها در لاراول
Eloquent نوشتن این روابط و کار با آنها را بسیار آسانتر کرده است. مخصوصا که از انواع ارتباط به خوبی پشتیبانی میکند. برای آموزش رابطه ها در لاراول باید تمام انواع رابطهها را مطالعه نمایید:
- One To One رابطه یک به یک
- One To Many رابطه یک به چند
- Many To Many رابطه چند به چند
- Has One Through رابطه یک به یک با واسطه
- Has Many Through رابطه یک به چند با واسطه
- One To One (Polymorphic)
- One To Many (Polymorphic)
- Many To Many (Polymorphic)
رابطههای Eloquent با نوشتن متدها در داخل Modelهای Eloquent تعریف میشوند. به عبارتی برای تعریف هر رابطه شما باید در داخل کلاس مدلهایتان، اقدام به نوشتن متد یا تابع یا فانکشن نمایید. دقیقا همانند مدلهای Eloquent، رابطهها نیز خود به عنوان یک query builder عمل میکنند، تعریف رابطهها به صورت توابع یا همان متدها این امکان را فراهم میکند تا اطلاعات را به صورت زنجیرهای از پایگاه داده صدا زده و قابلیتهای پرس و جوی بیشتری را در جستجوهای خود به کار بگیرید. مثلا:
$user->posts()->where('active', 1)->get();
در مثال بالا مشاهده میکنید که توانستیم جستجوی پیشرفتهای در بین پستهای یک کاربر انجام دهیم. به عبارتی باید گفت که میتوانیم از متدهای Eloquent در روابط لاراول بهره برده و پرس و جوی دقیقتری انجام دهیم.
آموزش رابطه ها در لاراول را با مطالعه روابط ساده شروع میکنیم.
رابطه یک به یک یا One To One
سادهترین و اساسیترین رابطه در لاراول، رابطه یک به یک میباشد. مثلا هر کاربری فقط یک شماره موبایل دارد و هر شماره موبایلی میتواند فقط متعلق به یک کاربر باشد نه بیشتر.
- پیاده سازی
اگر جدول users یا کاربران به این صورت باشد:
$table->bigIncrements('id'); $table->string('name');
جدول mobiles نیز باید اینگونه باشد:
$table->bigIncrements('id'); $table->string('number'); $table->unsignedBigInteger('user_id')->index(); $table->foreign('user_id')->references('id')->on('users') ->onDelete('cascade')->onUpdate('cascade');
در سطر سوم از دستورات بالا، به عبارت user_id کلید خارجی یا foreign key میگویند. مقدار این عبارت برابر خواهد بود با id کاربر. با استفاده از مقدار کلید خارجی مشخص میشود که یک شماره تلفن متعلق به کدام کاربر است.
در مدل User:
public function phone() { return $this->hasOne('App\Http\Models\Phone'); }
در مدل Phone:
public function user() { return $this->belongsTo('App\User'); }
بعد از انجام مراحل بالا کافیست جدول را نهایی کنیم:
php artisan migrate
حال برای فراخوانی شماره موبایل یک کاربر میتوانیم دستور زیر را بکار ببریم:
return User::first()->phone;
و یا:
$user = User::find(3); return $user->phone;
و همچنین برای فراخوانی کاربر مربوط به یک شماره تلفن:
return Phone::first()->user;
و یا:
$phone = Phone::find(5); return $phone->user;
– نکته:
کلید خارجی یا foreign key را میتوانید چیزی به جز user_id نیز قرار دهید اما باید در متد ()phone در مدل User و نیز در متد ()user در مدل Phone به این مطلب اشاره کنید. فرض کنید ما از عبارت person_id استفاده کردهایم:
در مدل User:
public function phone() { return $this->hasOne('App\Http\Models\Phone', 'person_id'); }
و در مدل Phone:
public function user() { return $this->belongsTo('App\User', 'person_id'); }
جهت آموزش کامل و کاربردی رابطه یک به یک، به فیلم آموزشی زیر مراجعه بفرمایید.
رابطه یک به چند یا One To Many
رابطه یک به چند زمانی تعریف میشود که یک مقدار از یک مدل Model دارای چند مقدار از مدلهای دیگر است. به طور مثال در یک سایت خبری، هر پست یا خبر، چندین و چندین comment یا دیدگاه دارد و در عین حال دیدگاه، فقط متعلق به یک خبر است.
- پیاده سازی
اگر جدول posts یا مطالب به این صورت باشد:
$table->bigIncrements('id'); $table->string('title');
جدول comments یا دیدگاهها نیز باید اینگونه باشد:
$table->bigIncrements('id'); $table->string('body'); $table->unsignedBigInteger('post_id')->index(); $table->foreign(' post _id')->references('id')->on('posts') ->onDelete('cascade')->onUpdate('cascade');
در سطر سوم از دستورات بالا، به عبارت post_id کلید خارجی یا foreign key میگویند. مقدار این عبارت برابر خواهد بود با id مطلب یا post مربوطه. با استفاده از مقدار کلید خارجی مشخص میشود که یک دیدگاه یا کامنت متعلق به کدام پست است.
در مدل Post:
public function comments() { return $this->hasMany('App\Comment); }
در مدل Comment:
public function post() { return $this->belongsTo('App\Post'); }
بعد از انجام مراحل بالا کافیست جدول را نهایی کنیم:
php artisan migrate
حال برای فراخوانی کامنتها یا دیدگاههای یک پست یا مطلب میتوانیم دستور زیر را بکار ببریم:
$comments = Post::first()->comments ;
و یا:
$comments = Post::find(3)->comments ;
و همچنین برای فراخوانی مطلب مربوط به یک دیدگاه:
return Comment::first()->post;
و یا:
$comment = Comment::find(5); return $comment ->post;
– نکته اول:
دقت کنید که در مدل Post نام متد را comments به صورت اسم جمع دادیم، زیرا هر پست یا مطلب میتواند بیش از یک دیدگاه یا کامنت داشته باشد.
– نکته دوم:
کلید خارجی یا foreign key را میتوانید چیزی به جز post_id نیز قرار دهید اما باید در متد ()comments در مدل Post و نیز در متد ()post در مدل Comment به این مطلب اشاره کنید. فرض کنید ما از عبارت relation_id استفاده کردهایم:
در مدل Post:
public function comments() { return $this->hasMany('App\Comment ', 'relation_id'); }
و در Comment.php:
public function post() { return $this->belongsTo('App\Post', ' relation_id'); }
جهت آموزش کامل و کاربردی رابطه یک به چند، به فیلم آموزشی زیر مراجعه بفرمایید.
رابطه چند به چند یا Many To Many
فرض کنید جدولی داریم به نام tags که بالطبع نام مدل آن Tag است و جدولی به نام posts که اسم مدل آن Post میباشد. حال فرض کنید یک post نوشتهایم به نام “روابط در لاراول” که میتواند این تگها را به خود اختصاص دهد: php، laravel، برنامهنویسی و… . در چنین حالتی، آموزش رابطه ها در لاراول میتواند به ما کمک کند تا بهدرستی این نوع روابط را، بهویژه در مثالهایی از این دست، پیادهسازی کنیم.
حال هر یک از تگهای بالا میتوانند در پستهای زیادی باشند. مثلا پستی با عنوان “سیستم احراز هویت لاراول” هم میتواند تگ php یا laravel را بگیرد. پس هر post میتواند چندین tag داشته باشد و هر tag میتواند متعلق به چندین post باشد.
بدین ترتیب دو جدول posts و tags میتوانند رابطه many to many داشته باشند. در لینک زیر پاورپوینت آماده فریم ورک لاراول قرار داده شده که میخواهیم شما را با سرعت توسعه لاراول و موضوعات مرتبط دیگر آشنا کنیم. این فایل در قالبی آکادمیک طراحی شده و مناسب ارائه شما عزیزان میباشد.
- پیاده سازی
در رابطه Many To Many به غیر از جداول posts و tags ، به جدول واسطه دیگری نیازمندیم که به آن pivot table نیز گفته میشود. در واقع در هیچ یک از جداول posts و tags نیاز به تعریف کلید خارجی نداریم و این کار را تنها در جدول واسطه انجام خواهیم داد. ابتدا جداول posts و tags را تعریف میکنیم:
جدول posts:
$table->bigIncrements('id'); $table->string('title'); $table->longText('body'); $table->timestamps();
جدول tags:
$table->bigIncrements('id'); $table->string('name'); $table->timestamps();
نوشتن جداول واسطه مهمترین بخش از آموزش رابطه ها در لاراول میباشد.حال نوبت به طراحی و تعریف جدول واسطه یا pivot table میرسد.
- اولین نکته در طراحی این جدول نام آن است. نام جدول واسطه باید از ترکیب نام دو جدول که رابطه many to many دارند ساخته شود.
- نام جدول واسطه باید ترکیب اسم مفرد جدول اول و اسم مفرد جدول دوم با یک آندرلاین (خط زیرین یا underscores) در میان آنها باشد. مثلا post_tag . درواقع اسم posts_tags برای نام pivot table صحیح نیست.
- در انتخاب نام باید ترتیب الفبایی رعایت شود. بدین صورت که اسم جدولی در ابتدا نوشته میشود که حرف اول آن نسبت به حرف اول اسم جدول دیگر اولویت داشته باشد. مثلا اگر اسم جدول اول post باشد و اسم جدول دوم tags باشد با مقایسه دو حرف p و t به این نتیجه میرسیم که حرف p نسبت به حرف t مقدم است. پس ابتدا باید post نوشته شود و سپس علامت _ و بعد tag، پس میشود : post_tag
مثال دیگر: نام جدول واسطه بین دو جدول users و roles خواهد شد:
role_user
مقادیر این جدول تنها id های مقادیر دو جدول مربوط به هم و کلیدهای خارجی خواهند بود:
$table->bigInteger('post_id')->unsigned()->index(); $table->bigInteger('tag_id')->unsigned()->index(); $table->foreign('post_id')->references('id')->on('posts') ->onDelete('cascade')->onUpdate('cascade'); $table->foreign('tag_id')->references('id')->on('tags') ->onDelete('cascade')->onUpdate('cascade');
در مدل Post:
public function tags() { return $this->belongsToMany('App\ Tag'); }
در مدل Tag:
public function posts() { return $this->belongsToMany('App\ Post'); }
مجددا به جمع بودن اسم هر دو متد و عبارت belongsTo در هر دوی آنها دقت نمایید.
جهت فراخوانی تگهای پست با id = 3 میتوان نوشت:
$tags = Post::find(3)->tags;
و برای فراخوانی پستهای تگ با id=4 داریم:
$posts =Tag::find(3)->posts;
Attac ،Dettach و Sync در روابط many to many
الوکوئنت Eloquent، متدها و helperهایی در اختیارمان گذاشته تا کار با رابطه many to many آسانتر شود. یکی از این متدها ()attach میباشد. Attach در لغت به معنای جسباندن و الصاق است و در لاراول جهت افزودن مدار به رابطه یک مقدار دیگر به کار میرود. مثلا:
$post = Post::first(); $post->tags()->attach($tagId);
مشاهده میفرمایید که ورودی متد ()attach باید id یا آرایهای از idها باشد. این به خاطر این است که در pivot table تنها id میتوان ذخیره کرد. در مثال دیگری میخواهیم چند tag را به تگهای یک post اضافه نماییم:
$post->tags()->attach([1,2,3,]);
و یا مثلا در تکه کد زیر:
$tags = $request->tags; $post->tags()->attach($tags);
متد ()detach نیز درست نقطه مقابل ()attach و برعکس آن عمل میکند. مثلا برای حذف یک یا چند مقدار از روابط یک مقدار دیگر. مثلا برای حذف tagهایی با idهای ۱ و ۲ از یک post معین میتوانیم بنویسیم:
$post->tags()->attach([1,2]);
چنانچه از متد ()sync استفاده نماییم، ابتدا تمام مقادیر حذف شده و سپس مقادیر ورودی متد ()sync اضافه خواهد شد. مثلا فرض کنید post با id=3 تگها با idهای ۱ و ۳ را دارد. چنانچه از دستور زیر استفاده کنیم:
$post ->tags() -> sync(2);
تنها تگ با id=2 جزو تگهای این پست خواهد بود و تگهای ۱ و ۳ از جدول واسطه حذف خواهند شد. این helper میتواند یک آرایه را نیز به عنوان ورودی بپذیرد.
رابطه یک به یک با واسطه یا Has One Through
رابطه hasOneThrough مدلها را از طریق یک واسطه پیوند میدهد به عبارتی ایجاد ارتباط بین دو جدول یا دو مدل از طریق جدول یا مدل دیگری میتواند برقرار شود. مثلا فرض کنید در یک آژانس تلفنی جدولی به نام drivers یا رانندهها داریم. جدول دیگری هم داریم به نام cars یا ماشینها. جدول سومی نیز وجود دارد به نام car_infos که مشخصات ماشینها از قبیل شماره پلاک، رنگ و … را ثبت میکند و نام مدل مربوط به آن CarInfo میباشد. ارتباط بین دو جدول drivers و cars از نوع One To One است. و ارتباط بین دو جدول cars و car_infos نیز One To One است.
یعنی هر رانندهای یک ماشین دارد و هر ماشینی یک اطلاعاتی و بالعکس هر اطلاعاتی متعلق به یک ماشین است و هر ماشینی متعلق به یک راننده. اما ارتباطی بین drivers و car_infos وجود ندارد. برای ارتباط بین این دو جدول، در نسخههای قبل از ۵.۸ بایستی تغییراتی در هر دو جدول میدادیم و از کلید خارجی استفاده میکردیم اما از نسخه ۵.۸ به بعد این کار لازم نیست و ما میتوانیم با جدولهای موجود ارتباط برقرار کنیم:
- پیاده سازی
جدول drivers:
$table->bigIncrements('id'); $table->string('name');
جدول cars:
$table->bigIncrements('id'); $table->unsignedBigInteger('driver_id')->index(); $table->foreign('driver_id')->references('id')->on('drivers') ->onDelete('cascade')->onUpdate('cascade');
جدول car_infos:
$table->bigIncrements('id'); $table->string('color'); $table->integer('car_number'); $table->unsignedBigInteger('car_id')->index(); $table->foreign('car_id')->references('id')->on('cars') ->onDelete('cascade')->onUpdate('cascade');
مشاهده میکنید که ارتباطی بین drivers و car_infos وجود ندارد.
برای ایجاد ارتباط بین این دو کافیست در مدل Driver:
public function info() { return $this->hasOneThrough('App\CarInfo', 'App\Car'); }
این دستور میگوید: جدول drivers با جدول car_infos ارتباط One To One دارد اما از طریق جدول cars.
برای فراخوانی شماره پلاک مربوط به راننده اول داریم:
$driver = \App\Driver::first(); $info = $driver->info;
ولی تا تاریخ تحریر این مقاله، لاراول برای این رابطه، رابطه برعکسی نگذاشته است به عبارتی نمیتوان در این رابطه از روی اطلاعات یک ماشین، راننده آن را پیدا کرد. اما میتوان در مدل CarInfo این کار را انجام داد:
public function driver() { return $this->car->driver(); }
و برای فراخوانی راننده یک شماره ماشین:
$info = \App\CarInfo::where('number', '47693')->first(); $driver = $info->driver;
رابطه یک به چند با واسطه یا Has Many Through
رابطه hasManyThrough مدلها را از طریق یک مدل واسطه ارتباط میدهد به عبارتی ایجاد ارتباط بین دو جدول یا دو مدل از طریق جدول یا مدل دیگری اتفاق میافتد. فرض کنید یک جدول داریم به نام countries یا کشور ها با مدل Country . جدول دیگری داریم به نام users یا کاربران با مدل User. جدول سوم هم متعلق به posts یا نوشتههاست که نام مدلش Post میباشد.
ارتباط بین Country و User به صورت One To Many است یعنی هر کشوری چندین کاربر دارد ولی هر کاربر تنها متعلق به یک کشور است. ارتباط بین User و Post نیز چنین است. هر کاربر چندین پست دارد ولی هر پست تنها متعلق به یک کاربر است.
رابطه has many through میگوید میتوانیم بین دو مدل Country و Post ارتباط برقرار کرده و همه postهای یک country را فراخوانی نمود. تنها کافیست در مدل Country:
public function posts() { return $this->hasManyThrough('App\Post', 'App\User'); }
این دستور میگوید: جدول countries با جدول posts ارتباطی از نوع One To Many دارد اما از طریق جدول users.
حال برای فراخوانی همه postهای یک کشور مشخص چنین عمل میکنیم:
$country = \App\Country::first(); $posts = $country->posts;
ولی برای فراخوانی عکس این رابطه فعلا راهی جز نوشتن متد دستی وجود ندارد. مطابق مثال بالا میتوان در مدل Post نوشت:
public function country() { return $this->user->country(); }
در این قسمت از مقاله آموزش رابطه ها در لاراول نوبت به بررسی روابط Polymorphic میرسد:
رابطه یک به یک پلی مورفیک یا One To One Polymorphic
آموزش رابطه ها در لاراول مستلزم درک صحیحی از روابط پلی مورفیک است. رابطه پلی مورفیک یک به یک مشابه رابطه ساده یک به یک است که در ابتدای مقاله بحث شد با این تفاوت که مدل هدف میتواند متعلق به چند مدل باشد و یا با چند مدل رابطه یک به یک داشته باشد.
فرض کنید مدلی داریم به نام Post با این شرط که به دلیل محدودیت دیتابیس، در هر پست تنها قادر خواهیم بود از یک عکس استفاده کنیم. مدل دیگری نیز به نام User داریم با این شرط که هر کاربر مجاز است تنها یک عکس داشته باشد. پس نیاز به مدل دیگری داریم به نام Image.
به عبارتی Image هم با Post رابطه یک به یک دارد هم با User. بنابراین میتوانیم برای این سه مدل رابطه One To One از نوع Polymorphic بنویسیم:
- پیاده سازی
جدول users:
$table->bigIncrements('id'); $table->string('name');
جدول posts:
$table->bigIncrements('id'); $table->string('title');
جدول images:
$table->bigIncrements('id'); $table->string('path'); $table->rememberToken(); $table->timestamps();
در مدل User:
public function image() { return $this->morphOne('App\Image', 'imageable'); }
در مدل Post:
public function image() { return $this->morphOne('App\Image', 'imageable'); }
در مدل Image:
public function imageable() { return $this->morphTo(); }
برای فراخوانی عکس یک کاربر:
$user = User::first(); $image = $user->image;
یا برای فراخوانی پست مربوط به یک عکس:
$image = Image::first(); $user = $image->imageable;
رابطه یک به چند پلی مورفیک یا One To Many Polymorphic
این رابطه شبیه رابطه One To Many یک به چند ساده است با این تفاوت که مدل هدف، میتواند با چند مدل ارتباط یک به چند برقرار کند.
فرض کنید در یک سایت هم تعدادی post به صورت متن و تکست داریم و هم تعدادی video که کاربران میتوانند برای هر دوی اینها دیدگاه یا کامنت comment بگذارند. به قولی دیگر مدل Comment با هر دو مدل Post و Video ارتباط one to many برقرار میکند.
به جای نوشتن چندین متد و کلید خارجی میتوان از رابطه یک به چند پلی مورفیک استفاده کرده و از شلوغی در مدلها و جدولها جلوگیری کرد. در لینک زیر فیلم آموزشی ساخت دستورات artisan در لاراول ۷ قرار داده شده که پیشنهاد می کنیم کلیک کنید.
- پیاده سازی
جدول comments:
$table->bigIncrements('id'); $table->longText('body'); $table->integer('commentable_id'); $table->string('commentable_type');
جدول posts:
$table->bigIncrements('id'); $table->string('title'); $table->longText('body');
جدول videos:
$table->bigIncrements('id'); $table->string('title'); $table->string('path');
مدل Comment:
public function commentable() { return $this->morphTo(); }
مدل Post:
public function comments() { return $this->morphMany('App\ Comment', 'commentable'); }
مدل Video:
public function comments() { return $this->morphMany('App\ Comment', 'commentable'); }
حال برای فراخوانی دیدگاههای یک پست:
$post = App\Post::find(1); foreach ($post->comments as $comment) { // }
و یا برای فراخوانی ویدیوی مربوط به یک دیدگاه:
$comment = App\Comment::find(1); $commentable = $comment->commentable;
رابطه چند به چند پلی مورفیک یا Many To Many Polymorphic
رابطهی چند به چند پلیمورفیک اندکی پیچیدهتر از سایر رابطههاست. از این نوع رابطه زمانی استفاده میکنیم که یک مدل با چند مدل دیگر ارتباط چند به چند داشته باشد. مثلاً مدل Tag با هر دو مدل Post و Video رابطهی چند به چند دارد. به عبارتی، هر ویدیو و پست میتواند چندین تگ داشته باشد و در عین حال، هر تگ به صورت همزمان میتواند متعلق به چندین پست و ویدیو باشد. آموزش رابطهها در لاراول به درک بهتر این نوع ارتباطها، مخصوصاً روابط پیچیدهتر مانند چند به چند پلیمورفیک، کمک شایانی میکند.
اگر از رابطه Many To Many Polymorphic استفاده نکنیم جدولها و مدلهایمان کثیف و شلوغ خواهند شد. برای این رابطه علاوه بر سه جدول posts و videos و tags بایستی جدول دیگری به نام taggables نیز بنویسیم. این جدول چیزی مشابه جدول واسطه است.
- پیاده سازی
جدول posts:
$table->bigIncrements('id'); $table->string('title'); $table->longText('body');
جدول videos:
$table->bigIncrements('id'); $table->string('title'); $table->string('path');
جدول tags:
$table->bigIncrements('id'); $table->string('name');
و اما جدول taggables:
$table->integer('tag_id'); $table->integer('tagable_id'); $table->string('tagable_type');
در مدل Tag متدهای زیر را می نویسیم:
public function posts() { return $this->morphedByMany('App\Post', 'taggable'); } public function videos() { return $this->morphedByMany('App\Video', 'taggable'); }
در هر دو مدل Video و Post متد زیر را مینویسیم:
public function tags() { return $this->morphToMany('App\ Tag', 'taggable'); }
برای فراخوانی تگهای یک پست:
$post = Post::first(); $tags = $post->tags;
یا جهت فراخوانی تگ های یک ویدیو:
$video = \App\Video::first(); $tags = $video->tags;
ولی برای فراخوانی ویدیوهای مربوط به یک تگ:
$tag = Tag::first(); $videos = $tag->videos;
نتیجه گیری
در نهایت، روابط در لاراول با بهرهگیری از Eloquent ORM، ابزاری قدرتمند برای مدیریت و سادهسازی ارتباط بین دادهها در پایگاه داده فراهم میکنند. این ویژگی به توسعهدهندگان کمک میکند تا با نوشتن کدی خوانا، قابل فهم و منظم، منطق پیچیدهی دادهها را به شکلی شیءگرایانه پیادهسازی کنند. آموزش رابطه ها در لاراول میتواند به درک بهتر این مفاهیم کمک کرده و مهارت لازم برای پیادهسازی آنها را فراهم کند. درک درست انواع روابط و استفاده صحیح از آنها، نقش کلیدی در طراحی ساختار بهینهی اپلیکیشنهای لاراولی دارد و موجب افزایش بهرهوری، کاهش خطا و تسریع در فرآیند توسعه میشود.
اقا انصافا دمتون گرم ایول