2013-06-15 3 views
2

У меня есть объект, который расширяет страницу («Thing»), которая имеет многозначные отношения с DataObject («тег»).Silverstripe - фильтр DataObject список по многим отношениям

class Thing extends Page 
{ 
    static $many_many = array(
     'Tags' => 'Tag' 
    ); 
} 

class Tag extends DataObject 
{ 
    static $belongs_many_many = array(
     'Things' => 'Thing' 
    ); 
} 

У меня есть массив идентификаторов тегов, и я хочу получить список вещей, в которых есть все эти теги.

Следующая должно быть возможно ...

$tag_ids = array(1,2,3,4); 
$things = Thing::get(); 
$things->filter('Tags.ID', array($tag_ids)); 

... но это просто возвращает нефильтрованное список. По-видимому, это еще не реализовано для отношений. Итак, как я могу это сделать?

ответ

1

По соображениям производительности вам, вероятно, будет лучше полагаться как можно больше на запрос sql.

$tag_ids = array(1,2,3); 
$objects = new ArrayList(); 
if (count($tag_ids)) { 
    $sql = "SELECT \"SiteTree\".*, \"Page\".*, \"Thing\".*,count(\"ThingID\") AS ThingIDCount FROM \"SiteTree\" "; 
    $sql.= "LEFT JOIN \"Page\" ON \"Page\".\"ID\" = \"SiteTree\".\"ID\" "; 
    $sql.= "LEFT JOIN \"Thing\" ON \"Thing\".\"ID\" = \"SiteTree\".\"ID\" "; 
    $sql.= "LEFT JOIN \"Thing_Tags\" ON \"Thing_Tags\".\"ThingID\" = \"SiteTree\".\"ID\" "; 
    $sql.= "LEFT JOIN \"Tag\" ON \"Thing_Tags\".\"TagID\" = \"Tag\".\"ID\" "; 
    $sql.= "WHERE \"TagID\" IN (" . implode(',', $tag_ids) . ") GROUP BY \"ThingID\" HAVING ThingIDCount >= " . count($tag_ids); 

    // Get records 
    $records = DB::query($sql); 
    foreach($records as $record) { 
     $objects->push(new Thing($record)); 
    } 
} 



// Display the Things for demo purposes 
foreach($objects as $thing){ 
    Debug::Dump($thing); 
} 

NB Я добавил левый присоединиться к столу Thing, как я полагаю, вы имеете несколько полого БД на нем, но падение линии (и \ «Вещь \». * На ЗЕЬЕСТЕ), если это не тот случай

1

Вы можете попробовать сделать следующее:

Tag::get()->byIds($tag_ids)->relation('Things') 

, который будет возвращать ManyManyList, что вы можете перебор, т.е.

foreach(Tag::get()->byIds($tag_ids)->relation('Things') as $thing){ 
    Debug::Dump($thing); // A 'Thing' object 
} 

Надеется, что это помогает

+0

Хммм ... на вторых мыслях, что это не совсем так. Это соответствует тем, которые имеют любой из выбранных тегов, а не все теги. –

+0

, возможно, стоит изменить вопрос тогда, поскольку не ясно, что все теги должны быть прикреплены к «предмету» – jfbarrois

+0

Да, мое плохое. Спасибо, в любом случае! –

2

Я не вижу простое решение делает это непосредственно с ORM. Но вы должны решить это с помощью некоторых циклов и фильтрации.

$tag_ids = array(1,2,3,4); 
$things = Thing::get(); 
$results = new ArrayList(); 

$tags_count = count($tag_ids); 
$matches = 0; 

foreach ($things as $thing) 
{ 
    foreach ($thing->Tags() as $tags) 
    { 
     $matches = 0; 
     foreach ($tags as $tag) 
     { 
      if (in_array($tag->ID, $tag_ids)) 
      { 
       $matches++; 
      } 
     } 
     if ($matches === $tags_count) 
     { 
      $results->push($thing); 
     } 
    } 
} 

Хотя не проверено, это должно оставить вас с $ things, содержащим те, у которых есть все теги и многое другое. (Предполагая, что вещь может быть отмечена только один раз одним и тем же тегом).

+0

Я вижу, что это должно сделать, но строка removeByID каким-то образом обнародует все Вещи! Я не понимаю, почему! –

+0

вы правы, removeByID удалит * $ things *, так как мы работаем непосредственно с DataList. Я обновляю код, поэтому результаты хранятся в другой переменной '$ results' – colymba

3

Я думаю, что если вы используете более старые версии для SilverStripe 3, вам нужно использовать ExactMatchMulti SearchFilter.

$tag_ids = array(1,2,3,4); 
$things = Thing::get(); 
$things->filter('Tags.ID:ExactMatchMulti', $tag_ids); 
+0

Это просто дает мне« ERROR [Notice]: массив для преобразования строк в POST/home/AJAXSubmit Line 380 в/public_html/framework/model/DataList .php "и возвращает все Вещи. Я попробовал просто передать в $ tag_ids, а не обернуть это в массив. Это избавило от ошибки, но все равно дает тот же результат. –

+0

Второй аргумент 'filter' должен быть массивом, так как' $ tag_ids' уже является массивом, нет необходимости снова его обертывать. Глядя на документ, ': ExactMatchMulti', похоже, возвращает' WHERE col IN (X, Y, Z) 'подобно тому, как использует обычный фильтр и передает массив без': ExactMatchMulti'? – colymba

+0

@ shea-dawson - это модификатор ExactMatchMulti действительно нужен - я просто не мог заставить его работать с ним (3.6.1) – munomono

Смежные вопросы