Jump to:

10929 Posts in 2613 Topics by 1809 members

All other Modules

SilverStripe Forums » All other Modules » CustomSearchForm snippet

Discuss all other Modules here.

Moderators: martimiz, Sean, biapar, Willr, Ingo, swaiba, simon_w

Page: 1 2 3 4 5 6
Go to End
Author Topic: 9733 Views
  • UncleCheese
    Avatar
    4085 Posts

    CustomSearchForm snippet Link to this post

    From time to time the question comes up -- how do I search on non-page objects? For example, last week, I had a client request that his GlossaryTerm dataobjects, which live on a GlossaryPage, added with a DataObjectManager, could be searchable by site search. I eventually came up with a solution. It's not the prettiest thing in the world, and not well tested, but low and behold, it works!

    Step One: Extend the SearchFormClass

    We need to overload the search function of the SearchForm class, and give it the ability to add DataObject classes to search. That class is attached to this post. You can place it in mysite/code.

    Step Two: Configure the DataObject

    To instruct the DataObject how to behave in site search, we need to add some configurations to the class.

    class GlossaryTerm extends DataObject
    {
       static $db = array(
          'Term' => 'Text',
          'Definition' => 'Text'
       );

       static $has_one = array(
          'Glossary' => 'Glossary'   
       );
       
       static $default_sort = "Term ASC";
       
       static $searchable_fields = array(
          'Term',
          'Definition'
       );
       
       static $search_heading = "Term";
       static $search_content = "Definition";
       
       static $indexes = array(
          "SearchFields" => "fulltext (Term, Definition)",
          "TitleSearchFields" => "fulltext (Term)"
       );
       
       public function Title()   
       {
          return DBField::create('Text',$this->record['Title']);
       }
       public function Content()
       {
          return DBField::create('Text',$this->record['Content']);
       }

       public function canView() {return true;}
       
       public function Link()
       {
          return DataObject::get_one("Glossary")->Link() ."#term".$this->ID;
       }
       

    Let's go through this step-by-step.

    - $searchable_fields: This tells the search class which fields will be examined for the relevance of keywords in the search.

    - $search_heading: This is the name of the field that will serve as the title, or heading, in the search results.

    - $search_content: The name of the field that will serve as the content in the search results page.

    - $indexes: "SearchFields" should be fulltext and include all the fields listed in your $searchable_fields array. "TitleSearchFields" should be fulltext and include only your $search_heading value.

    - Title() and Content() functions. The search results processor is pretty barebones. You'll get non-object errors unless you explicitly return FieldType objects.

    - canView(): because SearchForm is only designed to return SiteTree objects, you have to stick this in there to tell the SearchForm that it can be viewed.

    - Link(): How are we going to link off of the dataobject in a search result? In this case, I sent it to the Glossary page with a #ID in the url to skip down to the correct term.

    Step Three: Build the Search Form

    Nothing to it! Just change "SearchForm" to "CustomSearchForm" and add your new class!

       function SearchForm() {
          $searchText = isset($_REQUEST['Search']) ? $_REQUEST['Search'] : 'Search';
          $fields = new FieldSet(
           new TextField("Search", "", $searchText)
           );
          $actions = new FieldSet(
           new FormAction('results', 'Search')
           );

           $f = new CustomSearchForm($this, "SearchForm", $fields, $actions);
          $f->addSearchableClasses(array('GlossaryTerm'));       
           return $f;
       }

    That's it! There's a lot of room for automation in the way this is done. I wrestled with making a SearchableDataObject as a decorator, or even a subclass of DataObject, but there were numerous constraints. I'm anxious to wrestle some more with it, and I'm definitely open to any modifications or enhancements that come out of the community.

    Enjoy!

    Attached Files
  • schellmax
    Avatar
    Community Member
    126 Posts

    Re: CustomSearchForm snippet Link to this post

    this looks very promising.
    haven't tried it yet though, stumbled upon it searching for a slightly different problem:

    as mentioned in various threads, sitesearch doesn't look into fields other then content and title.
    as far as i can see, the customsearchform you posted here won't lift this restriction: although related dataobjects are searched, custom fields (using static $db = array()) are still unsearchable?

    did i get something wrong? can your solution here be used/modified to search in custom fields as well?

  • UncleCheese
    Avatar
    4085 Posts

    Re: CustomSearchForm snippet Link to this post

    Totally possible. There's no reason why you can't assign all those static vars to a SiteTree object. There is one thing that would need to be updated in the CustomSearchForm code, though.

  • schellmax
    Avatar
    Community Member
    126 Posts

    Re: CustomSearchForm snippet Link to this post

    nice!
    could you give me a little hint what needs to be updated in CustomSearchForm code?
    i'll try then, if i succeed, there's a number of threads that can be marked as 'Solved' then, such as

    http://www.silverstripe.org/archive/show/196669
    http://silverstripe.org/customising-the-cms/show/256621
    http://www.silverstripe.org/general-questions/show/255129
    http://www.silverstripe.org/form-questions/show/256689
    http://www.silverstripe.org/archive/show/12423
    ...

    thanks a lot

  • schellmax
    Avatar
    Community Member
    126 Posts

    Re: CustomSearchForm snippet Link to this post

    unclecheese, sure you're working to capacity supporting dataobjectmanager and i really don't want to disturb you there (this pays out for everyone of us!)
    but as you mentioned that there's only one thing to be updated in CustomSearchForm code to make it search in custom fields too, i'm still curious wheter it's really that easy?
    and one more: will it work for the new translatable too (searching in only one language tree)?
    thanks

  • UncleCheese
    Avatar
    4085 Posts

    Re: CustomSearchForm snippet Link to this post

    You know, now that I look at it, I think you might be okay. Can you try going through the process with a regular SiteTree object? Follow the steps exactly as they are -- that is, you'll still have to tell the search form which are your Title and Content fields. We can update that later, but for now, the class is expecting a DataObject, so it doesn't make any assumptions. The key for you is going to be $indexes and $searchable_fields. Try it out and let me know how you do.

  • Apophenian
    Avatar
    Community Member
    46 Posts

    Re: CustomSearchForm snippet Link to this post

    I had to fiddle with it a bit to get it working with regular site tree objects, but I have attached my version of customsearchform, and it seems to work for me for sitetree objects and normal dataobjects.

    I'm wondering if it is worth spending some time building a proper inverted index search engine.

    Does anyone have any thoughts?

    Attached Files
  • Shawn Parr
    Avatar
    Community Member
    60 Posts

    Re: CustomSearchForm snippet Link to this post

    I've been working on this, and I have it going 99%, but I need some help with that last 1%.

    Where I seem to have run into trouble is that my Dataobjects already have a link function. Many of the pages I build using dataobjects allow clicking on them to display them in their own page. As such I have a function like:

    function Link() {
    return $this->NewsPage()->Link('article/'.$this->ID);
    }

    If I leave that then the links in my search results don't work, as it seems when the search is run the context for $this-> doesn't return a path that is usable. I.e. on my test system a valid url would be http://server/shawn/news/home/article/10 and that returns http://server/shawn/news//article/10.

    I also can't use the example Uncle Cheese gives as that hardwires the links to a specific page, and there are quite a few installs we have going, and it is very unlikely all will name their pages the same, plus the issue of having multiple pages of that type.

    So I build the following link function to try to get around this:

    function Link() {

             $a = DataObject::get_one('NewsArticle', "`NewsArticle`.ID = '{$this->ID}'");
             $p = DataObject::get_one('NewsPage', "`SiteTree`.ID = '{$a->NewsPageID}'");
             
             $l = $p->Link('article/'.$this->ID);
             return $l;
          
          }

    This almost works. It seems that when it writes the URL, it is putting an additional / in front of it, so that the browser considers it an absolute URL rather than relative and thus doesn't add it to the base href like other links on the page. So what I get are URLs like: //shawn/news/home/article/10. These links are 100% correct if the extra forward slash would not be at the beginning.

    For the time being I guess I can use a php replace statement to remove those slashes. But I was curious if anyone had a better way of doing just within SS? Is there another function or call I should use to get those links? Am I missing something drop dead obvious that would make this more of a one/two liner?

    Thanks as always to Uncle Cheese for making our sites extremely more usable.

    9733 Views
Page: 1 2 3 4 5 6
Go to Top

Want to know more about the company that brought you SilverStripe? Then check out SilverStripe.com

Comments on this website? Please give feedback.