Silverstripe - filter DataObject list by many many relationship

一世执手 提交于 2019-12-11 03:30:01

问题


I have an Object that extends Page ("Thing") which has a many_many relationship with a DataObject ("Tag").

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

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

I have an array of Tag IDs and I want to get a list of Things that have all of these tags attached.

The following should be possible...

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

...but this just returns an unfiltered list. Apparently this hasn't been implemented for relationships yet. So how can I do it?


回答1:


For performance reasons you would probably be better off relying as much as possible on a sql query.

$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 I have added a left join to a Thing table, as I imagine you have some db fields on it, but drop the line (and the \"Thing\".* on the SELECT statement) if that's not the case




回答2:


I think if you are using older versions for SilverStripe 3, you need to use the ExactMatchMulti SearchFilter.

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



回答3:


I don't see an easy solution do do this directly with the ORM. But you should be able to solve this with some loops and filtering.

$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);
        }
    }
}

Although not tested, this should leave you with $things that contain those with all tags and more. (Assuming a thing can only be tagged once with the same tag).




回答4:


You can try to do the following:

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

which will return a ManyManyList that you can iterate over, ie

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

Hope this helps



来源:https://stackoverflow.com/questions/17127486/silverstripe-filter-dataobject-list-by-many-many-relationship

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!