Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Made it easy to use PRG with just specific actions. #52

Merged
merged 11 commits into from
Dec 20, 2015
76 changes: 61 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,27 +32,73 @@ or running command
```

## Usage

The plugin has three main parts which you will need to configure and include in
your application.

### Table class

There are three tasks during setup in your table class. Firstly you must add a
`use` statement for the `Search\Manager`. Next you need to attach the `Search`
behaviour to your table class. Lastly you must add a `searchConfiguration`
method to your table class so that you can configure how the search will work.
behaviour to your table class. Then you have two options to work with the search
filters:

The first way is the prefered way as it works the same as many core classes as
well. In your table classes `initialize()` method call the `searchManager()`
method, it will return a search manager instance. You can now add filters to the
manager by chaining them. The first arg of the `add()` method is the field, the
second the filter using the dot notation of cake to load filters from plugins.
The third one is an array o filter specific options.

```php
use Search\Manager;

class ExampleTable extends Table {

public function initialize(array $config)
{
parent::initialize();
// Add the behaviour to your table
$this->addBehavior('Search.Search');

$this->searchManager()
->add('author_id', 'Search.Value')
// Here we will alias the 'q' query param to search the `Articles.title`
// field and the `Articles.content` field, using a LIKE match, with `%`
// both before and after.
->add('q', 'Search.Like', [
'before' => true,
'after' => true,
'field' => [$this->aliasField('title'), $this->aliasField('content')]
])
->add('foo', 'Search.Callback', [
'callback' => function ($query, $args, $manager) {
// Modify $query as required
}
]);
}
```

The old way is to add a `searchConfiguration()` method to the class. The
behavior will look if such a method exists and if yes use it to get the search
manager instance from it. This method **must** return a search manager instance.

If you want to change the name of the method, or have multiple methods and
switch between them, you can configure the name of the method by setting the
behaviors option `searchConfigMethod` to the name of the method you want.

```php
use Search\Manager;

class ExampleTable extends Table {

public function initialize(array $config)
{
// Add the behaviour to your table
$this->addBehavior('Search.Search');
}
public function initialize(array $config)
{
// Add the behaviour to your table
$this->addBehavior('Search.Search');
}

// Configure how you want the search plugin to work with this table class
// Configure how you want the search plugin to work with this table class
public function searchConfiguration()
{
$search = new Manager($this)
Expand Down Expand Up @@ -86,8 +132,8 @@ accomodate this.
public function index()
{
$query = $this->Articles
// Use the plugins 'search' custom finder and pass in the
// processed query params
// Use the plugins 'search' custom finder and pass in the
// processed query params
->find('search', $this->Articles->filterParams($this->request->query))
// You can add extra things to the query if you need to
->contain(['Comments'])
Expand All @@ -105,16 +151,15 @@ Then add the Search Prg component to the necessary methods in your controller.

:warning: Make sure,
* That you add this in the controller's `initialize()` method.
* That you only add this to methods which are using search, such as your `index()` method.
* That you only add the methods which are using search, such as your `index()` method.

```php
public function initialize()
{
parent::initialize();

if ($this->request->action === 'index') {
$this->loadComponent('Search.Prg');
}
$this->loadComponent('Search.Prg', [
'actions' => ['index']
]);
}
```

Expand Down Expand Up @@ -164,6 +209,7 @@ easily create the search results you need. Use:
- ``callback`` to produce results using your own custom callable function

## Optional fields

Sometimes you might want to search your data based on two of three inputs in
your form. You can use the `filterEmpty` search option to ignore any empty fields.

Expand Down
61 changes: 53 additions & 8 deletions src/Controller/Component/PrgComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,71 @@
namespace Search\Controller\Component;

use Cake\Controller\Component;
use Cake\Event\Event;

class PrgComponent extends Component
{

/**
* Default config
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doc block could do with some explanation in my opinion about the actions configuration setting. Due to it possibly containing bool, string or array. From reading the code I assume you configure which controller actions you want the PrgComponent to run on, in which case wouldn't index be the default as it's the most used use-case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not using index as default because of backward compatibility. If you don't do the check manually in the controller it will apply to all actions by default.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it applies to all actions, edit and add actions using post will have their form data removed, which I think will be confusing for beginners.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree with that. This "WTF?" happened to me as well when I started using the plugin. IMHO you should have to explicit set the actions instead of the component processing all by default.

*
* ### Options
* - `actions` Method name(s) of actions to use PRG. Or bool for all or none.
* You can pass a single action as string or multiple as array. Default is
* true and all actions will be processed by the component.
*
* @var array
*/
protected $_defaultConfig = [
'actions' => true,
];

/**
* Checks if the current request has posted data and redirects the users
* to the same action after converting the post data into GET params
*
* @return void|Cake\Network\Response
* @return void|\Cake\Network\Response|null
*/
public function startup()
{
$controller = $this->_registry->getController();
$request = $controller->request;
if ($this->_actionCheck()) {
return $this->conversion();
}
}

if (!$request->is('post')) {
$request->data = $request->query;
return;
/**
* POST to GET / GET to POST conversion
*
* @param bool $redirect Redirect on post, default true.
* @return \Cake\Network\Response|null
*/
public function conversion($redirect = true)
{
if (!$this->request->is('post')) {
$this->request->data = $this->request->query;
return null;
}
if ($redirect) {
return $this->_registry->getController()->redirect(
$this->request->params['pass'] + ['?' => $this->request->data]
);
}
return null;
}

return $controller->redirect($request->params['pass'] + ['?' => $request->data]);
/**
* Checks if the action should be processed by the component.
*
* @return bool
*/
protected function _actionCheck()
{
$actions = $this->config('actions');
if (is_bool($actions)) {
return $actions;
}
if (is_string($actions)) {
$actions = [$actions];
}
return in_array($this->request->action, $actions);
}
}
20 changes: 17 additions & 3 deletions src/Model/Behavior/SearchBehavior.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ public function findSearch(Query $query, array $options)
$options = $options['search'];
}

foreach ($this->_table->{$this->config('searchConfigMethod')}()->all() as $config) {
$filters = $this->_getAllFilters();
foreach ($filters as $config) {
$config->args($options);
$config->query($query);
$config->process();
Expand All @@ -67,8 +68,7 @@ public function findSearch(Query $query, array $options)
*/
public function filterParams($params)
{
$valid = $this->_table->{$this->config('searchConfigMethod')}()->all();
return ['search' => array_intersect_key($params, $valid)];
return ['search' => array_intersect_key($params, $this->_getAllFilters())];
}

/**
Expand All @@ -83,4 +83,18 @@ public function searchManager()
}
return $this->_manager;
}

/**
* Gets all filters from the search manager.
*
* @return array An array of filters for the defined fields.
*/
protected function _getAllFilters()
{
$method = $this->config('searchConfigMethod');
if (method_exists($this->_table, $method)) {
return $this->_table->{$method}()->all();
}
return $this->searchManager()->all();
}
}
20 changes: 20 additions & 0 deletions tests/TestCase/Controller/Component/PrgComponentTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,25 @@ public function testInitializePost()

$response = $this->Prg->startup();
$this->assertEquals('http://localhost/index/pass?foo=bar', $response->header()['Location']);

$this->Prg->config('actions', false);
$response = $this->Prg->startup();
$this->assertEquals(null, $response);

$this->Prg->config('actions', 'does-not-exist', false);
$response = $this->Prg->startup();
$this->assertEquals(null, $response);

$this->Prg->config('actions', 'index', false);
$response = $this->Prg->startup();
$this->assertEquals('http://localhost/index/pass?foo=bar', $response->header()['Location']);

$this->Prg->config('actions', ['index', 'does-not-exist'], false);
$response = $this->Prg->startup();
$this->assertEquals('http://localhost/index/pass?foo=bar', $response->header()['Location']);

$this->Prg->config('actions', ['does-not-exist'], false);
$response = $this->Prg->startup();
$this->assertEquals(null, $response);
}
}