diff --git a/.gitignore b/.gitignore index 9fd998d..81361bc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,3 @@ +/build /vendor -composer.phar -composer.lock -.DS_Store -.idea -atlassian-ide-plugin.xml \ No newline at end of file +composer.lock \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 0a1c1cb..ac72420 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,27 @@ language: php -php: - - 5.3 - - 5.4 - - 5.5 +php: + - 5.6 + - 7.0 + +addons: + apt: + sources: + - elasticsearch-5.0 + packages: + - elasticsearch + +services: + - elasticsearch before_script: - - curl -s http://getcomposer.org/installer | php - - php composer.phar install --dev + - travis_retry composer self-update + - travis_retry composer update --prefer-dist --no-interaction + +script: + - mkdir -p build/logs + - vendor/bin/phpcs + - vendor/bin/phpunit -script: phpunit \ No newline at end of file +after_script: + - vendor/bin/coveralls -v \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..fce0e35 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,17 @@ +# Contributing + +Before you contribute code to laravel-elasticsearch, please make sure it conforms PHPCS coding standard and unit tests +still pass. + +## How to +Fork, then clone the repo: + + git clone git@github.com:your-username/laravel-elasticsearch.git + +Make sure the tests pass: + + composer test + +Make your change. Add tests for your change. Make the tests pass + +Push to your fork and [submit a pull request][pr]. \ No newline at end of file diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..b7e3ae2 --- /dev/null +++ b/LICENCE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Shift 31 Consulting + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 35eb4b7..a2b4f51 100644 --- a/README.md +++ b/README.md @@ -1,60 +1,66 @@ -Elasticsearch for Laravel -========================= -This is a Laravel (4+) Service Provider for the official Elasticsearch low-level client: -http://www.elasticsearch.org/guide/en/elasticsearch/client/php-api/current/index.html +Laravel Elasticsearch Service Provider (4.5.1) +================================================ +[![Latest Stable Version](https://poser.pugx.org/shift31/laravel-elasticsearch/v/stable)](https://packagist.org/packages/shift31/laravel-elasticsearch) +[![Total Downloads](https://poser.pugx.org/shift31/laravel-elasticsearch/downloads)](https://packagist.org/packages/shift31/laravel-elasticsearch) +[![Build Status](https://travis-ci.org/shift31/laravel-elasticsearch.svg?branch=4.5)](https://travis-ci.org/shift31/laravel-elasticsearch) +[![Coverage Status](https://coveralls.io/repos/github/shift31/laravel-elasticsearch/badge.svg?branch=4.5)](https://coveralls.io/github/shift31/laravel-elasticsearch?branch=master) +[![License](https://poser.pugx.org/shift31/laravel-elasticsearch/license)](https://packagist.org/packages/shift31/laravel-elasticsearch) +This is a Laravel (4.2) Service Provider for the [official Elasticsearch low-level client](http://www.elasticsearch.org/guide/en/elasticsearch/client/php-api/5.0/index.html). Version Matrix --------------- -Since there are breaking changes in Elasticsearch 1.0, your version of Elasticsearch must match the version of this library, which matches the version of the Elasticsearch low-level client. -If you are using a version older than 1.0, you must install the `0.4` laravel-elasticsearch branch. Otherwise, use the `1.0` branch. - -The master branch will always track the latest version. - -| Elasticsearch Version | laravel-elasticsearch branch | -| --------------------- | ---------------------------- | -| >= 1.0 | 1.0, 2.0 | -| <= 0.90.* | 0.4 | - -**Support for v1.1.x of the Elasticsearch client has been added in v1.1 of laravel-elasticsearch.** We'll try to be consistent with this convention going forward. +------------------ +Since there are breaking changes in Elasticsearch versions, your version of Elasticsearch must match the version of this +library, which matches the version of the Elasticsearch low-level client. + +|Shift31/laravel-elasticsearch| Elasticsearch | Laravel | +| :---: | :---: | :---: | +| 0.4| <= 0.90.* | 4.2 | +| 1.0, 2.0| \>= 1.0 | 4.x, 5.x | +|4.0| <= 0.90.* | 4.2| +|4.1| \>= 1.0 <= 2.0 | 4.2| +|4.2| \>= 2.0 <= 5.0| 4.2| +|4.5| \>= 5.0| 4.2| +|5.0| <= 0.90.* | 5.x| +|5.1| \>= 1.0 <= 2.0 | 5.x| +|5.2| \>= 2.0 <= 5.0| 5.x| +|5.5| \>= 5.0| 5.x| + +Attention: Until we launch new versions please keep using old stable versions (which are created as a branch) and don't use dev-master branch! Usage ----- -1. Run `composer require shift31/laravel-elasticsearch:~2.0` +1. Run `composer require shift31/laravel-elasticsearch:~4.5.0` -2. Create app/config/elasticsearch.php, modifying the following contents accordingly: +2. Publish config file + +Laravel artisan command +``` +$ php artisan config:publish shift31/laravel-elasticsearch +``` +You can always read config parameters with: ```php - array( - 'your.elasticsearch.server:9200' - ), - 'logPath' => 'path/to/your/elasticsearch/log', -); +\Config::get('shift31::elasticsearch'); ``` +Note: The keys of this array should be named according the parameters supported by [Elasticsearch\ClientBuilder](https://github.com/elastic/elasticsearch-php/blob/5.0/src/Elasticsearch/ClientBuilder.php#L119). -The keys of this array should be named according the parameters supported by Elasticsearch\Client. - -3. In the `'providers'` array in app/config/app.php, if you are using Laravel 4.x, add `'Shift31\LaravelElasticsearch\LaravelElasticsearchServiceProvider'`. - - **If you are using Laravel 5.x**, add `'Shift31\LaravelElasticsearch\ElasticsearchServiceProvider'`. The ServiceProvider will enable the 'Es' facade for you. +3. In the `'providers'` array in app/config/app.php, add `'Shift31\LaravelElasticsearch\ElasticsearchServiceProvider'`. 4. Use the `Es` facade to access any method from the `Elasticsearch\Client` class, for example: ```php $searchParams['index'] = 'your_index'; $searchParams['size'] = 50; $searchParams['body']['query']['query_string']['query'] = 'foofield:barstring'; - $result = Es::search($searchParams); ``` -**A friendly reminder:** If you use the facade in a namespaced class (i.e. in a Laravel 5.x controller), you must add `use Es;` at the top of your file (after `=5.4.0", - "illuminate/support": "~4|~5", - "elasticsearch/elasticsearch": "~2.0" - }, - "autoload": { - "classmap": [ - "src/migrations" - ], - "psr-0": { - "Shift31\\LaravelElasticsearch": "src/" - } + "name": "shift31/laravel-elasticsearch", + "description": "A Laravel Service Provider for the Elasticsearch API client", + "authors": [ + { + "name": "Shift 31 Consulting", + "email": "code@shift31.com" + } + ], + "require": { + "php": ">=7.2", + "illuminate/support": "^6.0|^7.0|^8.0", + "elasticsearch/elasticsearch": "~5.0" + }, + "require-dev": { + "squizlabs/php_codesniffer": "^2.8", + "satooshi/php-coveralls": "^1.0", + "orchestra/testbench": "^4.0|^5.0|^6.0" + }, + "autoload": { + "psr-0": { + "Shift31\\LaravelElasticsearch": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Shift31\\LaravelElasticsearch\\Tests\\": "tests/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "4.5.x-dev" }, - "minimum-stability": "dev", - "prefer-stable": true, - "require-dev": { - "laravel/framework": "^5.2" + "laravel": { + "providers": [ + "Shift31\\LaravelElasticsearch\\ElasticsearchServiceProvider" + ], + "aliases": { + "Es": "Shift31\\LaravelElasticsearch\\Facades\\Es" + } } -} + }, + "scripts": { + "test": [ + "vendor/bin/phpcs", + "vendor/bin/phpunit" + ] + } +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 05985ca..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,5 +0,0 @@ -elasticsearch: - image: elasticsearch - ports: - - "9300:9300" - - "9200:9200" \ No newline at end of file diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000..c7ea2bb --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,5 @@ + + + + ./src + \ No newline at end of file diff --git a/phpunit.xml b/phpunit.xml index e89ac6d..d840e70 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -8,11 +8,21 @@ convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" - syntaxCheck="false" -> + syntaxCheck="false"> - - ./tests/ + + ./tests/Unit + + + ./tests/Integration + + + ./src/Shift31/LaravelElasticsearch + + + + + \ No newline at end of file diff --git a/public/.gitkeep b/public/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/Shift31/LaravelElasticsearch/ElasticsearchServiceProvider.php b/src/Shift31/LaravelElasticsearch/ElasticsearchServiceProvider.php index 36fccba..9a3bca3 100644 --- a/src/Shift31/LaravelElasticsearch/ElasticsearchServiceProvider.php +++ b/src/Shift31/LaravelElasticsearch/ElasticsearchServiceProvider.php @@ -1,56 +1,50 @@ -app->singleton('Elasticsearch\Client', function () { - - $connParams = []; - $connParams['hosts'] = ['localhost:9200']; - $connParams['logPath'] = storage_path() . '/logs/elasticsearch-' . php_sapi_name() . '.log'; - - // merge settings from app/config/elasticsearch.php - $params = array_merge($connParams, $this->app['config']->get('elasticsearch', [])); - - $logger = ClientBuilder::defaultLogger($params['logPath']); - - return ClientBuilder::create()->setHosts($params['hosts'])->setLogger($logger)->build(); - - }); - - $this->app->alias('Elasticsearch\Client', 'elasticsearch'); + } - // Shortcut so developers don't need to add an Alias in app/config/app.php - $this->app->booting(function () { - $loader = AliasLoader::getInstance(); - $loader->alias('Es', 'Shift31\LaravelElasticsearch\Facades\Es'); + /** + * @inheritdoc + */ + public function register() + { + $this->mergeConfigFrom(realpath(__DIR__ . '/../../config/elasticsearch.php'), 'elasticsearch'); + $this->app->singleton('elasticsearch', function ($app) { + if (empty($app->config->get('elasticsearch.logger'))) { + $config = $app->config->get('elasticsearch'); + + $logPath = Arr::get($config, 'elasticsearch.logPath', storage_path('logs/elastic-search.log')); + $logLevel = Arr::get($config, 'elasticsearch.logLevel', Logger::WARNING); + $logger = ClientBuilder::defaultLogger($logPath, $logLevel); + unset($config['logLevel']); + unset($config['logPath']); + $config['logger'] = $logger; + $app->config->set('elasticsearch', $config); + } + + return ClientBuilder::fromConfig($app->config->get('elasticsearch')); }); } /** - * Get the services provided by the provider. - * - * @return array + * @inheritdoc */ public function provides() { - return ['elasticsearch', 'Elasticsearch\Client']; + return ['elasticsearch']; } } diff --git a/src/Shift31/LaravelElasticsearch/Facades/Es.php b/src/Shift31/LaravelElasticsearch/Facades/Es.php index bee2de5..3d60708 100644 --- a/src/Shift31/LaravelElasticsearch/Facades/Es.php +++ b/src/Shift31/LaravelElasticsearch/Facades/Es.php @@ -2,8 +2,14 @@ use Illuminate\Support\Facades\Facade; - -class Es extends Facade { - - protected static function getFacadeAccessor() { return 'elasticsearch'; } -} \ No newline at end of file +/** + * @see \Elasticsearch\Client + */ +class Es extends Facade +{ + + protected static function getFacadeAccessor() + { + return 'elasticsearch'; + } +} diff --git a/src/Shift31/LaravelElasticsearch/LaravelElasticsearchServiceProvider.php b/src/Shift31/LaravelElasticsearch/LaravelElasticsearchServiceProvider.php deleted file mode 100644 index 041490c..0000000 --- a/src/Shift31/LaravelElasticsearch/LaravelElasticsearchServiceProvider.php +++ /dev/null @@ -1,72 +0,0 @@ -package('shift31/laravel-elasticsearch'); - } - - /** - * Register the service provider. - * - * @return void - */ - public function register() - { - $this->app->singleton('elasticsearch', function() - { - - $connParams = []; - $connParams['hosts'] = array('localhost:9200'); - $connParams['logPath'] = storage_path() . '/logs/elasticsearch-' . php_sapi_name() . '.log'; - - // merge settings from app/config/elasticsearch.php - $params = array_merge($connParams, $this->app['config']->get('elasticsearch')); - - $logger = ClientBuilder::defaultLogger($params['logPath']); - - return ClientBuilder::create()->setHosts($params['hosts'])->setLogger($logger)->build(); - }); - - // Shortcut so developers don't need to add an Alias in app/config/app.php - $this->app->booting(function() - { - $loader = AliasLoader::getInstance(); - $loader->alias('Es', 'Shift31\LaravelElasticsearch\Facades\Es'); - }); - } - - /** - * Get the services provided by the provider. - * - * @return array - */ - public function provides() - { - return array('elasticsearch'); - } -} diff --git a/src/config/.gitkeep b/src/config/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/config/elasticsearch.php b/src/config/elasticsearch.php new file mode 100644 index 0000000..2065e63 --- /dev/null +++ b/src/config/elasticsearch.php @@ -0,0 +1,7 @@ + ['localhost:9200'], + 'logger' => Elasticsearch\ClientBuilder::defaultLogger(storage_path('logs/elastic-search.log'), 400), + 'retries' => 1, +]; diff --git a/src/controllers/.gitkeep b/src/controllers/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/lang/.gitkeep b/src/lang/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/migrations/.gitkeep b/src/migrations/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/views/.gitkeep b/src/views/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/tests/.gitkeep b/tests/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/tests/Integration/ElasticsearchServiceProviderIntegrationTest.php b/tests/Integration/ElasticsearchServiceProviderIntegrationTest.php new file mode 100644 index 0000000..5697504 --- /dev/null +++ b/tests/Integration/ElasticsearchServiceProviderIntegrationTest.php @@ -0,0 +1,43 @@ +create($indexParams); + $this->assertArrayHasKey('acknowledged', $result); + $this->assertTrue($result['acknowledged']); + } + + public function test_to_see_elasticsearch_log_file() + { + $logPath = storage_path('logs/elastic-search.log'); + $logger = ClientBuilder::defaultLogger($logPath, 100); + Config::set('shift31::elasticsearch.logger', $logger); + $indexParams['index'] = 'shift31'; + $result = Es::indices()->delete($indexParams); + $this->assertArrayHasKey('acknowledged', $result); + $this->assertTrue($result['acknowledged']); + $this->assertTrue(file_exists($logPath)); + } + + public function test_get_elasticsearch_config() + { + $config = Config::get('shift31::elasticsearch'); + $this->assertArrayHasKey('hosts', $config); + $this->assertArrayHasKey('logger', $config); + $this->assertArrayHasKey('retries', $config); + } + + protected function getPackageProviders() + { + return ['Shift31\LaravelElasticsearch\ElasticsearchServiceProvider']; + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..2d83285 --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,14 @@ +assertEquals(['elasticsearch'], $provider->provides()); + } + + public function test_boot_method_to_set_config_file() + { + $configPath = realpath($this->getSourcePath('config')); + $filesMock = Mockery::mock('Illuminate\Filesystem\Filesystem', function (MockInterface $m) use ($configPath) { + $m->shouldReceive('isDirectory')->with($configPath)->once()->andReturn(true); + $m->shouldReceive('isDirectory')->withAnyArgs()->times(3)->andReturn(false); + }); + $configMock = Mockery::mock('Illuminate\Config\Repository', function (MockInterface $m) use ($configPath) { + $m->shouldReceive('package') + ->with('shift31/laravel-elasticsearch', $configPath, 'shift31') + ->once() + ->andReturnSelf(); + }); + $application = Mockery::mock('Illuminate\Foundation\Application', + function (MockInterface $m) use ($configMock, $filesMock) { + $m->shouldReceive('offsetGet')->with('config')->once()->andReturn($configMock); + $m->shouldReceive('offsetGet')->with('files')->times(4)->andReturn($filesMock); + $m->shouldReceive('offsetGet')->with('path')->once()->andReturn($this->getSourcePath()); + }); + $provider = new ElasticsearchServiceProvider($application); + $this->assertNull($provider->boot()); + } + + public function test_register_method_to_set_singleton_elastic_search_client() + { + $configPath = $this->getSourcePath('config/elasticsearch.php'); + $configMock = Mockery::mock('Illuminate\Config\Repository', function (MockInterface $m) { + $m->shouldReceive('get')->with('shift31::elasticsearch')->andReturn([ + 'hosts' => [], + ]); + }); + $filesMock = Mockery::mock('Illuminate\Filesystem\Filesystem', function (MockInterface $m) use ($configPath) { + $m->shouldReceive('getRequire') + ->with($configPath) + ->once()->andReturn([]); + }); + $application = Mockery::mock('Illuminate\Foundation\Application', + function (MockInterface $m) use ($configMock, $filesMock) { + $m->shouldReceive('booting')->once()->andReturnSelf(); + $m->shouldReceive('offsetGet')->with('config')->andReturn($configMock); + $m->shouldReceive('offsetGet')->with('files')->andReturn($filesMock); + $m->shouldReceive('singleton')->with('elasticsearch', + Mockery::on(function ($closure) { + $this->assertInstanceOf('Elasticsearch\Client', $closure()); + + return true; + }))->once()->andReturnSelf(); + }); + $provider = new ElasticsearchServiceProvider($application); + $this->assertNull($provider->register()); + } + + public function test_register_method_to_set_elastic_search_facade() + { + $application = Mockery::mock('Illuminate\Foundation\Application', function (MockInterface $m) { + $m->shouldReceive('singleton')->once()->andReturnSelf(); + $m->shouldReceive('booting')->with(Mockery::on(function ($closure) { + $closure(); + $loader = AliasLoader::getInstance(); + $this->assertArrayHasKey('Es', $loader->getAliases()); + $this->assertEquals('Shift31\LaravelElasticsearch\Facades\Es', $loader->getAliases()['Es']); + + return true; + }))->andReturnSelf(); + }); + $provider = new ElasticsearchServiceProvider($application); + $this->assertNull($provider->register()); + } + + protected function getSourcePath($path = '') + { + return realpath(__DIR__ . '/../../src/' . $path); + } +} diff --git a/tests/Unit/EsUnitTest.php b/tests/Unit/EsUnitTest.php new file mode 100644 index 0000000..5d9ce44 --- /dev/null +++ b/tests/Unit/EsUnitTest.php @@ -0,0 +1,20 @@ +getMethod('getFacadeAccessor'); + $method->setAccessible(true); + $provider = new ElasticsearchServiceProvider(Mockery::mock('Illuminate\Foundation\Application')); + $this->assertEquals($provider->provides(), (array)$method->invoke(new Es())); + } +} \ No newline at end of file