Jump to content
  • 0

Custom Smart Search


Anton Qcl

Question

10 answers to this question

Recommended Posts

  • 0

The smart search can't be modified other than selecting which standard criteria is used for the smart search. Your best bet is to create a plugin that implements its own search, then you can configure it to search however you want. The plugin would create a new search option.

Link to comment
Share on other sites

  • 0

Paul, thank you for answer.
I also think I can implement much more dirty but convinient for customers solution: add some js on the page which intercept smart search form submitting, then do necessary search thru ajax.
I'll share the code here if somebody will face the same requirement

Link to comment
Share on other sites

  • 0

Paul, I actually tried to implement your idea.
Action I took:
 

1. Added row into config/routes.php

Router::route('^' . $admin_loc . '/search/?$', 'atn_smartsearch/atn_admin_search');

2. Created plugin atn_smartsearch with controller atn_admin_search.php. Code of the AtnAdminSearch Controller:

<?php

class AtnAdminSearch extends AdminSearch 
{
    public function preAction()
    {
        // do nothing
    }
    
    public function index()
    {
        die('cathced index');
    }
}


Please, take a look at this code. I expected that I can extend AdminSearch and redefine  function index() to implement my own algo of search. 
This code works but, as you can see, I also redefined function preAction(). I did not want to do it but without it system just redirect me to the main admin page.
I'm little bit confused because I did not find any errors in the logs which would indicate what I have done wrong. 
Can you, please, give me some advises?

PS I've also added a video

Link to comment
Share on other sites

  • 0

I think the best way to implement your own custom search is by using events and not a route. 

To implement it using events you need to register the event in the *_plugin.php file as in the following example: In this example we register the search() function from the AdminMain controller.

class SearchPlugin extends Plugin {
	
    // ...

    public function customSearch($event)
    {
        $params = $event->getParams();

        if (isset($params['options'])) {
            $params['options'] += [
                $params['base_uri'] . 'plugin/search/admin_main/search/' => 'Custom Search'
            ];
        }

        $event->setParams($params);
    }

    public function getEvents()
    {
        return [
            [
                'event' => 'Navigation.getSearchOptions',
                'callback' => ['this', 'customSearch']
            ]
        ];
    }
}

Now we need to create the AdminMain controller and implement the search() method, which will perform the search:

<?php
class AdminMain extends AppController
{
    public function preAction()
    {
        parent::preAction();

        $this->requireLogin();

        // Load the languages, models, components
        // and helpers needed by this controller.
    }

    public function index()
    {
        // There is nothing to see here
        $this->redirect($this->base_uri);
    }

    public function search()
    {
        // Restore structure view location of the admin portal
        $this->structure->setDefaultView(APPDIR);
        $this->structure->setView(null, $this->orig_structure_view);

        // Set page title
        $this->structure->set('page_title', 'My Custom Search');

        // Implement your amazing code here to do the search!

        if ($this->isAjax()) {
            return $this->renderAjaxWidgetIfAsync(
                isset($this->post['search']) ? null : isset($this->get['search'])
            );
        }
    }
}

 

Link to comment
Share on other sites

  • 0

@Abdy, good idea  but in this case client need to do two additional actions every time he wants to use custom search. It is not acceptable. Probably I could've try to go this way if there was an option to change default search. Right now it is 'Smart Search' and I did not find the way to change it. Is it possible?
However, I like the idea of using events, so I did it.

Following code monitor event Appcontroller.preAction and fire custom search function if detect that url is /admin/search. If clients are found, function will redirect user to the first client's page.
It is crutchy but, unfortunately, I did not find the way to get the controller in my function interceptAdminSearch - I could possible use only var $event which does not contain any useful information.

<?php

class AtnSmartsearchPlugin extends Plugin 
{
    public function __construct() 
    {
        $this->loadConfig(dirname(__FILE__) . DS . "config.json");
    }
    
    public function getEvents()
    {
        return [
            [
                'event'     => "Appcontroller.preAction",
                'callback'  => ["this", "interceptAdminSearch"]
            ]
        ];
    }
 
    public function interceptAdminSearch()
    {
        if (
            strpos($_SERVER['REQUEST_URI'], "/admin/search") === 0
            && $_SERVER['REQUEST_METHOD'] === "POST"
        ) {
            $this->searchClients();
        }
    }
    
    public function searchClients()
    {
        $this->requireLogin();
        
        $value = htmlentities(strip_tags($_POST['search']));
        $clients = $this->myFunctionToSearchClients($value);        
        
        if (!empty($clients)) {
            $client = array_pop($clients);                        
            $this->redirect($client->url);
        }
    }
    
    
    
    
    // Just an example, not a real function
    private function myFunctionToSearchClients($value)
    {
        if ($value == "example") {
            return [
                (object)['url'=>"/admin/clients/view/1/", 'name'=>"Test Client1"],
                (object)['url'=>"/admin/clients/view/2/", 'name'=>"	TestClient2"],
            ];
        }
    }

    private function requireLogin()
    {        
        if (empty($_SESSION['blesta_staff_id'])) {
            $this->redirect("/admin/login");
        }
    }
    
    private function redirect($url)
    {
        header("Location: {$url}");
        die();        
    }
}


Technically, this approach solves my problem but I would like to show information about other found clients somehow. It would be ok if I could add it in a flash message, but method flashMessage() is protected and defined in AppController.
I tried to create custom controller inside my plugin but it did not work - no errors, but no flash messages after using it:
 

<?php

class CustomSearch extends AppController
{
    public function setFlashMessage($message) 
    {
        $this->flashMessage("message", $message, ['preserve_tags'=>true], false);
    }
}
<?php

class AtnSmartsearchPlugin extends Plugin 
{    
    public function searchClients()
    {
        ...
        
        if (!empty($clients)) {
            $client = array_pop($clients);
            
            if (count($clients) > 0) {
                $this->addClientsInfoToFlashMessages($clients,);
            }        
                        
            ...
        }
    }

    
    private function addClientsInfoToFlashMessages($clients,)
    {
        $clientsInfo = array_map(function($client) {
            return "<a href='{$client->url}'>{$client->name}</a>";
        }, $clients);
        $clientsInfo = implode(nl2br(PHP_EOL), $clientsInfo);
        $message = "Also found clients:<br>{$clientsInfo}";
         
        $controller = $this->loadCustomSearchController();  
        $controller->setFlashMessage($message);
    }
    
    private function loadCustomSearchController()
    {
        require __DIR__ . "/controllers/custom_search.php";
        $controller = new \CustomSearch(null, null, false);
        
        return $controller;
    }
 
}


 

Link to comment
Share on other sites

  • 0

@Abdy, @Paul, I also found crutchy way to handle the case if we found few clients:

  1. Store results in session
  2. Handle event AppController.strucrure:
    1. Encode results and publish them as JS var
    2. inject JS script for handling result
  3. And then in JS script:
    1. Check if we are on the search results page
    2. Check if we have additional results
    3. Create html for row and append rows to the table. Create table if table does not exist.

 

I can publish the code if you or somebody else need it.

Link to comment
Share on other sites

  • 0
18 hours ago, Anton Qcl said:

@Abdy, @Paul, I also found crutchy way to handle the case if we found few clients:

  1. Store results in session
  2. Handle event AppController.strucrure:
    1. Encode results and publish them as JS var
    2. inject JS script for handling result
  3. And then in JS script:
    1. Check if we are on the search results page
    2. Check if we have additional results
    3. Create html for row and append rows to the table. Create table if table does not exist.

 

I can publish the code if you or somebody else need it.

You are welcome to publish the code if it may be helpful to others. I can imagine someone stumbling on this thread later and wishing it was included. :) 

Link to comment
Share on other sites

  • 0
On 7/27/2020 at 3:31 PM, Anton Qcl said:

@Abdy, good idea  but in this case client need to do two additional actions every time he wants to use custom search. It is not acceptable. Probably I could've try to go this way if there was an option to change default search. Right now it is 'Smart Search' and I did not find the way to change it. Is it possible?
However, I like the idea of using events, so I did it.

Following code monitor event Appcontroller.preAction and fire custom search function if detect that url is /admin/search. If clients are found, function will redirect user to the first client's page.
It is crutchy but, unfortunately, I did not find the way to get the controller in my function interceptAdminSearch - I could possible use only var $event which does not contain any useful information.


<?php

class AtnSmartsearchPlugin extends Plugin 
{
    public function __construct() 
    {
        $this->loadConfig(dirname(__FILE__) . DS . "config.json");
    }
    
    public function getEvents()
    {
        return [
            [
                'event'     => "Appcontroller.preAction",
                'callback'  => ["this", "interceptAdminSearch"]
            ]
        ];
    }
 
    public function interceptAdminSearch()
    {
        if (
            strpos($_SERVER['REQUEST_URI'], "/admin/search") === 0
            && $_SERVER['REQUEST_METHOD'] === "POST"
        ) {
            $this->searchClients();
        }
    }
    
    public function searchClients()
    {
        $this->requireLogin();
        
        $value = htmlentities(strip_tags($_POST['search']));
        $clients = $this->myFunctionToSearchClients($value);        
        
        if (!empty($clients)) {
            $client = array_pop($clients);                        
            $this->redirect($client->url);
        }
    }
    
    
    
    
    // Just an example, not a real function
    private function myFunctionToSearchClients($value)
    {
        if ($value == "example") {
            return [
                (object)['url'=>"/admin/clients/view/1/", 'name'=>"Test Client1"],
                (object)['url'=>"/admin/clients/view/2/", 'name'=>"	TestClient2"],
            ];
        }
    }

    private function requireLogin()
    {        
        if (empty($_SESSION['blesta_staff_id'])) {
            $this->redirect("/admin/login");
        }
    }
    
    private function redirect($url)
    {
        header("Location: {$url}");
        die();        
    }
}


Technically, this approach solves my problem but I would like to show information about other found clients somehow. It would be ok if I could add it in a flash message, but method flashMessage() is protected and defined in AppController.
I tried to create custom controller inside my plugin but it did not work - no errors, but no flash messages after using it:
 


<?php

class CustomSearch extends AppController
{
    public function setFlashMessage($message) 
    {
        $this->flashMessage("message", $message, ['preserve_tags'=>true], false);
    }
}

<?php

class AtnSmartsearchPlugin extends Plugin 
{    
    public function searchClients()
    {
        ...
        
        if (!empty($clients)) {
            $client = array_pop($clients);
            
            if (count($clients) > 0) {
                $this->addClientsInfoToFlashMessages($clients,);
            }        
                        
            ...
        }
    }

    
    private function addClientsInfoToFlashMessages($clients,)
    {
        $clientsInfo = array_map(function($client) {
            return "<a href='{$client->url}'>{$client->name}</a>";
        }, $clients);
        $clientsInfo = implode(nl2br(PHP_EOL), $clientsInfo);
        $message = "Also found clients:<br>{$clientsInfo}";
         
        $controller = $this->loadCustomSearchController();  
        $controller->setFlashMessage($message);
    }
    
    private function loadCustomSearchController()
    {
        require __DIR__ . "/controllers/custom_search.php";
        $controller = new \CustomSearch(null, null, false);
        
        return $controller;
    }
 
}


 

You can change Smart Search to another one, clicking on the magnifying glass icon.

408382899_ScreenShot2020-07-31at11_49_40AM.png.3a7bb0b7d560ed52b3e2fc9999a713d2.png

Link to comment
Share on other sites

  • 0

@Abdy, like I said earlier, this option is completely unacceptable by my client:
 * He needs to do two additional actions every search (click to open menu, another click to change the search)
 * If custom search was unsuccessful, system will not automatically use Smart Search after that.
Both of these problems are solved in my solution.


@Paul, ok, I'll publish the code after I finish the tests :)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Answer this question...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
×
×
  • Create New...