در حال ارتباط با سرور...




لطفا نظرات و پيشنهادات خود را بمنظور ارتقاء کيفي هرچه بيشتر سايت با ما در ميان بگذاريد.

ویدیوها مقالات کتاب ها اخبار پرسش و پاسخ
برنامه‌های رومیزی مبتنی بر وب زبان های برنامه سازی پایگاه داده سیستم عامل شبکه 
مقاله بگذارید
توسط : hamedkh دسته بندی : مبتنی بر وب تاریخ : 1391-6-9 13:21:58

در مقاله های گذشته دیدیم که چگونه می توان از طریق Active Record با یک جدول از بانک اطلاعاتی کار کرد.اما در بسیاری از سناریو ها ممکن است بخواهیم چندین جدول را با یکدیگر الحاق (join) کنیم.در این مقاله قصد داریم به چگونگی انجام این کار توسط AR ها بپردازیم.

برای استفاده از چنین AR هایی که AR های رابطه ای نامیده می شوند (relational AR)، برای جدول هایی که نیاز به الحاق دارند ابتدا کلید خارجی تعریف کرد.همانطور که می دانید این کلید های خارجی به حفظ یکپارچگی و جامعیت بانک اطلاعاتی شما کمک می کنند.

برای ساده تر شدن کار، در این مقاله از ساختار بانک اطلاعاتی زیر که در قالب نمودار ER نمایش داده شده است استفاده می کنیم.تمامی مثال هایی که در قسمت آورده خواهد شد بر اساس همین شما (schema) خواهد بود.

 

 

توجه داشته باشید که پشتیبانی از کلید خارجی در بانک های اطلاعاتی مختلف متفاوت است و برخی از آنها مانن موتود MyISAM در MySQL اصلا از این کلید ها پشتیبانی نمی کنند.

تعریف رابطه:

پیش از آنکه بتوان از یک AR برای اجرای پرس و جو های رابطه ای استفاده نمود باید مشخص کنیم که ارتباط یک AR با کلاس های دیگر خواهد بود.

ارتباطی که بین کلاس های AR وجود دارد بطور مستقیم مربوط می شود که ارتباط جدول هایی که بوسیله آنها نمایش داده می شوند.همانطور که می دانید ارتباط بین جدول ها در دیتابیس از سه حالت خارج نیست:

1.ارتباط یک به چند (مانند رابطه جدول های tbl_user و tbl_post)

2.ارتباط یک به یک (مانند رابطه جدول های tbl_user و tbl_profile)

3.ارتباط چند به چند (مانند جدول های tbl_category و tbl_post)

در مورد AR ها ما چهار نوع رابطه داریم:

 

·         BELONGS_TO: اگر ارتباط بین جدول A و B  از نوع یک به چند باشد، می گوییم B متعلق است به A (مثلا می گوییم Post متعلق است به User)

·         HAS_MANY: اگر ارتباط بین A و B از نوع یک به چند باشد، گوییک A چندین B دارد (User چندین Post دارد).

·         HAS_ONE: این رابطه حالت خاصی از HAS_MANY است که در آن A حداکثر یک B داشته باشد (مثلا User حداکثر یک Profile دارد).

·         MANY_MANY: این حالت مربوط به ارتباط چند به چند در بانک اطلاعاتی است.البته توجه داشته باشید از آنجایی که اغلب پایگاه های داده مستقیما از رابطه چند به چند پشتیبانی نمی کنند، این گونه روابط باید به چندین روابط یک به چند شکسته شوند.در مثال ما جدول tbl_post_category این هدف را برای ما انجام می دهد.در AR ها می توانیم ارتباط MANY_MANY را بعنوان ترکیبی از BELONGS_TO و HAS_MANY تعریف کنیم.برای مثال Post متعلق است به چندین Category و Category چندی Post دارد.

برای تعریف رابطه در AR باید متد relations() را از CActiveRecord باز نویسی نمود.این متد آرایه ای را باز می گرداند که تنظیمات همین رابطه هاست.هرکدام از عناصر این آرایه نماینده یک ارتباط می باشد که در قالب زیر تعریف می گردد:

 

'VarName'=>array('RelationType', 'ClassName', 'ForeignKey', ...additional options)

در این قالب varName نامی است که برای این رابطه انتخاب می کنیم، RelationType نوع رابطه ما می باشد که یک از چهار مقدار زیر را می تواندبخود بگیرد :

 self::BELONGS_TO, self::HAS_ONE, self::HAS_MANY, self::MANY_MANY

پارامتر ClassName نام AR ای را مشخص می کنید که به این کلاس AR مرتبط است و ForeignKey نیز کلید خارجی مورد استفاده در رابطه را معلوم می نماید.علاوه بر این موارد پارامتر های اختیاری دیگری نیز می توان به انتهای آرایه اضافه نمود که در بخش های بعدی توضیع داده خواهند شد.

برای آنکه بدانیم چگونه باید این ارتباطات را تعریف کنیم نگاهی به کد زیر می اندازیم که ارتباطاتی را برای کلاس های User و Post تعریف می نماید:

 

class Post extends CActiveRecord
{
    ......
 
    public function relations()
    {
        return array(
            'author'=>array(self::BELONGS_TO, 'User', 'author_id'),
            'categories'=>array(self::MANY_MANY, 'Category',
                'tbl_post_category(post_id, category_id)'),
        );
    }
}
 
class User extends CActiveRecord
{
    ......
 
    public function relations()
    {
        return array(
            'posts'=>array(self::HAS_MANY, 'Post', 'author_id'),
            'profile'=>array(self::HAS_ONE, 'Profile', 'owner_id'),
        );
    }
}

توجه داشته باشید که یک کلید خارجی می تواند بصورت ترکیبی از دو یا چند ستون باشد.در چنین حالتی باید نام ستون ها را مشخص کرده و آنها را با کاما از یکدیگر جدا کرد و یا می توان آن را بصورت آرایه ای مانند array(‘key1’,’key2’)  مشخص نمود.در شرایطی که نیاز است روابط PK->FK تعریف شود باید آرایه را بصورت array(‘fk’=>’pk’) تعریف کرد.برای کلید های ترکیبی در این حالت آرایه ای به این صورت خواهیم داشت :

 

array('fk_c1'=>'pk_c1','fk_c2'=>'pk_c2')

در روابط MANY_MANY دقت کنید که جدول مرتبط باید بهمراه کلید خارجی مشخص گردد.برای مثال رابطه categories  مربوط به Post با کلید خارجی tbl_post_category(post_id, category_id) تعریف می شود.تعریف و اعلان روابط در کلاس AR بطور ضمنی خاصیتی را به ازای هر رابطه به کلاس اضافه می کند.پس از آنکه پرس و جویی رابطه ای اجرا شد، خاصیت ذکر شده با نمونه AR ایجاد شده مقدار دهی می شود.برای مثال اگر $author نمونه ای از یک User AR باشد، می توانیم با استفاده از $author->posts به نمومه های Post مرتبط با Author دسترسی داشته باشیم.

 

اجرای پرس و جو های رابطه ای :

ساده ترین روش برای اجرای پرس و جو های رابطه ای این است که خاصیت رابطه ای مربوط به نمونه AR را بخوانیم.در صورتی که این خاصیت قبلا مورد دستیابی قرار گرفته نشده باشد، یک پرس و جوی رابطه ای برای نخستین بار اجرا می شود.در اثر این اجرا دو جدول  مرتبط به هم با یکدیگر join می شوند و بر اساس کلید اصلی نمونه AR جاری فیلتر می شوند.نتیجه این پرس و جو در خاصیتی از کلاس AR مربوطه ذخیره می شود.به این روش Lazy-loading می گویند.در مثال زیر روش این کار آورده شده است:

 

// retrieve the post whose ID is 10
$post=Post::model()->findByPk(10);
// retrieve the post's author: a relational query will be performed here
$author=$post->author;

در صورتی که هیچ نمونه مرتبطی یافت نشد، آرایه ای خالی و یا null باز می گردد.در مورد روابط نوع BELONGS_TO و HAS_ONE نتیجه بازگشتی null می باشد.این در حالی است که در روابط MANY_MANY و HAS_MANY این نتیجه یک آرایه خالی است.توجه کنید که روابط HAS_MANY و MANY_MANY آرایه ای از اشیاء را باز می گردانند، در این حالت شما باید پیش از دستیابی به خاصیت های بازگشتی توسط حلقه تکرار این آرایه را پیمایش کنید.در غیر این صورت خطای Trying to get property of non-object” را دریافت خواهید کرد.

اگر چه استفاده از روش Lazy-loading  بسیار آسان و راحت است، اما در برخی از سناریو ها ناکار آمد می باشد.برای مثال اگر بخواهید به اطلاعات مربوط نوسند گان N پست (Post) دست پیدا کنیم، به تعداد N  پرس و جوی join اجرا خواهد شد که سربار سنگینی را به سیستم وارد می کند.در چنین شرایطی بهتر است از eager-loading استفاده کنیم.

راهکار eager-loading نمونه های AR مرتبط را بهمراه نمونه AR اصلی باز یابی می کند.این کار با استفاده از متد with() بهمراه یکی از متد های find() و یا findAll() انجام می پذیرد.بعنوان مثال:

 

$posts=Post::model()->with('author')->findAll();

کد بالا آرایه از نمونه های  Post را به ما باز می گرداند.برخلاف روش Lazy-loading، در این روش خاصیت author در هر نمونه از Post،پیش از انجام دسترسی به آن بر اساس نمونه User مربوطه مقدار دهی می شود.بجای آنکه برای هر پست یک پرس و جوی join انجام شود، راهکار eager-loading تمامی پست ها (post) را بهمراه نویسندگانشان با استفاده از تنها یک پرس و جوی join باز می گرداند که باعث وارد آمدن سربار کمتری به سیستم می شود.

این امکان وجود دارد که چندین رابطه را در متد with() بیاوریم و بدین صورت نمونه های مرتبط به آنها همگی در یک تلاش باز گردانده می شوند.برای مثال در تکه کد زیر پست ها بهمراه نویسندگان و گروه های آنها باز گردانده خواهد شد.

 

$posts=Post::model()->with('author','categories')->findAll();

این امکان وجود دارد که Eager-loading را بصورت تو در تو اجرا کنیم.بجای لیستی از اسامی رابطه ها، آنها را در قالبی سلسله مراتبی به متد with می فرستیم.مانند آنچه در مثال زیر داریم:

 

$posts=Post::model()->with(
    'author.profile',
    'author.posts',
    'categories')->findAll();

در مثال بالا تمامی پست ها بهمراه نویسندگان و گروه های آنها باز گردانده می شود.علاوه بر اینها پروفایل مربوط به هر پست و نویسنده نیز باز گردانده می گردد.

 

می توان بجای استفاده از کد بالا eager-loading را با استفاده از تعریف خاصیت CDbCriteria::with بصورت زیر اجرا نمود:

 

$criteria=new CDbCriteria;
$criteria->with=array(
    'author.profile',
    'author.posts',
    'categories',
);
$posts=Post::model()->findAll($criteria);

همچنین می توان این کار را بصورت زیر انجام داد

 

$posts=Post::model()->findAll(array(
    'with'=>array(
        'author.profile',
        'author.posts',
        'categories',
    )
));

اجرای پرس و جو های رابطه ای بدون بازگرداندن مدل های مرتبط:

در برخی از کاربرد ها نیاز است پرس و جویی را با استفاده از رابطه ای انجام دهیم اما ممکن است نخواهیم مدل های مرتبط را دریافت کنیم.برای مثال این طور در نظر بگیرید که کاربر ها (User) داریم که پست های مختلفی را در سیستم درج کرده اند.این پست ها و مطالب می توانند هم در وضعیت منتشر شده باشند و هم در وضعیت پیش نویس و تایید نشده.این حالت ها با استفاده از فیلد published در مدل Post مشخص می شوند.حال فرض کنیم کاربرانی را می خواهیم که پست های آنها در وضعیت منتشر شده هستند ولی نیازی داشتن پست های انها نداریم.این کار را می توان با استفاده از روش زیر انجام داد:

 

$users=User::model()->with(array(
    'posts'=>array(
        // we don't want to select posts
        'select'=>false,
        // but want to get only users with published posts
        'joinType'=>'INNER JOIN',
        'condition'=>'posts.published=1',
    ),
))->findAll();

موفق و پیروز باشید

hamedkh
5.8 k     1     18     72
نظرات

دمت گرم

عالی بود

آموزش زبان برنامه نویسی C#
آموزش jquery

آموزش زبان جاوا Java
آموزش زبان انگلیسی
آموزش برنامه نویسی C
آموزش برنامه نویسی C++
آموزش جی کوئری jQuery
آموزش زبان سی شارپ C#
آموزش برنامه نویسی اندروید
آموزش برنامه نویسی اندروید
آموزش زبان اسمبلی Assembly
آموزش جاوا اسکریپت JavaScript
آموزش برنامه نویسی به زبان PHP