├── .gitignore ├── composer.json ├── src ├── Handlers │ ├── BaseHandler.php │ └── EnvironmentIndexPrefixHandler.php ├── ServiceProvider.php ├── Manager.php └── Client.php ├── LICENSE ├── config └── elasticsearch-handlers.php └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | composer.lock 3 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cviebrock/laravel-elasticsearch-handlers", 3 | "description": "Further easiness when using Elasticsearch with Laravel", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Colin Viebrock", 8 | "email": "colin@viebrock.ca" 9 | } 10 | ], 11 | "require": { 12 | "php": ">=5.4.0", 13 | "illuminate/support": "~4|~5", 14 | "cviebrock/laravel-elasticsearch": "~0.9" 15 | }, 16 | "autoload": { 17 | "psr-4": { 18 | "Cviebrock\\LaravelElasticsearchHandlers\\": "src/" 19 | } 20 | }, 21 | "minimum-stability": "beta", 22 | "prefer-stable": true 23 | } 24 | -------------------------------------------------------------------------------- /src/Handlers/BaseHandler.php: -------------------------------------------------------------------------------- 1 | config = $config; 22 | } 23 | 24 | /** 25 | * Use the underlying client to make sure all the index templates are in place. 26 | * 27 | * @param Client $client 28 | */ 29 | public function boot(Client $client) { } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/Handlers/EnvironmentIndexPrefixHandler.php: -------------------------------------------------------------------------------- 1 | app; 28 | 29 | if (version_compare($app::VERSION, '5.0') >= 0) { 30 | // Laravel 5 31 | $configPath = realpath(__DIR__ . '/../config/elasticsearch-handlers.php'); 32 | $this->publishes([ 33 | $configPath => config_path('elasticsearch-handlers.php') 34 | ]); 35 | } 36 | } 37 | 38 | /** 39 | * Register the service provider. This will rebind the base Elasticsearch Manager 40 | * with this package's version, that wraps the ES client ... allowing us to 41 | * modify the base functionality before sending data to ES or after reading it. 42 | * 43 | * @return void 44 | */ 45 | public function register() { 46 | 47 | $this->app->bindShared('elasticsearch', function ($app) { 48 | return new Manager($app, $app['elasticsearch.factory']); 49 | }); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Manager.php: -------------------------------------------------------------------------------- 1 | getHandlerClass(); 26 | 27 | // Load the handlers for this configuration. 28 | $handlers = $this->getConnectionHandlers($name); 29 | 30 | // Create a "wrapped" client passing in the appropriate configuration 31 | return new $handlerClass($baseClient, $handlers); 32 | } 33 | 34 | /** 35 | * Get the default handler class. 36 | * 37 | * @return string 38 | */ 39 | public function getHandlerClass() { 40 | return $this->app['config']['elasticsearch-handlers.defaultClass']; 41 | } 42 | 43 | /** 44 | * Get the handlers for the given connection name. 45 | * 46 | * @param string $name 47 | * @return array 48 | */ 49 | protected function getConnectionHandlers($name) { 50 | 51 | $connections = $this->app['config']['elasticsearch-handlers.connections']; 52 | 53 | return array_get($connections, $name, []); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /config/elasticsearch-handlers.php: -------------------------------------------------------------------------------- 1 | 'Cviebrock\LaravelElasticsearch\Handlers\Client', 13 | 14 | /** 15 | * This array should match the array defined in `app\elasticsearch.php`. 16 | * The key represents the connection name, and the value is an array of 17 | * handlers to apply to that connection's client. The elements of the array 18 | * can either classnames, or classname=>configurationArray pairs (for those 19 | * handlers that require configuration), i.e.: 20 | * 21 | * [ 22 | * 'handlerClass1', 23 | * 'handlerClass2' => [ configurationArray ], 24 | * ... 25 | * ] 26 | */ 27 | 28 | 'connections' => [ 29 | 30 | /** 31 | * The following configuration applies to clients on the "default" connection: 32 | */ 33 | 'default' => [ 34 | 35 | /** 36 | * The "EnvironmentIndexPrefixHandler" prefixes the _index_ key with the 37 | * current Laravel environment when indexing documents. 38 | */ 39 | 40 | // 'Cviebrock\LaravelElasticsearchHandlers\Handlers\EnvironmentIndexPrefixHandler', 41 | 42 | /** 43 | * The "IndexTemplateHandler" makes sure that the configured Elasticsearch 44 | * index templates are in place when the client is instantiated. 45 | */ 46 | 47 | // 'Cviebrock\LaravelElasticsearchHandlers\Handlers\IndexTemplateHandler' => [ 48 | // 49 | // ] 50 | 51 | ] 52 | 53 | ] 54 | 55 | ]; 56 | -------------------------------------------------------------------------------- /src/Client.php: -------------------------------------------------------------------------------- 1 | client = $client; 44 | $this->registerHandlers($handlers); 45 | } 46 | 47 | /** 48 | * Register the handlers for the client. 49 | * 50 | * @param array $handlers 51 | */ 52 | public function registerHandlers(array $handlers = []) { 53 | 54 | foreach ($handlers as $handlerClass => $configuration) { 55 | 56 | // If the handler takes a configuration, load that from the array and 57 | // build the class. If not, then just build the class. 58 | 59 | if (is_numeric($handlerClass)) { 60 | $handlerClass = $configuration; 61 | $class = new $handlerClass; 62 | } else { 63 | $class = new $handlerClass($configuration); 64 | } 65 | 66 | // Use reflection to figure out what Elasticsearch methods the handler handles. 67 | 68 | $reflect = new ReflectionClass($class); 69 | 70 | // Find the handled methods and merge them into the client's list. 71 | 72 | $classMethods = $reflect->getMethods(ReflectionMethod::IS_PUBLIC); 73 | 74 | array_walk($classMethods, function ($classMethod) use ($handlerClass) { 75 | if (starts_with($classMethod->name, 'handle')) { 76 | $method = lcfirst(substr($classMethod->name, 6)); 77 | 78 | $this->handledMethods[$method][] = $handlerClass; 79 | } 80 | }); 81 | 82 | // Save the handler instance. 83 | 84 | $this->handlers[$handlerClass] = $class; 85 | } 86 | 87 | // Boot all the handlers with the client instance. 88 | 89 | foreach ($this->handlers as $handler) { 90 | $handler->boot($this); 91 | } 92 | } 93 | 94 | /** 95 | * Get the list of all handlers that should be run for the given method. 96 | * 97 | * @param $methodName 98 | * @return array 99 | */ 100 | protected function getHandlers($methodName) { 101 | return array_get($this->handledMethods, $methodName, []); 102 | } 103 | 104 | /** 105 | * Magic method to pass commands through handlers before sending them to the base client. 106 | * 107 | * @param $method 108 | * @param $parameters 109 | * @return mixed 110 | */ 111 | public function __call($method, $parameters) { 112 | 113 | $parameterCount = count($parameters); 114 | 115 | // First, find out which handlers are registered for this method. 116 | 117 | $handlers = $this->getHandlers($method); 118 | 119 | // And get the real class method name we will call. 120 | 121 | $classMethod = 'handle' . ucfirst($method); 122 | 123 | // Then run the parameters through any registered handlers. 124 | 125 | foreach ($handlers as $handler) { 126 | $parameters = call_user_func_array([$this->handlers[$handler], $classMethod], $parameters); 127 | if ($parameterCount == 1) { 128 | $parameters = [$parameters]; 129 | } 130 | } 131 | 132 | // Finally, run the parameters through the base client. 133 | return call_user_func_array([$this->client, $method], $parameters); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel-Elasticsearch-Handlers 2 | 3 | An even easier way to use the official Elastic Search client in your Laravel applications. 4 | 5 | [![Build Status](https://travis-ci.org/cviebrock/laravel-elasticsearch-handlers.svg)](https://travis-ci.org/cviebrock/laravel-elasticsearch-handlers) 6 | [![Total Downloads](https://poser.pugx.org/cviebrock/laravel-elasticsearch-handlers/downloads.png)](https://packagist.org/packages/cviebrock/laravel-elasticsearch-handlers) 7 | [![Latest Stable Version](https://poser.pugx.org/cviebrock/laravel-elasticsearch-handlers/v/stable.png)](https://packagist.org/packages/cviebrock/laravel-elasticsearch-handlers) 8 | [![Latest Stable Version](https://poser.pugx.org/cviebrock/laravel-elasticsearch-handlers/v/unstable.png)](https://packagist.org/packages/cviebrock/laravel-elasticsearch-handlers) 9 | 10 | * [Installation and Configuration](#installation) 11 | * [Usage](#usage) 12 | * [Creating Handlers](#creating-handlers) 13 | * [Special `boot` Method](#special-boot-method) 14 | * [Pre-Defined Handlers](#pre-defined-handlers) 15 | * [EnvironmentIndexPrefixHandler](#environment-index-prefix-handler) 16 | * [Bugs, Suggestions and Contributions](#bugs) 17 | * [Copyright and License](#copyright) 18 | 19 | 20 | 21 | 22 | ## Installation and Configuration 23 | 24 | 1. Install the `cviebrock/laravel-elasticsearch-handlers` package via composer: 25 | 26 | ```shell 27 | $ composer require cviebrock/laravel-elasticsearch-handlers 28 | ``` 29 | 30 | 2. Publish the configuration file. For Laravel 5: 31 | 32 | ```shell 33 | php artisan vendor:publish cviebrock/laravel-elasticsearch-handlers 34 | ``` 35 | 36 | In order to make this package also work with Laravel 4, we can't do the 37 | standard configuration publishing like most Laravel 4 packages do. You will 38 | need to simply copy the configuration file into your application's configuration folder: 39 | 40 | ```shell 41 | cp vendor/cviebrock/laravel-elasticsearch-handlers/config/elasticsearch-handlers.php app/config/ 42 | ``` 43 | 44 | 3. Add the service provider (`config/app.php` for Laravel 5 or `app/config/app.php` for Laravel 4). 45 | The service provider needs to come after the `LaravelElasticsearch` provider, since we "hijack" 46 | the Manager class from that package and use our own. 47 | 48 | ```php 49 | 'providers' => array( 50 | ... 51 | 'Cviebrock\LaravelElasticSearch\ServiceProvider', 52 | 'Cviebrock\LaravelElasticSearchHandlers\ServiceProvider', 53 | ) 54 | ``` 55 | 56 | 57 | 58 | ## Usage 59 | 60 | This package extends the `laravel-elasticsearch` package by returning a "decorated" 61 | Elasticsearch client class, instead of the default PHP client. You can configure 62 | how the client is decorated on a per-connection basis. 63 | 64 | Take the following example `elasticsearch-handlers.php` configuration: 65 | 66 | ```php 67 | [ 71 | 'default' => [ 72 | 'clientClass' => 'Cviebrock\LaravelElasticsearchHandlers\Client', 73 | 'handlers' => [] 74 | ] 75 | ] 76 | ]; 77 | ``` 78 | 79 | When you instantiate an Elasticsearch client with: 80 | 81 | ```php 82 | $client = Elasticsearch::connection('default'); 83 | ``` 84 | 85 | the package will create a base client (using the base `elasticsearch.php` 86 | configuration) then wrap it in the class defined by the _clientClass_ setting, 87 | and inject the _handlers_ array. So, in this case, an instance of 88 | `Cviebrock\LaravelElasticsearchHandlers\Client` is returned. 89 | 90 | This class the bare minimum client wrapper. It doesn't do anything except 91 | pass-through all commands to the wrapped base Elasticsearch class. 92 | 93 | Let's make it more useful ... 94 | 95 | 96 | 97 | ## Creating Handlers 98 | 99 | Pretend you only have one Elasticsearch instance running, but you need it to 100 | support indices for several of your Laravel application environments (e.g. 101 | "beta", "live", etc.).. 102 | 103 | Instead of updating your code so that every time you index a document you make 104 | sure the right index name is specified, what if the client automatically 105 | prefixed the index name with the name of the current Laravel environment? 106 | 107 | First, set up the package configuration like so: 108 | 109 | ```php 110 | return [ 111 | 112 | 'defaultClass' => 'Cviebrock\LaravelElasticsearchHandlers\Client', 113 | 114 | 'connections' => [ 115 | 'default' => [ 116 | 'MyEnvironmentIndexPrefixHandler' 117 | ] 118 | 119 | ] 120 | 121 | ]; 122 | ``` 123 | 124 | Then create the Handler class (extending the BaseHandler class): 125 | 126 | ```php 127 | class MyEnvironmentIndexPrefixHandler extends Cviebrock\LaravelElasticsearchHandlers\Handlers\BaseHandler { 128 | 129 | /** 130 | * Auto-prefix the document index name with the current Laravel 131 | * environment. 132 | * 133 | * @param array $parameters 134 | * @return array 135 | */ 136 | public function handleIndex($parameters) { 137 | 138 | if ($index = array_get($parameters, 'index')) { 139 | $environment = mb_strtolower(preg_replace('/[^a-z0-9_\-]+/', '-', \App::environment())); 140 | $parameters['index'] = trim($environment, '-') . '-_' . $index; 141 | } 142 | 143 | return $parameters; 144 | } 145 | } 146 | ``` 147 | 148 | Now, every time you index a document using the default Elasticsearch connection, 149 | the current Laravel environment name will be prefixed to the _index_ key of your 150 | data array. 151 | 152 | ```php 153 | $data = [ 154 | 'index' => 'my-index', 155 | 'type' => 'my-doctype', 156 | 'body' => [ 157 | 'content' => 'Lorem ipsum', 158 | ] 159 | ]; 160 | $return = Elasticsearch::index($data); 161 | ``` 162 | 163 | This returns: 164 | 165 | ``` 166 | array (size=5) 167 | '_index' => string 'local-my-index' (length=14) 168 | '_type' => string 'my-doctype' (length=10) 169 | '_id' => string 'AU3U9R3kOpwouG512345' (length=20) 170 | '_version' => int 1 171 | 'created' => boolean true 172 | ``` 173 | 174 | The _index_ was prepended automatically, so your application will work across 175 | all environments without checks or changes. 176 | 177 | Also, you can register more than one handler per connection, which means that 178 | the functionality is "chainable". E.g., prepend the environment to the _index_, 179 | and also add some default parameters to the _body_, etc.. 180 | 181 | 182 | 183 | 184 | 185 | ### Special `boot` Method 186 | 187 | Handlers can also define a `boot` method with the following signature: 188 | 189 | ```php 190 | public function boot(\Cviebrock\LaravelElasticsearchHandlers\Client $client) {} 191 | ``` 192 | 193 | This is method is run when the handler is registered so it could be used, for 194 | example, to alter the behaviour of the client upon instantiation. The client 195 | is passed in as the only parameter. 196 | 197 | 198 | 199 | ## Pre-Defined Handlers 200 | 201 | The package ships with a few pre-defined handlers, all of which are in the 202 | `Cviebrock\LaravelElasticsearchHandlers\Handlers` namespace. 203 | 204 | 205 | ### EnvironmentIndexPrefixHandler 206 | 207 | Operates on the follow methods: 208 | 209 | * index 210 | 211 | This is basically the same handler as used in the example above. It will take 212 | the current Laravel environment, mangles it a bit so it matches Elasticsearch's 213 | constraints for index names, and prepends it to the _index_ key in the given 214 | document. 215 | 216 | 217 | 218 | 219 | 220 | ## Bugs, Suggestions and Contributions 221 | 222 | Please use Github for bugs, comments, suggestions. 223 | 224 | 1. Fork the project. 225 | 2. Create your bugfix/feature branch and write your (well-commented) code. 226 | 3. Commit your changes (and your tests) and push to your branch. 227 | 4. Create a new pull request against the `master` branch. 228 | 229 | 230 | 231 | ## Copyright and License 232 | 233 | Laravel-Elasticsearch-Handlers was written by Colin Viebrock and released under the MIT License. See the LICENSE file for details. 234 | 235 | Copyright 2015 Colin Viebrock 236 | --------------------------------------------------------------------------------