چطوری لیست پستها با چند موضوع رو نمایش بدیم؟

[right][font="Tahoma"]با سلام

از نمونه اصلی خود یی برای گفتن سوالم استفاده میکنم.

در این نمونه چند جدول هست برای یوزر + پستها + موضوعات + جدول موضوع و پستها که آیدی پست رو به موضوعاتی مرتبط میکنه.

هدف:

نمایش لیستی از پستها با استفاده از موضوع

شروع:

با استفاه از یک آیدی مربوط به موضوعی خاص میشه پستهای اون موضوع رو نمایش داد

کد نمونه:

[left]




$model = TestPost::model()->

        with('categories')->findAll(

        "category_id = 2"

    );

foreach ($model as $model){

    echo $model->id. "- ".$model->title;

    echo "<br>";

}



[/left]

مشکل و سوال:

چطور میشه پستهای چندین موضوع رو فراخوانی کرد؟ مثلا بگوییم پستهایی که موضوع 2 و 3 رو حتما همزمان شامل میشن نمایش بده. یعنی اگر پستی موضوع 2 را داشت ولی شامل موضوع 3 نبود نمایش داده نشه؟[/font][/right]

[right][rtl][font="Tahoma"]

یه راهش استفاده از select تو در تو هست.

ولی چون درآوردن select تو در تو با criteria یکم سخت میشه (راستش پیگیری نکردم شایدم راحت باشه :D ) میشه تبدیلش کرد به دو تا پرسش:

[/font][/rtl][/right]




$postsWithCategoryOne = TestPost::model()->

        with('categories')->findAll(

        "category_id = 1"

    );


$postsWithCategoryOneIds = Helper::getIds($postsWithCategoryOne);  // ye function sade k yek array integer az primary key javab ha return mikone


$criteria = new CDbCriteria;

$criteria->with = array('categories');

$criteria->condition = " category_id = 2 ";

$criteria->addInCondition('id', $postsWithCategoryOneIds);


$finalResult = TestPost::model()->findAll($criteria);



[right][rtl][font="Tahoma"]

البته کد اجرایی نیست ها، الان نوشتم شاید مشکل داشته باشه، اما روش کار ه

[/font][/rtl][/right]

[right][right][left][right][left][right][right][left][right][rtl]نمیدونم درست متوجه شدم یا نه، ولی حس میکنم Hesam Khaki عزیز یکم پیچوندش یا از CDbCriteria استفاده میکنیم و با تابع AddCondition شروط لازمرو اضافه میکنیم مثل زیر[/rtl]


$criteria = new CDbCriteria;$criteria->with = array('categories');$criteria->addCondition("category_id = 2");$criteria->addCondition("category_id = 3");$finalResult = TestPost::model()->findAll($criteria);

[rtl]یا کد خودتون[/rtl]


$model = TestPost::model()->with('categories')->findAll("category_id = 2 AND category_id=3");

[/right][right]

[/right][right]بخدا روانی شدم، این چجور ادیتوریه دیگه این داره >؟؟؟؟؟؟؟[/right][right][color="#666600"][size="2"]هرچی میزنم هی [/size][/color][color="#666600"][size="2"][/[/size][/color][size="2"]left[/size][color="#666600"][size="2"]][/[/size][/color][size="2"]right[/size][color="#666600"][size="2"]][[/size][/color][size="2"]left[/size][color="#666600"][size="2"]][[/size][/color][size="2"]right[/size][color="#666600"][size="2"]][/size][/color][color="#666600"][size="2"][/[/size][/color][size="2"]left[/size][color="#666600"][size="2"]][/[/size][/color][size="2"]right[/size][color="#666600"][size="2"]][[/size][/color][size="2"]left[/size][color="#666600"][size="2"]][[/size][/color][size="2"]right[/size][color="#666600"][size="2"]] اضافه میکنه أه :|[/size][/color][/right][/left][/right][/right][/left][/right][/left][/right][/right]

[font="Tahoma"][right]ممنون عماد عزیز.

این کار چند درخواست ایجاد میکنه ؟ فکر میکنم دو تا کوئری میشه درسته ؟[/right][/font]

[right][rtl][font="Tahoma"]

عماد جان اگر بین اونها OR بود حق با شما بود، اما با AND اون جواب نمیده :)

من فرض رو بر این قرار دادم که بین جدول پست ها و جدول موضوعات، یک جدول میانی هست که post_id و category_id رو نگهداری میکنه، با این فرض فقط با select تو در تو میشه به اون جواب رسید

ضمنا AND کردن دو مقدار برای یک فیلد کلا بی معنی هست! مثل اینکه بگیم از جدول کاربران کسانی رو بده که نامشون حسام هست و (AND) نامشون عماد هست! خب مشخصه که پاسخی بر نمیگرده چون از دو مجموعه کاملا مجزا اشتراک گرفتیم. اگر بگیم حسام یا (OR) عماد، اجتماع گرفتیم و دیتابیس بهمون پاسخ های موجود رو برمیگردونه

[/font][/rtl][/right]

[right][font="Tahoma"]

درسته با OR همچین چیزی میشه ولی خب چیزی که من میخواستم نمیشد. دیتابیسم هم حاوی جدولی که گفتید هست. به هر حال ممنون من راهمو عوض کردم[/font][/right]

[font="Tahoma"][rtl]

من هم چند وقت پیش تو یه پروژه با همچین مساله ای مواجه شدم، راه حل معمول استفاده از select های تو در تو هست که من تا حد امکان ازشون استفاده نمیکنم بنابراین برای حل این مشکل از GROUP_CONCAT و REGEXP استفاده کردم و شرط رو به جای WHERE تو HAVING گذاشتم که کدش رو براتون میگذارم

با این فرض که ارتباط بین پستها و موضوعات همونطور که گفتید توسط یک جدول میانی با post_id و category_id انجام شده باشه میتونیم به صورت زیر عمل کنیم

[/rtl][/font]





$categoriesList=array(1,3,4); //example


$criteria= new CDbCriteria; 


$criteria->with = array('categories');


$criteria->group='t.id'; //group by post_id


$criteria->having="CONCAT('#,',GROUP_CONCAT(category_id ORDER BY category_id),',#') 

                    REGEXP '^#(,[1-9]*)*,".implode('(,[1-9]*)*,',$categoriesList)."(,[1-9]*)*,#$'";



[rtl]درسته، من فرض و بر این قرار دادم که خوب هر

پست به یه موضوع وصل شده،

واسه جدول میانی من همیشه اینطوری SQL رو مینویسم

[/rtl]

[sql]SELECT * FROM posts p LEFT JOIN post_to_category ptc ON ptc.post_id = p.id WHERE ptc.category_id IN(2,3,4);[/sql]

[rtl]حالا من نمیدونم، دوستان توی نوشتن SQL خیلی وارد هستن، روش آقا رضا خیلی جالبه،

واسم سوال شد چقد تفاوت سرعت توی Select کردن این دو روشه :-/[/rtl]

[rtl]خدا خیرتون بده یکی بگه :D[/rtl]

[right][rtl][font="Tahoma"]

آره روش آقا رضا رو بار اول بود میدیدم. تا حالا از regexp تو کوئری استفاده نکردم

روش select تو در تو بطور کلی توصیه نمیشه و حسابی به کارایی لطمه میزنه. منتها روشی که من گفتم منطق پشتش select تو در تو هست، ولی در عمل مثل همین کوئری کار میکنه که شما الان مثال زدی، یعنی یک InCondition ساده اضافه میشه به شرایط جستجوی دوم. سرعتش میشه مثل دو تا select زدن ساده روی جدول. تا الان هم تو دو تا پروژه برای جستجوی پیشرفته از همین متد استفاده کردم و زیر بار سنگین خوب جواب داده، تازه تا ۵،۶ تا شرط رو به همین روش با هم AND میکنه و پاسخ نهایی رو میده.

از لحاظ کارایی بعید نیست regex بهینه تر باشه ولی از لحاظ انعطاف و نگهداری و خوانایی کد به نظرم روش select بهتره

[/font][/rtl][/right]

[rtl]جالب انگیزه!

مرسی [/rtl]

[right][rtl]

[font="Tahoma"]من هم اولین راهی که به نظرم اومد چیزی شبیه به راهه حسام بود به نظرم راحتتر و قابل فهم تره و بعید میدونم کارایی رو پایین بیاره

راه آقا رضا هم درعین حال که جالبه اما من شخصا نمیتونم در موردش نطری بدم. فکر کنم برای این مساله یه خورده overkill باشه[/font]

[/rtl]

[/right]

[font="Tahoma"][rtl]

روش آقا حسام روش سرراست و روشنیه اما مشکل اینه که باید به تعداد موضوعات از دیتابیس کوئری بگیری و بعد اونها رو تو کوئری نهایی استفاده کنی

البته مشکلی نیست ولی فکر میکنم اگه تعداد موضوعات مورد جستجو زیاد بشه کارائیش کمی پایین بیاد

اما تو روش استفاده از GROUP_CONCAT همیشه یک کوئری میگیره و تعداد موضوعاتی که AND میشند تغییری تو تعداد کوئری ایجاد نمیکنه و شخصا فکر میکنم سریعتر باشه

در مورد قابل فهم بودن هم شاید در نگاه اول شلوغ به نظر بیاد ولی منطق خیلی ساده ای داره، به این صورت که id تمام موضوعات یک پست رو در قالب یک فیلد متنی ردیف میکنه و بعد با regular expression بررسی میکنه که آیا موضوعات مورد جستجو تو اونها هستند یا نه

البته آقا عماد sql ای که شما تو آخرین پستتون گذاشتین مساله مورد نظر رو حل نمیکنه چون موضوعات رو باهم OR میکنه ، نه AND

[/rtl][/font]

[right][rtl][font="Tahoma"]

اینکه در همه حال نیاز به اجرای یک کوئری برای رسیدن به جواب هست خیلی جالبه، ولی راستش من ایده زیادی از regex ها ندارم، تو این روش آیا میشه اون string که الان hard code شده رو به صورت داینامیک هم در آورد؟ فرض کنید اینکه پست های ما باید شامل چه موضوعاتی باشند رو در زمان اجرا متوجه شیم ـ که فرض محتملی هم هست ـ میشه regexp مناسب رو نوشت و به دیتابیس فرستاد؟

[/font][/rtl][/right]

[font="Tahoma"]

[rtl]

اگه به کد دقت کنید میبینید که دقیقا همین کار انجام شده و موضوعات مورد جستجو رو به صورت آرایه دریافت میکنه و بعد متن REGEXP رو به صورت داینامیک از رو اون میسازه پس فقط کافیه مقدار اون آرایه رو از کاربر بگیرید

به عنوان مثال برای ایجاد متن جستجو برای پستهای با موضوعات 2 و 3 و 5 به صورت داینامیک به صورت زیر عمل میکنیم

[/rtl][/font]




$categoriesList=array(2,3,5); //mishe meghdare in array ro az user dar form search gereft


$searchString="'^#(,[1-9]*)*,".implode('(,[1-9]*)*,',$categoriesList)."(,[1-9]*)*,#$'"



[font="Tahoma"][rtl]

و متنی که در این فرایند ایجاد میشه:

[/rtl][/font]

‘^#(,[1-9]),[b]2/b,[b]3/b,[b]5/b,#$’

[right][rtl][font="Tahoma"]

راستش دقت کرده بودم اما به نتیجه خاصی نرسیده بودم : ))

ممنون خیلی جالب تر شد در آینده موردی پیش اومد امتحانش میکنم من هم ;)

اون regexp رو هم باید یاد گرفت ک به درد بخوره…

[/font][/rtl][/right]

[right][font=“Tahoma”]نه به اونکه چند روز اول که من خیلی اصرار داشتم که کاری که میخوامو انجام بدم و منتظر پاسخ بودم کسی جواب نمیداد، نه به الان که من نبودم و ماشاالله دوستان مارو مستفیض کردن با نکات جالبشون. :rolleyes:

اولا باید بگم فعلا فرصت تست ندارم و سر فرصت همه گفته هارو بررسی کامل میکنم. دوم هم میخوام نکته کلیدی رو بگم و اون اینکه کمترین کوئری گرفتن و کمترین استفاده از حافظه و پردازنده مد نظرمه و نمیخوام پردازش و بار اضافی داشته باشم روی هیچ چیزی. این عادتمه که به شدت روی حذف اضافات تاکید دارم. به هر حال خوشحالم که بحث و پاسخها متنوع و در نوع خودش جالب بوده. به زودی نتیحه بررسی روی نظرات دوستان رو هم همینجا مینویسم.[/font][/right]