Jump to content

Question

Posted

Hi,

Is there a way to extend custom search the way I need?
For example, in some  plugins I have additional information attached to client and I want to be able to find clients thru Smart Search using only this information.

10 answers to this question

Recommended Posts

  • 0
Posted

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.

  • 0
Posted

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

  • 0
Posted

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

  • 0
Posted

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'])
            );
        }
    }
}

 

  • 0
Posted

@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;
    }
 
}


 

  • 0
Posted

@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.

  • 0
Posted
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. :) 

  • 0
Posted
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

  • 0
Posted

@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 :)

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...